使用 win32 和 C 从 WDK 驱动程序中的 UNICODE_STRING 中提取路径名组件

Extracting pathname components from UNICODE_STRING within a WDK Driver using win32 and C

我试图将 UNICODE_STRING 路径名的组成部分分开,以便创建从设备根到文件叶的目录树。这需要在 WDK 驱动程序中完成。

我需要使用 ZwCreateFile() 一次构建一个目录结构,因为它只能在一次调用中创建最终目录或叶,而不是整个路径。

很抱歉向各位 C 工程师提出这样一个简单的问题,但我在解决这个问题并在驱动程序中使用它时遇到了问题。

我目前的做法是将UNICODE_STRING转换为char并使用strtok_s()函数将路径名分解为其组成目录和文件。

我想用

char string1[] =
    "\Device\HarddiskVolume";

char seps[] = "\";
char *token1 = NULL;

char *next_token1 = NULL;

token1 = strtok_s(string1, seps, &next_token1);

但我需要将 UNICODE_STRING 转换为 char 字符串。

这是一段代码。

code.c:

#include <Windows.h>
#include <SubAuth.h>


char* unicodeStringToPChar(UNICODE_STRING *pUS) {
    size_t wcLen = 0, cLen = 0;
    wchar_t *pWBuf = NULL;
    char *pBuf = NULL;
    errno_t res = 0;
    if (!pUS || !pUS->Length) {
        return NULL;
    }
    wcLen = pUS->Length / sizeof(wchar_t) + 1;
    pWBuf = calloc(1, wcLen * sizeof(wchar_t));
    if (!pWBuf) {
        return NULL;
    }
    if (wcsncpy_s(pWBuf, wcLen, pUS->Buffer, wcLen - 1)) {
        free(pWBuf);
        return NULL;
    }
    wcstombs_s(&cLen, NULL, 0, pWBuf, 0);
    if (!cLen) {
        free(pWBuf);
        return NULL;
    }
    pBuf = calloc(1, cLen);
    if (!pBuf) {
        free(pWBuf);
        return NULL;
    }
    res = wcstombs_s(NULL, pBuf, cLen, pWBuf, cLen - 1);
    free(pWBuf);
    if (res) {
        free(pBuf);
        return NULL;
    }
    return pBuf;
}

