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

我试图分离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字符串。

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

您需要检查这是否满足您的需求,并进行任何额外的调整,以获得您想要的。

我也使用wchar_t来进行测试。 您可能需要更改为UNICODE_STRING或类似的东西。

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

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

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

 #include  #include  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" 

也可以看看

目录相对ZwCreateFile

Win32到NT路径转换的权威指南

Nt vs. Zw - 清除Native API上的混乱

这是一段代码。

code.c

 #include  #include  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指针 (如果你有一个普通的结构,别忘了引用)
  • 返回一个char* ,如果无法转换字符串,则返回NULL (无论是空的,还是发生了一些错误)
    • 您可以在return NULL;之前添加一些输出消息(例如printfreturn NULL; 声明
    • 完成后不要忘记free返回的值以避免内存泄漏(在调用者中)
  • 有两个步骤:

    • 将“ UNICODE_STRING内容“保存”在wchar_t*
      • 分配wchar_t*calloc
      • 复制内容( wcsncpy_s
      • 这个步骤可能没有必要(可以直接对UNICODE_STRING.Buffer进行操作 – 因此认为这一步是一种过度杀伤),但我想要严格并且只为返回值分配所需的字节数(查看下一项)
    • wchar_t*转换为char*

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

        • wcstombs_s 第一次调用用于确定char*缓冲区需要多少空间。 我使用(中间) wchar_t*因为根据:

          • [MSDN]:UNICODE_STRING结构 :

            缓冲
            指向宽字符串的指针。 请注意,各种LSA函数返回的字符串可能不会以空值终止

          • [MSDN]:wcstombs_s,_wcstombs_s_l :

            如果wcstombs_s成功转换源字符串,它会将转换后的字符串的大小(包括空终止符)放入*``pReturnValue (假设pReturnValue不为NULL )。 即使mbstr参数为NULL并且提供了确定所需缓冲区大小的方法,也会发生这种情况。 请注意,如果mbstrNULL则忽略 count

          有些情况下, UNICODE_STRING.Buffer无法实现(当它不以NULL字符结尾,并包含特殊字符(占用2个字节))

我没有彻底测试这个function,请告诉我它是如何工作的。

经过更多澄清后,我明白以上都不适用于驱动程序。 但是在[CodeGuru]:ZwCreatefile / ZwReadfile中 ,有一个使用ZwCreatefileUNICODE_STRING (通过RtlInitUnicodeStringInitializeObjectAttributes )的例子,我将从下面粘贴(没有测试任何东西):

 #include  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);