c - 如何在 64 位进程(Windows)上正确保存和恢复线程上下文?

标签 c assembly 64-bit code-injection threadcontext

我有下面的代码,用于从某个 64 位进程中释放库。它完成了它的工作,但问题是恢复保存的上下文后,目标进程就会崩溃。不知道这里有什么问题。它应该将所有寄存器和标志设置为以前的样子,对吧?我做错了什么?

#ifdef _WIN64

const static unsigned char FreeLibrary_InjectionCodeRAW_x64[] =
{
    0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //mov rax, value
    0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //mov rcx, value
    0xFF, 0xD0, //call rax (FreeLibrary)
    0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //mov rax, value
    0xC7, 0x00, 0x01, 0x00, 0x00, 0x00, //mov [rax],1
    0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //mov rax, value
    0xB9, 0x64, 0x00, 0x00, 0x00, //mov ecx, 0x64
    0xFF, 0xD0, //call Sleep 
    0xEB, 0xED, //jmp
    0x00, 0x00, 0x00, 0x00 //status
};

#pragma pack(push, 1)
struct FreeLibrary_InjectionCode_x64
{
    FreeLibrary_InjectionCode_x64()
    {
        memcpy(this, FreeLibrary_InjectionCodeRAW_x64, sizeof(FreeLibrary_InjectionCodeRAW_x64));
    }

    char code_1[2];
    FARPROC lpFreeLibrary;
    char code_2[2];
    HMODULE hLib;
    char code_3[4];
    LPVOID lpStatusAddress;
    char code_4[8];
    FARPROC lpSleep;
    char code_5[9];
    int status;
};
#pragma pack(pop)

#endif

void FreeLib(const char what[], const char where[])
{
    HANDLE hToken;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
    SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
    CloseHandle(hToken);
    OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken);
    SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
    CloseHandle(hToken);

    HMODULE hMod;
    DWORD dwProcessId = GetProcessIdByName(where);
    if ((hMod = GetModuleHandleInProcess(what, dwProcessId)) != NULL)
    {
        HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | SYNCHRONIZE, FALSE, dwProcessId);
        if (hProcess != NULL)
        {
            HMODULE hKernel = LoadLibrary("kernel32.dll");
            FARPROC FLaddr = GetProcAddress(hKernel, "FreeLibrary");
            FARPROC Saddr = GetProcAddress(hKernel, "Sleep");

            HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION | THREAD_SUSPEND_RESUME,
                FALSE, GetValidThreadIdInProcess(dwProcessId));

            if (hThread != NULL && FLaddr != NULL && Saddr != NULL)
            {
                LPVOID addr = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                LPVOID lpStatusAddress = (PUCHAR)addr + (sizeof(FreeLibrary_InjectionCode_x64)-sizeof(int));
                FreeLibrary_InjectionCode_x64 code = FreeLibrary_InjectionCode_x64();
                code.hLib = hMod;
                code.lpFreeLibrary = FLaddr;
                code.lpSleep = Saddr;
                code.lpStatusAddress = lpStatusAddress;
                WriteProcessMemory(hProcess, addr, &code, sizeof(FreeLibrary_InjectionCode_x64), NULL);

                CONTEXT ctx, oldCtx;
                ctx.ContextFlags = CONTEXT_ALL;

                SuspendThread(hThread);
                GetThreadContext(hThread, &ctx);

                memcpy(&oldCtx, &ctx, sizeof(CONTEXT));
                ctx.Rip = (DWORD64)addr; 

                SetThreadContext(hThread, &ctx);
                ResumeThread(hThread);

                while (!code.status)
                {
                    Sleep(15);
                    ReadProcessMemory(hProcess, addr, &code, sizeof(FreeLibrary_InjectionCode_x64), NULL);
                }

                SuspendThread(hThread);
                SetThreadContext(hThread, &oldCtx);
                ResumeThread(hThread);

                VirtualFreeEx(hProcess, addr, 4096, MEM_DECOMMIT);

                CloseHandle(hThread);
            }

            CloseHandle(hProcess);
        }
    }
}

最佳答案

Windows 64 位使用 fastcall 调用约定。在此约定中,函数的调用者负责在堆栈上保留 4 * 64 位(32 字节),供被调用函数保存寄存器。 这意味着您的调用应如下所示:

sub rsp, 32
call rax
add rsp, 32

在您的代码中,对 FreeLibrary 或 Sleep 的调用会覆盖不属于其堆栈帧的堆栈,从而导致稍后崩溃。

关于c - 如何在 64 位进程(Windows)上正确保存和恢复线程上下文?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20389132/

相关文章:

c - 试图理解 C 中的函数指针

assembly - 我们何时以及为何签署扩展并使用带有 mul/div 的 cdq?

c++ - 如何为 _beginthreadex 提供 64 位线程标识符

c - 意外的 C 结构指针大小行为

C头文件依赖

c++ - 在每个操作系统上哪里可以找到受信任的根证书来满足 mbedtls?

objective-c - 以编程方式获取 macOS 上的硬盘驱动器信息

assembly - 模拟 Intel TSX 指令

assembly - x86 程序集中的 `or al, al`

c# - 使用 C# 的 64 位解决方案 ASP.NET 和一个使用 C++ 的 32 位项目