Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az alábbi nagyon egyszerű függvény az x64 hívási konvencióját szemlélteti.
int Simple(int i, int j)
{
return i*5 + j + 3;
}
Ez a következő kódra fordít:
01001080 lea eax,[rdx+rcx*4] ; eax = rdx+rcx*4
01001083 lea eax,[rcx+rax+0x3] ; eax = rcx+rax+3
01001087 ret
Az i és j paraméterek az ecx és az edx regiszterekben vannak átadva. Mivel csak két paraméter létezik, a rutin egyáltalán nem használja a vermet.
A létrehozott kód három trükköt használ, amelyek közül az egyik az x64-hez tartozik:
A lea művelet egyetlen műveletként több egyszerű aritmetikai művelet végrehajtására használható. Az első utasítás a j+i*4-et eaxban tárolja, a második utasítás pedig i+3-at ad hozzá az eredményhez, összesen j+i*5+3 értékben.
Számos művelet, például összeadás és szorzás is elvégezhető extra pontossággal, majd csonkolható a megfelelő pontossággal. Ebben a példában a kód 64 bites összeadást és szorzást használ. Biztonságosan csonkíthatjuk az eredményt 32 bitre.
Az x64-ben minden olyan művelet, amely 32 bites regiszterre ad ki, automatikusan nullára növeli az eredményt. Ebben az esetben a kimenet az eax-ra azt az eredményt 32 bitre truncálja.
A visszaadott értékek a rax regiszterben lesznek átadva. Ebben az esetben az eredmény már szerepel a rax regiszterben, így a függvény visszatér.
A következőkben egy bonyolultabb függvényt veszünk figyelembe, amely a tipikus x64-szétszerelést mutatja be:
HRESULT Meaningless(IDispatch *pdisp, DISPID dispid, BOOL fUnique, LPCWSTR pszExe)
{
IQueryAssociations *pqa;
HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (void**)&pqa);
if (SUCCEEDED(hr)) {
hr = pqa->Init(ASSOCF_INIT_BYEXENAME, pszExe, NULL, NULL);
if (SUCCEEDED(hr)) {
WCHAR wszName[MAX_PATH];
DWORD cchName = MAX_PATH;
hr = pqa->GetString(0, ASSOCSTR_FRIENDLYAPPNAME, NULL, wszName, &cchName);
if (SUCCEEDED(hr)) {
VARIANTARG rgvarg[2] = { 0 };
V_VT(&rgvarg[0]) = VT_BSTR;
V_BSTR(&rgvarg[0]) = SysAllocString(wszName);
if (V_BSTR(&rgvarg[0])) {
DISPPARAMS dp;
LONG lUnique = InterlockedIncrement(&lCounter);
V_VT(&rgvarg[1]) = VT_I4;
V_I4(&rgvarg[1]) = fUnique ? lUnique : 0;
dp.rgvarg = rgvarg;
dp.cArgs = 2;
dp.rgdispidNamedArgs = NULL;
dp.cNamedArgs = 0;
hr = pdisp->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dp, NULL, NULL, NULL);
VariantClear(&rgvarg[0]);
VariantClear(&rgvarg[1]);
} else {
hr = E_OUTOFMEMORY;
}
}
}
pqa->Release();
}
return hr;
}
Ezen a függvényen és a vele egyenértékű assembly nyelvi kódon sorról sorra végigmegyünk.
Amikor be van írva, a függvény paraméterei a következőképpen lesznek tárolva:
rcx = pdisp.
Rdx = dispid.
r8 = fUnique.
r9 = pszExe.
Ne feledje, hogy az első négy paramétert a rendszer a regiszterekben adja át. Mivel ez a függvény csak négy paramétert használ, egyik sem lesz átadva a veremnek.
Az összeszerelés a következő módon kezdődik:
Meaningless:
010010e0 push rbx ; save
010010e1 push rsi ; save
010010e2 push rdi ; save
010010e3 push r12d ; save
010010e5 push r13d ; save
010010e7 push r14d ; save
010010e9 push r15d ; save
010010eb sub rsp,0x2c0 ; reserve stack
010010f2 mov rbx,r9 ; rbx = pszExe
010010f5 mov r12d,r8d ; r12 = fUnique (zero-extend)
010010f8 mov r13d,edx ; r13 = dispid (zero-extend)
010010fb mov rsi,rcx ; rsi = pdisp
A függvény a nemvolatilis regiszterek mentésével, majd a helyi változók veremterületének megőrzésével kezdődik. Ezután nemvolatilis regiszterekben menti a paramétereket. Vegye figyelembe, hogy a középső két mov utasítás célja egy 32 bites regiszter, ezért implicit módon 64 bitre vannak kiterjesztve.
IQueryAssociations *pqa;
HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (void**)&pqa);
Az AssocCreate első paramétere egy érték szerint átadott 128 bites CLSID. Mivel ez nem fér el egy 64 bites regiszterben, a CLSID-t a verembe másolják, és ehelyett a verem helyére mutató mutatót adnak át.
010010fe movdqu xmm0,oword ptr [CLSID_QueryAssociations (01001060)]
01001106 movdqu oword ptr [rsp+0x60],xmm0 ; temp buffer for first parameter
0100110c lea r8,[rsp+0x58] ; arg3 = &pqa
01001111 lea rdx,[IID_IQueryAssociations (01001070)] ; arg2 = &IID_IQueryAssociations
01001118 lea rcx,[rsp+0x60] ; arg1 = &temporary
0100111d call qword ptr [_imp_AssocCreate (01001028)] ; call
Az movdqu utasítás 128 bites értékeket ad át xmmn regiszterekbe és onnan. Ebben a példában az assembly kód arra használja, hogy a CLSID-t a verembe másolja. A CLSID mutatója r8-ban lesz átadva. A másik két argumentumot rcx és rdx adja át.
if (SUCCEEDED(hr)) {
01001123 test eax,eax
01001125 jl ReturnEAX (01001281)
A kód ellenőrzi, hogy a visszatérési érték sikeres-e.
hr = pqa->Init(ASSOCF_INIT_BYEXENAME, pszExe, NULL, NULL);
0100112b mov rcx,[rsp+0x58] ; arg1 = pqa
01001130 mov rax,[rcx] ; rax = pqa.vtbl
01001133 xor r14d,r14d ; r14 = 0
01001136 mov [rsp+0x20],r14 ; arg5 = 0
0100113b xor r9d,r9d ; arg4 = 0
0100113e mov r8,rbx ; arg3 = pszExe
01001141 mov r15d,0x2 ; r15 = 2 (for later)
01001147 mov edx,r15d ; arg2 = 2 (ASSOCF_INIT_BY_EXENAME)
0100114a call qword ptr [rax+0x18] ; call Init method
Ez egy C++ virtuális táblát használó közvetett függvényhívás. Ez a mutató rcx formátumban lesz átadva első paraméterként. Az első három paramétert a rendszer a regiszterekben adja át, míg az utolsó paramétert a veremen adják át. A függvény 16 bájtot foglal le a regiszterekben átadott paraméterekhez, így az ötödik paraméter rsp+0x20 időpontban kezdődik.
if (SUCCEEDED(hr)) {
0100114d mov ebx,eax ; ebx = hr
0100114f test ebx,ebx ; FAILED?
01001151 jl ReleasePQA (01001274) ; jump if so
A szerelvénynyelvi kód ebx formátumban menti az eredményt, és ellenőrzi, hogy sikeres-e.
WCHAR wszName[MAX_PATH];
DWORD cchName = MAX_PATH;
hr = pqa->GetString(0, ASSOCSTR_FRIENDLYAPPNAME, NULL, wszName, &cchName);
if (SUCCEEDED(hr)) {
01001157 mov dword ptr [rsp+0x50],0x104 ; cchName = MAX_PATH
0100115f mov rcx,[rsp+0x58] ; arg1 = pqa
01001164 mov rax,[rcx] ; rax = pqa.vtbl
01001167 lea rdx,[rsp+0x50] ; rdx = &cchName
0100116c mov [rsp+0x28],rdx ; arg6 = cchName
01001171 lea rdx,[rsp+0xb0] ; rdx = &wszName[0]
01001179 mov [rsp+0x20],rdx ; arg5 = &wszName[0]
0100117e xor r9d,r9d ; arg4 = 0
01001181 mov r8d,0x4 ; arg3 = 4 (ASSOCSTR_FRIENDLYNAME)
01001187 xor edx,edx ; arg2 = 0
01001189 call qword ptr [rax+0x20] ; call GetString method
0100118c mov ebx,eax ; ebx = hr
0100118e test ebx,ebx ; FAILED?
01001190 jl ReleasePQA (01001274) ; jump if so
Ismét beállítjuk a paramétereket, és meghívunk egy függvényt, majd teszteljük a visszatérési értéket a sikeresség érdekében.
VARIANTARG rgvarg[2] = { 0 };
01001196 lea rdi,[rsp+0x82] ; rdi = &rgvarg
0100119e xor eax,eax ; rax = 0
010011a0 mov ecx,0x2e ; rcx = sizeof(rgvarg)
010011a5 rep stosb ; Zero it out
A puffer x64-en való nullázására szolgáló idiomatikus módszer megegyezik az x86-zal.
V_VT(&rgvarg[0]) = VT_BSTR;
V_BSTR(&rgvarg[0]) = SysAllocString(wszName);
if (V_BSTR(&rgvarg[0])) {
010011a7 mov word ptr [rsp+0x80],0x8 ; V_VT(&rgvarg[0]) = VT_BSTR
010011b1 lea rcx,[rsp+0xb0] ; arg1 = &wszName[0]
010011b9 call qword ptr [_imp_SysAllocString (01001010)] ; call
010011bf mov [rsp+0x88],rax ; V_BSTR(&rgvarg[0]) = result
010011c7 test rax,rax ; anything allocated?
010011ca je OutOfMemory (0100126f) ; jump if failed
DISPPARAMS dp;
LONG lUnique = InterlockedIncrement(&lCounter);
010011d0 lea rax,[lCounter (01002000)]
010011d7 mov ecx,0x1
010011dc lock xadd [rax],ecx ; interlocked exchange and add
010011e0 add ecx,0x1
Az InterlockedIncrement közvetlenül gépi kódra fordít. A lock xadd utasítás egy atomikus cserét és összeadást hajt végre. A végeredmény az ecxben van tárolva.
V_VT(&rgvarg[1]) = VT_I4;
V_I4(&rgvarg[1]) = fUnique ? lUnique : 0;
010011e3 mov word ptr [rsp+0x98],0x3 ; V_VT(&rgvarg[1]) = VT_I4;
010011ed mov eax,r14d ; rax = 0 (r14d is still zero)
010011f0 test r12d,r12d ; fUnique set?
010011f3 cmovne eax,ecx ; if so, then set rax=lCounter
010011f6 mov [rsp+0xa0],eax ; V_I4(&rgvarg[1]) = ...
Mivel az x64 támogatja a cmov utasítást, a ?: szerkezet fordítása ugrás nélkül is végezhető.
dp.rgvarg = rgvarg;
dp.cArgs = 2;
dp.rgdispidNamedArgs = NULL;
dp.cNamedArgs = 0;
010011fd lea rax,[rsp+0x80] ; rax = &rgvarg[0]
01001205 mov [rsp+0x60],rax ; dp.rgvarg = rgvarg
0100120a mov [rsp+0x70],r15d ; dp.cArgs = 2 (r15 is still 2)
0100120f mov [rsp+0x68],r14 ; dp.rgdispidNamedArgs = NULL
01001214 mov [rsp+0x74],r14d ; dp.cNamedArgs = 0
Ez a kód inicializálja a DISPPARAMS többi tagját. Vegye figyelembe, hogy a fordító újrafelhasználja a CLSID által korábban használt verem memóriaterületet.
hr = pdisp->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dp, NULL, NULL, NULL);
01001219 mov rax,[rsi] ; rax = pdisp.vtbl
0100121c mov [rsp+0x40],r14 ; arg9 = 0
01001221 mov [rsp+0x38],r14 ; arg8 = 0
01001226 mov [rsp+0x30],r14 ; arg7 = 0
0100122b lea rcx,[rsp+0x60] ; rcx = &dp
01001230 mov [rsp+0x28],rcx ; arg6 = &dp
01001235 mov word ptr [rsp+0x20],0x1 ; arg5 = 1 (DISPATCH_METHOD)
0100123c xor r9d,r9d ; arg4 = 0
0100123f lea r8,[GUID_NULL (01001080)] ; arg3 = &IID_NULL
01001246 mov edx,r13d ; arg2 = dispid
01001249 mov rcx,rsi ; arg1 = pdisp
0100124c call qword ptr [rax+0x30] ; call Invoke method
0100124f mov ebx,eax ; hr = result
A kód ezután beállítja a paramétereket, és meghívja az Invoke metódust.
VariantClear(&rgvarg[0]);
VariantClear(&rgvarg[1]);
01001251 lea rcx,[rsp+0x80] ; arg1 = &rgvarg[0]
01001259 call qword ptr [_imp_VariantClear (01001018)]
0100125f lea rcx,[rsp+0x98] ; arg1 = &rgvarg[1]
01001267 call qword ptr [_imp_VariantClear (01001018)]
0100126d jmp ReleasePQA (01001274)
A kód befejezi a feltételes ág aktuális ágát, és átugrik a másik ágon.
} else {
hr = E_OUTOFMEMORY;
}
}
OutOfMemory:
0100126f mov ebx,0x8007000e ; hr = E_OUTOFMEMORY
pqa->Release();
ReleasePQA:
01001274 mov rcx,[rsp+0x58] ; arg1 = pqa
01001279 mov rax,[rcx] ; rax = pqa.vtbl
0100127c call qword ptr [rax+0x10] ; release
A else ág.
return hr;
}
0100127f mov eax,ebx ; rax = hr (for return value)
ReturnEAX:
01001281 add rsp,0x2c0 ; clean up the stack
01001288 pop r15d ; restore
0100128a pop r14d ; restore
0100128c pop r13d ; restore
0100128e pop r12d ; restore
01001290 pop rdi ; restore
01001291 pop rsi ; restore
01001292 pop rbx ; restore
01001293 ret ; return (do not pop arguments)
A visszatérési érték raxban van tárolva, majd a visszaérkezés előtt a rendszer visszaállítja a nem illékony regisztereket.
Lásd még
x64-architektúra