Search code examples
visual-studioassemblyinline-assemblyattdll-injection

AT&T to inline asm in Visual Studio 2017


I'm trying to convert assembly written in AT&T syntax from a DevC++ project to inline assembly in Visual Studio.

This is the AT&T I'm trying to convert:

void Painter::drawRectangle(int surface, int x, int y, int width, int height, int red, int green, int blue) {
    asm("mov %0, %%eax":: "r" (0x004EAA90));
    asm("call *%eax");
    asm("mov %eax, %ecx");
    asm("mov (%ecx), %eax");
    asm("push %0":: "m" (blue));
    asm("push %0":: "m" (green));
    asm("push %0":: "m" (red));
    asm("push %0":: "m" (height));
    asm("push %0":: "m" (width));
    asm("push %0":: "m" (y));
    asm("push %0":: "m" (x));
    asm("push %0":: "m" (surface));
    asm("call *0x14(%eax)");
}

what i've done so far:

void _drawrectangle(int surface, int x, int y, int width, int height, int red, int green, int blue)
{    
    __asm
    {
        mov eax, 0x004eaa90
        call dword ptr [eax]
        mov ecx, eax
        mov eax, [ecx]
        push blue
        push green
        push red
        push height
        push width
        push y
        push x
        push surface
        call dword ptr [eax + 0x14]
    }
}

I'm writing this in my DLL, which I've already injected into the game. The game crashes on opening. And I've already hooked another drawing function in C++, which worked.

Hopefully you can help me/guide me in the right direction. Thank you.


Solution

  • Here's how you could write your function in C++ without the use of inline assembly:

    #ifndef _MSC_VER
    /* For GCC and clang */
    #undef __thiscall
    #define __thiscall __attribute__((thiscall))
    #endif
    
    struct some_interface {
        virtual void _unknown_0() = 0;
        virtual void _unknown_4() = 0;
        virtual void _unknown_8() = 0;
        virtual void _unknown_C() = 0;
        virtual void _unknown_10() = 0;
        virtual void __thiscall drawRectangle(int surface, int x, int y,
                              int width, int height,
                              int red, int green, int blue) = 0;
    };
    
    const auto get_interface = (some_interface *(*)()) 0x4EAA90;
    
    void 
    drawRectangle(int surface, int x, int y, int width, int height,
              int red, int green, int blue) {
        get_interface()->drawRectangle(surface, x, y, width, height,
                           red, green, blue);
    }
    

    The code you're trying to translate first calls a function that returns a pointer to some class object with at least 6 virtual methods defined. It then calls the 6th virtual method of that object. The some_interface struct minimally recreates that class so the 6th virtual method can be called. The get_interface constant is a function pointer that points to the function located at 0x4EAA90 and in C++ function pointers can be used just like a function.

    The above code generates the following assembly in GCC 8.2:

    drawRectangle(int, int, int, int, int, int, int, int):
            subl    $12, %esp
            movl    $5155472, %eax
            call    *%eax
            movl    (%eax), %edx
            movl    %eax, %ecx
            pushl   44(%esp)
            pushl   44(%esp)
            pushl   44(%esp)
            pushl   44(%esp)
            pushl   44(%esp)
            pushl   44(%esp)
            pushl   44(%esp)
            pushl   44(%esp)
            call    *20(%edx)
            addl    $12, %esp
            ret
    

    and the following assembly with Visual C++ 2017:

    void drawRectangle(int,int,int,int,int,int,int,int) PROC                  ; drawRectangle, COMDAT
            mov     eax, 5155472                          ; 004eaa90H
            call    eax
            push    DWORD PTR _blue$[esp-4]
            mov     ecx, eax
            push    DWORD PTR _green$[esp]
            mov     edx, DWORD PTR [eax]
            push    DWORD PTR _red$[esp+4]
            push    DWORD PTR _height$[esp+8]
            push    DWORD PTR _width$[esp+12]
            push    DWORD PTR _y$[esp+16]
            push    DWORD PTR _x$[esp+20]
            push    DWORD PTR _surface$[esp+24]
            call    DWORD PTR [edx+20]
            ret     0
    void drawRectangle(int,int,int,int,int,int,int,int) ENDP                  ; drawRectangle