备注:

  • 函数接收一个指向UNICODE_STRING指针(如果你有一个简单的结构,不要忘记引用)
  • Returns a char*, NULL 如果它不能转换字符串(无论是空的,还是发生了一些错误)
    • 您可以在 return NULL; 语句
    • 之前添加一些输出消息(例如 printf
    • 不要忘记释放 returned 值,一旦你用完它 以避免内存泄漏(在来电中)
  • 有两个步骤:

    • "Save" UNICODE_STRING wchar_t*中的内容:
      • 分配 wchar_t* (calloc)
      • 复制内容(wcsncpy_s)
      • 这一步可能不是必需的(可以直接在 UNICODE_STRING.Buffer 上操作 - 因此认为这一步有点矫枉过正),但我想严格一点,只为 return 值(检查下一项)
    • wchar_t* 转换为 char*:

      • 再次分配 char* (calloc)
      • 执行转换 (wcstombs_s)

        • wcstombs_s的第1st调用是用来判断有多少[=149= char* 缓冲区需要 ]。我使用了(中介)wchar_t* 因为根据:

          • [MS.Docs]: UNICODE_STRING structure((部分)强调是我的):

            Buffer
                       Pointer to a wide-character string. Note that the strings returned by the various LSA functions might not be null-terminated

          • [MS.Docs]: wcstombs_s, _wcstombs_s_l:

            If wcstombs_s successfully converts the source string, it puts the size in bytes of the converted string, including the null terminator, into *pReturnValue (provided pReturnValue is not NULL). This occurs even if the mbstr argument is NULL and provides a way to determine the required buffer size. Note that if mbstr is NULL*, count **is ignored.

          在某些情况下,UNICODE\_STRING.Buffer 是不可能的(当它不以 NULL char 结尾,并且包含特殊(宽)chars(占用 2 个字节))

我没有彻底测试这个功能,让我知道它是如何为你工作的。

经过进一步的了解,我了解到上面的none在驱动程序中是可用的。但是在 [CodeGuru]: ZwCreatefile/ ZwReadfile 上,有一个使用 ZwCreatefileUNICODE_STRING 的示例(通过 RtlInitUnicodeStringInitializeObjectAttributes) 我将从下面粘贴(没有测试任何东西):

#include <ntddk.h>

HANDLE handle;
NTSTATUS ntstatus;
UNICODE_STRING uniName;
OBJECT_ATTRIBUTES  objAttr;
RtlInitUnicodeString(&uniName, L"\SystemRoot\native.txt");
InitializeObjectAttributes(&objAttr, &uniName,
                           OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                           NULL, NULL);
ntstatus = ZwCreateFile(&handle,
                        GENERIC_READ,
                        &objAttr, &ioStatusBlock,
                        NULL,
                        FILE_ATTRIBUTE_NORMAL,
                        0,
                        FILE_OPEN, 
                        FILE_SYNCHRONOUS_IO_NONALERT,
                        NULL, 0);

这是您可以开始的示例。函数 PathBuf() 遍历一个字符串,将路径名的各个部分复制到目标缓冲区中。该函数通过多次调用直到到达字符串末尾来完成此操作。

您将需要检查这是否满足您的需求,并进行您可能需要的任何其他调整以获得您想要的内容。

我还使用了 wchar_t 来进行测试。您可能需要更改为 UNICODE_STRING 或类似内容。

请注意,存在一些边缘情况,例如没有任何中间文本的两个路径分隔符。空格应该只是路径名部分中的另一个字符。

在 Windows 路径名中有网络设备类型的语法,例如“\device\file”,因此您可能需要添加一些内容以了解第一部分是否是使用两个斜杠引入的设备或没有。

我还做了这个,以便它可以处理 Windows 路径名分隔符(反斜杠)或 Linux 路径名分隔符(正斜杠),这似乎是相当标准的方法。

#include <stdlib.h>
#include <stdio.h>

wchar_t *PathBuf(wchar_t *pDest, const wchar_t *pSrc)
{
    // if not NULL source string and not the end of the source string, process it.
    if (pSrc && *pSrc) {
        short iState = 0;  // start state off as no characters found.
        do {
            // determine whether this is a path separator or a file path
            // path component text character. set the current state based
            // on the current character in the source text string.
            switch (*pSrc) {
                case L'\':    // backslash path separator found
                case L'/':     // forward slash path separator found
                    iState = (iState == 0) ? 1 : 2;  // first instance or not?
                    break;
                default:
                    *pDest++ = *pSrc;  // copy the character from source to destination buffer
                    iState = 1;  // indicate at least one character found
                    break;
            }
            // continue the loop until either ending path separator found
            // or we have reached end of the source string.
            // we will continue on the next call after the path separator.
        } while (*pSrc && *pSrc++ && iState < 2);
    }
    *pDest = 0;   // end of string terminator for destination buffer

    return pSrc;  // return our current place in the source string
}

int testfunc(void)
{
    wchar_t *list[] = {
        L"\state",
        L"state2",
        L"\\state3\",
        L"\statex\state4",
        L"xx"
    };
    int i;

    for (i = 0; i < sizeof(list) / sizeof(list[0]); i++) {
        wchar_t *p1;         // pointer to source string which is updated
        wchar_t buff[128];   // destination buffer for each component
        p1 = list[i];        // start the source string with the next test item
        printf("Doing %S\n", p1);   // print out the entire test string
        while (*p1) {
            p1 = PathBuf(buff, p1);    // copy first path component into buff, update source string pointer
            printf ("  \"%S\"", buff);  // print out the path component found within double quotes
            // at this point you could use ZwCreateFile() to create the path component.
            // a sanity check on the text such as empty string may be in order.
        }
        printf("\n");
    }
}

此源将输出以下内容:

Doing \state
  "state"
Doing state2
  "state2"
Doing \state3\
  ""  "state3"
Doing \statex\state4
  "statex"  "state4"
Doing xx
  "xx"

另见

Directory relative ZwCreateFile

The Definitive Guide on Win32 to NT Path Conversion

Nt vs. Zw - Clearing Confusion On The Native API