Search code examples
winapiassemblyvisual-c++x86calling-convention

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


I have a jump from original function to my hook, which runs assembly that executes a function. I'm trying to pass arguments from the original function to the function mWSARecv.

Here's how I do it:

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;
    }
}

I then save registers and flags. Push the desired argument [ebp + 0x24] and call the function which outputs those. It works once, but the next time it causes an execption.

The original function calling convention is __stdcall.

First jump:

enter image description here

Assembly hook:

enter image description here

What am I doing wrong?


Solution

  • By default (without overriding the calling convention) the following is CDECL calling convention:

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

    Per the calling convention, the MSDN documentation says:

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

    This is different from STDCALL which has this rule for argument cleanup:

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

    With this in mind the issue in your code is in hookWSARecv in these lines:

        push[ebp + 0x24];
        call mWSARecv;
    
        popfd;
    

    Because mWSARecv is CDECL the parameters you push have to be cleaned up after the call. Failure to do this means that when popfd and subsequent stack operations occur they will all be restored from the wrong place on the stack. In this case to cleanup the stack of the one 4-byte parameter pushed you need to add 4 to ESP after the call. The fix would look like:

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