我正在64位Windows 10上的32位进程中运行以下命令:
#ifndef _DEBUG
WCHAR buffPath[MAX_PATH] = {0};
FARPROC pfn = (FARPROC)::GetModuleHandleEx;
HMODULE hMod = NULL;
::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)pfn, &hMod);
PVOID pOldVal = NULL;
if(::Wow64DisableWow64FsRedirection(&pOldVal))
{
::GetModuleFileNameEx(::GetCurrentProcess(), hMod, buffPath, _countof(buffPath));
::Wow64RevertWow64FsRedirection(pOldVal);
wprintf(L"Path=%s\n", buffPath);
}
#else
#error run_in_release_mode
#endif
我希望收到路径为c:\windows\syswow64\KERNEL32.DLL
,但它给了我:
Path=C:\Windows\System32\KERNEL32.DLL
知道为什么吗?
答案 0 :(得分:6)
当我们通过LoadLibrary[Ex]
或LdrLoadDll
加载dll时 - 首先是一些预处理传输的dll名称(比如将api-*
转换为实际的dll名称或基于清单重定向dll名称 - 众所周知示例comctl32.dll
)然后使用此(可能已修改的)dll名称将文件加载为dll。但是重定向 - 在这个阶段没有预处理。如果dll成功加载 - 系统分配LDR_DATA_TABLE_ENTRY
结构并保存传输(在预处理之后)dll名称。
GetModuleFileNameEx
只需遍历LDR_DATA_TABLE_ENTRY
双重链接列表和搜索条目DllBase == hModule
- 如果找到 - 将FullDllName
复制到lpFilename
(如果缓冲区足够大) )。所以它只返回加载dll期间使用的dll路径。 Wow64DisableWow64FsRedirection
对此次通话没有任何影响。
如果我们想获得dll的真实(规范)完整路径 - 需要GetMappedFileNameW
使用MAX_PATH
或ZwQueryVirtualMemory
所以代码可以(如果我们希望WCHAR path[MAX_PATH];
GetMappedFileNameW(NtCurrentProcess(), hmod, path, RTL_NUMBER_OF(path));
足够的话)
NTSTATUS GetDllName(PVOID AddressInDll, PUNICODE_STRING NtImageName)
{
NTSTATUS status;
union {
PVOID buf;
PUNICODE_STRING ImageName;
};
static volatile UCHAR guz;
PVOID stack = alloca(guz);
SIZE_T cb = 0, rcb = 0x200;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = ZwQueryVirtualMemory(NtCurrentProcess(), AddressInDll,
MemoryMappedFilenameInformation, buf, cb, &rcb)))
{
return RtlDuplicateUnicodeString(
RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
ImageName, NtImageName);
}
} while (status == STATUS_BUFFER_OVERFLOW);
return status;
}
UNICODE_STRING NtImageName;
if (0 <= GetDllName(hmod, &NtImageName))
{
RtlFreeUnicodeString(&NtImageName);
}
或者如果使用ntapi并正确处理任何路径长度:
NtOpenFile
关于“将其转换为win32表单的方式”的问题 - 有一个反问题 - 为什么?
首先,我们可以按原样使用CreateFileW
(记录良好的api),第二种 - 转换为win32形式的最简单方式,由\\?\globalroot
接受 - 将X:
前缀添加到nt路径。但并非所有win32 api(主shell api)都接受此表单。如果我们想要完全dos-device形式路径(又名#include <mountmgr.h>
ULONG NtToDosPath(HANDLE hMM, PUNICODE_STRING ImageName, PWSTR* ppsz)
{
static MOUNTMGR_MOUNT_POINT MountPoint;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
PMOUNTMGR_MOUNT_POINTS pmmp = 0;
DWORD cb = 0, rcb = 0x200, BytesReturned;
ULONG err = NOERROR;
do
{
if (cb < rcb) cb = RtlPointerToOffset(pmmp = (PMOUNTMGR_MOUNT_POINTS)alloca(rcb - cb), stack);
if (DeviceIoControl(hMM, IOCTL_MOUNTMGR_QUERY_POINTS,
&MountPoint, sizeof(MOUNTMGR_MOUNT_POINT),
pmmp, cb, &BytesReturned, 0))
{
if (ULONG NumberOfMountPoints = pmmp->NumberOfMountPoints)
{
PMOUNTMGR_MOUNT_POINT MountPoints = pmmp->MountPoints;
do
{
UNICODE_STRING SymbolicLinkName = {
MountPoints->SymbolicLinkNameLength,
SymbolicLinkName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset)
};
UNICODE_STRING DeviceName = {
MountPoints->DeviceNameLength,
DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
PWSTR FsPath;
if (RtlPrefixUnicodeString(&DeviceName, ImageName, TRUE) &&
DeviceName.Length < ImageName->Length &&
*(FsPath = (PWSTR)RtlOffsetToPointer(ImageName->Buffer, DeviceName.Length)) == '\\' &&
MOUNTMGR_IS_DRIVE_LETTER(&SymbolicLinkName))
{
cb = ImageName->Length - DeviceName.Length;
if (PWSTR psz = new WCHAR[3 + cb/sizeof(WCHAR)])
{
*ppsz = psz;
psz[0] = SymbolicLinkName.Buffer[12];
psz[1] = ':';
memcpy(psz + 2, FsPath, cb + sizeof(WCHAR));
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
} while (MountPoints++, --NumberOfMountPoints);
}
return ERROR_NOT_FOUND;
}
rcb = pmmp->Size;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
return err;
}
ULONG NtToDosPath(PWSTR lpFilename, PWSTR* ppsz)
{
HANDLE hMM = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hMM == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
UNICODE_STRING us;
RtlInitUnicodeString(&us, lpFilename);
ULONG err = NtToDosPath(hMM, &us, ppsz);
CloseHandle(hMM);
return err;
}
PWSTR psz;
if (NtToDosPath(path, &psz) == NOERROR)
{
DbgPrint("%S\n", psz);
delete [] psz;
}
)需要使用MemoryMappedFilenameInformation
- 在IOCTL_MOUNTMGR_QUERY_POINTS
结构中获取MOUNTMGR_MOUNT_POINT
数组并搜索DeviceName,它是我们的前缀nt path和SymbolicLinkName有驱动程序字母形式。代码可以〜
MOUNTMGR_IS_VOLUME_NAME(&SymbolicLinkName)
我们也可以将代码更改为$forecast = file_get_contents("https://www.astrospeak.com/horoscope/taurus");
以获取卷(持久)名称格式而不是驱动程序字母格式