Teilen über


Kommentierte x86-Demontage

Im folgenden Abschnitt werden Sie durch ein Demontagebeispiel geführt.

Quellcode

Im Folgenden sehen Sie den Code für die Funktion, die analysiert wird.

HRESULT CUserView::CloseView(void)
{
    if (m_fDestroyed) return S_OK;

    BOOL fViewObjectChanged = FALSE;
    ReleaseAndNull(&m_pdtgt);

    if (m_psv) {
        m_psb->EnableModelessSB(FALSE);
        if(m_pws) m_pws->ViewReleased();

        IShellView* psv;

        HWND hwndCapture = GetCapture();
        if (hwndCapture && hwndCapture == m_hwnd) {
            SendMessage(m_hwnd, WM_CANCELMODE, 0, 0);
        }

        m_fHandsOff = TRUE;
        m_fRecursing = TRUE;
        NotifyClients(m_psv, NOTIFY_CLOSING);
        m_fRecursing = FALSE;

        m_psv->UIActivate(SVUIA_DEACTIVATE);

        psv = m_psv;
        m_psv = NULL;

        ReleaseAndNull(&_pctView);

        if (m_pvo) {
            IAdviseSink *pSink;
            if (SUCCEEDED(m_pvo->GetAdvise(NULL, NULL, &pSink)) && pSink) {
                if (pSink == (IAdviseSink *)this)
                    m_pvo->SetAdvise(0, 0, NULL);
                pSink->Release();
            }

            fViewObjectChanged = TRUE;
            ReleaseAndNull(&m_pvo);
        }

        if (psv) {
            psv->SaveViewState();
            psv->DestroyViewWindow();
            psv->Release();
        }

        m_hwndView = NULL;
        m_fHandsOff = FALSE;

        if (m_pcache) {
            GlobalFree(m_pcache);
            m_pcache = NULL;
        }

        m_psb->EnableModelessSB(TRUE);

        CancelPendingActions();
    }

    ReleaseAndNull(&_psf);

    if (fViewObjectChanged)
        NotifyViewClients(DVASPECT_CONTENT, -1);

    if (m_pszTitle) {
        LocalFree(m_pszTitle);
        m_pszTitle = NULL;
    }

    SetRect(&m_rcBounds, 0, 0, 0, 0);
    return S_OK;
}

Assemblycode

Dieser Abschnitt enthält das kommentierte Disassembly-Beispiel.

Funktionen, die das Ebp-Register als Framezeiger verwenden, beginnen wie folgt:

HRESULT CUserView::CloseView(void)
SAMPLE!CUserView__CloseView:
71517134 55               push    ebp
71517135 8bec             mov     ebp,esp

Dadurch wird der Frame so eingerichtet, dass die Funktion auf seine Parameter als positive Offsets von ebp und lokale Variablen als negative Offsets zugreifen kann.

Dies ist eine Methode für eine private COM-Schnittstelle, sodass die aufrufende Konvention __stdcall ist. Dies bedeutet, dass Parameter von rechts nach links verschoben werden (in diesem Fall gibt es keine), der Zeiger "dieses" wird verschoben, und dann wird die Funktion aufgerufen. Daher sieht der Stapel beim Einstieg in die Funktion wie folgt aus:

[esp+0] = return address
[esp+4] = this

Nach den beiden vorherigen Anweisungen können Sie auf die Parameter zugreifen:

[ebp+0] = previous ebp pushed on stack
[ebp+4] = return address
[ebp+8] = this

Für eine Funktion, die ebp als Framepointer verwendet, kann der erste pushed-Parameter unter [ebp+8] aufgerufen werden; Nachfolgende Parameter sind bei aufeinander folgenden höheren DWORD-Adressen zugänglich.

71517137 51               push    ecx
71517138 51               push    ecx

Diese Funktion erfordert nur zwei lokale Stapelvariablen, also eine Sub esp, 8 Anweisung. Die Pushwerte sind dann als [ebp-4] und [ebp-8] verfügbar.

