x86 asm 在 __declspec(naked) 函数中访问和编辑 stdcall 函数参数

x86 asm access and edit stdcall function argument in a __declspec(naked) function

我使用以下代码为“NtCreateSection”函数制作了一个 WOW64 系统调用挂钩:

#include "Funcs.h"
#include <cstdio>
#include <Windows.h>

const int PAGE_SIZE = 0x1000;
const int SYSCALL_INTERCEPT = 0x4A;
const int NUM_WOW64_BYTES = 0x9;

using pNtCreateSection =
NTSTATUS (NTAPI*)(PHANDLE SectionHandle, ULONG DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
                  PLARGE_INTEGER MaximumSize, ULONG PageAttributess, ULONG SectionAttributes, HANDLE FileHandle);
pNtCreateSection NtCreateSection = nullptr;

DWORD_PTR dwWow64Address = 0;
LPVOID lpJmpRealloc = nullptr;
ULONG SectionAttributes;

void __declspec(naked) NtCreateSectionHook()
{
    __asm
    {
        pushad
    }

    fprintf(stderr, "NtCreateSectionHook called !\n");

    __asm
    {
        popad
        jmp lpJmpRealloc
    }
}

DWORD_PTR __declspec(naked) GetWow64Address()
{
    __asm
    {
        mov eax, dword ptr fs:[0xC0]
        ret
    }
}

void __declspec(naked) Wow64Trampoline()
{
    __asm
    {
        cmp eax, SYSCALL_INTERCEPT
        jz NtCreateSectionHook
        jmp lpJmpRealloc
    }
}

LPVOID CreateNewJump(const DWORD_PTR dwWow64Address)
{
    lpJmpRealloc = VirtualAlloc(nullptr, PAGE_SIZE, MEM_RESERVE | MEM_COMMIT,
        PAGE_EXECUTE_READWRITE);

    (void)memcpy(lpJmpRealloc, (const void *)dwWow64Address, NUM_WOW64_BYTES);

    return lpJmpRealloc;
}

void EnableWow64Redirect(const DWORD_PTR dwWow64Address, const LPVOID lpNewJumpLocation)
{
    unsigned char trampolineBytes[] =
    {
        0x68, 0xDD, 0xCC, 0xBB, 0xAA,       /*push 0xAABBCCDD*/
        0xC3,                               /*ret*/
        0xCC, 0xCC, 0xCC                    /*padding*/
    };

    memcpy(&trampolineBytes[1], &lpNewJumpLocation, sizeof(DWORD_PTR));
    WriteJump(dwWow64Address, trampolineBytes, sizeof trampolineBytes);
}

void WriteJump(const DWORD_PTR dwWow64Address, const void *pBuffer, size_t ulSize)
{
    DWORD dwOldProtect = 0;
    (void)VirtualProtect(reinterpret_cast<LPVOID>(dwWow64Address), PAGE_SIZE, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    (void)memcpy(reinterpret_cast<void *>(dwWow64Address), pBuffer, ulSize);
    (void)VirtualProtect(reinterpret_cast<LPVOID>(dwWow64Address), PAGE_SIZE, dwOldProtect, &dwOldProtect);
}

int main(int argc, char *argv[])
{
    const auto hModule = GetModuleHandle(L"ntdll.dll");
    NtCreateSection = reinterpret_cast<pNtCreateSection>(GetProcAddress(hModule, "NtCreateSection"));

    dwWow64Address = GetWow64Address();

    const auto lpNewJumpLocation = CreateNewJump(dwWow64Address);
    EnableWow64Redirect(dwWow64Address, static_cast<LPVOID>(Wow64Trampoline));

    //Test syscall
    HANDLE hSection;
    NtCreateSection(&hSection, SECTION_ALL_ACCESS, nullptr, nullptr, PAGE_EXECUTE_READWRITE, SEC_COMMIT | SEC_NOCHANGE, nullptr);

    getchar();

    return 0;
}

代码工作正常,直到我将钩子函数更改为此

void __declspec(naked) NtCreateSectionHook()
{
    __asm
    {
        pushad
        mov eax, [esp + 28]
        mov SectionAttributes, eax
    }

    fprintf(stderr, "NtCreateSectionHook called !\n");

    if ((SectionAttributes & SEC_NOCHANGE) != 0)
    {
        fprintf(stderr, "SEC_NOCHANGE found !\n");
    }

    __asm
    {
        popad
        jmp lpJmpRealloc
    }
}

我的代码中的问题是 pushad 指令与 esp 混淆,因此我无法再访问堆栈,如果我不使用 pushad/popap 应用程序崩溃,因为我弄乱堆栈然后跳转到真正的函数地址。 我想要访问和更改的参数是 NtCreateSection 的第 6 个参数 function.

pushad 不会阻止您访问堆栈。 pushad 将 32 个字节(8 个寄存器,每个寄存器 4 个字节)推入堆栈,因此,pushad 之后的任何偏移量都应通过添加 32.

进行更正