为什么通过内联汇编将参数传递给函数不一致?

Why passing arguments to function via inline assembly isn't consistent?

我有一个从原始函数到我的挂钩的跳转,它运行执行一个函数的程序集。我正在尝试将参数从原始函数传递给函数 mWSARecv.

这是我的做法:

void mWSARecv(LPWSABUF lpBuffers)
{
    std::cout << "WSARecv: " << lpBuffers->buf << " Len: " << lpBuffers->len << std::endl;
}

__declspec(naked) int hookWSARecv() // Original -> Here
{
    __asm
    {
        pushad;
        pushfd;

        push[ebp + 0x24];
        call mWSARecv;

        popfd;
        popad;

        jmp WSARecvTramp;
    }
}

然后我保存寄存器和标志。推送所需的参数 [ebp + 0x24] 并调用输出这些参数的函数。它工作一次,但下一次它会导致异常。

原函数调用约定为__stdcall.

第一跳:

装配钩子:

我做错了什么?

默认情况下(不覆盖调用约定)以下是 CDECL 调用约定:

void mWSARecv(LPWSABUF lpBuffers)
{
    std::cout << "WSARecv: " << lpBuffers->buf << " Len: " << lpBuffers->len << std::endl;
}

根据调用约定,MSDN 文档说:

Stack-maintenance responsibility - Calling function pops the arguments from the stack.

这与 STDCALL 不同,后者具有此参数清理规则:

Stack-maintenance responsibility - Called function pops its own arguments from the stack.

考虑到这一点,您的代码中的问题在 hookWSARecv 这些行中:

    push[ebp + 0x24];
    call mWSARecv;

    popfd;

因为mWSARecvCDECL所以你push的参数在调用之后要清理。不这样做意味着当 popfd 和后续堆栈操作发生时,它们将从堆栈上的错误位置恢复。在这种情况下,要清理推送的一个 4 字节参数的堆栈,您需要在调用后将 4 添加到 ESP。修复看起来像:

    push[ebp + 0x24];
    call mWSARecv;
    add esp, 4;
    popfd;