Search code examples
assemblycallinline-assembly

Msvc Inline assembly inner function calls


first i'd like to say i researched this matter but couldn't find anything relevant related to it.

I am writting a c++ console program on msvc 2013 on release mode 32 bit. I am using inline assmebly in one of the file and it works great except when my inline assembly function_1 calls my function_2, when that happens there are intructions which get added to function_2 and therefore the stack gets damaged and the program crashs. If i stop using calls but merely "lea ebx,[eip+5]", push ebx, jmp xxxxxx instead it works fine.

So in my case and to be a little more concrete function 2 is defined like this :

    void test()
{

_asm{
f_01758630:  // ; <= Procedure Start

    PUSH EBP
            MOV EBP, ESP
            PUSH ESI
            PUSH EDI
            mov edi, [ebp + 0x0C]
            XOR ESI, ESI
            SHR EDI, 0x2
            TEST EDI, EDI
            JLE f_0175866B
            PUSH EBX
            mov ebx, [ebp + 0x08]

    f_01758645:

        MOV EDX, DWORD PTR DS : [EBX + ESI * 0x4]
            ROL EDX, 0x10
            MOV ECX, EDX
            MOV EAX, EDX
            SHR ECX, 0x8
            SHL EAX, 0x8
            XOR ECX, EAX
            SHL EDX, 0x8
            AND ECX, 0xFF00FF
            XOR ECX, EDX
            MOV DWORD PTR DS : [EBX + ESI * 0x4], ECX
            INC ESI
            CMP ESI, EDI
            JL TERA_01758645
            POP EBX

        f_0175866B :

        POP EDI
            POP ESI
            POP EBP
            RETN//; <= Procedure End
}
}

However when i debug the running program I can see the function is implemented like this :

push ebx push esi push edi push ebp mov ebp,esp push esi push edi

Ie msvc implemented 3 pushs, is that maybe related to _asm{} which is within the function any insight to how I can fix this ?


Solution

  • The first

    push    ebp
    mov     ebp, esp
    push    ebx
    push    esi
    push    edi
    

    is the automatically generated prologue of the function. At the end of the function is the epilogue:

    pop     ebx
    pop     edi
    pop     esi
    pop     ebx
    pop     ebp
    ret
    

    Your _asm block has its own prologue and epilogue, so the code is done twice. Worse, the ret inside the _asm block gets a wrong return address and the program will crash. You can avoid the function prologue/epilogue by declaring the function as naked:

    __declspec (naked) void test()
    {
        _asm
        {
            own prolog
            ...
            own epilog
            ret
        }
    }
    

    This is dangerous, since you can forget to preserve registers that are to be returned unchanged, "callee saved registers": EBX, EBP, EDI, ESI. In MSVC inline assembly, it's easy to use the function arguments and the local variables, so it isn't necessary to take the control over epilog and prolog.

    Look at this example (as close as possible to your code):

    #include <stdio.h>
    
    void function_2(unsigned* reg_ebx, unsigned reg_edi)
    {
        _asm
        {
                mov edi, reg_edi      // take the second argument
                xor esi, esi
                shr edi, 2
                test edi, edi
                jle f_0175866b
                mov ebx, reg_ebx     // take the first argument
    
            f_01758645:
    
                mov edx, dword ptr ds : [ebx + esi * 0x4]
                rol edx, 16
                mov ecx, edx
                mov eax, edx
                shr ecx, 8
                shl eax, 8
                xor ecx, eax
                shl edx, 8
                and ecx, 0xff00ff
                xor ecx, edx
                mov dword ptr ds : [ebx + esi * 0x4], ecx
                inc esi
                cmp esi, edi
                jl f_01758645
    
            f_0175866b :
        }
    }
    
    void function_1 ()
    {
        unsigned arr[8] = {1000,2000,3000,4000,5000,6000,7000,8000};
        int i;
        for (i=0; i < sizeof(arr)/sizeof(arr[0]); ++i) printf ("%08X ",arr[i]); puts ("");
    
        _asm
        {
            push LENGTH arr
            lea eax, arr
            push eax
            call function_2
        }
    
        for (i=0; i < sizeof(arr)/sizeof(arr[0]); ++i) printf ("%08X ",arr[i]); puts ("");
    }
    
    int main ( void )
    {
        function_1();
        return 0;
    }