我正在尝试找出一种方法(通过负载库或最好通过手动)将32位映像(dll)从运行64位的应用程序映射到32位进程。有什么想法吗?
当前,注入/映射一切看起来都正常,但是当使用仅创建一个消息框的空dll进行测试时,它实际上并未创建该消息框。当来自32位应用程序时,注入工作正常。
这是我对LoadLibrary进行测试的方式
EnsureElevation( ); // ensures that we're run as admin and that we have debug privs
// messy code because it's test code
LPVOID LoadLib = ( LPVOID )GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "LoadLibraryA" );
HANDLE Proc = OpenProcess( PROCESS_ALL_ACCESS, false, GetProcessID( L"target.exe" ) );
std::cout << Proc << '\n';
std::cout << "Proc Error: 0x" << std::hex << GetLastError( ) << '\n'; // 0x0
LPVOID RemoteString = VirtualAllocEx( Proc, NULL, strlen( R"(C:\Users\Username\Desktop\MessageBox.dll)" ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
std::cout << RemoteString << '\n';
std::cout << "String Error: 0x" << std::hex << GetLastError( ) << '\n'; // 0x0
std::cout << WriteProcessMemory( Proc, RemoteString, R"(C:\Users\Username\Desktop\MessageBox.dll)", strlen( R"(C:\Users\Username\Desktop\MessageBox.dll)" ), NULL ) << '\n';
std::cout << "Write Error: 0x" << std::hex << GetLastError( ) << '\n'; // 0x0
std::cout << CreateRemoteThread( Proc, NULL, NULL, ( LPTHREAD_START_ROUTINE )LoadLib, ( LPVOID )RemoteString, NULL, NULL ) << '\n';
std::cout << "Create Error: 0x" << std::hex << GetLastError( ) << '\n'; // 0x0
答案 0 :(得分:3)
在wow64进程内部创建的线程无论如何都开始以64位模式执行-从64位LdrInitializeThunk
内部的ntdll.dll
开始执行。
在其中,当进程为wow64时,将调用Run64IfContextIs64
函数-系统尝试确定-线程必须在本机上下文还是wow64上下文中执行。这是通过检查踏步起始地址来完成的-如果它在本地dll中(当前为ntdll.dll
,wow64.dll
,wow64win.dll
,wow64cpu.dll
)-系统在本地dll中运行(64位模式)。调用图是
在调试器中,我们可以看到下一个调试打印:
<pid>:<tid> Found inside *.dll
<pid>:<tid> InitialPC <rip> is within a native dll. Running native context unchanged.
特殊情况是InitialPC位于64位kernel32.dll或64位user32.dll(查找DllsToRemap
中)-在Run64IfContextIs64
内部称为MapContextAddress64TO32
的系统检查地址位于本机dll内部。在某些情况下(例如,DebugBreak
),系统尝试将其重定向到32位kernel32.dll DebugBreak
并以wow64模式执行,但是..这里的错误-崩溃了。在大多数情况下-系统重定向地址从64位kernel32.dll
(或user32.dll
)到Wow64pBreakPoint
内部的wow64.dll
(非导出函数)。此函数以本机(64位模式)执行-如果连接了调试器,则调用int 3
(断点)。之后,只需终止线程即可。在调试输出中,我们可以查看
<pid>:<tid> InitialPC <rip> found in the space reserved for 64-bit kernel32.dll
这确实是您的情况-您在64位LoadLibraryA
中传递了kernel32.dll
的地址。当然是错误的。
否则(如果PC不在64位模块中)-系统在wow64上下文中执行此线程,这是我们需要的,在这里
<pid>:<tid> ThunkStartupContent64TO32: Original InitialPC <rip>, StartupAddress <eip>, Arg1 <pv>
<pid>:<tid> ThunkStartupContext64TO32: Thunking RTL user thread start
在BTCpuSimulate
线程之后,从32位ntdll.dll转到LdrInitializeThunk
并在wow64上下文中照常执行。
因此,我们需要在64位LoadLibraryA
内的kernel32.dll
地址得到在32位LoadLibraryW
内的kernel32.dll
地址。但是,如果仅尝试使用已记录的win32 api,则此任务并不简单。我使用此ntdll api(其中一些未记录)。但是是这样:
PVOID getRVA(PVOID Base, ULONG_PTR BaseAddress, PCSTR Name)
{
if (PIMAGE_NT_HEADERS32 pinth = (PIMAGE_NT_HEADERS32)RtlImageNtHeader(Base))
{
BaseAddress -= pinth->OptionalHeader.AddressOfEntryPoint;
DWORD Size, exportRVA;
if (PIMAGE_EXPORT_DIRECTORY pied = (PIMAGE_EXPORT_DIRECTORY)
RtlImageDirectoryEntryToData(Base, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &Size))
{
exportRVA = RtlPointerToOffset(Base, pied);
DWORD NumberOfFunctions = pied->NumberOfFunctions;
DWORD NumberOfNames = pied->NumberOfNames;
if (0 < NumberOfNames && NumberOfNames <= NumberOfFunctions)
{
PDWORD AddressOfFunctions = (PDWORD)RtlOffsetToPointer(Base, pied->AddressOfFunctions);
PDWORD AddressOfNames = (PDWORD)RtlOffsetToPointer(Base, pied->AddressOfNames);
PWORD AddressOfNameOrdinals = (PWORD)RtlOffsetToPointer(Base, pied->AddressOfNameOrdinals);
DWORD a = 0, b = NumberOfNames, o;
do
{
o = (a + b) >> 1;
int i = strcmp(RtlOffsetToPointer(Base, AddressOfNames[o]), Name);
if (!i)
{
DWORD Rva = AddressOfFunctions[AddressOfNameOrdinals[o]];
return (ULONG_PTR)Rva - (ULONG_PTR)exportRVA < Size ? 0 : RtlOffsetToPointer(BaseAddress, Rva);
}
0 > i ? a = o + 1 : b = o;
} while (a < b);
}
}
}
return 0;
}
PVOID GetWowLoadLibraryW()
{
PVOID pv = 0;
STATIC_OBJECT_ATTRIBUTES(oa, "\\KnownDlls32\\kernel32.dll");
HANDLE hSection;
if (0 <= ZwOpenSection(&hSection, SECTION_QUERY|SECTION_MAP_READ, &oa))
{
SECTION_IMAGE_INFORMATION sii;
if (0 <= ZwQuerySection(hSection, SectionImageInformation, &sii, sizeof(sii), 0))
{
PVOID BaseAddress = 0;
SIZE_T ViewSize = 0;
if (0 <= ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY))
{
__try {
pv = getRVA(BaseAddress, (ULONG_PTR)sii.TransferAddress, "LoadLibraryW");
} __except( EXCEPTION_EXECUTE_HANDLER) {
}
ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
}
NtClose(hSection);
}
return pv;
}
这个任务变得微不足道之后:
if (PVOID wowLoadLibraryW = GetWowLoadLibraryW())
{
//PCWSTR szLibPath = ...
SIZE_T s = (wcslen(szLibPath) + 1) * sizeof(WCHAR);
if (HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, <pid>))
{
if (PVOID pv = VirtualAllocEx(hProcess, 0, s, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE))
{
if (WriteProcessMemory(hProcess, pv, szLibPath, s, 0))
{
if (HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)wowLoadLibraryW, pv, 0, 0))
{
CloseHandle(hThread);
}
}
}
CloseHandle(hProcess);
}
}