Search code examples
assemblystackcorruptionstack-pointer

How do I stop ESP from being corrupted in a __fastcall?


I'm attempting to write a function in assembly that sets a block of memory to a specified value, much like memset(), however, when I go to get the third argument off the stack (it uses the fastcall calling convention), the register, ECX, gets some garbled value.

By putting the code into Visual Studio using inline assembly, I see that ESP is significantly changed when the function is called. The first two arguments are being put into ECX and EDX without any issue, it's just the third one that is causing trouble.

I know the code works the block of memory is filled with the correct value when I manually set the value in the register whilst debugging in VS.

I'm relatively new to assembly, so my code is probably a bit dodgy, but does anyone know how to fix this problem?

The code is below:



    #ifdef __GNUC__
    #define __fastcall  __attribute__((fastcall)) // 'Cause I'm a M$ fanboy
    #endif

    void __fastcall memset(void *pDest, int iValue, int iSize)
    {
        __asm
        {
            ; Assume the pointer to the memory is stored in ECX
            ; Assume the value is stored in EDX
            ; Assume the size of the block is stored on the stack

                mov eax, esi        ; Put ESI somewhere it won't be touched (I think)

                mov esi, ecx        ; Move the address of the memory into ESI
                xor ecx, ecx        ; Zero ECX

                pop ecx             ; Get the size of the block into ECX. ECX is our loop counter

            memset_count:
                cmp ecx, 0          ; If we are at the end of the block,
                jz memset_return    ; Jump to return

                mov [esi], edx      ; Move our value into the memory
                inc esi             ; Otherwise, increment out position in the memory
                dec ecx             ; Decrement out counter
                jmp memset_count    ; Start again

            memset_return:
                mov esi, eax        ; Restore ESI
                add esp, 4          ; Remove our third argument from the stack
                ret
        }
    }

    #define ret return

    int main(int argc, char **argv)
    {
        char szText[3];

        /*
        __asm
        {
            push 3
            mov edx, 65
            lea ecx, szText2
            call memset
        }
        */
        memset(&szText, 'A', 3);

        ret 42;
    }



Solution

  • The first thing on the called code's stack will be the return address for the call. The second thing will be the first argument.

    To avoid changing ESP, and to fix the "popping the wrong thing" problem, try something like "mov ecx,[esp+4]" (instead of the "pop ecx").