Antidebug techniky Juraj Bartko bartkoateset sk vod a
Anti-debug techniky Juraj Bartko bartko[at]eset. sk
Úvod a rozdelenie (1/2) Debugovanie: – Postup zameraný na lokalizovanie chýb programov a znižovaniu ich počtu. – Postup zameraný na pochopenie činnosti programu. Anti-debuggovacie techniky: – Triky alebo techniky, prostredníctvom ktorých sa autor programu snaží sťažiť alebo zabrániť analýze svojho programového kódu – Často použité pri ochrane: • Komerčného softvéru - proti neautorizovanému kopírovaniu (DRM, Anti. Crackingove ochrany) • Voľne dostupného softvéru - na zabránenie pochopenia jeho činnosti (Skype) • Malvéru 2
Úvod a rozdelenie (1/2) 3
Úvod a rozdelenie (2/2) Anti-techniky reverzného inžinierstva: – Anti-debuggovacie – Anti-disassemblovacie – Anti-dumpovacie – Anti-emulačné – Anti-VM – Anti-… – Packery (kombinujú vyššie spomenuté techniky) Najznámenšie triky: – http: //www. codeproject. com/Articles/30815/An-Anti-Reverse. Engineering-Guide – http: //pferrie. host 22. com/papers/antidebug. pdf – “Cracking a ochrana pred ním“ – Seriál článkov na http: //www. zive. sk 4
Anti-debug: API – Is. Debugger. Present() C++: if (Is. Debugger. Present()) { Exit. Process(0); } //. . . pokračovanie 5 Assembler: ANTIDEBUG: call Is. Debugger. Present cmp EAX, 0 jz CONTINUE push 0 call Exit. Process CONTINUE: //. . . pokračovanie
Možné reakcie programu na debugovanie – Ukončenie programu – API funkcia Exit. Process(); – Poškodenie-sa • Poškodenie logálnych/globálnych premenných – Napr. vynulovanie predtým získaného handle na otvorený súbor • Poškodenie dátových oblastí interných štruktúr – Zmena pointrov zoznamov/binárnych stromov atď. . • Zmena/vymazanie inštrukcií programu – – 6 Zhodenie debuggera Vypnutie PC (reštart)/ vypnutie monitora Markove pravidlá na tvorbu anticrackingových ochrán Atď. . .
Možné reakcie programu na debugovanie 7
Metódy obchádzania anti-debugovacích trikov – Zmena hodnôt registrov/pamäti/flagov – Preskočenie programového kódu • Hrozí riziko, že sa nevykonajú niektoré dôležité inštrukcie a program skončí chybou (napr. zle nastavené ESP, preskočenie inicializácie premenných, atď. . . ) – Editácia procesu počas jeho behu • Odstránenie triku – „vynopovanie“ – Zmena spustiteľného súboru • Trvalé pridanie, odstránenie alebo zmena inštrukcií/funkcií priamo do spustiteľného súboru 8
Použitie segmentových registrov (všeobecne) Segmentové registre: – CS, DS, SS, ES, FS, GS Formátovanie: – mov EAX, [DS: 0] ≡ mov EAX, DS: [0] 9
Použitie segmentových registrov (Windows) Prístup k pamäti a vzťahy (obrázok): – [0 x 0000] ≡ CS: [0 x 0] ≡ DS: [0 x 0] … – lea EAX, FS: [0] ; //EAX=0 – lea EAX, FS: [0 x 50000000]; //EAX=0 x 50000000 10
Dôležité informačné štruktúry procesu Process Environment block (PEB) – Štruktúra obsahujúca základné informácie o procese – V každom procese sa vyskytuje iba raz Thread information block (TIB) – Štruktúra obsahujúca dôležité informácie o vlákne – Každé spustené vlákno má svoj TIB – Pristupuje sa k nemu prostredníctvom segmentového registra FS: [x] – FS: [0 x 30] ukazuje na PEB 11
Dôležité informačné štruktúry procesu Process Environment block (PEB) – Štruktúra obsahujúca základné informácie o procese – V každom procese sa vyskytuje iba raz Thread information block (TIB) – Štruktúra obsahujúca dôležité informácie o vlákne – Každé spustené vlákno má svoj TIB – Pristupuje sa k nemu prostredníctvom segmentového registra FS: [x] – FS: [0 x 30] ukazuje na PEB 12
Anti-debug: API – Is. Debugger. Present() • Použitie akejkoľvek API funkcie je možné pomocou breakpointov odhaliť • Niekedy je možné funkcionalitu API funkcie prepísať do programu a zabrániť tak odhaleniu cez prípadne nastavený break-point • Takýto kód však môže byť nekompatibilný medzi rôznymi verziami operačných systémov 13 Assembler: ANTIDEBUG: mov EAX, FS: [0 x 30] movzx EAX, [EAX+0 x 02] cmp EAX, 0 je CONTINUE push 0 call Exit. Process CONTINUE: //. . . pokračovanie
Anti-debug: API – ostatné API funkcie API funkcia: Nt. Query. Information. Process( Process. Handle, Process. Information. Class, Process. Information, [OUT] Process. Information. Length, Return. Length [OUT/OPTIONAL] ) – Informačné štruktúry (Process. Information. Class): • Process. Debug. Port = 0 x 07 – Detekcia prítomnosti Ring 3 debuggera • Debug Object Handle = 0 x 1 E (nedokumentované) – V prípade ak je program debuggovaný, je možné získať handle k „Debug. Objektu“ • Process. Debug. Flags = 0 x 1 F (nedokumentované) – Ak je debugger aktívny, funkcia vráti opak PROCESS->No. Debug. Inherit API funkcia: Check. Remote. Debugger. Present() 14
Anti-debug: deoptimalizácia/nič-nerobiaci kód Myšlienka: • Spomaliť analýzu programu prostredníctvom deoptimalizácie („nafúknutia“ veľkosti kódu) Implementovanie: • Použitie navzájom inverzných operácií: • push/pop • inc/dec, sub/add • 2 x aplikovanie xchg • . . . • Nepoužitie výsledkov výpočtu • . . . 15 Assembler: push mov RX_LOOP: ror xor loop sub add pop EAX EBX EAX, 0 x. F 93321 C 9 ECX, 0 x 20 EAX, 1 EAX, 0 x 01 RX_LOOP EAX, 0 x 02 EAX, 0 x 05 EAX, 0 x 07 EBX EAX
Anti-debug: Triky zamerané na konkrétne debuggery – Olly. Dbg (anti-de)bug: • Output. Debug. String("%s%s%s%s%s%s%s%s%s%s%s%s%s") – Inicializačné naplnenie registrov • Napr. program spustený pod Olly. Dbg môže obsahovať na začiatku: ESI=0 x. FFFF • . . . – hľadanie debuggera podľa názvu okna: • Find. Window("Win. Dbg. Frame. Class", NULL); • Find. Window("Olly. Dbg", NULL); • . . . 16
Anti-debug: Časové rozdiely medzi dvomi bodmi v programe (1/2) API funkcia Get. Tick. Count() – Funkcia slúži na meranie času od štartu PC (v milisekundách) C++: int time 01 = Get. Tick. Count(); //some code int time 02 = Get. Tick. Count(); if ((time 02 -time 01) > 5000) { Exit. Process(0); } 17 Assembler: call Get. Tick. Count mov [time 01], EAX //some code call Get. Tick. Count sub EAX, [time 01] cmp EAX, 5000 jb CONTINUE push dword 0 call Exit. Process CONTINUE:
Anti-debug: Časové rozdiely medzi dvomi bodmi v programe (2/2) Inštrukcia RDTSC: - Time Stamp Counter - 64 bitový výsledok uložený do registrov EDX: EAX 18 Assembler: rdtsc mov [lo. Value], EAX mov [hi. Value], EDX //. . . rdtsc sub EAX, [lo. Value] sbb EDX, [hi. Value] cmp EDX, 0 jnz EXIT cmp EAX, 500000 jb CONTINUE EXIT: push dword 0 call Exit. Process CONTINUE:
SEH (Windows) – Reťaz výnimiek C++: Moth try { //{some code} (Moľa) Assembler: push SEH_HANDLER push FS: [0] mov FS: [0], ESP //{some code} }catch(. . . ){ //SEH_HANDLER }; 19 pop dword ptr FS: [0]//unwind add ESP, 4
SEH (Windows) – Reťaz výnimiek C++: try { //{some code #1} try { //{some code #2} }catch(. . . ){ //HANDLER_B }; //{some code #3} try { //{some code #4} }catch(. . . ){ //HANDLER_C }; } catch(. . . ){ //HANDLER_A }; 20 Assembler: push HANDLER_A //prvý “try” push FS: [0] // mov FS: [0], ESP //{some code #1} push HANDLER_B //vnorený “try” push FS: [0] mov FS: [0], ESP //{some code #2} pop dword ptr FS: [0]//unwind HANDLER_B add ESP, 4 //{some code #3} push HANDLER_C //druhý vnorený “try” push FS: [0] mov FS: [0], ESP //{some code #4} pop dword ptr FS: [0]//unwind HANDLER_C add ESP, 4 pop dword ptr FS: [0]//unwind HANDLER_A add ESP, 4
SEH (Windows) – Spracovanie výnimiek Sekvencia krokov: 1. Operačný systém o chybe informuje aktívny debugger 2. Zavolanie prvej obsluhy z reťaze výnimiek FS: [0] (try. . catch) 3. Ak prvá obsluha nedokázala ošetriť chybu, volajú sa ďalšie v poradí 4. Informácia sa odošle spať debuggeru, že chybu nebolo možné ošetriť 5. Zavolá sa obsluha nastaviteľná API funkciou Set. Unhandled. Exception. Filter() 6. Vypíše sa chyba, že program vykonala neplatnú operáciu a ukončí sa. Ak akýkoľvek z krokov uspeje, ďalšie sa už nevolajú!!! 21
SEH (Windows) – SEH Handler (1/2) SEH Handler: – Funkcia v programe volaná operačným systémom v prípade vzniku výnimky Vstupné naplnenie zásobníka: - 22 [ESP+0 x 00] [ESP+0 x 04] [ESP+0 x 08] [ESP+0 x 0 C] [ESP+0 x 10] return address pointer to EXCEPTION_RECORD record (param 1) pointer to establisher frame record (param 2) pointer to CONTEXT record (param 3) pointer to dispatcher context record (param 4)
SEH (Windows) – SEH Handler (1/2) typedef struct _CONTEXT { DWORD Context. Flags; DWORD DWORD Dr 0; Dr 1; Dr 2; Dr 3; Dr 6; Dr 7; SEH Handler: typedef struct _EXCEPTION_RECORD { DWORD Exception. Code; DWORD Exception. Flags; struct _EXCEPTION_RECORD *Exception. Record; PVOID Exception. Address; DWORD Number. Parameters; ULONG *Exception. Information[EXCEPTION_MAXIMUM_PARAMETERS]; } – Funkcia v programe volaná operačným systémom v prípade vzniku výnimky FLOATING_SAVE_AREA Float. Save; DWORD Seg. Gs; Vstupné naplnenie zásobníka: DWORD Seg. Fs; DWORD Seg. Es; [ESP+0 x 00] Seg. Ds; DWORD Edi; [ESP+0 x 04] DWORD Esi; DWORD Ebx; [ESP+0 x 08] DWORD Edx; DWORD Ecx; [ESP+0 x 0 C] DWORD Eax; DWORD Ebp; [ESP+0 x 10] DWORD Eip; - 23 return address pointer to EXCEPTION_RECORD record (param 1) pointer to establisher frame record (param 2) pointer to CONTEXT record (param 3) pointer to dispatcher context record (param 4) Seg. Cs; EFlags; Esp; Seg. Ss; BYTE Extended. Registers[MAXIMUM_SUPPORTED_EXTENSION]; }; DWORD
SEH (Windows) – SEH Handler (2/2) Výstupné parametre: -EAX – výsledok funkcie: – Exception. Continue. Execution = 0 – Chybu sa podarilo ošetriť – Exception. Continue. Search = 1 – Chybu sa nepodarilo ošetriť. Pokračuje sa ďalšou obsluhou v poradí -Štruktúry CONTEXT and EXCEPTION_RECORD - Nastavenie EIP - Nastavenie ESP, EBP - „Unwind“ SEH obsluhy (obnova predchádzajúcej SEH obsluhy) 24
25
Anti-debug: využitie SEH Myšlienka: v bloku try. . catch úmyselne spôsobíme chybu. Inštrukcie za touto chybou sa môžu vykonať jedine s aktívnym debuggerom. C++: try { int zero=0; int d= (int) 5 / zero; //sem sa program nedostane Exit. Process(0); } catch(. . . ){ }; 26 Assembler: Chybu môžeme spôsobiť: – INT 1/INT 3/INT 2 D – Nastavenie trap flag-u • PUSHFD • OR DWORD [ESP], 0 x 100 h • POPFD – Delenie nulou DIV, IDIV – Access violation MOV, ADD, … • Zápis na Read. Only stránku • Čítanie na nevytvorenú stránku – …
Činnosť debuggerov - krokovanie Druhy krokovaní (podľa Olly. Dbg): – „F 7“ – s vnáraním sa do funkcií – „F 8“ – bez vnárania – „F 9“ – beh po najbližší break-point Implementovanie: – HW breakpointy (DR 0 -DR 3, DR 6, DR 7) – Nahradzovanie inštrukcií inštrukciou „int 3“ (byte-kód: 0 x. CC) – „Trap flag“ – Nastavenie oprávnení stránky (memory manager) 27
Činnosť debuggerov – memory breakpointy Breakpointy na prístup (čítanie/zápis) do pamäte HW – implementované cez debug-registre SW – implementované cez oprávnenia stránok 28
Anti-debug: Kontrola prítomnosti breakpointov - HW breakpointy: - Priame čítanie alebo zápis z/do debug-registrov v RING 3 nie je možné (Privileged instruction error) - SEH - je ich možné nielen prečítať, ale aj do nich zapísať (vynulovať ich, zmeniť) - Softvérové breakpointy: - Nastavenie break-pointu modifikuje inštrukcie programu - Kontrola prítomnosti inštrukcie INT 3 (znak 0 x. CC) na rôznych adresách (vlastné funkcie, API funkcie, . . . ) - Výpočet CRC určitej oblasti programu 29
Anti-debug: Kontrola breakpointov - „Perlička“ Vírus rodiny Yankee_Doodle: - Opravoval programový kód „poškodený“ break-pointami prostredníctvom samoopravných kódov - Hammingovo kódovanie - Dokázal „opraviť“ až 16 break-pointov alebo inak modifikovaných bytov v pamäti 30
Ďakujem za pozornosť Juraj Bartko bartko[at]eset. sk
- Slides: 31