Pembingkaran x86 yang diannotasi
Bagian berikut akan memandu Anda melalui contoh pembbongkaran.
Kode Sumber
Berikut ini adalah kode untuk fungsi yang akan dianalisis.
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;
}
Kode Rakitan
Bagian ini berisi contoh pembbongkaran yang diannotasi.
Fungsi yang menggunakan register ebp sebagai penunjuk bingkai dimulai sebagai berikut:
HRESULT CUserView::CloseView(void)
SAMPLE!CUserView__CloseView:
71517134 55 push ebp
71517135 8bec mov ebp,esp
Ini mengatur bingkai sehingga fungsi dapat mengakses parameternya sebagai offset positif dari ebp, dan variabel lokal sebagai offset negatif.
Ini adalah metode pada antarmuka COM privat, sehingga konvensi panggilan __stdcall. Ini berarti bahwa parameter didorong ke kanan ke kiri (dalam hal ini, tidak ada), penunjuk "ini" didorong, dan kemudian fungsi dipanggil. Dengan demikian, setelah masuk ke fungsi, tumpukan terlihat seperti ini:
[esp+0] = return address
[esp+4] = this
Setelah dua instruksi sebelumnya, parameter dapat diakses sebagai:
[ebp+0] = previous ebp pushed on stack
[ebp+4] = return address
[ebp+8] = this
Untuk fungsi yang menggunakan ebp sebagai penunjuk bingkai, parameter pertama yang didorong dapat diakses di [ebp+8]; Parameter berikutnya dapat diakses pada alamat DWORD yang lebih tinggi berturut-turut.
71517137 51 push ecx
71517138 51 push ecx
Fungsi ini hanya memerlukan dua variabel tumpukan lokal, sehingga sub esp, 8 instruksi. Nilai yang didorong kemudian tersedia sebagai [ebp-4] dan [ebp-8].
Untuk fungsi yang menggunakan ebp sebagai penunjuk bingkai, tumpukan variabel lokal dapat diakses pada offset negatif dari register ebp .
71517139 56 push esi
Sekarang pengkompilasi menyimpan register yang diperlukan untuk dipertahankan di seluruh panggilan fungsi. Sebenarnya, itu menyimpannya dalam bit dan potongan- potongan, diselingi dengan baris pertama kode aktual.
7151713a 8b7508 mov esi,[ebp+0x8] ; esi = this
7151713d 57 push edi ; save another registers
Kebetulan CloseView adalah metode pada ViewState, yang berada di offset 12 di objek yang mendasar. Akibatnya, ini adalah penunjuk ke kelas ViewState, meskipun ketika ada kemungkinan kebingungan dengan kelas dasar lain, itu akan lebih ditentukan dengan hati-hati sebagai (ViewState*)ini.
if (m_fDestroyed)
7151713e 33ff xor edi,edi ; edi = 0
XORing register dengan sendirinya adalah cara standar untuk nol keluar.
71517140 39beac000000 cmp [esi+0xac],edi ; this->m_fDestroyed == 0?
71517146 7407 jz NotDestroyed (7151714f) ; jump if equal
Instruksi cmp membandingkan dua nilai (dengan menguranginya). Instruksi jz memeriksa apakah hasilnya nol, menunjukkan bahwa dua nilai yang dibandingkan sama.
Instruksi cmp membandingkan dua nilai; instruksi j berikutnya melompat berdasarkan hasil perbandingan.
return S_OK;
71517148 33c0 xor eax,eax ; eax = 0 = S_OK
7151714a e972010000 jmp ReturnNoEBX (715172c1) ; return, do not pop EBX
Kompilator menunda penyimpanan register EBX sampai nanti dalam fungsi, jadi jika program akan "awal keluar" pada pengujian ini, maka jalur keluar harus menjadi salah satu yang tidak memulihkan EBX.
BOOL fViewObjectChanged = FALSE;
ReleaseAndNull(&m_pdtgt);
Eksekusi dua baris kode ini diselingi, jadi perhatikan.
NotDestroyed:
7151714f 8d86c0000000 lea eax,[esi+0xc0] ; eax = &m_pdtgt
Instruksi lea menghitung alamat efek akses memori dan menyimpannya di tujuan. Alamat memori aktual tidak didereferensikan.
Instruksi lea mengambil alamat variabel.
71517155 53 push ebx
Anda harus menyimpan register EBX sebelum rusak.
71517156 8b1d10195071 mov ebx,[_imp__ReleaseAndNull]
Karena Anda akan sering memanggil ReleaseAndNull , ada baiknya untuk menyimpan alamatnya di EBX.
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
Ingatlah bahwa Anda nol keluar pendaftaran EDI beberapa waktu lalu dan bahwa EDI adalah register yang dipertahankan di seluruh panggilan fungsi (sehingga panggilan ke ReleaseAndNull tidak mengubahnya). Oleh karena itu, ia masih memegang nilai nol dan Anda dapat menggunakannya untuk menguji nol dengan cepat.
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
Pola di atas adalah tanda telltale dari panggilan metode COM.
Panggilan metode COM cukup populer, jadi ada baiknya untuk belajar mengenalinya. Secara khusus, Anda harus dapat mengenali tiga metode IUnknown langsung dari offset Vtable mereka: QueryInterface=0, AddRef=4, dan 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
Panggilan tidak langsung melalui global adalah bagaimana impor fungsi diterapkan di Microsoft Win32. Loader memperbaiki global untuk menunjuk ke alamat aktual target. Ini adalah cara yang berguna untuk mendapatkan bantalan Anda ketika Anda menyelidiki mesin yang mengalami crash. Cari panggilan ke fungsi yang diimpor dan di target. Anda biasanya akan memiliki nama beberapa fungsi yang diimpor, yang dapat Anda gunakan untuk menentukan di mana Anda berada dalam kode sumber.
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
Nilai pengembalian fungsi ditempatkan dalam register EAX.
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
Perhatikan bagaimana Anda harus mengubah penunjuk "ini" Saat memanggil metode pada kelas dasar yang berbeda dari kelas Anda sendiri.
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
Variabel lokal pertama adalah 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
Perhatikan bahwa kompilator secara spekulatif menyiapkan alamat anggota m_pvo , karena Anda akan sering menggunakannya untuk sementara waktu. Dengan demikian, memiliki alamat yang berguna akan menghasilkan kode yang lebih kecil.
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)
Perhatikan bahwa kompilator menyimpulkan bahwa parameter "ini" yang masuk tidak diperlukan (karena sudah lama menyimpannya ke dalam register ESI). Dengan demikian, ia menggunakan kembali memori sebagai variabel lokal pSink.
Jika fungsi menggunakan bingkai EBP, parameter masuk tiba pada offset positif dari EBP dan variabel lokal ditempatkan pada offset negatif. Tetapi, seperti dalam hal ini, pengkompilasi bebas menggunakan kembali memori tersebut untuk tujuan apa pun.
Jika Anda memperhatikan dengan ketat, Anda akan melihat bahwa pengkompilasi bisa mengoptimalkan kode ini sedikit lebih baik. Ini bisa menunda instruksi lea edi, [esi+0xa8] sampai setelah dua instruksi 0x0 dorong , menggantinya dengan push edi. Ini akan menghemat 2 byte.
if (pSink == (IAdviseSink *)this)
Beberapa baris berikutnya ini adalah untuk mengimbangi fakta bahwa dalam C++, (IAdviseSink *)NULL masih harus NULL. Jadi jika "ini" Anda benar-benar "(ViewState*)NULL", maka hasil pemeran harus NULL dan bukan jarak antara IAdviseSink dan 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
Meskipun Pentium memiliki instruksi pemindahan kondisional, arsitektur i386 dasar tidak, sehingga kompilator menggunakan teknik khusus untuk mensimulasikan instruksi pemindahan kondisional tanpa melompat.
Pola umum untuk evaluasi bersyarah adalah sebagai berikut:
neg r
sbb r, r
and r, (val1 - val2)
add r, val2
Neg r mengatur bendera carry jika r bukan nol, karena neg meniadakan nilai dengan mengurangi dari nol. Dan, mengurangi dari nol akan menghasilkan pinjam (atur carry) jika Anda mengurangi nilai bukan nol. Ini juga merusak nilai dalam register r , tetapi itu dapat diterima karena Anda akan menimpanya pula.
Selanjutnya, instruksi sbb r, r mengurangi nilai dari dirinya sendiri, yang selalu menghasilkan nol. Namun, ini juga mengurangi bit carry (pinjam), sehingga hasil bersihnya adalah mengatur r ke nol atau -1, tergantung pada apakah carry jelas atau ditetapkan, masing-masing.
Oleh karena itu, sbb r, r menetapkan r ke nol jika nilai asli r adalah nol, atau ke -1 jika nilai aslinya bukan nol.
Instruksi ketiga melakukan masker. Karena register r adalah nol atau -1, "ini" berfungsi baik untuk meninggalkan r nol atau untuk mengubah r dari -1 ke (val1 - val1), dalam anding nilai apa pun dengan -1 meninggalkan nilai asli.
Oleh karena itu, hasil dari "dan r, (val1 - val1)" adalah mengatur r ke nol jika nilai asli r adalah nol, atau ke "(val1 - val2)" jika nilai asli r bukan nol.
Terakhir, Anda menambahkan val2 ke r, menghasilkan val2 atau (val1 - val2) + val2 = val1.
Dengan demikian, hasil akhir dari rangkaian instruksi ini adalah mengatur r ke val2 jika awalnya nol atau ke val1 jika bukan nol. Ini adalah rakitan yang setara dengan r = r ? val1 : val2.
Dalam contoh khusus ini, Anda dapat melihat bahwa val2 = 0 dan val1 = (IAdviseSink*)ini. (Perhatikan bahwa pengkompilasi memilih instruksi tambahkan eax akhir, 0 karena tidak berpengaruh.)
7151720e 394508 cmp [ebp+0x8],eax ; pSink == (IAdviseSink*)this?
71517211 750b jnz No_SetAdvise (7151721e) ; jump if not equal
Sebelumnya di bagian ini, Anda mengatur EDI ke alamat anggota m_pvo . Anda akan menggunakannya sekarang. Anda juga mencocokkan register ECX sebelumnya.
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:
Semua panggilan metode COM ini akan terlihat sangat akrab.
Evaluasi dari dua pernyataan berikutnya diselingi. Jangan lupa bahwa EBX berisi alamat ReleaseAndNull.
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
Berikut adalah lebih banyak panggilan metode COM.
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
MENGANDing lokasi memori dengan nol sama dengan mengaturnya ke nol, karena apa pun DAN nol adalah nol. Kompilator menggunakan formulir ini karena, meskipun lebih lambat, itu jauh lebih pendek daripada instruksi mov yang setara. (Kode ini dioptimalkan untuk ukuran, bukan kecepatan.)
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();
Untuk memanggil CancelPendingActions, Anda harus berpindah dari (ViewState*)ini ke (CUserView*)ini. Perhatikan juga bahwa CancelPendingActions menggunakan konvensi panggilan __thiscall alih-alih __stdcall. Menurut __thiscall, penunjuk "ini" diteruskan dalam register ECX alih-alih diteruskan pada tumpukan.
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;
Ingat bahwa EDI masih nol dan EBX masih &m_pszTitle, karena register tersebut dipertahankan oleh panggilan fungsi.
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]
Perhatikan bahwa Anda tidak memerlukan nilai "ini" lagi, sehingga pengkompilasi menggunakan instruksi tambahkan untuk memodifikasinya di tempat alih-alih menggunakan register lain untuk menyimpan alamat. Ini sebenarnya merupakan kemenangan performa karena pipa Pentium u/v, karena pipa v dapat melakukan aritmatika, tetapi tidak mengatasi komputasi.
return S_OK;
715172be 33c0 xor eax,eax ; eax = S_OK
Terakhir, Anda memulihkan register yang diperlukan untuk mempertahankan, membersihkan tumpukan, dan kembali ke pemanggil Anda, menghapus parameter masuk.
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