获取有效的Win32文件路径的最简单方法是从文件nt-path中添加L"\\\\?\\globalroot"
(\\?\globalroot
)前缀。这是因为CreateFileW
从\??\
目录中查找,并且globalroot
是\??\
中的符号链接,使我们可以跳转到nt命名空间的根。
例如 - \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll
是nt的绝对路径。而\\?\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll
是用于CreateFileW
的有效win32路径 - 此API将众所周知的前缀\\?\
转换为nt前缀\??\
并将名称\??\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll
传递给内核。在解析此名称时 - 在处理指向命名空间根的符号链接globalroot
之后 - 我们再次获得\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll
- 正确的nt路径。
因此,如果我们需要在CreateFileW
中使用有效的win32路径 - 只需将此前缀附加到nt路径即可。但是某些shell32 API不接受此形式的路径。而且它在UI中看起来并不好看。如果我们想从路径中获取DOS驱动器字母(这是有效win32路径的子集) - 我们可以使用IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH
将设备名称转换为驱动器字母。此ioctl以MOUNTDEV_NAME
(在mountmgr.h中声明)作为输入,并且输出缓冲区为MOUNTMGR_VOLUME_PATHS
。在MOUNTDEV_NAME
缓冲区中必须恰好是设备名称,不包括文件路径。因此,我们需要将返回的nt路径分成2个组件。例如,在\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll
中:
\Device\HarddiskVolume9
- 设备路径
\Windows\SysWOW64\ntdll.dll
- 文件系统路径
正确的方法是首先打开文件并调用GetFileInformationByHandleEx
,使用FileNameInfo
- 我们获得了输出中的文件系统路径。通过这个,我们可以使用wcsstr
来分离设备路径。同时,如果我们打开文件句柄 - 我们可以在调用GetFinalPathNameByHandleW
时使用VOLUME_NAME_DOS
。这个API正好会做我们想要的事情 - 查询文件路径,分离设备路径并调用IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH
。+ 打开/关闭挂载管理器。
但通常的NT文件路径以\Device\HarddiskVolumeX
开始。这样可以先尝试快速方式 - 避免打开文件并查询其路径。
因此,我们首先需要打开挂载管理器:
HANDLE hMountManager = CreateFile(MOUNTMGR_DOS_DEVICE_NAME,
0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0)
然后我们可以运行下面的代码:
void dumpModules(HANDLE hMountManager, HANDLE hProcess)
{
ULONG cb = 0, cbNeeded = 16;
volatile static UCHAR guz;
PVOID stack = alloca(guz);
HMODULE *hMods, hmod;
__continue:
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))
{
#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])
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";
if (!memcmp(pmdn->Name, HarddiskVolume, sizeof(HarddiskVolume) - sizeof(WCHAR)))
{
c = wcschr(pmdn->Name + RTL_NUMBER_OF(HarddiskVolume) - 1, '\\');
}
{
pmdn->NameLength = NameLength;
HANDLE hFile = CreateFile(win32Path, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
WCHAR DosPath[MAX_PATH];
if (GetFinalPathNameByHandleW(hFile, DosPath, RTL_NUMBER_OF(DosPath), VOLUME_NAME_DOS))
{
DbgPrint("%S\n", DosPath);
}
RtlGetLastNtStatus();
BOOL fOk = GetFileInformationByHandleEx(hFile, FileNameInfo, pfni, FILE_NAME_INFO_buffer_size);
CloseHandle(hFile);
if (fOk)
{
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);
}
}
}
并为记事本提供演示输出:
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
LIST_MODULES_ALL
时,系统实际上会像您设置了LIST_MODULES_32BIT
一样工作。您需要两次调用EnumProcessModulesEx
:一次使用LIST_MODULES_64BIT
(您将获得4个64位模块 - ntdll、wow64、wow64win、wow64cpu),另一次使用LIST_MODULES_32BIT
。 - RbMmZwQueryVirtualMemory
和MemoryMappedFilenameInformation
来获取目标进程中任何模块的规范化、NT格式路径。或者你只需要Win32格式的路径? - RbMm