GetModuleFileNameEx on 32bit process from 64bit process on windows 10

久未见 提交于 2019-12-10 17:15:41

问题


I'm trying to enumerate 32bit process modules names from 64bit application using the following code:

if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
{
    for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
    {
       TCHAR szModName[MAX_PATH] = { 0 };

        if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
            sizeof(szModName) / sizeof(TCHAR)))
        {
            printf("module name is: %S", szModName);
        }
    }
}

The code works as expected in Windows 7, as part of the results are:

...

C:\Windows\**SysWOW64**\ntdll.dll

...

In Windows 10 the above code returns the full path but with System32 instead of SysWOW64. e.g,

...

C:\Windows\**System32**\ntdll.dll

...

Looking deeper for the cause, I notice that GetModuleFileNameEx reads the remote process PEB and LDR_TABLE_ENTRY, and starting from Windows 10 the LDR_TABLE_ENTRY contains the full path with System32 and not SysWOW64 - also for 32bit applications.

I also tried to use GetMappedFileName but it isn't straight forward and efficient to translate the path from dos path (\device\harddiskvolume) to standard (c:\) path.

I wonder if there are any other easy way to extract the full syswow64 path.


回答1:


for get valid win32 file path from file nt-path - simplest way - add L"\\\\?\\globalroot" (\\?\globalroot) prefix. this is because CreateFileW looked from \??\ directory and globalroot is symbolic link in \??\ which let as to jump to root of nt namespace.

for example - \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll is nt absolute path. and \\?\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll is valid win32 path for CreateFileW - this api convert well known prefix \\?\ to nt prefix \??\ and pass name \??\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll to kernel. when parsing this name - after process symbolic link globalroot which point to root of namespace - we again got \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll - correct nt path.

so if we need valid win32 path for use in CreateFileW - simply append this prefix to nt path. however some shell32 api not accept this form path. also it not nice looked in UI. if we want got DOS drive letter form path (this is subset of valid win32 paths) - we can use IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH which convert device name to drive letter. this ioctl take as input MOUNTDEV_NAME (declared in mountmgr.h) and output buffer is MOUNTMGR_VOLUME_PATHS. in MOUNTDEV_NAME buffer must be exactly device name, without file path. so we need break returned nt path to 2 components. for example in \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll :

  • \Device\HarddiskVolume9 - device path
  • \Windows\SysWOW64\ntdll.dll - file system path

correct way here first open file and call GetFileInformationByHandleEx with FileNameInfo - we got file system path in output. with this we can use wcsstr for separate device path. also if we open file handle - we can use it in call GetFinalPathNameByHandleW with VOLUME_NAME_DOS. this api do exactly which we will be do - query file path, separate device path and call IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH. + open/close mount manager.

but usual nt file path begin from \Device\HarddiskVolumeX. this allow first try fast way - avoid open file and query it path.

so first we need open mount manager:

#include <mountmgr.h>
HANDLE hMountManager = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, 
    0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

then we can run next code:

