Anmerkung
Der Zugriff auf diese Seite erfordert eine Genehmigung. Du kannst versuchen, dich anzumelden oder die Verzeichnisse zu wechseln.
Der Zugriff auf diese Seite erfordert eine Genehmigung. Du kannst versuchen , die Verzeichnisse zu wechseln.
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