Für eine Funktion, die ebp als Framepointer verwendet, sind lokale Stapelvariablen bei negativen Offsets aus dem Ebp-Register zugänglich.

71517139 56               push    esi

Jetzt speichert der Compiler die Register, die über Funktionsaufrufe hinweg beibehalten werden müssen. Tatsächlich speichert es sie in Bits und Teilen, die mit der ersten Zeile des tatsächlichen Codes interleaviert sind.

7151713a 8b7508           mov     esi,[ebp+0x8]     ; esi = this
7151713d 57               push    edi               ; save another registers

Es geschieht also, dass CloseView eine Methode für ViewState ist, die sich im Offset 12 im zugrunde liegenden Objekt befindet. Daher ist dies ein Zeiger auf eine ViewState-Klasse, jedoch wenn es mögliche Verwirrungen mit einer anderen Basisklasse gibt, wird dies genauer als (ViewState*) dies angegeben.

    if (m_fDestroyed)
7151713e 33ff             xor     edi,edi           ; edi = 0

Das Verknüpfen eines Registers mittels XOR mit sich selbst ist eine Standardmethode, um es auf null zu setzen.

71517140 39beac000000     cmp     [esi+0xac],edi    ; this->m_fDestroyed == 0?
71517146 7407             jz      NotDestroyed (7151714f)  ; jump if equal

Die cmp-Anweisung vergleicht zwei Werte (indem sie subtrahiert werden). Die jz-Anweisung überprüft, ob das Ergebnis null ist und angibt, dass die beiden Vergleichswerte gleich sind.

Die cmp-Anweisung vergleicht zwei Werte; eine nachfolgende j-Anweisung springt basierend auf dem Ergebnis des Vergleichs.

    return S_OK;
71517148 33c0             xor     eax,eax           ; eax = 0 = S_OK
7151714a e972010000       jmp     ReturnNoEBX (715172c1) ; return, do not pop EBX

Der Compiler verzögerte das Speichern des EBX-Registers bis zu einem späteren Zeitpunkt in der Funktion. Wenn das Programm also bei diesem Test "vorzeitig beendet" wird, muss der Exit-Pfad derjenige sein, der EBX nicht wiederherstellt.

    BOOL fViewObjectChanged = FALSE;
    ReleaseAndNull(&m_pdtgt);

Die Ausführung dieser beiden Codezeilen erfolgt abwechselnd, also seien Sie aufmerksam.

NotDestroyed:
7151714f 8d86c0000000     lea     eax,[esi+0xc0]    ; eax = &m_pdtgt

Dielea-Anweisung berechnet die Effektadresse eines Speicherzugriffs und speichert sie im Ziel. Die tatsächliche Speicheradresse wird nicht dereferenziert.

Die lea-Anweisung verwendet die Adresse einer Variablen.

71517155 53               push    ebx

Sie sollten dieses EBX-Register speichern, bevor es beschädigt ist.

71517156 8b1d10195071     mov ebx,[_imp__ReleaseAndNull]

Da Sie ReleaseAndNull häufig aufrufen, empfiehlt es sich, die Adresse in EBX zwischenzuspeichern.