void dumpModules(HANDLE hMountManager, HANDLE hProcess)
{
    ULONG cb = 0, cbNeeded = 16;

    volatile static UCHAR guz;
    PVOID stack = alloca(guz);
    HMODULE *hMods, hmod;

__continue:

    // cumulative allocate memory in stack, not need free it
    cb = RtlPointerToOffset(hMods = (HMODULE*)alloca(cbNeeded - cb), stack);

    if (EnumProcessModulesEx(hProcess, hMods, cb, &cbNeeded, LIST_MODULES_32BIT))
    {
        if (cb < cbNeeded)
        {
            goto __continue;
        }

        if (cbNeeded /= sizeof(HMODULE))
        {
            //i use hard coded size buffers, for reduce code and show main idea
#define FILE_NAME_INFO_buffer_size  FIELD_OFFSET(FILE_NAME_INFO, FileName[MAX_PATH])
#define MOUNTDEV_NAME_buffer_size  FIELD_OFFSET(MOUNTDEV_NAME, Name[MAX_PATH])
#define MOUNTMGR_VOLUME_PATHS_buffer_size  FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz[64])

            // + space for 0 at the end
            PFILE_NAME_INFO pfni = (PFILE_NAME_INFO)alloca(FILE_NAME_INFO_buffer_size + sizeof(WCHAR));

            PMOUNTMGR_VOLUME_PATHS pmvp = (PMOUNTMGR_VOLUME_PATHS)alloca(MOUNTMGR_VOLUME_PATHS_buffer_size);
            PMOUNTDEV_NAME pmdn = (PMOUNTDEV_NAME)alloca(MOUNTDEV_NAME_buffer_size);

            static WCHAR globalroot[] = L"\\\\.\\globalroot";

            alloca(sizeof(globalroot));
            PWSTR win32Path = pmdn->Name - RTL_NUMBER_OF(globalroot) + 1;

            memcpy(win32Path, globalroot, sizeof(globalroot));
            USHORT NameLength = pmdn->NameLength;

            do 
            {
                hmod = *hMods++;

                if (GetMappedFileNameW(hProcess, hmod, pmdn->Name, MAX_PATH))
                {
                    DbgPrint("%p %S\n",hmod, pmdn->Name);

                    PWSTR c = 0;

                    static const WCHAR HarddiskVolume[] = L"\\Device\\HarddiskVolume";

                    // fast way
                    if (!memcmp(pmdn->Name, HarddiskVolume, sizeof(HarddiskVolume) - sizeof(WCHAR)))
                    {
                        c = wcschr(pmdn->Name + RTL_NUMBER_OF(HarddiskVolume) - 1, '\\');
                    }
                    // else - for demo
                    {
                        pmdn->NameLength = NameLength;

                        HANDLE hFile = CreateFile(win32Path, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

                        if (hFile != INVALID_HANDLE_VALUE)
                        {
                            //++ just for demo
                            WCHAR DosPath[MAX_PATH];
                            if (GetFinalPathNameByHandleW(hFile, DosPath, RTL_NUMBER_OF(DosPath), VOLUME_NAME_DOS))
                            {
                                DbgPrint("%S\n", DosPath);
                            }
                            RtlGetLastNtStatus();
                            //-- just for demo

                            BOOL fOk = GetFileInformationByHandleEx(hFile, FileNameInfo, pfni, FILE_NAME_INFO_buffer_size);

                            CloseHandle(hFile);

                            if (fOk)
                            {
                                // FileName not 0 terminated
                                pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] = 0;

                                c = wcsstr(pmdn->Name, pfni->FileName);
                            }
                        }

                    }

                    if (c)
                    {
                        pmdn->NameLength = (USHORT)RtlPointerToOffset(pmdn->Name, c);

                        if (DeviceIoControl(hMountManager, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
                            pmdn, MOUNTDEV_NAME_buffer_size, 
                            pmvp, MOUNTMGR_VOLUME_PATHS_buffer_size, &cb, NULL))
                        {
                            DbgPrint("%S%S\n", pmvp->MultiSz, c);
                        }
                    }
                }

            } while (--cbNeeded);
        }
    }
}

and demo output for notepad:

0000000000170000 \Device\HarddiskVolume9\Windows\SysWOW64\notepad.exe
\\?\C:\Windows\SysWOW64\notepad.exe
C:\Windows\SysWOW64\notepad.exe
0000000077A90000 \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll
\\?\C:\Windows\SysWOW64\ntdll.dll
0000000075460000 \Device\HarddiskVolume9\Windows\SysWOW64\kernel32.dll
\\?\C:\Windows\SysWOW64\kernel32.dll
C:\Windows\SysWOW64\kernel32.dll
0000000074A30000 \Device\HarddiskVolume9\Windows\SysWOW64\KernelBase.dll
\\?\C:\Windows\SysWOW64\KernelBase.dll
C:\Windows\SysWOW64\KernelBase.dll
00000000749B0000 \Device\HarddiskVolume9\Windows\SysWOW64\advapi32.dll
\\?\C:\Windows\SysWOW64\advapi32.dll


来源:https://stackoverflow.com/questions/46403532/getmodulefilenameex-on-32bit-process-from-64bit-process-on-windows-10

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!