从64位应用程序将32位图像映射到32位进程

时间:2018-08-12 04:45:14

标签: c++ windows windows-10 code-injection dll-injection

我正在尝试找出一种方法(通过负载库或最好通过手动)将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

1 个答案:

答案 0 :(得分:3)

在wow64进程内部创建的线程无论如何都开始以64位模式执行-从64位LdrInitializeThunk内部的ntdll.dll开始执行。

在其中,当进程为wow64时,将调用Run64IfContextIs64函数-系统尝试确定-线程必须在本机上下文还是wow64上下文中执行。这是通过检查踏步起始地址来完成的-如果它在本地dll中(当前为ntdll.dllwow64.dllwow64win.dllwow64cpu.dll)-系统在本地dll中运行(64位模式)。调用图是

enter image description here

在调试器中,我们可以看到下一个调试打印:

<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上下文中执行此线程,这是我们需要的,在这里

enter image description here

<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);
    }
}