64位代码洞返回错误的入口点位置

64-bit Code Cave Returning Incorrect Entry Point Location

本文关键字:入口 位置 错误 代码 返回 64位      更新时间:2023-10-16

我一直试图运行一个64位DLL纯粹在一个进程虚拟内存没有 '手动映射'它(即手动解析重定位/导入)。

计划是将代码注入目标应用程序,并通过传统的方法(如LoadLibrary)加载模块。

我假设LoadLibrary会自己修复模块的重定位/导入,因为这就是它的设计目的。

加载模块后,注入的代码将使用GetModuleInformation获取关于模块的信息,将其传输到临时内存缓冲区,释放模块,在最初加载模块的同一地址分配内存,将其写回,并执行入口点。

最后一步是我认为错误发生的地方。

为了测试这个理论,我硬编码了入口点地址,通过Visual Studio的"附加到进程"功能调试了远程应用程序,模拟了一个类似的环境来纠正错误的指针算术,所有这些都是为了获得更多的信息,关于错误可能是什么。


这里是一些可能有用也可能没用的一般信息:

  • 两个应用程序(注入器和DLL)都编译为在64位体系结构中运行
  • 我一直用来测试注入方法的测试应用程序是windows更新应用程序(wuauclt.exe -位于/System32/),它当然被编译为作为64位PE运行
  • 主机:Windows 7 Home Premium(系统类型:64位操作系统)

直接与注入器相关的信息:

  • 主要的代码注入方法是有效的(据我所知),我已经通过使用MessageBoxA
  • 进行调试证明了这一点
  • 项目使用多字节字符集,禁用代码优化。代码是使用VS 2013 Ultimate编译的(两个项目都是为x64版本构建的)
  • SDL检查关闭,因为使用了不安全的函数(strcpy和友元)
  • 注入器在每次运行时都以更高的权限(最高为SE_DEBUG_PRIVILEGES)进行调试。

代码前言:下面展示的代码无论如何都不意味着看起来漂亮或展示良好的编程实践。在查看代码时请记住这一点。它专门设计用于测试代码注入方法以验证其是否有效。如果您对程序布局、结构等有问题,请自行纠正和/或重新构建它们。这不是我来的原因。除非这是导致错误的原因,否则这完全是我在这里的原因:)

注入器的代码: http://pastebin.com/FF5G9nnR

