Dana je binarna datoteka bez prevelikog konteksta, dodatnih informacija (osim da je obfuscirana). Iako naziv zadatka odaje o čemu je riječ, korisno je znati kako prepoznati “pakiranu” datoteku. Najčešći pokazatelj je izlaz funkcije strings koja vraća besmislene stringove (ne postoje nazivi funkcija itd…). Drugi pokazatelj su besmislene instrukcije koje se izvršavaju pokušajem debuggiranja. Smiao “pakiranja” je da se oteža ručni reversing, kao i otežati automatsko prepoznavanje malwarea.
Dva su načina kako se pakirane datoteke mogu otpakirati. Prvi je da se sazna kojim programom je datoteka zapakirana, a drugi je ručno unpackanje. U nastavku su objašnjena oba načina, na primjeru zadatka Packer.
Kako bi se otkrilo koji alat za “pakiranje” je korišten, mogu se iskoristiti alati/usluge za analizu, poput VirusTotal. VirusTotal je web aplikacija koja analizira malware korištenjem pattern matchinga i sl. Također, prepoznaje poznate packere, ako su korišteni za obfusciranje datoteke. Rezultat analize ovog zadatka vraća naziv “kiteshield”. Kiteshield je open source packer za datoteke ELF formata. Može se pronaći ovdje ovdje. Kako bi se datoteka unpackala mogu se pronaći razni writeupovi na tu temu (npr. ovdje). Korištenjem skripte za unpackanje jednostavno se dobije originalna main funkcija.
Jednom kada se dobije originalna main funkcija, lagano se dođe do flaga.
Recimo da se radi o packeru koji nije poznat, tj. ne postoje već dostupne skripte za unpackanje. Ne preostaje ništa drugo osim ručne analize koda. Dobri packeri otežavaju ručnu analizu korištenjem puno nebitnih instrukcija što čini kod nerazumljivim. Međutim, većina packera imaju zajedničko svojstvo da koriste sistemske pozive (engl. syscall) kojima alociraju prostor u memoriji (mmap) u koje se zapisuju dekriptirani payload tj. binarna datoteka, postavljaju potrebne permissione (mprotect), otvaraju razne datoteke poput libc.so (read) itd… Znajući to, debuggiranje se može olakšati zaustavljanjem na pozivima naredbe syscall.
Prolaskom kroz program vide se sljedeći syscall pozivi:
1. getpid 2. stat 3. open 4. read 5. close 6. exit
Nakon poziva “read” program završava (exit) te je očito da se radi o nekoj vrsti anti-debugging mjere. Prije exita poziva se syscall read s putanjom /proc/%d/status gdje je %d pid programa. Primjer djelomičnog ispisa jest sljedeći:
Vrijednost TracerPid pokazuje process id procesa koji se koristi za debugiranje (u ovom slučaju gdb). Programi koji nisu debugirani imaju TracerPid postavljen na vrijednost 0. Postavljanjem vrijednosti TracerPid na nula pomoću naredbe patch, možemo uvjeriti program da nije debugiran.
Tok programa se izmijenio, sada se umjesto exita poziva syscall fork.
Nastavljanjem kroz program, pozivaju se razni syscallovi sve dok ne dođe do syscallova wait i exit.
Postavljanjem follow-fork-mode na child nakon syscall-a fork debuggiranje se prebacuje ne forkani process.
Praćenjem syscallova vidimo sličan uzorak kao i u parent procesu. Nakon poziva “setrlimit” ponovno se dohvaća pid te gleda /proc/%d/status.
Istom metodom izmijeni se TrackerPid i nastavi s debuggiranjem. Dolazi se do raznih poziva mmap, memset i read. Prolaskom kroz njih virtualna memorija sada liči na normalan proces.
Očito je da je proces unpackanja završio. Korištenjem naredbe “dump binary memory dump.out 0x0000000800000000 0x0000000800005000” dobije se dump binarne datoteke.
Iako je zbog načina obfuskacije koju kiteshield koristi kod obfusciran (ne može se pokrenuti) za ovaj zadatak to je dovoljno jer je string za flag hardkodiran te se može dobiti naredbom strings.
“The second layer of encryption (referred to in the codebase as the “inner layer”) consists of individual encryption of almost every function in the input binary (identified via the symbol table at pack-time). A ptrace-based runtime engine is triggered on every function entry and exit via replacement of each function's entry instruction and all its return instructions with int3 instructions (which deliver a SIGTRAP when executed). Upon receiving a trap, the runtime engine looks up the current function and encrypts or decrypts it as needed such that only functions within the current call stack are decrypted at any point in time.”
Ideja je da int3 instrukcije šalju signal ptraceru koji dinamički dekriptira funkciju koja se izvršava. Izgled main funkcije u dobivenom dumpu jest:
Jasno je da se funkcija ne može izvršiti zbog invalidnih instrukcija.
Za usporedbu, ovo je izgled iste funkcije kroz binarnu datoteku dobivenu korištenjem jednih od skritpti za unpackanje kiteshielda: