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

时间:2017-04-27 16:42:36

标签: winapi assembly visual-c++ x86 calling-convention

我从原始函数跳转到我的钩子,它运行执行函数的程序集。我正在尝试将原始函数中的参数传递给函数 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

第一跳:

enter image description here

汇编挂钩:

enter image description here

我做错了什么?

1 个答案:

答案 0 :(得分:4)

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

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

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

  

堆栈维护责任 - 调用函数从堆栈中弹出参数。

这与具有此参数清除规则的STDCALL不同:

  

堆栈维护责任 - 被调用函数从堆栈中弹出自己的参数。

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

    push[ebp + 0x24];
    call mWSARecv;

    popfd;

因为mWSARecv CDECL ,所以您推送的参数必须在通话后进行清理。如果不这样做,则意味着当popfd和后续堆栈操作发生时,它们都将从堆栈中的错误位置恢复。在这种情况下,要清理一个4字节参数的堆栈,您需要在调用后向 ESP 添加4。该修复程序如下所示:

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