Extraction de composants de noms de chemins à partir de UNICODE_STRING dans un pilote WDK à l’aide de win32 et de C

J’essaie de séparer les composants d’un nom de chemin UNICODE_STRING afin de créer l’arborescence de répertoires de la racine du périphérique à la feuille de fichier. Cela doit être fait dans un pilote WDK.

J’ai besoin de construire la structure de répertoires pièce par pièce à l’aide de ZwCreateFile() car elle ne peut créer que le répertoire final ou la feuille finale plutôt que le chemin d’access complet en un seul appel.

Toutes mes excuses pour une question aussi simple qui s’adresse à vous, ingénieurs C, mais j’ai du mal à comprendre ce que je pense et à l’utiliser dans un pilote.

Mon approche actuelle consiste à convertir UNICODE_STRING en caractère et à utiliser la fonction strtok_s() pour décomposer le nom du chemin d’access dans ses répertoires et fichiers de composants.

Je cherche à utiliser

 char ssortingng1[] = "\\Device\\HarddiskVolume"; char seps[] = "\\"; char *token1 = NULL; char *next_token1 = NULL; token1 = strtok_s(ssortingng1, seps, &next_token1); 

Mais je dois convertir UNICODE_STRING en chaîne de caractères.

Voici un exemple avec lequel vous pouvez commencer. La fonction PathBuf() parcourt une chaîne en copiant les parties d’un chemin dans un tampon de destination. Pour ce faire, la fonction est appelée plusieurs fois jusqu’à la fin de la chaîne.

Vous devrez vérifier que cela répond à vos besoins et faire les ajustements supplémentaires dont vous pourriez avoir besoin pour obtenir ce que vous voulez.

J’ai aussi utilisé wchar_t afin de faire mes tests. Vous devrez probablement passer à UNICODE_STRING ou à quelque chose de similaire.

Notez qu’il existe quelques cas d’extrémité tels que deux séparateurs de chemin sans texte intermédiaire. Les espaces doivent être juste un autre personnage dans la pièce de chemin d’access.

Dans les noms de chemins Windows, il existe une syntaxe de type de périphérique réseau, telle que “\ device \ file”. Vous devrez peut-être append quelque chose pour savoir si le premier élément est un périphérique introduit avec deux barres obliques ou non.

J’ai également fait cela pour qu’il puisse gérer les séparateurs de noms de chemins Windows (barre oblique inverse) ou les séparateurs de chemins Linux (barre oblique), ce qui semble être une approche relativement standard.

 #include  #include  wchar_t *PathBuf(wchar_t *pDest, const wchar_t *pSrc) { // if not NULL source ssortingng and not the end of the source ssortingng, 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 ssortingng. 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 ssortingng. // 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"); } } 

Cette source produira les informations suivantes:

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

Voir également

Répertoire relatif ZwCreateFile

Le guide définitif sur la conversion de chemin Win32 vers NT

Nt vs. Zw - Effacement de la confusion sur l'API native

Voici un morceau de code.

code.c :

 #include  #include  char* unicodeSsortingngToPChar(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; } 

Notes :

  • La fonction reçoit un pointeur sur UNICODE_STRING (n’oubliez pas de faire référence à une structure simple)
  • Renvoie un caractère char* , NULL s’il ne peut pas convertir la chaîne (qu’elle soit vide ou qu’une erreur se soit produite)
    • Vous pouvez append des messages de sortie (par exemple, printf ) avant de return NULL; des déclarations
    • N’oubliez pas de free la valeur renvoyée une fois que vous en avez terminé pour éviter les memory leaks (dans l’appelant).
  • Il y a 2 étapes:

    • “Sauvegarder” le contenu de UNICODE_STRING dans un wchar_t* :
      • Allouer le wchar_t* ( calloc )
      • Copier le contenu ( wcsncpy_s )
      • Cette étape n’est peut-être pas nécessaire (on pourrait opérer directement sur UNICODE_STRING.Buffer – et donc considérer cette étape comme une surpopulation), mais je voulais être rigoureux et n’allouer que le nombre d’octets requirejs pour la valeur de retour (cochez l’élément suivant).
    • Convertissez le wchar_t* en un caractère char* :

      • Encore une fois, allouer le char* ( calloc )
      • Effectuer la conversion ( wcstombs_s )

        • Le 1 er appel à wcstombs_s est utilisé pour déterminer la quantité d’espace nécessaire pour la mémoire tampon char* . J’ai utilisé le wchar_t* (intermédiaire) car selon:

          • [MSDN]: structure UNICODE_STRING :

            Tampon
            Pointeur sur une chaîne de caractères larges. Notez que les chaînes renvoyées par les différentes fonctions LSA peuvent ne pas être terminées par zéro

          • [MSDN]: wcstombs_s, _wcstombs_s_l :

            Si wcstombs_s convertit avec succès la chaîne source, il met la taille en octets de la chaîne convertie, y compris le terminateur null, dans *``pReturnValue (si pReturnValue n’est pas NULL ). Cela se produit même si l’argument mbstr est NULL et fournit un moyen de déterminer la taille de la mémoire tampon requirejse. Notez que si mbstr est NULL , count est ignoré .

          il y a des cas où ce n’est pas possible à partir de UNICODE_STRING.Buffer (lorsqu’il ne se termine pas par un caractère NULL et qu’il contient des caractères spéciaux (qui prennent 2 octets))

Je n’ai pas testé la fonction à fond, laissez-moi savoir comment cela fonctionne pour vous.

Après plus de précisions, j’ai compris qu’aucune de ces solutions n’est utilisable dans un pilote. Cependant sur [CodeGuru]: ZwCreatefile / ZwReadfile , il y a un exemple d’utilisation de ZwCreatefile et de UNICODE_STRING (via RtlInitUnicodeSsortingng et InitializeObjectAtsortingbutes ) que je vais coller depuis ci-dessous (sans rien tester):

 #include  HANDLE handle; NTSTATUS ntstatus; UNICODE_STRING uniName; OBJECT_ATTRIBUTES objAttr; RtlInitUnicodeSsortingng(&uniName, L"\\SystemRoot\\native.txt"); InitializeObjectAtsortingbutes(&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);