X86-Disassemblierung mit Anmerkungen
Der folgende Abschnitt führt Sie durch ein Disassemblierungsbeispiel.
Quellcode
Im Folgenden finden 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 Beispiel für die Disassemblierung mit Anmerkungen.
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 ihre Parameter als positive Offsets von ebp und lokale Variablen als negative Offsets zugreifen kann.
Dies ist eine Methode auf einer privaten COM-Schnittstelle, sodass die Aufrufkonvention __stdcall ist. Dies bedeutet, dass Parameter von rechts nach links gepusht werden (in diesem Fall gibt es keine), der Zeiger "this" wird gepusht und dann 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 kann auf die Parameter wie folgt zugegriffen werden:
[ebp+0] = previous ebp pushed on stack
[ebp+4] = return address
[ebp+8] = this
Für eine Funktion, die ebp als Framezeiger verwendet, ist der erste pushte Parameter unter [ebp+8] zugänglich. Auf nachfolgende Parameter kann unter aufeinanderfolgenden höheren DWORD-Adressen zugegriffen werden.
71517137 51 push ecx
71517138 51 push ecx
Diese Funktion erfordert nur zwei lokale Stapelvariablen, also eine sub esp, 8-Anweisung. Die gepushten Werte sind dann als [ebp-4] und [ebp-8] verfügbar.
Für eine Funktion, die ebp als Framezeiger 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 überlappen.
7151713a 8b7508 mov esi,[ebp+0x8] ; esi = this
7151713d 57 push edi ; save another registers
Es kommt also vor, dass CloseView eine Methode in ViewState ist, die sich im zugrunde liegenden Objekt im Offset 12 befindet. Folglich ist dies ein Zeiger auf eine ViewState-Klasse, obwohl bei möglichen Verwechslungen mit einer anderen Basisklasse sorgfältiger als (ViewState*) angegeben wird.
if (m_fDestroyed)
7151713e 33ff xor edi,edi ; edi = 0
Das XORingen eines Registers mit sich selbst ist eine Standardmethode, um es auf null zu machen.
71517140 39beac000000 cmp [esi+0xac],edi ; this->m_fDestroyed == 0?
71517146 7407 jz NotDestroyed (7151714f) ; jump if equal
Die cmp-Anweisung vergleicht zwei Werte (durch Subtrahieren). Die jz-Anweisung überprüft, ob das Ergebnis null ist, was angibt, dass die beiden verglichenen Werte 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 hat das Speichern des EBX-Registers bis zu einem späteren Zeitpunkt in der Funktion verzögert. Wenn das Programm also in diesem Test "early-out" wird, muss der Exitpfad der sein, der EBX nicht wiederhergestellt.
BOOL fViewObjectChanged = FALSE;
ReleaseAndNull(&m_pdtgt);
Die Ausführung dieser beiden Codezeilen ist überlappend, also achten Sie darauf.
NotDestroyed:
7151714f 8d86c0000000 lea eax,[esi+0xc0] ; eax = &m_pdtgt
Die lea-Anweisung berechnet die Effektadresse eines Speicherzugriffs und speichert sie im Ziel. Die tatsächliche Speicheradresse wird nicht dereferenziert.
Die lea-Anweisung akzeptiert die Adresse einer Variablen.
71517155 53 push ebx
Sie sollten dieses EBX-Register speichern, bevor es beschädigt wird.
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
Denken Sie daran, dass Sie das EDI-Register vor einer Weile 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 es weiterhin den Wert 0 (null), und Sie können ihn verwenden, um schnell auf 0 (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 verräterisches Zeichen eines COM-Methodenaufrufs.
COM-Methodenaufrufe sind sehr beliebt, daher ist es eine gute Idee, 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 globale Elemente sind die Implementierung von Funktionsimporten in Microsoft Win32. Das Ladeprogramm korrigiert die globalen Werte so, 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 importierter Funktionen und im Ziel. Sie haben in der Regel den Namen einer importierten Funktion, die Sie verwenden können, um zu bestimmen, 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 ihren "this"-Zeiger ändern mussten, wenn Sie eine Methode für eine andere Basisklasse als Ihre eigene aufrufen.
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 die Adresse des m_pvo-Members spekulativ vorbereitet hat, da Sie sie für eine Weile häufig verwenden werden. Wenn Die Adresse zur Hand ist, führt dies zu kleinerem 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 den Parameter längst in das ESI-Register eingefügt hat). Daher wurde der Speicher als lokale Variable pSink wiederverwendet.
Wenn die Funktion einen EBP-Frame verwendet, erhalten eingehende Parameter positive Offsets von EBP, und lokale Variablen werden an negativen Offsets platziert. Aber wie in diesem Fall kann der Compiler diesen Speicher für jeden Zweck wiederverwenden.
Wenn Sie genau darauf achten, werden Sie sehen, dass der Compiler diesen Code etwas besser optimiert hätte. Es hätte die Anweisung lea edi, [esi+0xa8] bis nach den beiden Push-0x0 Anweisungen verzögern können und sie durch push edi ersetzt. Dadurch wären 2 Bytes eingespart worden.
if (pSink == (IAdviseSink *)this)
Diese nächsten Zeilen sollen die Tatsache kompensieren, dass in C++ (IAdviseSink *)NULL immer noch NULL sein muss. Wenn Ihr "this" also wirklich "(ViewState*)NULL" ist, sollte das Ergebnis der Umwandlung NULL und nicht der Abstand zwischen IAdviseSink und IBrowserService sein.
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 eine Anweisung für bedingtes Verschieben verfügt, ist dies in der basisbasierten i386-Architektur nicht der Fall, sodass der Compiler bestimmte Techniken verwendet, um eine Anweisung zum bedingten Verschieben ohne Sprünge zu simulieren.
Das allgemeine Muster für eine bedingte Auswertung sieht wie folgt aus:
neg r
sbb r, r
and r, (val1 - val2)
add r, val2
Die Neg-r legt das Carry-Flag fest, wenn r ungleich null ist, da neg den Wert negiert, indem es von null subtrahiert wird. Durch Subtrahieren von 0 (null) wird ein Anleihewert generiert (legen Sie den Carry fest), wenn Sie einen Wert ungleich 0 (null) subtrahieren. Es beschädigt auch den Wert im r-Register , aber das ist akzeptabel, da Sie ihn sowieso überschreiben.
Als Nächstes subtrahiert die Anweisung sbb r, r einen Wert von sich selbst, was immer 0 (null) ergibt. Es subtrahiert jedoch auch das Carry-Bit (Leihen), sodass das Nettoergebnis darin besteht, r auf Null oder -1 festzulegen, je nachdem, ob der Carry klar oder festgelegt war.
Daher legt sbb r, r auf null fest, wenn der ursprüngliche Wert von r null war, oder auf -1, wenn der ursprüngliche Wert ungleich null war.
Die dritte Anweisung führt eine Maske aus. Da das r-Register null oder -1 ist, dient "this" entweder dazu, r null zu belassen oder r von -1 in (val1 - val1) zu ändern, sodass anding jeder Wert mit -1 den ursprünglichen Wert belässt.
Daher besteht das Ergebnis von "und r, (val1 - val1)" darin, r 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.
Daher besteht das endgültige Ergebnis dieser Reihe von Anweisungen darin, r auf val2 festzulegen, wenn es ursprünglich null war, oder val1 , wenn es ungleich null war. Dies ist das Assemblyäquivalent von r = r ? val1 : val2.
In diesem speziellen instance können Sie sehen, dass val2 = 0 und val1 = (IAdviseSink*)this. (Beachten Sie, dass der Compiler die endgültige Anweisung zum Hinzufügen von eax, 0 entfernt hat, da sie keine Auswirkungen 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-Members fest. Sie werden es jetzt verwenden. Sie haben auch das ECX-Register zuvor auf null gesetzt.
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:
Alle diese COM-Methodenaufrufe sollten sehr vertraut aussehen.
Die Auswertung der nächsten beiden Anweisungen ist überlappend. 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 aufgeführt.
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 ANDing eines Speicherorts mit 0 (null) ist identisch mit dem Festlegen auf 0(0), da ALLES AND null null ist. Der Compiler verwendet diese Form, da es, obwohl es langsamer ist, viel kürzer als die entsprechende mov-Anweisung ist. (Dieser Code wurde für größe und nicht für 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*) wechseln. Beachten Sie auch, dass CancelPendingActions anstelle von __stdcall die __thiscall Aufrufkonvention verwendet. Laut __thiscall wird der Zeiger "this" im ECX-Register übergeben, anstatt im Stapel übergeben 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 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, sodass der Compiler die Add-Anweisung verwendet, um ihn vor Ort zu ändern, anstatt ein anderes Register für die Adresse zu verwenden. Dies ist tatsächlich ein Leistungsgewinn aufgrund des Pentium u/v-Pipelinings, da die v-Pipe arithmetische, aber keine Berechnungen adressieren 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, sauber den Stapel nach oben, und kehren zu Ihrem Aufrufer zurück, indem 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