64位代码洞返回不正确的入口点位置

时间:2016-10-11 20:24:20

标签: c++ c windows security code-injection

我一直试图在进程虚拟内存中运行64位DLL而不用'手动映射'它(即手动解析重定位/导入)。

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

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

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

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

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

以下是一些可能有用或无用的一般信息:

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

就直接与注射器有关的信息而言:

  • 主代码注入方法有效(据我所知),我已经通过使用MessageBoxA的穴居人调试证明了这一点
  • 项目使用禁用代码优化的多字节字符集。代码是使用VS 2013 Ultimate(两个为Release x64构建的项目)编译的。
  • 由于使用了不安全的功能(strcpy和朋友),因此SDL检查已关闭
  • 每次运行时,都会使用提升的权限(高达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的调试器中逐字运行上面的代码时的错误(假设您也应用了上述设置并具有类似的环境)如下:

“wuauclt.exe中0x000007FEEA5125D4处的未处理异常:0xC0000005:访问冲突执行位置0x000007FEEA5125D4。”

在Process Hacker中查看进程“wuauclt.exe”后,我可以清楚地看到模块最初是在(通过LoadLibrary加载时)分配在0x7fef67c0000。这在上下文菜单中显示 - &gt;在其他 - &gt;卸载模块下。

双击“wuauclt.exe”后,您可以浏览应用程序的虚拟内存,以确保一切正常。我可以确认当前会话,已经在0x7fef67c0000分配了一个RWX内存缓冲区,其中包含了卸载模块的确切大小,包含injectme.dll模块。当使用CFF资源管理器挖掘injectme.dll时,入口点RVA似乎是0x132C,它没有加起来,考虑到内存中的错误距离更远。另外,我可以验证另外两个包含代码注入存根和信息结构的RWX内存缓冲区。回顾信息结构可能不需要RWX。无论如何,我不能为我的生活找出错误。

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

1 个答案:

答案 0 :(得分:1)

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

Windows本身非常关注您在开发中使用的编程语言。您可以获得CLR代码(.Net)或本机代码。在这种情况下,它是x64。但Windows真的不关心strcpy或SDL检查。这是编译器要处理的,而不是操作系统。当代码完全内联时,机会strcpy甚至无法生存。但是你出于一些奇怪的原因显然已经关闭了优化 - 再次是编译器与操作系统的混淆。

但是,Windows 确实关心您未提及的其他概念。主要是那些将是ASLR和DEP - 地址空间布局随机化和数据执行预防。他们是阻止黑客入侵的技术,而且你是黑客。所以这并不奇怪。

我不确定“RWX”是否意味着“写入执行”,因为你应该知道这是在寻找问题.DEP的灵感来自更恰当的名字W ^ X,写XOR eXecute。

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

最后一个错误可能是您无法理解 重定位的位置。为了改善可共享页面的数量,重定位在导入地址表上完成,而不是代码本身。 IAT是一个trampoline表,因此是可执行的。你的失败也可能是失踪的IAT。