/*
    Some of the code was truncated (functions not pertaining to the injection), but
    I have verified the code compiles and works correctly with it's injeteme.dll counterpart
*/
#include <Windows.h>
#include <Psapi.h>
#define TARGET_PID                1124
typedef BOOL(WINAPI* pFreeLibrary)(HMODULE);
typedef HMODULE(WINAPI* pLoadLibraryA)(LPCSTR);
typedef HANDLE(WINAPI* pGetCurrentProcess)(void);
typedef BOOL(WINAPI* DLL_MAIN)(HMODULE, DWORD, LPVOID);
typedef HANDLE(WINAPI* pOpenProcess)(DWORD, BOOL, DWORD);
typedef BOOL(WINAPI* pVirtualFree)(LPVOID, SIZE_T, DWORD);
typedef int(__stdcall* pMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
typedef LPVOID(WINAPI* pVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL(WINAPI* pGetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
typedef BOOL(WINAPI* pWriteProcessMemory)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
//////////////////////////////////////////////////////////////////
struct IINFO
{
    LPVOID stubAddr;
    LPVOID retStatusPtr;
    char fullModulePath[MAX_PATH];
    DWORD pId, sizeOfCurrStruct;
    // DEBUG
    pMessageBoxA messageBox;
    pOpenProcess openProcess;
    pVirtualFree virtualFree;
    pFreeLibrary freeLibrary;
    pLoadLibraryA loadLibrary;
    pVirtualAlloc virtualAlloc;
    pGetCurrentProcess getCurrProc;
    pWriteProcessMemory writeMemory;
    pGetModuleInformation getModInfo;
};
static DWORD WINAPI stub(IINFO *iInfo)
{
    HMODULE hMod;
    MODULEINFO mInfo;
    DLL_MAIN dllMain;
    LPVOID lpNewMod, lpTempModBuff;
    PIMAGE_DOS_HEADER pIDH;
    PIMAGE_NT_HEADERS pINH;
    iInfo->messageBox(NULL, iInfo->fullModulePath, NULL, 0);
    hMod = iInfo->loadLibrary(iInfo->fullModulePath);
    if (!hMod)
        return 0;
    if (!iInfo->getModInfo(iInfo->getCurrProc(), hMod, &mInfo, sizeof(MODULEINFO)))
        return 0;
    lpTempModBuff = iInfo->virtualAlloc(NULL, mInfo.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpTempModBuff)
        return 0;
    if (!iInfo->writeMemory(iInfo->getCurrProc(), lpTempModBuff, mInfo.lpBaseOfDll, mInfo.SizeOfImage, NULL))
        return 0;
    if (!iInfo->freeLibrary(hMod))
        return 0;
    lpNewMod = iInfo->virtualAlloc(mInfo.lpBaseOfDll, mInfo.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpNewMod)
        return 0;
    // using wpm since we have already acquired the function
    if (!iInfo->writeMemory(iInfo->getCurrProc(), lpNewMod, lpTempModBuff, mInfo.SizeOfImage, NULL))
        return 0;
    if (!iInfo->virtualFree(lpTempModBuff, 0, MEM_RELEASE))
        return 0;
    /*if (!iInfo->virtualFree(iInfo, 0, MEM_RELEASE))
    return 0;
    iInfo->messageBox(NULL, NULL, NULL, 0); */
    pIDH = (PIMAGE_DOS_HEADER)lpNewMod;
    if (!pIDH)
        return 0;
    pINH = (PIMAGE_NT_HEADERS)((LPBYTE)lpNewMod + pIDH->e_lfanew);
    if (!pINH)
        return 0;
    dllMain = (DLL_MAIN)((LPBYTE)lpNewMod + pINH->OptionalHeader.AddressOfEntryPoint);
    if (!dllMain)
        return 0;
    iInfo->messageBox(NULL, NULL, NULL, 0);
    dllMain((HINSTANCE)lpNewMod, DLL_PROCESS_ATTACH, NULL);
    return 1;
}
static DWORD WINAPI stubEnd(){ return 0; }
//////////////////////////////////////////////////////////////////
int main()
{
    HANDLE hThread = 0;
    DWORD dwStubSize = 0;
    int sucResp = 0, count = 0;
    HMODULE hUser32 = 0, hNtdll = 0;
    char fullPathName[] = "C:\injectme.dll";
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TARGET_PID);
    if (!hProc || hProc == INVALID_HANDLE_VALUE)
        return 0;
    __int64 SizeOfStub = (LPBYTE)stubEnd - (LPBYTE)stub;
    LPVOID lpStub = VirtualAllocEx(hProc, NULL, SizeOfStub, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpStub)
        return 0;
    hUser32 = LoadLibraryA("user32.dll");
    if (!hUser32)
        return 0;
    hNtdll = LoadLibraryA("kernel32.dll");
    if (!hNtdll)
        return 0;
    IINFO iInfo = {};
    iInfo.retStatusPtr = &sucResp;
    strcpy(iInfo.fullModulePath, fullPathName);
    iInfo.sizeOfCurrStruct = sizeof(IINFO);
    iInfo.stubAddr = lpStub;
    iInfo.pId = GetCurrentProcessId();
    iInfo.messageBox = (pMessageBoxA)GetProcAddress(hUser32, "MessageBoxA");
    iInfo.openProcess = (pOpenProcess)GetProcAddress(hNtdll, "OpenProcess");
    iInfo.virtualFree = (pVirtualFree)GetProcAddress(hNtdll, "VirtualFree");
    iInfo.freeLibrary = (pFreeLibrary)GetProcAddress(hNtdll, "FreeLibrary");
    iInfo.loadLibrary = (pLoadLibraryA)GetProcAddress(hNtdll, "LoadLibraryA");
    iInfo.virtualAlloc = (pVirtualAlloc)GetProcAddress(hNtdll, "VirtualAlloc");
    iInfo.getCurrProc = (pGetCurrentProcess)GetProcAddress(hNtdll, "GetCurrentProcess");
    iInfo.writeMemory = (pWriteProcessMemory)GetProcAddress(hNtdll, "WriteProcessMemory");
    iInfo.getModInfo = (pGetModuleInformation)GetProcAddress(hNtdll, "K32GetModuleInformation");
    LPVOID lpStubInfo = VirtualAllocEx(hProc, NULL, sizeof(IINFO), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpStubInfo)
        return 0;
    if (!WriteProcessMemory(hProc, lpStub, stub, SizeOfStub, NULL))
        return 0;
    if (!WriteProcessMemory(hProc, lpStubInfo, &iInfo, sizeof(iInfo), NULL))
        return 0;
    hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpStub, lpStubInfo, 0, NULL);
    if (!hThread || hThread == INVALID_HANDLE_VALUE)
        return 0;
    WaitForSingleObject(hThread, INFINITE);
    return 1;
}

要注入的DLL代码: http://pastebin.com/8WXxcpu1

#include <Windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpParam)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
        {
            MessageBoxA(NULL, "Hello from injectme.dll!", "", MB_OK | MB_ICONINFORMATION);
            break;
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

在VS2013的调试器中逐字运行上述代码时的错误(假设您也应用了上述设置并具有类似的环境)如下:

"在wuaucult .exe中0x000007FEEA5125D4的未处理异常:0xC0000005:访问违反执行位置0x000007FEEA5125D4。"

在process Hacker中查看进程"wuauclt.exe"时,我可以清楚地看到该模块最初是在0x7fef67c0000处分配的(通过LoadLibrary加载后)。这显示在上下文菜单-> miscellaneous->unloaded modules下。

一旦双击"wuauclt.exe",你可以浏览应用程序的虚拟内存,以确保一切正常工作。我可以确认,对于当前会话,RWX内存缓冲区已在0x7fef67c0000处分配,具有卸载模块的确切大小,包含injectme.dll模块。当使用CFF资源管理器挖掘injectme.dll时,入口点RVA似乎是0x132C,考虑到错误在内存中更远,这并不加起来。此外,我还可以验证另外两个RWX内存缓冲区,其中包含代码注入存根和信息结构。回顾一下信息结构,可能不需要RWX。不管怎样,我怎么也弄不清这个错误。

我希望你能帮助我。非常感谢您的宝贵时间。

我的直觉是你对这样一个具有挑战性的项目缺乏基本的理解。你把不同领域的概念混在一起了。

Windows本身非常、非常不关心你在开发中使用的编程语言。你要么得到CLR代码(. net),要么得到本机代码。在本例中是x64。但是Windows并不关心strcpy或SDL检查。这是编译器要处理的,而不是操作系统。当strcpy的代码完全内联时,它甚至可能无法存活。但是你显然关闭了优化,出于一些奇怪的原因——又一次是编译器和操作系统的混淆。

然而,Windows 确实关心你没有提到的其他概念。主要是ASLR和DEP——地址空间布局随机化和数据执行预防。它们是防止黑客入侵的技术,而你正在入侵。所以这并不奇怪。

我不确定"RWX"是否指"读、写、执行",因为你应该知道这是在找麻烦。DEP的灵感来自于更恰当的W^X,写XOR执行。

然而,更有可能的罪魁祸首是ASLR。Windows在设计上试图在不可预测的地址加载DLL,因为这消除了整个黑客类。看起来你假设了一个加载地址,而Windows实际上使用的是另一个地址。

最后一个错误可能是您没有理解重定位的位置。为了提高可共享页面的数量,重定位是在导入地址表上完成的,而不是代码本身。IAT是一个蹦床桌,因此是可执行的。你的失败也可能是一个遗漏的IAT。