Hilfe mit Assembler Code, this-Pointer!



  • Hallo Community,
    Ich spiele gerade einwenig mit OllyDBG rum und betrachte mein Code maal im Debugger und versuche ihn sozusagen zu reversen, wie auch immer ihr es nennen wollt. Es geht mir im folgendem speziel um Aufrufe von MEthoden einer Klasse.

    Hier mal mein CodeSample:

    class CTest {
    
        private:
    
            int m_iValue;
    
        public:
    
            CTest( ) {
    
                m_iValue = 0;
            }
    
            CTest( int iVal ) : m_iValue( iVal ) {
    
            }
    
            ~CTest( ) { }
    
            void ShowValue( ) {
    
                printf( "Value = %d\n", this->m_iValue );
            }
    };
    
    int main( ) {
    
        CTest test( 5 );
    
        test.ShowValue( );
    
        getchar( );
        return 0;
    }
    

    Klein und fein. So un so sieht das ganze im Debugger aus:

    main( )

    CPU Disasm
    Address   Hex dump          Command                                  Comments
    004113D0  /> \55            PUSH EBP                                 ; EP = main( )
    004113D1  |.  8BEC          MOV EBP,ESP
    004113D3  |.  6A FF         PUSH -1
    004113D5  |.  68 38464100   PUSH test.00414638
    004113DA  |.  64:A1 0000000 MOV EAX,DWORD PTR FS:[0]
    004113E0  |.  50            PUSH EAX
    004113E1  |.  81EC D8000000 SUB ESP,0D8
    004113E7  |.  53            PUSH EBX
    004113E8  |.  56            PUSH ESI
    004113E9  |.  57            PUSH EDI
    004113EA  |.  8DBD 1CFFFFFF LEA EDI,[LOCAL.57]
    004113F0  |.  B9 36000000   MOV ECX,36
    004113F5  |.  B8 CCCCCCCC   MOV EAX,CCCCCCCC
    004113FA  |.  F3:AB         REP STOS DWORD PTR ES:[EDI]
    004113FC  |.  A1 08804100   MOV EAX,DWORD PTR DS:[418008]
    00411401  |.  33C5          XOR EAX,EBP
    00411403  |.  50            PUSH EAX
    00411404  |.  8D45 F4       LEA EAX,[LOCAL.3]
    00411407  |.  64:A3 0000000 MOV DWORD PTR FS:[0],EAX
    0041140D  |.  6A 05         PUSH 5                                   ; 1. Parameter = 5
    0041140F  |.  8D4D EC       LEA ECX,[LOCAL.5]                        ; Laedt Pointer zur Klasse
    00411412  |.  E8 9DFCFFFF   CALL 004110B4                            ; Konstruktor = CTest( int iVal )
    00411417  |.  C745 FC 00000 MOV DWORD PTR SS:[LOCAL.1],0
    0041141E  |.  8D4D EC       LEA ECX,[LOCAL.5]                        ; Laedt Pointer zur Klasse
    00411421  |.  E8 83FDFFFF   CALL 004111A9                            ; CTest::ShowValue()
    00411426  |.  8BF4          MOV ESI,ESP
    00411428  |.  FF15 CC924100 CALL DWORD PTR DS:[<&MSVCR90D.getchar>]
    0041142E  |.  3BF4          CMP ESI,ESP
    00411430  |.  E8 10FDFFFF   CALL 00411145
    00411435  |.  C785 20FFFFFF MOV DWORD PTR SS:[LOCAL.56],0
    0041143F  |.  C745 FC FFFFF MOV DWORD PTR SS:[LOCAL.1],-1
    00411446  |.  8D4D EC       LEA ECX,[LOCAL.5]                        ; Laedt Pointer zur Klasse
    00411449  |.  E8 BCFBFFFF   CALL 0041100A                            ; Destruktor = ~CTest()
    0041144E  |.  8B85 20FFFFFF MOV EAX,DWORD PTR SS:[LOCAL.56]
    00411454  |.  52            PUSH EDX
    00411455  |.  8BCD          MOV ECX,EBP
    00411457  |.  50            PUSH EAX
    00411458  |.  8D15 84144100 LEA EDX,[411484]
    0041145E  |.  E8 29FCFFFF   CALL 0041108C
    00411463  |.  58            POP EAX
    00411464  |.  5A            POP EDX
    00411465  |.  8B4D F4       MOV ECX,DWORD PTR SS:[LOCAL.3]
    00411468  |.  64:890D 00000 MOV DWORD PTR FS:[0],ECX
    0041146F  |.  59            POP ECX
    00411470  |.  5F            POP EDI
    00411471  |.  5E            POP ESI
    00411472  |.  5B            POP EBX
    00411473  |.  81C4 E4000000 ADD ESP,0E4
    00411479  |.  3BEC          CMP EBP,ESP
    0041147B  |.  E8 C5FCFFFF   CALL 00411145
    00411480  |.  8BE5          MOV ESP,EBP
    00411482  |.  5D            POP EBP
    00411483  \.  C3            RET                                      ; EXIT = main()
    

    ::ShowValue( )

    CPU Disasm
    Address   Hex dump          Command                                  Comments
    00411560  /> \55            PUSH EBP                                 ; EP = CTest::ShowValue( )
    00411561  |.  8BEC          MOV EBP,ESP
    00411563  |.  81EC CC000000 SUB ESP,0CC
    00411569  |.  53            PUSH EBX
    0041156A  |.  56            PUSH ESI
    0041156B  |.  57            PUSH EDI
    0041156C  |.  51            PUSH ECX
    0041156D  |.  8DBD 34FFFFFF LEA EDI,[LOCAL.51]
    00411573  |.  B9 33000000   MOV ECX,33
    00411578  |.  B8 CCCCCCCC   MOV EAX,CCCCCCCC
    0041157D  |.  F3:AB         REP STOS DWORD PTR ES:[EDI]
    0041157F  |.  59            POP ECX
    00411580  |.  894D F8       MOV DWORD PTR SS:[LOCAL.2],ECX
    00411583  |.  8BF4          MOV ESI,ESP
    00411585  |.  8B45 F8       MOV EAX,DWORD PTR SS:[LOCAL.2]
    00411588  |.  8B08          MOV ECX,DWORD PTR DS:[EAX]
    0041158A  |.  51            PUSH ECX
    0041158B  |.  68 00684100   PUSH OFFSET test.00416800                ; ASCII "Value = %d\n"
    00411590  |.  FF15 C0924100 CALL DWORD PTR DS:[<&MSVCR90D.printf>]
    00411596  |.  83C4 08       ADD ESP,8
    00411599  |.  3BF4          CMP ESI,ESP
    0041159B  |.  E8 A5FBFFFF   CALL 00411145
    004115A0  |.  5F            POP EDI
    004115A1  |.  5E            POP ESI
    004115A2  |.  5B            POP EBX
    004115A3  |.  81C4 CC000000 ADD ESP,0CC
    004115A9  |.  3BEC          CMP EBP,ESP
    004115AB  |.  E8 95FBFFFF   CALL 00411145
    004115B0  |.  8BE5          MOV ESP,EBP
    004115B2  |.  5D            POP EBP
    004115B3  \.  C3            RET                                      ; EXIT = CTest::ShowValue()
    

    Okay nachdem was ich so gelesen hab wird bei einem __thiscall die Referenz zur Klasse ins ECX Register geladen, die Parameter werden in vertauschter Reihenfolge auf den Stack gepackt. Soweit ist alles klar, nur moechte ich jetzt wissen wie ich an die Referenz rankomme? Im Code oben wird die Referenz so geladen "LEA ECX,[LOCAL.5]" (LOCAL.5 = EBP - 14), doch wo find ich wiesen Pointer, ich miene der muss doch irgendwo im Code als statiche Adresse angegeben sein, doch ich finds einfach nicht.

    Ich hoffe ihr koennt mir da weiter helfen und noch etwas mehr Licht in den fuer so dunklen Code bringen.

    Gruß Tobi



  • T0bi schrieb:

    Im Code oben wird die Referenz so geladen "LEA ECX,[LOCAL.5]" (LOCAL.5 = EBP - 14), doch wo find ich wiesen Pointer, ich miene der muss doch irgendwo im Code als statiche Adresse angegeben sein, doch ich finds einfach nicht.

    Noe, wozu auch? Das Objekt wird hier auf dem Stack erzeugt, und der ist idR. von Natur aus dynamisch. Ansonsten weiss ich leider nicht, was du meinst.



  • Ich will halt den Pointer zum Objekt, ich moechte eine DLL in mein Prossez laden und dann von dort auf mit einer Function die Methoden dieses Objektes ausfuehren. Dazu brauch ich aber halt die Referenz zum Objekt, wie bekomm ich die also?



  • Ich sehe da so leider immer noch keine konkrete Frage, die etwas mit Assembler zu tun haette.

    Wie gesagt: Da der Compiler diese lokale Objekt-Instanz dynamisch auf dem Stack haelt, gibt es keine statische Adresse, auf die du immer zugreifen kannst. Wie du prinzipiell in Assembler an diese auf dem Stack abgelegte Referenz deines Objekts rankommst, kannst du ja im Code sehen. Wie es halt so mit lokalen Variablen ist, verschwinden diese, wenn du die Funktion verlaesst, in der sie definiert wurden, bzw. du kannst von anderen Funktionen aus nicht ohne irgendeinen Pointer oder Referenz auf sie zugreifen. Dh. du koenntest zB. von Funktionen (in einer DLL zB.) auf deine Klassen-Instanz zugreifen, wenn du diesen Funktionen die Referenz auf die Klasse als Parameter uebergibst. Wie das in Assembler prinzipiell laeuft, solltest du dir eigentlich leicht zusammenreimen koennen.

    Falls du allgemein wissen moechtest, wie du nun einen Pointer/Referenz auf dein Objekt bekommst, frage bitte im C++-Forum, bzw. bei allgemeinen Fragen zu DLLs evtl. bei WinAPI.
    Ich halte es aus naheliegenden Gruenden BTW fuer keine gute Idee, mit Assembler derart in den Innereien von abstrakten Hochsprachen rum zu basteln, bzw. es wird dir in diesem Fall kein Stueck weiter helfen, falls du das dachtest.



  • Ich versuch mal noch mal mein Vorhaben zu beschreiben, also:
    - ich habe hier mein Programm A, das eine Klasse CTest beinhaltet
    - ich will eine eigne DLL in Prozess A injizieren, aus der ich eine Methode von Klasse CTest aufrufen will
    - da ich ja nun nicht einfach einen Funktionspointer auf die Memberfunktion definieren kann Bsp:

    typedef void (*fpShowValue)( void );
    fpShowValue fpShow = (fpShowValue)(0x12345678)
    
    fpShow( ); // ~> crash
    

    Dies wuerde ja zum crash fuehren, mit der Fehlermeldung, dass irgendwo in der Funktion auf leeren Speicher bzw. falschen Speicher zugegriffen wurde. Also dachte ich ich stricke das ganze so um:

    typedef void (__thiscall *fpShowValue)( void );
    fpShowValue fpShow = (fpShowValue)(0x12345678)
    
    void CallShow( ) {
    
        _asm {
    
            mov eax, ClassPointer
            lea ecx, eax
            push ecx
        }
        fpShow( );
    }
    

    ECX soll ja nach __thiscall immer die Referenz zur Klasse enthalten. Bin mir jetzt nicht sicher, ob die definition von CallShow( ) so ganz richtig ist, aber ich denke nun verstehst du wieso ich den Pointer brauch?

    Gruß Tobi



  • Ich glaub ich habs, hier mien Code:

    class CTest {
    
        private:
    
            int m_iValue;
    
        public:
    
            CTest( ) {
    
                m_iValue = 0;
            }
    
            CTest( int iVal ) : m_iValue( iVal ) {
    
            }
    
            ~CTest( ) { }
    
            void SetValue( int iNewValue ) {
    
                this->m_iValue = iNewValue;
            }
    
            void ShowValue( ) {
    
                printf( "Value = %d\n", this->m_iValue );
            }
    };
    
    #define CTest_class      0x00418148
    #define func_ShowValue   0x004116A0
    #define func_SetNewValue 0x00411650
    
    CTest *test = 0;
    
    int main( ) {
    
        typedef void (__thiscall *fpShowValue)( void );
        fpShowValue fpShow   = (fpShowValue)func_ShowValue; 
    
        typedef void (__thiscall *fpSetNewValue)( int );
        fpSetNewValue fpNew  = (fpSetNewValue)func_SetNewValue;
    
        test = new CTest( 0 );
        test->ShowValue( );
    
        printf( "Normal call ...\n" );
        test->SetValue( 1 );  
        test->ShowValue( );
    
        printf( "Not normal call ...\n" );
    
        _asm {
    
            push 2
            mov ecx, dword ptr ds:[CTest_class]
            call fpNew
        }
    
        _asm mov ecx, dword ptr ds:[CTest_class]  
        fpShow( );
    
        delete test;
    
        getchar( );
        return 0;
    }
    

    Wenn irgendwo Fehler sein sollten oder etwas nicht richtig geloest ist, bitte sagts mir.

    Gruß Tobi



  • ein tipp habich noch für dich. die direkte addresse zu verwenden ist arge
    gefährlich. als erstes wäre es besser das ganze mit dem offset zur programminstanz
    im speicher zu machen.

    (unter windows)
    mit z.b. GetModuleHandle(0) bekommst du ein HMODULE zu deinem programm. der wert
    des handles ist die startaddresse deiner .exe im speicher. meist ist das
    unter msvc 0x00400000, was deine addressen ja gut belegen. dann musst du nurnoch
    den offset zur laufzeit draufaddieren. am besten wäre es noch, wenn du den ESP
    wert in der funktion/an der stelle abfragst wo du die funktion aufrufst.
    die differenz sollte sich nicht unterscheiden, wenn du keine weiteren funktionen
    aufrufst. also ein paarmal testen und dann einbauen. 🙂



  • Hm, irgendwie will mir nach wie vor nicht ganz in den Kopf, dass es keine praktischere Art gibt, das zu bewerkstelligen. Naja, egal: 1. ist das hier nicht Thema und 2. kenne ich mich eh nicht mit c++ aus (d'oh!).



  • @ Helferlein: mit Offset meinst du dann sicher:
    #define CTest_class 0x00418148 (Offset_Class = 18148h)

    Und nach deinem Schema sollte ich das ganze so aendern:

    HMODULE hInst = GetModuleHandle( 0 );

    #define CTest_class ((DWORD)hInst + Offset_Class)

    So etwa?



  • jo genau so 🙂

    kannst auch mit dem ESP versuchen

    void __declspec(naked) func()
    {
        __asm
        {
            push ebp
            mov ebp, esp // funktionsprolog
    
            mov ecx, [ebp+8] // addresse von "i" auf main()
    
            pop ebp      // funktionsepilog
            ret
        }
    }
    int main()
    {
        int i;
        func();
    }
    

    du musst halt aufpassen wieviele lokalen variablen angelegt werden.
    durch __declspec(naked) kannst du verhindern, dass der kompilier selber
    code für den pro und epilog erzeugt. klappt aber nur unter msvc.

    meist sind da auchnoch andere aufrufe dazwischen, einfach mal testen



  • Du hast da glaub nen kleinen Fehler drin 😉

    [cpp]
    mov ecx, [ebp**-**8] // addresse von "i" auf main()
    [/cpp]

    So ist es doch richtig oder?
    Ich danke euch erst mal.

    Gruß Tobi



  • T0bi schrieb:

    #define CTest_class 0x00418148 (Offset_Class = 18148h)
    
    // Und nach deinem Schema sollte ich das ganze so aendern: 
    
    HMODULE hInst = GetModuleHandle( 0 ); 
    #define CTest_class ((DWORD)hInst + Offset_Class)
    

    Du kannst die Adresse einer Klasse nicht bestimmen. Die gibt es nicht.

    Du kannst nur
    -> die Adressen der Methoden (Funktionen) einer Klasse bestimmen,
    und
    -> die Adresse der Eigenschaften (Variablen, bzw. Instanz) einer Klasse bestimmen.
    -> Das wäre aber das, was z.B. "new (Klasse)" zurückgibt.



  • Lol? Offensichtlich weiß der Threadersteller nicht wie man das Problem richtig löst und statt ihn in ein geeignetes Unterforum zu verschieben werden hier ernsthaft falsche "Lösungen" gepostet??

    Du übergibst der DLL ganz einfach einen Zeiger auf das gewünschte Objekt bzw. der EXE von der DLL aus, je nachdem in welche Richtung du willst.
    Und das machst du ganz klassisch über extern "C" deklarierte Funktionen, z.B.:

    extern "C" __declspec(export) CTest* GetCTestObject() {
       return das_ctest_object;
    }
    

    Und laden kannst du diese Funktion wahlweise über LoadLibrary und GetProcAdress, oder durch Linken und unter Verwendung von __declspec(import).


Anmelden zum Antworten