7151715c 50               push    eax               ; parameter to ReleaseAndNull
7151715d 897dfc           mov     [ebp-0x4],edi     ; fViewObjectChanged = FALSE
71517160 ffd3             call    ebx               ; call ReleaseAndNull
    if (m_psv) {
71517162 397e74           cmp     [esi+0x74],edi    ; this->m_psv == 0?
71517165 0f8411010000     je      No_Psv (7151727c) ; jump if zero

Vergessen Sie nicht, dass Sie das EDI-Register vor einiger Zeit auf null gesetzt haben und dass EDI ein Register ist, das über Funktionsaufrufe hinweg beibehalten wird (sodass der Aufruf von ReleaseAndNull es nicht geändert hat). Daher enthält sie weiterhin den Wert Null, und Sie können ihn verwenden, um schnell auf Null zu testen.

        m_psb->EnableModelessSB(FALSE);
7151716b 8b4638           mov     eax,[esi+0x38]    ; eax = this->m_psb
7151716e 57               push    edi               ; FALSE
7151716f 50               push    eax               ; "this" for callee
71517170 8b08             mov     ecx,[eax]         ; ecx = m_psb->lpVtbl
71517172 ff5124           call    [ecx+0x24]        ; __stdcall EnableModelessSB

Das obige Muster ist ein telltales Zeichen eines COM-Methodenaufrufs.

COM-Methodenaufrufe sind ziemlich beliebt, daher empfiehlt es sich, sie zu erkennen. Insbesondere sollten Sie die drei IUnknown-Methoden direkt aus ihren Vtable-Offsets erkennen können: QueryInterface=0, AddRef=4 und Release=8.

        if(m_pws) m_pws->ViewReleased();
71517175 8b8614010000     mov     eax,[esi+0x114]   ; eax = this->m_pws
7151717b 3bc7             cmp     eax,edi           ; eax == 0?
7151717d 7406             jz      NoWS (71517185) ; if so, then jump
7151717f 8b08             mov     ecx,[eax]         ; ecx = m_pws->lpVtbl
71517181 50               push    eax               ; "this" for callee
71517182 ff510c           call    [ecx+0xc]         ; __stdcall ViewReleased
NoWS:
        HWND hwndCapture = GetCapture();
71517185 ff15e01a5071    call [_imp__GetCapture]    ; call GetCapture

Indirekte Aufrufe über globalen Variablen werden implementiert durch den Import von Funktionen in Microsoft Win32. Das Ladeprogramm passt die globalen Variablen so an, dass sie auf die tatsächliche Adresse des Ziels verweisen. Dies ist eine praktische Möglichkeit, sich zu orientieren, wenn Sie eine abgestürzte Maschine untersuchen. Suchen Sie nach den Aufrufen von importierten Funktionen und im Ziel. Normalerweise haben Sie den Namen einiger importierter Funktionen, mit denen Sie bestimmen können, wo sie sich im Quellcode befinden.

        if (hwndCapture && hwndCapture == m_hwnd) {
            SendMessage(m_hwnd, WM_CANCELMODE, 0, 0);
        }
7151718b 3bc7             cmp     eax,edi           ; hwndCapture == 0?
7151718d 7412             jz      No_Capture (715171a1) ; jump if zero

Der Rückgabewert der Funktion wird im EAX-Register platziert.

7151718f 8b4e44           mov     ecx,[esi+0x44]    ; ecx = this->m_hwnd
71517192 3bc1             cmp     eax,ecx           ; hwndCapture = ecx?
71517194 750b             jnz     No_Capture (715171a1) ; jump if not

71517196 57               push    edi               ; 0
71517197 57               push    edi               ; 0
71517198 6a1f             push    0x1f              ; WM_CANCELMODE
7151719a 51               push    ecx               ; hwndCapture
7151719b ff1518195071     call    [_imp__SendMessageW] ; SendMessage
No_Capture:
        m_fHandsOff = TRUE;
        m_fRecursing = TRUE;
715171a1 66818e0c0100000180 or    word ptr [esi+0x10c],0x8001 ; set both flags at once

        NotifyClients(m_psv, NOTIFY_CLOSING);
715171aa 8b4e20           mov     ecx,[esi+0x20]    ; ecx = (CNotifySource*)this.vtbl
715171ad 6a04             push    0x4               ; NOTIFY_CLOSING
715171af 8d4620           lea     eax,[esi+0x20]    ; eax = (CNotifySource*)this
715171b2 ff7674           push    [esi+0x74]        ; m_psv
715171b5 50               push    eax               ; "this" for callee
715171b6 ff510c           call    [ecx+0xc]         ; __stdcall NotifyClients

Beachten Sie, dass Sie beim Aufrufen einer Methode für eine andere Basisklasse ihren "this"-Zeiger ändern mussten.

        m_fRecursing = FALSE;
715171b9 80a60d0100007f   and     byte ptr [esi+0x10d],0x7f
        m_psv->UIActivate(SVUIA_DEACTIVATE);
715171c0 8b4674           mov     eax,[esi+0x74]    ; eax = m_psv
715171c3 57               push    edi               ; SVUIA_DEACTIVATE = 0
715171c4 50               push    eax               ; "this" for callee
715171c5 8b08             mov     ecx,[eax]         ; ecx = vtbl
715171c7 ff511c           call    [ecx+0x1c]        ; __stdcall UIActivate
        psv = m_psv;
        m_psv = NULL;
715171ca 8b4674           mov     eax,[esi+0x74]    ; eax = m_psv
715171cd 897e74           mov     [esi+0x74],edi    ; m_psv = NULL
715171d0 8945f8           mov     [ebp-0x8],eax     ; psv = eax

Die erste lokale Variable ist psv.

        ReleaseAndNull(&_pctView);
715171d3 8d466c           lea     eax,[esi+0x6c]    ; eax = &_pctView
715171d6 50               push    eax               ; parameter
715171d7 ffd3             call    ebx               ; call ReleaseAndNull
        if (m_pvo) {
715171d9 8b86a8000000     mov     eax,[esi+0xa8]    ; eax = m_pvo
715171df 8dbea8000000     lea     edi,[esi+0xa8]    ; edi = &m_pvo
715171e5 85c0             test    eax,eax           ; eax == 0?
715171e7 7448             jz      No_Pvo (71517231) ; jump if zero

Beachten Sie, dass der Compiler spekulativ die Adresse des m_pvo-Members vorbereitet hat, da Sie sie häufig für eine Weile verwenden werden. Daher führt das Vorhandensein der Adresse zu einem kleineren Code.

            if (SUCCEEDED(m_pvo->GetAdvise(NULL, NULL, &pSink)) && pSink) {
715171e9 8b08             mov     ecx,[eax]         ; ecx = m_pvo->lpVtbl
715171eb 8d5508           lea     edx,[ebp+0x8]     ; edx = &pSink
715171ee 52               push    edx               ; parameter
715171ef 6a00             push    0x0               ; NULL
715171f1 6a00             push    0x0               ; NULL
715171f3 50               push    eax               ; "this" for callee
715171f4 ff5120           call    [ecx+0x20]        ; __stdcall GetAdvise
715171f7 85c0             test    eax,eax           ; test bits of eax
715171f9 7c2c             jl      No_Advise (71517227) ; jump if less than zero
715171fb 33c9             xor     ecx,ecx           ; ecx = 0
715171fd 394d08           cmp     [ebp+0x8],ecx     ; _pSink == ecx?
71517200 7425             jz      No_Advise (71517227)

Beachten Sie, dass der Compiler zu dem Schluss kam, dass der eingehende Parameter "this" nicht erforderlich war, da er diesen bereits vor langer Zeit im ESI-Register abgelegt hat. Daher hat es den Speicher als lokale Variable pSink wiederverwendet.

Wenn die Funktion einen EBP-Frame verwendet, werden eingehende Parameter bei positiven Offsets von EBP und lokale Variablen bei negativen Offsets platziert. Wie in diesem Fall ist der Compiler jedoch frei, diesen Speicher für jeden Zweck wiederzuverwenden.

Wenn Sie genau darauf achten, sehen Sie, dass der Compiler diesen Code etwas besser optimiert haben könnte. Es könnte die lea edi, [esi+0xa8] Anweisung verzögert haben, bis nach den beiden push 0x0-Anweisungen, indem es sie durch push edi ersetzt. Dies hätte 2 Byte gespeichert.

                if (pSink == (IAdviseSink *)this)

Diese nächsten Zeilen sollen die Tatsache ausgleichen, dass in C++ (IAdviseSink *)NULL noch NULL sein muss. Wenn Ihr "this" also wirklich "(ViewState*)NULL" ist, sollte das Ergebnis der Umwandlung NULL sein und nicht der Abstand zwischen IAdviseSink und IBrowserService.

71517202 8d46ec           lea     eax,[esi-0x14]    ; eax = -(IAdviseSink*)this
71517205 8d5614           lea     edx,[esi+0x14]    ; edx = (IAdviseSink*)this
71517208 f7d8             neg     eax               ; eax = -eax (sets carry if != 0)
7151720a 1bc0             sbb     eax,eax           ; eax = eax - eax - carry
7151720c 23c2             and     eax,edx           ; eax = NULL or edx

Obwohl der Pentium über einen bedingten Befehl verfügt, hat die Basis-i386-Architektur diesen nicht, sodass der Compiler spezielle Techniken anwendet, um einen bedingten Befehl ohne Sprünge umzusetzen.

Das allgemeine Muster für eine bedingte Auswertung lautet wie folgt:

        neg     r
        sbb     r, r
        and     r, (val1 - val2)
        add     r, val2

Die Neg r legt das Schleppflagge fest, wenn r ungleich Null ist, da neg den Wert durch Subtrahieren von Null ablöst. Und das Subtrahieren von einem Null-Wert führt zu einem Unterlauf (setzt den Übertrag), wenn Sie einen Nicht-Null-Wert subtrahieren. Außerdem wird der Wert im r-Register beschädigt, das ist jedoch akzeptabel, da Sie es trotzdem überschreiben möchten.

Als Nächstes subtrahiert die sbb r, r Anweisung einen Wert von sich selbst, was immer zu Null führt. Es subtrahiert jedoch auch das Carry-Bit (Übertrag), sodass das Nettoergebnis darin besteht, r auf Null oder -1 zu setzen, je nachdem, ob das Übertrag-Bit gelöscht oder gesetzt war.

Daher wird sbb r, r auf r Null gesetzt, wenn der ursprüngliche Wert r null war, oder auf -1, wenn der ursprüngliche Wert von r nicht null war.

Die dritte Anweisung führt eine Maskierung aus. Da das r-Register null oder -1 ist, dient "this" entweder zum Verlassen von r null oder zum Ändern von r von -1 in (val1 - val1), da ANDing einen beliebigen Wert mit -1 den ursprünglichen Wert verlässt.

Daher ist das Ergebnis von "and r, (val1 - val1)" auf Null festzulegen, wenn der ursprüngliche Wert von r null war, oder auf "(val1 - val2)", wenn der ursprüngliche Wert von r ungleich Null war.

Schließlich fügen Sie val2 zu r hinzu, was zu Val2 oder (val1 - val2) + val2 = val1 führt.

Das endgültige Ergebnis dieser Abfolge von Anweisungen ist, r auf val2 zu setzen, wenn es ursprünglich null war, oder auf val1, wenn es ungleich null war. Dies ist das Assemblyäquivalent von r = r ? val1 : val2.

In diesem bestimmten Fall können Sie sehen, dass val2 = 0 und val1 = (IAdviseSink*) dies ist. (Beachten Sie, dass der Compiler die letzte add eax, 0-Anweisung entfernt hat, da sie keine Auswirkung hat.)

7151720e 394508           cmp     [ebp+0x8],eax ; pSink == (IAdviseSink*)this?
71517211 750b             jnz     No_SetAdvise (7151721e) ; jump if not equal

Weiter oben in diesem Abschnitt legen Sie EDI auf die Adresse des m_pvo Mitglieds fest. Sie werden es jetzt verwenden. Sie haben auch zuvor das ECX-Register nulliert.

                    m_pvo->SetAdvise(0, 0, NULL);
71517213 8b07             mov     eax,[edi]         ; eax = m_pvo
71517215 51               push    ecx               ; NULL
71517216 51               push    ecx               ; 0
71517217 51               push    ecx               ; 0
71517218 8b10             mov     edx,[eax]         ; edx = m_pvo->lpVtbl
7151721a 50               push    eax               ; "this" for callee
7151721b ff521c           call    [edx+0x1c]        ; __stdcall SetAdvise
No_SetAdvise:
                pSink->Release();
7151721e 8b4508           mov     eax,[ebp+0x8]     ; eax = pSink
71517221 50               push    eax               ; "this" for callee
71517222 8b08             mov     ecx,[eax]         ; ecx = pSink->lpVtbl
71517224 ff5108           call    [ecx+0x8]         ; __stdcall Release
No_Advise:

Diese COM-Methodenaufrufe sollten Ihnen vertraut sein.

Die Auswertung der beiden folgenden Anweisungen wird verflochten. Vergessen Sie nicht, dass EBX die Adresse von ReleaseAndNull enthält.

            fViewObjectChanged = TRUE;
            ReleaseAndNull(&m_pvo);
71517227 57               push    edi               ; &m_pvo
71517228 c745fc01000000   mov     dword ptr [ebp-0x4],0x1 ; fViewObjectChanged = TRUE
7151722f ffd3             call    ebx               ; call ReleaseAndNull
No_Pvo:
        if (psv) {
71517231 8b7df8           mov     edi,[ebp-0x8]     ; edi = psv
71517234 85ff             test    edi,edi           ; edi == 0?
71517236 7412             jz      No_Psv2 (7151724a) ; jump if zero
            psv->SaveViewState();
71517238 8b07             mov     eax,[edi]         ; eax = psv->lpVtbl
7151723a 57               push    edi               ; "this" for callee
7151723b ff5034           call    [eax+0x34]        ; __stdcall SaveViewState

Hier sind weitere COM-Methodenaufrufe.

            psv->DestroyViewWindow();
7151723e 8b07             mov     eax,[edi]         ; eax = psv->lpVtbl
71517240 57               push    edi               ; "this" for callee
71517241 ff5028           call    [eax+0x28]        ; __stdcall DestroyViewWindow
            psv->Release();
71517244 8b07             mov     eax,[edi]         ; eax = psv->lpVtbl
71517246 57               push    edi               ; "this" for callee
71517247 ff5008           call    [eax+0x8]         ; __stdcall Release
No_Psv2:
        m_hwndView = NULL;
7151724a 83667c00         and     dword ptr [esi+0x7c],0x0 ; m_hwndView = 0

Das AND-Verknüpfen eines Speicherorts mit Null ist dasselbe, wie es auf Null zu setzen, da alles UND Null gleich Null ist. Der Compiler verwendet dieses Formular, da er zwar langsamer ist, aber viel kürzer ist als die entsprechende mov-Anweisung . (Dieser Code wurde für die Größe und nicht für die Geschwindigkeit optimiert.)

        m_fHandsOff = FALSE;
7151724e 83a60c010000fe   and     dword ptr [esi+0x10c],0xfe
        if (m_pcache) {
71517255 8b4670           mov     eax,[esi+0x70]    ; eax = m_pcache
71517258 85c0             test    eax,eax           ; eax == 0?
7151725a 740b             jz      No_Cache (71517267) ; jump if zero
            GlobalFree(m_pcache);
7151725c 50               push    eax               ; m_pcache
7151725d ff15b4135071     call    [_imp__GlobalFree]    ; call GlobalFree
            m_pcache = NULL;
71517263 83667000         and     dword ptr [esi+0x70],0x0 ; m_pcache = 0
No_Cache:
        m_psb->EnableModelessSB(TRUE);
71517267 8b4638           mov     eax,[esi+0x38]    ; eax = this->m_psb
7151726a 6a01             push    0x1               ; TRUE
7151726c 50               push    eax               ; "this" for callee
7151726d 8b08             mov     ecx,[eax]         ; ecx = m_psb->lpVtbl
7151726f ff5124           call    [ecx+0x24]        ; __stdcall EnableModelessSB
        CancelPendingActions();

Um CancelPendingActions aufzurufen, müssen Sie von (ViewState*)this zu (CUserView*)this wechseln. Beachten Sie auch, dass CancelPendingActions anstelle von __stdcall die __thiscall Aufrufkonvention verwendet. Laut __thiscall wird der Zeiger "this" im ECX-Register übergeben, anstatt an den Stapel weitergegeben zu werden.

71517272 8d4eec           lea     ecx,[esi-0x14]    ; ecx = (CUserView*)this
71517275 e832fbffff       call CUserView::CancelPendingActions (71516dac) ; __thiscall
    ReleaseAndNull(&_psf);
7151727a 33ff             xor     edi,edi           ; edi = 0 (for later)
No_Psv:
7151727c 8d4678           lea     eax,[esi+0x78]    ; eax = &_psf
7151727f 50               push    eax               ; parameter
71517280 ffd3             call    ebx               ; call ReleaseAndNull
    if (fViewObjectChanged)
71517282 397dfc           cmp     [ebp-0x4],edi     ; fViewObjectChanged == 0?
71517285 740d             jz      NoNotifyViewClients (71517294) ; jump if zero
       NotifyViewClients(DVASPECT_CONTENT, -1);
71517287 8b46ec           mov     eax,[esi-0x14]    ; eax = ((CUserView*)this)->lpVtbl
7151728a 8d4eec           lea     ecx,[esi-0x14]    ; ecx = (CUserView*)this
7151728d 6aff             push    0xff              ; -1
7151728f 6a01             push    0x1               ; DVASPECT_CONTENT = 1
71517291 ff5024           call    [eax+0x24]        ; __thiscall NotifyViewClients
NoNotifyViewClients:
    if (m_pszTitle)
71517294 8b8680000000     mov     eax,[esi+0x80]    ; eax = m_pszTitle
7151729a 8d9e80000000     lea     ebx,[esi+0x80]    ; ebx = &m_pszTitle (for later)
715172a0 3bc7             cmp     eax,edi           ; eax == 0?
715172a2 7409             jz      No_Title (715172ad) ; jump if zero
        LocalFree(m_pszTitle);
715172a4 50               push    eax               ; m_pszTitle
715172a5 ff1538125071     call   [_imp__LocalFree]
        m_pszTitle = NULL;

Denken Sie daran, dass EDI immer noch null ist und EBX immer noch &m_pszTitle ist, da diese Register durch Funktionsaufrufe beibehalten werden.

715172ab 893b             mov     [ebx],edi         ; m_pszTitle = 0
No_Title:
    SetRect(&m_rcBounds, 0, 0, 0, 0);
715172ad 57               push    edi               ; 0
715172ae 57               push    edi               ; 0
715172af 57               push    edi               ; 0
715172b0 81c6fc000000     add     esi,0xfc          ; esi = &this->m_rcBounds
715172b6 57               push    edi               ; 0
715172b7 56               push    esi               ; &m_rcBounds
715172b8 ff15e41a5071     call   [_imp__SetRect]

Beachten Sie, dass Sie den Wert von "this" nicht mehr benötigen, daher verwendet der Compiler die Add-Anweisung , um sie anstelle eines anderen Registers zu ändern, um die Adresse zu speichern. Dies ist tatsächlich ein Leistungsgewinn aufgrund des Pentium u/v-Pipelining, da die v-Leitung arithmetische Operationen durchführen, aber keine Adressberechnungen ausführen kann.

    return S_OK;
715172be 33c0             xor     eax,eax           ; eax = S_OK

Schließlich stellen Sie die Register wieder her, die Sie beibehalten müssen, bereinigen den Stapel und kehren zu Ihrem Aufrufer zurück, wobei Sie die eingehenden Parameter entfernen.

715172c0 5b               pop     ebx               ; restore
ReturnNoEBX:
715172c1 5f               pop     edi               ; restore
715172c2 5e               pop     esi               ; restore
715172c3 c9               leave                     ; restores EBP and ESP simultaneously
715172c4 c20400           ret     0x4               ; return and clear parameters