c++windowsdriverswdk

Windows driver kernel: How enumerate all subdirectories and files?


I'm working in a small antirootkit and i need add a functionality that is:

So, firstly is necessary know all these directories and files, right?

To this, i have code below that already make half of this task. He enumerate all directories and files of a specific directory but not "see" subdirectories ( files and folders).

Eg:

enter image description here

Output:

enter image description here

Code:

#include <ntifs.h>

 typedef unsigned int UINT;

    NTSTATUS EnumFilesInDir()
    {

        HANDLE hFile = NULL;
        UNICODE_STRING szFileName = { 0 };
        OBJECT_ATTRIBUTES Oa = { 0 };
        NTSTATUS ntStatus = 0;
        IO_STATUS_BLOCK Iosb = { 0 };
        UINT uSize = sizeof(FILE_BOTH_DIR_INFORMATION);
        FILE_BOTH_DIR_INFORMATION *pfbInfo = NULL;
        BOOLEAN bIsStarted = TRUE;

        RtlInitUnicodeString(&szFileName, L"\\??\\C:\\MyDirectory"); 
        InitializeObjectAttributes(&Oa, &szFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
        ntStatus = ZwCreateFile(&hFile, GENERIC_READ | SYNCHRONIZE, &Oa, &Iosb, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
        if (!NT_SUCCESS(ntStatus)) { return ntStatus; }
        pfbInfo = ExAllocatePoolWithTag(PagedPool, uSize, '0000');
        if (pfbInfo == NULL)
        {
            ZwClose(hFile); return STATUS_NO_MEMORY;
        }
        while (TRUE)
        {
        lbl_retry:
            RtlZeroMemory(pfbInfo, uSize);
            ntStatus = ZwQueryDirectoryFile(hFile, 0, NULL, NULL, &Iosb, pfbInfo, uSize, FileBothDirectoryInformation, FALSE, NULL, bIsStarted);
            if (STATUS_BUFFER_OVERFLOW == ntStatus) {
                ExFreePoolWithTag(pfbInfo, '000');
                uSize = uSize * 2;
                pfbInfo = ExAllocatePoolWithTag(PagedPool, uSize, '0000');
                if (pfbInfo == NULL) { ZwClose(hFile); return STATUS_NO_MEMORY; }
                goto lbl_retry;
            }
            else if (STATUS_NO_MORE_FILES == ntStatus)
            {
                ExFreePoolWithTag(pfbInfo, '000');
                ZwClose(hFile); return STATUS_SUCCESS;
            }
            else if (STATUS_SUCCESS != ntStatus)
            {
                ExFreePoolWithTag(pfbInfo, '000');
                ZwClose(hFile);
                return ntStatus;
            }
            if (bIsStarted)
            {
                bIsStarted = FALSE;
            }
            while (TRUE)
            {
                WCHAR * szWellFormedFileName = ExAllocatePoolWithTag(PagedPool, (pfbInfo->FileNameLength + sizeof(WCHAR)), '0001');
                if (szWellFormedFileName)
                {
                    RtlZeroMemory(szWellFormedFileName, (pfbInfo->FileNameLength + sizeof(WCHAR)));
                    RtlCopyMemory(szWellFormedFileName, pfbInfo->FileName, pfbInfo->FileNameLength);
                    //KdPrint(("File name is: %S\n", szWellFormedFileName));
                    KdPrint((" %S\n", szWellFormedFileName));
                    ExFreePoolWithTag(szWellFormedFileName, '000');
                }
                if (pfbInfo->NextEntryOffset == 0) { break; }
                pfbInfo += pfbInfo->NextEntryOffset;
            }
        }
        ZwClose(hFile);
        ExFreePoolWithTag(pfbInfo, '000');
        return ntStatus;
    }

So, how do this?

Thank in advance by any help or suggestion.


--------------------------------------------------------EDIT:--------------------------------------------------------------------

I found a possible solution, but i'm getting a BSOD in this line:

if ( (*pDir)->NextEntryOffset)

In KernelFindNextFile method.

Some suggestion?

Here is the code that i found:

#include <ntifs.h>
#include <stdlib.h>

HANDLE KernelCreateFile(IN PUNICODE_STRING pstrFile,IN BOOLEAN bIsDir)
{
    HANDLE hFile = NULL;
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    IO_STATUS_BLOCK StatusBlock = {0};
    ULONG ulShareAccess = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
    ULONG ulCreateOpt = FILE_SYNCHRONOUS_IO_NONALERT;

    OBJECT_ATTRIBUTES objAttrib = {0};
    ULONG ulAttributes = OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE;
    InitializeObjectAttributes(&objAttrib,pstrFile,ulAttributes,NULL,NULL);

    ulCreateOpt |= bIsDir?FILE_DIRECTORY_FILE:FILE_NON_DIRECTORY_FILE;
    Status = ZwCreateFile(
        &hFile,
        GENERIC_ALL,
        &objAttrib,
        &StatusBlock,
        0,
        FILE_ATTRIBUTE_NORMAL,
        ulShareAccess,
        FILE_OPEN_IF,
        ulCreateOpt,
        NULL,
        0);
    if (!NT_SUCCESS(Status))
    {
        return (HANDLE)-1;
    }
    return hFile;
}

PFILE_BOTH_DIR_INFORMATION KernelFindFirstFile(IN HANDLE hFile,IN ULONG ulLen,OUT PFILE_BOTH_DIR_INFORMATION pDir)
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    IO_STATUS_BLOCK StatusBlock = {0};
    PFILE_BOTH_DIR_INFORMATION pFileList = (PFILE_BOTH_DIR_INFORMATION)ExAllocatePool(PagedPool,ulLen);
    Status = ZwQueryDirectoryFile(
        hFile,NULL,NULL,NULL,
        &StatusBlock,
        pDir,
        ulLen,
        FileBothDirectoryInformation,
        TRUE,
        NULL,
        FALSE);
    RtlCopyMemory(pFileList,pDir,ulLen);
    Status = ZwQueryDirectoryFile(
        hFile,NULL,NULL,NULL,
        &StatusBlock,
        pFileList,
        ulLen,
        FileBothDirectoryInformation,
        FALSE,
        NULL,
        FALSE);
    return pFileList;
}

NTSTATUS KernelFindNextFile(IN OUT PFILE_BOTH_DIR_INFORMATION* pDir)
{
    if ( (*pDir)->NextEntryOffset)
    {
        (*pDir)=(PFILE_BOTH_DIR_INFORMATION)((UINT32)(*pDir)+(*pDir)->NextEntryOffset); 
        return STATUS_SUCCESS;
    }
    return STATUS_UNSUCCESSFUL;
}

void Traversal()
{
    UNICODE_STRING ustrFolder = {0};
    WCHAR szSymbol[0x512] = L"\\??\\";
    UNICODE_STRING ustrPath = RTL_CONSTANT_STRING(L"C:\\MyDirectory");
    HANDLE hFile = NULL;
    SIZE_T nFileInfoSize = sizeof(FILE_BOTH_DIR_INFORMATION)+270*sizeof(WCHAR);
    SIZE_T nSize = nFileInfoSize*0x256;
    char strFileName[0x256] = {0};
    PFILE_BOTH_DIR_INFORMATION pFileListBuf = NULL;
    PFILE_BOTH_DIR_INFORMATION pFileList = NULL;
    PFILE_BOTH_DIR_INFORMATION pFileDirInfo = (PFILE_BOTH_DIR_INFORMATION)ExAllocatePool(PagedPool,nSize);

    wcscat_s(szSymbol,_countof(szSymbol),ustrPath.Buffer);
    RtlInitUnicodeString(&ustrFolder,szSymbol);
    hFile = KernelCreateFile(&ustrFolder,TRUE);
    pFileList = pFileListBuf;
    KernelFindFirstFile(hFile,nSize,pFileDirInfo);
    if (pFileList)
    {
        RtlZeroMemory(strFileName,0x256);
        RtlCopyMemory(strFileName,pFileDirInfo->FileName,pFileDirInfo->FileNameLength);
        if (strcmp(strFileName,"..")!=0 || strcmp(strFileName,".")!=0)
        {
            if (pFileDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                DbgPrint("[Directory]%S\n",strFileName);
            }
            else
            {
                DbgPrint("[File]%S\n",strFileName);
            }
        }
    }
    while (NT_SUCCESS(KernelFindNextFile(&pFileList)))
    {
        RtlZeroMemory(strFileName,0x256);
        RtlCopyMemory(strFileName,pFileList->FileName,pFileList->FileNameLength);
        if (strcmp(strFileName,"..")==0 || strcmp(strFileName,".")==0)
        {
            continue;
        }
        if (pFileList->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            DbgPrint("[Directory]%S\n",strFileName);
        } 
        else
        {
            DbgPrint("[File]%S\n",strFileName);
        }
    }
    RtlZeroMemory(strFileName,0x256);
    RtlCopyMemory(strFileName,pFileListBuf->FileName,pFileListBuf->FileNameLength);
    if (strcmp(strFileName,"..")!=0 || strcmp(strFileName,".")!=0)
    {
        if (pFileDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            DbgPrint("[Directory]%S\n",strFileName);
        }
        else
        {
            DbgPrint("[File]%S\n",strFileName);
        }
        ExFreePool(pFileListBuf);
        ExFreePool(pFileDirInfo);
    }
}

BSOD:

FAULTING_SOURCE_LINE_NUMBER:  263

FAULTING_SOURCE_CODE:  
   259: }
   260: 
   261: NTSTATUS KernelFindNextFile(IN OUT PFILE_BOTH_DIR_INFORMATION* pDir)
   262: {
>  263:     if ((*pDir)->NextEntryOffset)
   264:     {
   265:         (*pDir) = (PFILE_BOTH_DIR_INFORMATION)((UINT32)(*pDir) + (*pDir)->NextEntryOffset);
   266:         return STATUS_SUCCESS;
   267:     }
   268: 

Solution

  • ok, here code which tested and works. if somebody can not use it or got BSOD - probably problem not in code but in somebody skills

    several notes - if you have previous mode kernel - use Nt* api (when exported) but not Zw* api. or Io* api. if you not understand why, or what is your previous mode - better even not try programming in kernel.

    mandatory use FILE_OPEN_REPARSE_POINT option or even not try run this code if not understand what is this and why need use

    for delete - open files with FILE_DELETE_ON_CLOSE option, for dump only - with FILE_DIRECTORY_FILE option instead.

    code yourself used <= 0x1800 bytes of stack in x64 in deepest folders, like c:\Users - so this is ok for kernel, but always check stack space with IoGetRemainingStackSize

    i will be not correct every comma in your code, if you can not do this yourself

    #define ALLOCSIZE PAGE_SIZE
    
    #ifdef _REAL_DELETE_
    #define USE_DELETE_ON_CLOSE FILE_DELETE_ON_CLOSE
    #define FILE_ACCESS FILE_GENERIC_READ|DELETE
    #else
    #define USE_DELETE_ON_CLOSE FILE_DIRECTORY_FILE
    #define FILE_ACCESS FILE_GENERIC_READ
    #endif
    
    
    // int nLevel, PSTR prefix for debug only
    void ntTraverse(POBJECT_ATTRIBUTES poa, ULONG FileAttributes, int nLevel, PSTR prefix)
    {
        if (IoGetRemainingStackSize() < PAGE_SIZE)
        {
            DbgPrint("no stack!\n");
            return ;
        }
    
        if (!nLevel)
        {
            DbgPrint("!nLevel\n");
            return ;
        }
    
        NTSTATUS status;
        IO_STATUS_BLOCK iosb;
        UNICODE_STRING ObjectName;
        OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
    
        DbgPrint("%s[<%wZ>]\n", prefix, poa->ObjectName);
    
    #ifdef _REAL_DELETE_
        if (FileAttributes & FILE_ATTRIBUTE_READONLY)
        {
            if (0 <= NtOpenFile(&oa.RootDirectory, FILE_WRITE_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT|FILE_OPEN_REPARSE_POINT))
            {
                static FILE_BASIC_INFORMATION fbi = { {}, {}, {}, {}, FILE_ATTRIBUTE_NORMAL };
                NtSetInformationFile(oa.RootDirectory, &iosb, &fbi, sizeof(fbi), FileBasicInformation);
                NtClose(oa.RootDirectory);
            }
        }
    #endif//_REAL_DELETE_
    
        if (0 <= (status = NtOpenFile(&oa.RootDirectory, FILE_ACCESS, poa, &iosb, FILE_SHARE_VALID_FLAGS, 
            FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|USE_DELETE_ON_CLOSE)))
        {
            if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                if (PVOID buffer = ExAllocatePool(PagedPool, ALLOCSIZE))
                {
                    union {
                        PVOID pv;
                        PBYTE pb;
                        PFILE_DIRECTORY_INFORMATION DirInfo;
                    };
    
                    while (0 <= (status = NtQueryDirectoryFile(oa.RootDirectory, NULL, NULL, NULL, &iosb, 
                        pv = buffer, ALLOCSIZE, FileDirectoryInformation, 0, NULL, FALSE)))
                    {
    
                        ULONG NextEntryOffset = 0;
    
                        do 
                        {
                            pb += NextEntryOffset;
    
                            ObjectName.Buffer = DirInfo->FileName;
    
                            switch (ObjectName.Length = (USHORT)DirInfo->FileNameLength)
                            {
                            case 2*sizeof(WCHAR):
                                if (ObjectName.Buffer[1] != '.') break;
                            case sizeof(WCHAR):
                                if (ObjectName.Buffer[0] == '.') continue;
                            }
    
                            ObjectName.MaximumLength = ObjectName.Length;
    
    #ifndef _REAL_DELETE_
                            if (DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    #endif
                            {
                                ntTraverse(&oa, DirInfo->FileAttributes, nLevel - 1, prefix - 1);
                            }
    #ifndef _REAL_DELETE_
                            else
    #endif
                            {
                                DbgPrint("%s%8I64u <%wZ>\n", prefix, DirInfo->EndOfFile.QuadPart, &ObjectName);
                            }
    
                        } while (NextEntryOffset = DirInfo->NextEntryOffset);
                    }
    
                    ExFreePool(buffer);
    
                    if (status == STATUS_NO_MORE_FILES)
                    {
                        status = STATUS_SUCCESS;
                    }
                }
            }
    
            NtClose(oa.RootDirectory);
        }
    
        if (0 > status)
        {
            DbgPrint("---- %x %wZ\n", status, poa->ObjectName);
        }
    }
    
    void ntTraverse()
    {
        char prefix[MAXUCHAR + 1];
        memset(prefix, '\t', MAXUCHAR);
        prefix[MAXUCHAR] = 0;
    
        STATIC_OBJECT_ATTRIBUTES(oa, "\\??\\c:\\users");
        //STATIC_OBJECT_ATTRIBUTES(oa, "\\systemroot");
        ntTraverse(&oa, FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_READONLY, MAXUCHAR, prefix + MAXUCHAR);
    }