Přijďte se naučit základy, o které se můžete opřít. Pustíme se do crackmes od RubberDucka a probereme nějakou teorii okolo. Workshop volně vychází ze seriálu Nebojte se reverzního inženýrství. Je potřeba alespoň Windows 7, nejlíp 64bitový. Stačí, když poběží ve virtuálce. Stáhněte si zdrojáky a binárky. Budeme používat OllyDbg 2.01 a x64dbg.
2. Pojetí workshopu
• Solidní základy pro reverzování
• Windows: tradiční platforma pro reverzery
• OllyDbg: nejoblíbenější debugger
• x64dbg: částečná náhrada za nedokončený 64-bit
OllyDbg, postupy bývají stejné
• Slidy pro pozdější studium
3. Podklady
• Nebojte se reverzního inženýrství I.
• Nebojte se reverzního inženýrství II.
• Nebojte se reverzního inženýrství III.
• x86asm.net/links
• ref.x86asm.net/coder32.html
• ref.x86asm.net/coder64.html
• Poděkování Lukáši „RubberDuck“ Čižmarovi
• Security Cave
4. 1. část
• Crackme #1 by RubberDuck
• + opkódy instrukcí
• + x64
• + x64 verze crackme #1
12. IA-32 general-purpose registers
• Osm 32bitových všeobecných registrů, kód 0-7 (3 bity)
EAX ECX EDX EBX ESP EBP ESI EDI
0 1 2 3 4 5 6 7
13. General-purpose registers - příklady
• EAX = 44332211 – 32 bitů, velikost DWORD
• AX: 2211 – 16 bitů, velikost WORD
• AL: 11 – dolních 8 bitů, velikost BYTE
• AH: 22 – horních 8 bitů, velikost BYTE
• EBP = 40302010
• BP: 2010
• Horní WORD nemá název a nejde ho adresovat
• Pozn. 4 bitům (půlbajt) se říká nibble
15. Skupina opkódů 0x83
• Osm aritmetických a logických instrukcí
• Operandy: r/m16/32, imm8
• Viz taky opkódy 80, 81
16. 2. instrukce podrobně
• CMP: compare two operands
• CMP nemění žádný operand
• Operace CMP = operace SUB
• SUB: subtract two operands
• SUB EAX, 1: EAX = EAX - 1
• CMP nastavuje šest stavových příznaků (status flags):
CF, PF, AF, ZF, SF, OF
• Viz instrukční reference:
• Příznaky jsou uloženy v registru EFlags
17. Registr EFlags, operace CMP
• Šest stavových příznaků v bitech 0, 2, 4, 6, 7 a 11
• Hromada jiných, ty teď neřešíme
• CMP nastaví ZF (Zero Flag), pokud mají operandy
stejnou hodnotu
• Stejně jako SUB nastaví ZF, pokud je výsledek odečítání
nula
18. crackme1.exe – 3. instrukce JE
• 00401008 74 0C JE SHORT 00401016
• Jednobajtový primární opkód 0x74
• ref.x86asm.net/coder32.html#x74
• Mnemonic JE: Jump if Equal
• Mnemonic JZ: Jump if Zero
• SHORT znamená skok v rozmezí -128 a +127 bajtů
• Skupina opkódů 70-7F: 16 podmíněných skoků Jcc
• 8-bit relative offset 0x0C
• Relativní offset se přičítá k registru EIP
19. 3. instrukce JE a reg. EIP podrobně
• EIP: Instruction Pointer Register
• EIP obsahuje virtuální adresu následující instrukce, tzn. v
době spouštění instrukce JE je EIP 40100A
• Operace:
if (ZF == 1)
EIP = EIP + sign extended rel8
• Kalkulace návěští (label) skoku: adresa instrukce JE je
401008, je dlouhá 2 bajty:
401008 + 2 + 0C = 401016
20. crackme1.exe – 4. instrukce MOV
• 0040100A B8 14304000
MOV EAX, OFFSET 00403014
• Viz 1. instrukce MOV
• OFFSET: MASM příchuť, důsledek analýzy – OllyDbg
rozeznal, že jde o adresu, ne o nahodilou hodnotu
• Na adrese 403014 je řetězec "Please patch me! :("
21. crackme1.exe – 5. instrukce MOV
• 0040100F B9 30000000 MOV ECX, 30
• Viz 1. instrukce MOV
• Připomenutí: opkód B9 = B8+r, kód ECX je 1
22. crackme1.exe – 6. instrukce JMP
• 00401014 EB 0A JMP SHORT 00401020
• ref.x86asm.net/coder32.html#xEB: JMP rel8
• Mnemonic JMP: Unconditional Jump
• SHORT znamená skok v rozmezí -128 a +127 bajtů
• 8-bit relative offset 0x0A
• Relativní offset se přičítá k registru EIP
• Kalkulace návěští skoku: 401014 + 2 + 0A = 401020
23. crackme1.exe – 9. instrukce PUSH
• 00401020 51 PUSH ECX
• ref.x86asm.net/coder32.html#x50: 50+r PUSH r16/32
• Skupina opkódů 50-57
• Uloží hodnotu operandu na zásobník ve dvou krocích:
ESP = ESP – 4
*ESP = ECX
• Zásobník je zatím prostě nějaká paměť, víc to neřešíme
• OllyDbg: zásobník je vidět vpravo dole
24. Parametry WinAPI MessageBoxA()
• Prototyp funkce z MSDN:
int WINAPI MessageBox(
_In_opt_ HWND hWnd,
_In_opt_ LPCTSTR lpText,
_In_opt_ LPCTSTR lpCaption,
_In_ UINT uType
);
26. Volací konvence stdcall
• Konvence je vidět v prototypu:
• int WINAPI MessageBox();
• WinDef.h (Windows SDKs):
• #define WINAPI __stdcall
• MSDN stdcall calling convention
• Parametry jsou uložené zprava doleva
• Zásobník čistí volaná funkce
• Návratová hodnota je v registru EAX
• Registry EAX, ECX a EDX může volaná funkce změnit, ostatní
musí zachovat
• stdcall používá většina WinAPI funkcí
27. Další formy instrukce PUSH
• PUSH immediate value: buď 32bitová hodnota, nebo
znaménkově rozšířený imm8 na 32 bitů
68 28304000 PUSH OFFSET 403028
6A 00 PUSH 0
28. crackme1.exe – 13. instrukce CALL
• 00401029 E8 0E000000
CALL <jmp.&user32.MessageBoxA>
• Trochu jinak:
• CALL 0040103C
• Jednobajtový primární opkód E8 CALL rel16/32
• Operace:
PUSH EIP
JMP 0040103C
• Tento CALL volá MessageBoxA() nepřímo přes JMP
umístěný na konci kódu, je to záležitost importování
funkcí a assembleru MASM, toto teď neřešíme
29. Volání funkce: pár CALL - RET
CALL func
...
func:
...
RET ; taky „RETN“
• Operace CALL:
• ESP = ESP – 4
• *ESP = EIP
• JMP func
• Operace RET:
• EIP = *ESP
• ESP = ESP + 4
30. OllyDbg: základní akce
• Step into (klávesa F7): dojde k přechodu na návěští CALL
• Step over (klávesa F8): krokování pokračuje až po
návratu z CALL
• Run (klávesa F9): spustí debugovaný program, tzn.
nekrokuje ho
• Restart (Ctrl+F2): zabije aktuální session a načte
debugovaný program znovu; dobrá věc, pokud
krokováním dojdu donikam a potřebuju začít znovu
33. crackme1.exe – 15. instrukce CALL
• 00401030 E8 01000000
CALL <jmp.&kernel32.ExitProcess>
• Trochu jinak:
• CALL 00401036
• Viz předchozí instrukce CALL
• Jde o ukončení procesu: funkce ExitProcess() v
knihovně kernel32.dll
• I když nikdy nedojde k návratu, stejně se použivá CALL a
ne JMP
35. OllyDbg assembler
• Např. nekonečná smyčka – instrukce MOV, SUB a JMP:
1. Assembluj MOV EAX, 5 na adrese 401000 (= EIP)
2. Assembluj SUB EAX, 1 na adrese 401005
3. Assembluj JMP 401005 na adrese 401008
• Změny se obarví červeně, super věc na učení se opkódů
36. Instrukce INT3; NOP; ADD [EAX], AL
• CC INT3
• Výplň kódu (i dat) na místech, kam se nemá dostat řízení
• Způsobí ladící výjimku; tu teď neřešíme
• 90 NOP
• No OPeration
• Výplň pro zarovnání kódu
• 0000 ADD [EAX], AL
• Podezřelá instrukce
• Inicializační hodnota při alokaci paměti, nejde o kód
40. Problémy s označením architektury
• Hromada zjednodušujících názvů pro komplikovanou
architekturu:
• Intel
• IA-32 architecture
• Intel64 architecture (nový „64-bit mode“)
• IA-64 je úplně jiná (mrtvá) architektura
• AMD64 architecture
• x86 architecture, „legacy mode“
• 64-bit x86 architecture (nový „Long Mode“)
• Microsoft
• x86
• x64
44. crackme1_x64: 1. instrukce SUB
• 000000013FC0100 48 83 EC 08 SUB RSP, 8
• Vypadá jako alokace 8 bajtů na zásobníku
• Ve skutečnosti zarovnání zásobníku na 16 bajtů,
vynucené volací konvencí (viz následující slajdy)
• Komplikace při programování v assembleru
• Jednobajtový primární operační znak 0x83
• ref.x86asm.net/coder64.html#x83
• Nová věc: prefix 48 REX.W
• ref.x86asm.net/coder64.html#x48
• Nejčastější REX.W zvětší velikost operandu na 64 bitů
• REX prefixy na x86 neexistují
46. 64-bit general-purpose registers
• RAX = 8877665544332211, velikost QWORD
• EAX: 44332211
• AX: 2211
• AL: 11
• AH: 22
• RSP – ESP – SP – SPL
• RBP – EBP – BP – BPL
• RSI – ESI – SI – SIL
• RDI – EDI – DI – DIL
• R15 - R15D - R15W - R15B
• Horní DWORD nejde adresovat a nemá název
47. crackme1_x64: 5. instrukce MOVABS
• 000000013FC0100 48 B8 1430C03F01000000
MOVABS RAX, 13FC03014
• Jednobajtový primární opkód 0xB8
• Prefix 48 REX.W
• Mnemonic MOVABS neexistuje, jde o MOV
• lidová tvořivost, odpovídá to správnějšímu OFFSET v OllyDbg
48. crackme1_x64: volání MessageBoxA
• Předávání parametrů je zhruba vidět:
SUB RSP, 20
MOV RCX, 0 ; hWnd
MOV RDX, RAX ; lpText
MOVABS R8, 13FC03028 ; lpCaption
MOV R9, RBX ; uType
CALL <crackme1_x64.MessageBoxA>
ADD RSP, 20
• První 4 parametry jdou vždycky do registrů RCX, RDX,
R8 a R9
• Ale co ten SUB/ADD RSP, 20?
49. Volací konvence fastcall (1)
• Pozor, nejde o x86 fastcall, i když je podobný
• Ještě jednou, konvence bývá vidět v prototypu:
• int WINAPI MessageBox();
• WinDef.h (Windows SDKs): #define WINAPI
• tentokrát je to „prázdná“ hodnota, všechno je fastcall (s výjimkami)
• MSDN x64 fastcall calling convention
• První 4 parametry jdou vždycky do registrů RCX, RDX, R8 a R9,
ostatní na zásobník
• Zásobník čistí volaná funkce
• Návratová hodnota je v registru RAX
• Registry RAX, RCX, RDX, R8, R9, R10 a R11 může volaná funkce
změnit, ostatní musí zachovat
50. Volací konvence fastcall (2)
• První 4 parametry jsou v registrech, ale na zásobníku
musí být tak jako tak alokovaný prostor, další jdou na
zásobník jako u stdcall
• Alokace a uvolnění 4 parametrů pomocí
SUB/ADD RSP, 20
• 0x20 = 32 = 4*8
51. fastcall a zarovnání zásobníku
• MSDN: The stack will always be maintained 16-byte
aligned
• Vstupní bod programu je vlastně vstupní bod funkce
main()
• Volání funkce zruší zarovnání na 16 bytů:
1. Před provedením CALL je zásobník zarovnaný na 16
bytů
2. CALL provede PUSH RIP (8 bytů)
3. Na vstupu do funkce je potřeba zásobník znovu
zarovnat pomocí SUB RSP, 8
52. crackme1_x64: volání ExitProcess
• ExitProcess() má jenom jeden parametr, ale na zásobníku
je potřeba vždycky alokovat 4 parametry:
SUB RSP, 20 ; 4*QWORD
MOV ECX, 0
CALL crackme1_x64.RtlExitUserProcess
54. Úkoly
1. Crackněte crackme1 (x86 i x64) podle návodu v článku
Nebojte se reverzního inženýrství I.
2. Crackněte crackme1 ještě jinak než podle návodu
3. Pokud v crackme1 změníte instrukci CMP na SUB, jaký
vliv to bude mít na funkčnost programu?
4. Vytvořte přímo v OllyDbg assembleru co nejkratší kód,
který způsobí vyčerpání paměti zásobníku
55. 2. část
• Crackme #2 by RubberDuck
• + opkódy instrukcí
• + x64 verze crackme #2
• Stack, heap, virtual memory
• Memory model, TEB (TIB)
• Exception handling
• Entry point, kód, data
• Importy
• Binární formát instrukce
58. crackme2.exe – 1. instrukce PUSH
• 00401000 6A F5 PUSH -0B
• ref.x86asm.net/coder32.html#x6A
• 6A PUSH (sign extended) imm8
• imm8: 8bitová immediate value
• Takže jinak (velikost položky na zásobníku je DWORD):
• PUSH FFFFFFF5
• Je to první parametr volání WinAPI funkce
GetStdHandle()
59. GetStdHandle()
• MSDN: Retrieves a handle to the specified standard
device (standard input, standard output, or standard
error).
HANDLE WINAPI GetStdHandle(
_In_ DWORD nStdHandle
);
• Potřebujeme návratovou hodnotu HANDLE
• winnt.h (Microsoft SDKs):
• typedef void *HANDLE;
• Volací konvence stdcall: návratová hodnota je v EAX, ta
je uložena pomocí následující instrukce na později
60. crackme2.exe – 3. instrukce MOV
• 00401007 A3 95304000 MOV [403095], EAX
• ref.x86asm.net/coder32.html#xA3
• A3 MOV moffs16/32, eAX
• Opkódy A0-A3
• speciální forma MOV mezi AL/AX/EAX a pamětí
• Registr eAX (tzn. AX nebo EAX):
• accumulator, historicky optimalizovaný pro častý přístup
• Spousta opkódů podporujících accumulator, umožňujících
kratší kódování instrukcí (viz třeba 0x04, 0x05, 0x0C, ...)
• Dědictví historie, dnes nemá podstatný význam
• moffs - memory offset: konstatní hodnota adresuje paměť
61. crackme2.exe – 3. instrukce MOV
• MOV [403095], EAX
• 0x403095 = 4206741 = nezarovnaná paměť při ukládání
DWORD hodnoty
• Architektura x86 nevynucuje zarovnání paměti na určitou
hodnotu
• Pozor, architektura x64 typicky (dá se to vypnout na
úrovni OS) vyžaduje zarovnání paměti na „přirozenou“
hodnotu, tzn. DWORD je potřeba uložit na adresu
zarovnanou na 4 byty atd.
• Připomenutí: 64-bit Windows navíc vynucuje zarovnání
zásobníku na 16 bytů (ještě z jiných důvodů)
• Přístup k zarovnané paměti je rychlejší
62. crackme2.exe – 10. a 11. instr. PUSH
• 00401024 68 00304000
PUSH OFFSET 00403000
• 00401029 FF35 95304000
PUSH DWORD PTR [403095]
• ref.x86asm.net/coder32.html#xFF
• Skupina různých instrukcí
• FF /6 PUSH r/m16/32
• ModR/M byte 0x35 (viz dál)
• DWORD PTR: označení velikosti odkazované hodnoty
63. crackme2.exe – porovnání hesel
• WriteConsoleA(): vypíše výzvu k zadání hesla
• ReadConsoleA(): přečte zadané heslo
• lstrcmp(): WinAPI funkce porovnávající řetězce,
podobná funkci ze standardní C knihovny
int WINAPI lstrcmp(
_In_ LPCTSTR lpString1,
_In_ LPCTSTR lpString2
);
• „If the strings are equal, the return value is zero.“
64. crackme2.exe – vyhodnocení hesla
• 85C0 TEST EAX, EAX
• Zvláštní případ na vyhodnocení nulové hodnoty v registru
• Opkódy 84 a 85: TEST r/m, r
• ModR/M byte C0
• TEST: logical compare
• TEST nemění žádný operand
• Operace TEST = operace AND
• TEST nastavuje šest stavových příznaků, viz:
• Smysl dávají tři: SF, ZF a PF
• 0 AND 0 = 0 → nastaví se ZF
65. crackme1.exe – vyhodnocení CMP
• crackme1:
83F8 01 CMP EAX, 1
74 0C JE SHORT 00401016
• crackme2:
85C0 TEST EAX, EAX
74 1E JZ SHORT 00401081
• TEST EAX, EAX má kratší opkód než CMP EAX, 0
• Zajímavost: OllyDbg disassembler vyhodnotil, že
vhodnější je mnemonic JZ
66. Binární formát instrukce (opkód)
• Teoreticky:
• Formát instrukce je jenom jeden (ignorujeme VEX)
• Instrukce se skládá z 6 částí
• Primární opkód se vyskytuje vždycky, ostatní volitelně
1. Prefix(y)
2. Primární opkód: 1, 2 nebo 3 byty, volitelně +3 bity
3. ModR/M byte
4. SIB byte (zatím neřešíme)
5. Displacement / memory offset
6. Immediate
73. Stack: rychlá dočasná paměť
• LIFO (Last In First Out)
• Roste směrem k nižším adresám
• rSP: Stack Pointer register; pod ním je všechno volatile
• rSP ukazuje na platnou položku, ne pod
• Alokace jednoho DWORD: rSP = rSP – 4
• Jazyk C: alokace automatic variables probíhá na stacku
• Páry instrukcí
• CALL – RET; PUSH – CALL – RET imm16
• PUSH – POP
• INT – IRET (doručení přerušení a návrat z přerušení)
• Každý thread má svůj stack
74. Stack frame – „rámec funkce“
• Problém: rSP se často mění
• rBP: stack frame register
push ebp ; EBP je nonvolatile (stdcall convention)
mov ebp, esp
sub esp, x ; alokace automatic variables
... ; konec prologu
mov eax, [ebp+y] ; výběr parametru funkce
mov [ebp-z], eax ; inicializace automatické proměnné
... ; začátek epilogu
mov esp, ebp ; uvolnění proměnných – ukončení scope
pop ebp ; obnova nonvolatile registru
ret
76. Paměť stacku (2)
• Počáteční hodnota ESP při startu procesu: 1A0000
• Velikost alokovaná systémem: 3000 (12 KB, 3 stránky)
• Hodnota ESP na entry pointu: 19FF84
• Tzn. nějaká inicializace a zavolání kódu programu zabralo
31 položek (DWORDs) na stacku
• Za alokovaným zásobníkem na adrese 19B000 je guard
page (několik) o velikosti 2000 (8 KB, 2 stránky)
• Další guard pages jsou od adresy 95000 o velikosti B000
(44 KB)
• Vyčerpání stacku nastane na adrese A3000
• Konkrétní hodnoty se liší podle aplikace a podle verze OS
77. Stack a guard page
• Cílem je při inicializaci procesu alokovat jenom tolik
stacku, aby stačil po celou dobu jeho života
• Další paměť se alokuje dynamicky „na požádání“ čtením
nebo zápisem pod alokovanou paměť
• Exception zachytí memory manager, alokuje další stránku
• Paměť zasobníku nemusí alokovat jenom zvláštní
instrukce, ale i přímý přístup k rSP, např. SUB ESP, 100
• Alokace příliš velkých bloků paměti může obejít guard
pages, je proto potřeba provádět stack probe nebo použít
_alloca()
78. Velikost položky na 32-bit zásobníku
• Vždycky DWORD
• Zarovnání se (na Windows) neočekává
• Příklady:
• PUSH 11223344
• PUSH EAX
• PUSH DWORD PTR [EAX]
• Jde ale i WORD:
• PUSH 1122
• PUSH AX
• PUSH WORD PTR [EAX]
79. Velikost položky na 64-bit zásobníku
• Vždycky QWORD
• Pozor, Windows očekává zarovnání zásobníku na 16 bytů
před voláním (CALL) funkce
• Příklady:
• PUSH 11223344 (jde jenom sign extended imm32)
• PUSH RAX
• PUSH QWORD PTR [RAX]
• Paradox: nejde DWORD, ale WORD ano:
• PUSH EAX
• PUSH AX
• PUSH 1122
• PUSH WORD PTR [RAX]
80. Heap („hromada“)
• Vysokoúrovňový memory management
• Heap má svůj HANDLE
• Proces má víc heaps najednou
• Windows loader vytvoří default Process heap
• WinAPI: HeapAlloc (GlobalAlloc, LocalAlloc)
• C runtime heap: malloc, operator new
• Viz Sysinternals VMMap
81. Manipulace s virtuální pamětí
• Nízkoúrovňový přístup přímo ke stránkám paměti
• 4KB stránka
• Rezervace virtuálních adres (reserve)
• Alokace virtuálních adres (commit)
• VirtualAlloc, VirtualQuery
• VirtualProtect: dynamická generace a spuštění kódu
• Virtuální paměť = nefyzická paměť
82. Přístupné virtuální adresy
• Proces nemůže přistupovat ke všem existujícím adresám
(neplatí pro drivery)
• OllyDbg to dokáže částečně zobrazit
• 32-bit Windows:
• část prostoru musí zůstat systému
• první 2 GB paměti (adresy 0x00000000 až 0x7FFFFFFF)
• 4-gigabyte tuning (aka /3GB switch): prvním 3 GB paměti
(0x00000000 až 0xBFFFFFFF)
• 64-bit Windows:
• 32-bit proces: vždy celé 4 GB, bez ohledu na další nastavení
• 64-bit proces: teoreticky všechny 64-bit adresy, prakticky omezení
systémem, např. Windows 7 - 192 GB, Windows 10 - 2 TB
83. Paměťový model (memory model)
• Flat memory model
• Všechno se řeší přes stránkování (paging)
• Kontinuální (nesegmentovaný) virtuální paměťový prostor,
tzn. všechny segmenty mají bázi 0, viz OllyDbg
• Zjednodušeně: paměť začíná od adresy 0 až po nejvyšší
hodnotu (danou systémem)
• Reziduum segmentace: paměť TEB (TIB)
• 32-bit Windows: adresace pomocí FS segmentu
• SEH chain: FS:[0]
• 64-bit Windows: GS segment; FS je nevyužitý
84. Thread Information Block (1)
• aka Thread Environment Block
• Nedokumentovaná struktura (Bingem jde vygooglit)
• Všechny informace o threadu
• 32-bit Windows
• adresace pomocí segmentu FS
• OllyDbg: FS 0053 32bit 3C0000(FFF)
• 64-bit Windows
• adresace pomocí segmentu GS
• x64Dbg neumí zobrazit báze segmentů
88. SEH (2)
• Instalace vlastního handleru do řetězce handlerů:
push pointer_to_exception_handler
push dword ptr fs:[0]
mov fs:[0], esp
• Odinstalace:
pop dword ptr fs:[0]
add esp, 4
89. SEH: ukázka
• OllyDbg assembler:
• Instalace handleru na 0040101A
• INT 3 vyvolá výjimku
• JMP způsobí zacyklení
90. Table-based exception handling
• 64bitový Windows nezná SEH
• Table-based exception handling je popsaný v datech,
neinstaluje se během spouštění kódu
91. Entry point, kód, importy, data
• PE header je taky v paměti
• Entry point určuje PE header
• Kód, importy, data atd. načtené ze sekcí v souboru
92. Importy
• „.rdata“ sekce: read-only initialized data
• Knihovny, které program používá
• OllyDbg: View – Executable modules
• User-mode systémové knihovny načtené těsně pod
hranicí 2 GB
93. Shrnutí instrukcí
• MOV
• CMP, SUB
• TEST, AND
• JE, Jcc
• JMP
• PUSH, POP
• CALL, RET
• Adresace paměti pomocí [moffs] a [disp32]
• INT3
• NOP
• ADD [EAX], AL
94. Domácí úkoly
1. Crackněte crackme2, jakobyste neznali heslo
3. Pokud nahradíme instrukci TEST za AND, jak se změní
chování crackme2?
4. Jakou jinou logickou instrukci se stejnými operandy a
stejným testováním výsledku bysme mohli k
vyhodnocení použít?
101. Přídavek: formát instrukce podrobně
• Pokud lidi neodejdou, poveselit je ještě popisem formátu
instrukce
102. Jenom jednobajtový primární opkód
• 90 NOP – No OPeration
• CC INT3
• 50+r PUSH r
• 5 horních bitů opkód (01010), 3 dolní bity kód registru
• Příklady:
• 01010 000 PUSH rAX
• 01010 001 PUSH rCX
• 01010 010 PUSH rDX
• 01010 111 PUSH rDI
103. Registr zakódovaný v opkódu + imm
• B8+r MOV r32/64, imm32/64
• BA 10203040 MOV EDX, 40302010
• Registrů na x64 je 16, tzn. jsou potřeba 4 bity pro kód
• Přístup k novým registrům pomocí prefixu REX.B
41 BA 10203040 MOV R10D, 40302010
• Přístup k operandům r64, imm64 pomocí prefixu REX.W:
48 BA 1020304050607080
MOV RDX, 8070605040302010
• Kombinace - prefix REX.WB:
49 BA 1020304050607080
MOV R10, 8070605040302010
104. REX prefixy
• Opkódy 40-4F
• ref.x86asm.net/coder64.html#x40
• x64: 16 registrů, ale jednotlivých 8bitových částí je 20...
• Opkód 40 - „plain“ REX prefix přepíná:
AH CH DH BH bez jakéhokoliv REX prefixu
SPL BPL SIL DIL s jakýmkoliv REX prefixem
105. ModR/M byte
• 2 bity 7-6: pole Mod
• 3 bity 5-3: kód registru nebo 3-bit opcode extension
• 3 bity 2-0: kód registru nebo kód paměťového operandu
106. ModR/M byte: operandy reg, reg
• 85C0 TEST EAX, EAX
• Obecně:
• 85 /r TEST r/m16/32, r16/32
• /r v manuálech značí přítomnost ModR/M byte
• ModR/M C0: 11‘000‘000
• Mod 11: operand R/M je registr
• Reg 000: EAX
• R/M 000: EAX
112. Mod 01 a 10
• Mod 01: přidání disp8
• Mod 10: přidání disp32
113. ModR/M: nejenom GP registry
• ModR/M kóduje i MMX, XMM, segment, control a debug
registry, ty neřešíme
114. Motivace pro ruční disassembling
• Chyby a lidová tvořivost v disassemblerech
• Disassembler někdy není po ruce
• Patchování, hlavně když není dost místa
• Shellcode: „cení se každý ušetřený bajt, který neohrozí
funkčnost shellcodu“
• Dis/assemblování z hlavy v hospodě
115. Shrnutí opkódů
• A0-A3: zvláštní skupina opkódů, jsou další
• rAX: operace s „accumulator“ často využívají vlastní
kratší opkódy
• Jenom jeden formát instrukce
• Jednobajtový opkód (NOP)
• Jednobajtový opkód + kód registru
• Některé REX prefixy
• Kompletní ModR/M byte
• Displacement, memory offset
• Immediate
116. Trocha historie
• Původně se registr
SP nedal pro
adresaci paměti
vůbec použít (8086)
• Vyřešilo to přidání
SIB byte později
(80286)