Programovanie v assembleri vo Windows x64 (x86-64): Rozdiel medzi revíziami

Smazaný obsah Přidaný obsah
Fabcde (diskusia | príspevky)
d Revízia 19808 používateľa JAnDbot (diskusia) bola vrátená
Značka: Vrátenie
Fabcde (diskusia | príspevky)
d formulácie
Riadok 94:
G:\>objdump -fhD HelloWorld.obj
</syntaxhighlight>Direktíva <code>global main</code> deklaruje návestie main ako globálne a linker ho môže použiť ako štartovaciu adresu programu.
 
 
Direktíva <code>extern GetStdHandle</code> deklaruje symbol GetStdHandle (WinAPI funkcia) ako externý, čiže nachádzajúci sa v niektorom z ďalších pripojených súborov, v tomto prípade v dynamicky linkovanej knižnici kernel32.dll. Napriek tej 32 v názve, vo Windows x64 je to 64-bitová knižnica, o čom sa môžeme presvedčiť ďalším užitočným programom 7-Zip:<syntaxhighlight>
G:\>"C:\Program Files\7-Zip\7z.exe" l c:\windows\system32\kernel32.dll
</syntaxhighlight>
</syntaxhighlight>Direktíva <code>section .bss use64</code> definuje oblasť neinicializovaných dát dostupných na čítanie aj zápis z ktorejkoľvek časti programu (neinicializovaná globálna premenná). V nej je vymedzený priestor 4 bajty pre uloženie počtu úspešne vypísaných bajtov textu pozdravu funkciou WriteFile. Program túto hodnotu ďalej nepoužíva a určite by bolo vhodnejšie uložiť ju do zásobníka ako lokálnu premennú (pozri [[Programovanie v assembleri vo Windows x64 (x86-64)#Hello, World! v.2|Hello, World! v.2]]).
 
Direktíva <code>section .text use64</code> uvádza nasledujúci segment ako programový.
 
Na adrese message je uložený text pozdravu. Keďže sa nachádza v sekcii kódu, obsah tejto pamäťovej oblasti program môže čítať, ale pokus o jej modifikáciu vedie ku okamžitému ukončeniu programu. (Dá sa o tom jednoducho presvedčiť preusnutím premennej lpNumberOfBytesWritten do sekcii kódu. Program skončí chybou ERRORLEVEL=-1073741819 "Access violation.")
Na adrese mesage je uložený text pozdravu. Pristupuje sa k nej síce ako k premennej, nachádza sa však v sekcii programu, preto je readonly.
 
Konštanta MESSAGE_LEN obsahuje počet bajtov (dĺžku textu), ktorý chceme vypísať. Konštanta, na rozdiel od premennej, nie je uložená na nejakej konkrétnej adrese, ale v čase kompilácie bude každý odkaz na ňu, napríklad <code>mov r8d, dword MESSAGE_LEN</code> nahradený jej skutočnou hodnotou <code>mov r8d, 15</code> (podobne ako <code>#define MESSAGE_LEN 15</code> v jazyku C).
Riadok 243:
 
=== Volacia konvencia Microsoft x64 ===
64-bitové verzie funkcií WindowsAPI, rovnako ako aj funkcie knižníc GCC, MS Visual Studio (do 2013), Delphi, používajú volaciu konvenciu Microsoft x64<ref name=":0" />.
 
Prvé štyri argumenty sú uložené v registroch (64-bitová architektúra x86-64 má oproti 32-bitovej architektúre x86 k dispozícii viac registrov). V prípade celočíselných hodnôt (vrátane ukazovateľov), v RCX, RDX, R8 a R9 (v tomto poradí), v prípade argumentov s pohyblivou desatinnou čiarkou v XMM0, XMM1, XMM2, XMM3. Prvý argument je teda uložený buď v registri RCX alebo v XMM0, druhý v RDX alebo v XMM1, tretí v R8 alebo v XMM2, štvrtý v R9 alebo v XMM3. Parametre menšie než 64 bitov ignorujú vyššie bity, netreba ich nulovať. Napríklad prvý parameter typu integer (aj v x86-64 je to 32 bitové celé číslo) stačí uložiť do ECX.
Riadok 304:
extern WriteFile
extern ExitProcess
 
 
section .bss use64 ; neinicializovana datova oblast
lpNumberOfBytesWritten: resd 1
 
 
Řádek 315 ⟶ 311:
 
main:
sub rsp, 28h38h ; rezervovanie miesta v zasobniku pre shadow space (32B), 5-ty argument funkcie WriteFile (8B), shadowlokalnu spacepremennu alpNumberOfBytesWritten (8B), zarovnanie (8B) ((instrukcia CALL vlozi do zasobnika este navratovu adresu, cize dalsich 8B))
 
; rax = GetStdHandle(-11)
Řádek 326 ⟶ 322:
; BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped)
; WriteConsole(handle, &msg[0], 13, &written, 0)
mov rcx, rax ; 1. param _In_ HANDLE hFile (8B)
mov rdx, qword message ; 2. param _In_ LPCVOID lpBuffer (8B)
mov r8d, dword MESSAGE_LEN ; 3. param _In_ DWORD nNumberOfBytesToWrite (4B)
mov r9, lpNumberOfBytesWrittenqword [rsp+28h] ; 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten (8B)
mov qword [rsp+20h], 0 ; 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped (8B)
call WriteFile
 
Řádek 338 ⟶ 334:
call ExitProcess
 
add rsp, 28h38h ; uvolnenie rezervovaneho miesta
 
</syntaxhighlight>Rozdiel v porovnaní s prvou verziou spočíva v rezervovaní miesta v zásobníku tak, aby sa sem vošlivošla lokálna premenná lpNumberOfBytesWritten (8 bajtov), argumenty (8 bajtov pre piaty argument funkcie WriteFile) a, shadow space (32 bajtov) a zarovnanie (8B). Vďaka inštrukcii CALL, ktorá ešte na vrchol zásobníka vloží obsah RIP (8 bajtov) bude zásobník zarovnaný na požadovaných 16 bajtov:<syntaxhighlight lang="nasm" line="1" start="2723">
sub rsp, 28h38h ; rezervovanie miesta v zasobniku pre shadow space (32B), 5-ty argument funkcie WriteFile (8B), shadowlokalnu spacepremennu alpNumberOfBytesWritten (8B), zarovnanie (8B) ((instrukcia CALL vlozi do zasobnika este navratovu adresu, cize dalsich 8B))
</syntaxhighlight>Piaty argument potom samozrejme nie je možné vložiť na požadovanú pozíciu inštrukciou PUSH, ale:<syntaxhighlight lang="nasm" line="1" start="4238">
mov qword [rsp+20h], 0 ; 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped (8B)
</syntaxhighlight>Zásobník je pripravený aj pre funkciu ExitProcess, stačí ju zavolať a až potom uvoľniť rezervované miesto:<syntaxhighlight lang="nasm" line="1" start="5046">
add rsp, 28h38h ; uvolnenie rezervovaneho miesta
</syntaxhighlight>Poslednou zmenou je nahradenie inštrukcie<syntaxhighlight lang="nasm" line="1" start="4643">
mov ecx, 0 ; 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
</syntaxhighlight>inštrukciou<syntaxhighlight lang="nasm" line="1" start="4743">
xor ecx, ecx ; 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
</syntaxhighlight>
Řádek 365 ⟶ 361:
 
.global main
 
.section .bss
lpNumberOfBytesWritten: .space 4
 
.section .text
Řádek 374 ⟶ 367:
 
main:
sub $0x280x38, %rsp /*# rezervovanie miesta v zasobniku pre shadow space (32B), 5-ty argument funkcie WriteFile (8B), shadowlokalnu spacepremennu alpNumberOfBytesWritten (8B), zarovnanie (8B) ((instrukcia CALL vlozi do zasobnika este navratovu adresu, cize dalsich 8B))
 
#/* rax = GetStdHandle(-11) */
#/* HANDLE hStdHandle = WINAPI GetStdHandle (_In_ DWORD nStdHandle) */
#/* nStdHandle: STD_INPUT_HANDLE=-10 , STD_OUTPUT_HANDLE=-11 , STD_ERROR_HANDLE=-12 */
mov $-11, %ecx /*# 1. param _In_ DWORD nStdHandle */
call GetStdHandle
 
#/* rax = WriteFile(%rax, $message, $MESSAGE_LEN, %rsp-4, 0) */
#/* BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped) */
#/* WriteConsole(handle, &msg[0], 13, &written, 0)*/
mov %rax, %rcx /*# 1. param _In_ HANDLE hFile */(8B)
mov $message, %rdx /*# 2. param _In_ LPCVOID lpBuffer */(8B)
mov $MESSAGE_LEN, %r8d /*# 3. param _In_ DWORD nNumberOfBytesToWrite */(4B)
mov $lpNumberOfBytesWritten0x28(%rsp), %r9 /* # 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten */(8B)
movq $0, 0x20(%rsp) /*# 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped */(8B)
call WriteFile
 
#/* ExitProcess(0) */
#/* VOID WINAPI ExitProcess( _In_ UINT uExitCode) */
xor %ecx, %ecx /*# 1. param _In_ UINT uExitCode, UINT je 32 bit aj v 64 bitovom prostredi */(4B)
call ExitProcess
 
add $0x280x38, %rsp /*# uvolnenie rezervovaneho miesta */
 
</syntaxhighlight>Za povšimnutie stojí zápis nepriamej adresácie (používanej napríklad pri indexovaní poľa). Kým v NASM to bolo: <code>mov qword [rsp + 20h], 0</code>, v GAS je "index" uvedený pred zátvorkou: <code>movq  $0, 0x20(%rsp)</code>
Řádek 414 ⟶ 407:
 
=== Hello, World! v.3 ===
Posledná verzia programu Hello, World vypíše text pomocou funkcie printf štandardnej knižnce jazyka C printf.
 
'''Výpis 3a''' HelloWorld.asm:<syntaxhighlight lang="nasm" line="1">
Řádek 447 ⟶ 440:
ret
 
</syntaxhighlight>VolanieAko už bolo spomenuté vyššie, volanie funkcie printf z libmsvcrt.a v 64-bitovom windows tiež používa volaciu konvenciu Microsoft x64<ref name=":0">https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions</ref>.
 
== Rozširujeme ==