Reverse engineering APK file
Temu rezervirao: Filip Aleksić
Sadržaj |
Reverzno inženjerstvo
Reverzno inženjerstvo je proces izvlačenja informacija i znanja iz proizvoda, te pomoću tih informacija kreiranje tog proizvoda ili dijela tog proizvoda. Kad govorimo o programskom reverznom inženjerstvu, tada najčešće mislimo na uzimanje postojećeg progama od kojeg nemamo izvorni kod i pokušavamo saznati kako je taj program implementiran i dizajniran.
APK - Android Package File
Apk je paket koji Android sustav koristi za distribuciju i instalaciju aplikacija. Izvorni kod se prvo kompilira i onda se pakira u APK. On je zapravo zip format baziran na JAR formatu. Zip je format datoteke koji služi za arhiviranje podataka u kompresiranom obliku bez gubitka podataka, a jar je paket koji obično sadrži više Java klasa, resursa, itd.
Sadržaj APK datoteke je strukturiran na sljedeći način:
- META-INF
- MANIFEST.MF - Tekstualna datoteka koja sadrži imena svih datoteka i SHA256 hash blob-a svake datoteke.
- CERT.RSA - Certifikat aplikacije
- CERT.SF - Sadrži imena datoteka i SHA256 hash odgovarajućih linija iz MANIFEST.MF
- lib - datoteka koja sadrži kompajlirane biblioteke podijeljene u datoteke ovisno o arhitekturi procesora
- res - resursi koje aplikacija koristi, nisu kompajlirani, slike, xml, binary datoteke, glazba, itd.
- assets - raw datoteke koje su učitavane u aplikaciju pomoću AssetManagera
- AndroidManifest.xml - kompajlirani Android Manifest XML, podatci poput imena, dopuštenja koje aplikacija traži, biblioteke koje koristi, itd.
- classes.dex - klase koje su kompajlirane u dex format koje je razumljiv Androidovoj virtualnoj mašini Dalvik i Android Runtimeu
- resources.arsc - kompajlirane android resurs datoteke poput R.java, strings.xml, itd.
Trenutni sadržaj možemo dobiti tako da unzipamo datoteku nekim alatom ili putem komandne linije unzip -e <ime_datoteke>.apk -d <ime_izlazne_datoteke>
. Takav kod je jako teško analizirati jer je velik dio kompajliran te zbog toga koristimo razne alate koji nam olakšavaju analizu.
Kako doći do .apk datoteke?
Do datoteke je moguće doći na više načina. Ako je aplikacija instalirana na Android uređaj možemo koristiti alata koji se zove ADB (Android debug bridge)
. On nam omogućuje komuniciranje s uređajem putem komandne linije na računalu, uključen je u Android SDK tako da ako recimo razvijate za Android nije potrebno ništa dodatno instalirati. Pomoću naredbe adb shell pm list packages
možete vidjeti sve pakete instalirane na uređaju. Nakon što smo vidjeli sva imena paketa, pronađemo onaj koji želimo "izvući" na naše računalo i upišemo naredbu adb pull /data/app/<ime_paketa>.apk <lokacija_spremanja>
.
Osim preko uređaja moguće je koristiti web stranice poput APKMirror, APK Downloader, itd ili korištenjem ne službenog API-a Google Play API.
Proces kompajliranja
Android kod je najčešće pisan u Java programskom jeziku iako je u posljednje vrijeme sve češći i Kotlin. U ovom odjeljku ću objasniti kako se Java kod kompajlira.
Prvo se Java kod kompajlira u .class
datoteke pomoću javac
naredbe. Datoteke sadrže Java bytecode koji predstavlja Java bajt kod. Ovakav kod se uobičajeno pokreće na Java virtualnim mašinama JVM, ali Android ne koristi taj kod već drugu vrstu bajt koda koja se zove Dalvik. Kod se iz Java bajt koda i bilo kojih potrebnih .jar
biblioteka u Dalvik bajt kod pretvara pomoću naredbe dx
i izlazne datoteke su tipa .dex
. Potom su sve class.dex
datoteke, resursi, manifest i ostale kompresirane u .apk
datoteku i može se napraviti pomoću alata aapt
. Nakon što smo dobili .apk
datoteku možemo ju distribuirati. Ako ju želimo distribuirati putem Google Play Storea onda aplikacija mora biti potpisana. Potpisivanje aplikacija se sastoji od dodavanja sljedećih datoteka u .apk
datoteku: datoteka koja sadrži checksum i generiranog privatnog ključa. Potpisivanje se obavlja s alatom jarsigner
koji se također koristi za potpisivanje jar datoteka. Za kraj potrebno je napraviti byte align što je način rasporeda bajtova. Android to zahtjeva kako bi mogao lakše i brže čitati datoteku.
Alati
Jedni od alata koji nam mogu pomoći u analizi koda su sljedeći:
- apktool - disassemble resurse približno originalu i moguće je ponovo izgraditi u apk nakon promjena
- smali - praktički je nemoguće čitati .dex code te zbog toga je napravljen smali jezik koji nam olakšava čitanje dex koda. apktool kad rastavlja kod pretvara .class datoteku u smali
- IDA Pro - komercijalni alat koji je disassembler, debugger i interactivan. Ne služi samo za Android već za i ostale programe.
- jeb komercijalni alati koji je Android debugger i decompiler
- androguard - besplatan Python alat koji disassebler i decompiler Android aplikacija
- enjarify - alat za pretvaranje Dalvik bajtkoda u Java bajtkod, koristan je jer nakon pretvorbe možemo analizirati kod pomoću alata za analiziranje Jave kojih ima puno više
- dex2jar - kao što mu samo ime kaže omogućuje pretvaranje dex datoteka u class datoteke kompresirane kao jar, osim toga moguće je uređivati dex kod i pretvoriti kod u smali
- jadx - decompiler dex u java
- radare - je moćan prijenosni alat za revezno inženjerstvo, sličan IDA Pro
- 010Editor Templates - olakšava uređivanje binarnih datoteka
Kali operacijski sustav ima u sebi sljedeće od spomenutih alata: apktool, dex2jar i radare2.
Primjeri
Ovo poglavlje ću prikazati praktične primjere. Prvi primjer će biti reverzno inženjerstvo jako jednostavne Android aplikacija kako bi vidjeli strukturu aplikacije, kako možemo izmjeniti kod i vratiti nazad u apk.
Za sljedeći primjer ćemo koristiti istu aplikaciju, ali će njen kod biti zamršen (obfuskiran). Objasnit ću što je i kako se radi obfuskacija, te mogućnosti zaobilaženja obuskacije.
Zadnji primer će biti stvarna aplikacija koju ćemo preuzeti s Google Play na uređaj, pa s uređaja na računalo i analizirati ju.
Primjer 1 - Hello Reverse
Hello Reverse je jako jednostavna aplikacija koja je kreirana pomoću empty predloška od Android Studia, jedina izmjena je da umjesto Hello World piše Hello Reverse. Nakon navedenih izmjena sam pokrenuo Run u Android Studiu i aplikacija se pokrenula. Znamo da je potrebna .apk
datoteka kako bi se aplikacija mogla pokretati na uređaju i nalazi se na lokaciji <root_projekta>/app/build/outputs/apk/debug/app-debug.apk
.
Sad kad smo pronašli .apk
datoteku možemo započeti s radom.
Napravit ćemo disassemble pomoću apktool
. Pozicioniramo se u lokaciju gdje je .apk
smješten i pokrenemo komandu apktool d app-debug.apk
.
Izlaz bi trebao izgledati otprilike ovako:
Alat je sve izlazne datoteke spremio u datoteku nazvanu kao i sama aplikacija, u ovo slučaju app-debug
. Datoteka sadrži disassemblane resurse u XML
datoteke koje su čitljive i pretvorio je sve class
datoteke u smali
format. Sad kad imamo čitljive datoteke napravit ćemo promjenu u aplikaciji. Krenimo prvo od AndroidManifest.xml
datoteke jer u njoj kao manifest tipu datoteke pišu osnovne informacije o aplikaciji. Vidimo razne informacije, ali one koje su nama zanimljive za naš zadatak su aktivnosti. U ovoj aplikaciji ima samo jedna i zove se MainActivity. Znamo da želimo promjeniti tekst koji se ispisuje u dizajnu što znači da nam treba XML
te aktivnosti. Poznavajući uobičajene konvencije pisanja Android koda taj tekst se može najvjerojatnije nalaziti samo na 2 mjesta. Prvo je u strings.xml
datoteci koja sadrži sve tekstove iz aplikacije, a drugo je u njezinom layoutu. Ako se Java datoteka aktivnosti zove
MainActivity.java
tada se njen XML zove activity_main.xml
. Zadnje moguće mjesto gdje je moguće da je programer stavio tekst je u sam java kod. Pa krenimo redoslijedom tražeći "Hello Reverse!" tekst.
Znamo da se string.xml datoteka inače nalazi u res/values pa ćemo i ovdje ići tim putem i stvarno pronaći naš strings.xml, no tokom analiziranja XML-a možemo vidjeti da se tu ne nalazi naš tekst. Sljedeći korak je activity_main.xml, on se inače nalazi u res/layout. Otvaranjem te datoteke možemo stvarno pronaći activity_main.xml i vidimo da TextView ima atribut text u kojem piše naša vrijednost. Možemo ju sad promjeniti pomoću bilo kojeg editora, ja ću ju promjeniti u "Hacked".
Sad je potrebno ponovo stvoriti .apk datoteku koju je moguće pokrenuti i apktool nam to omogućava.
Naredbom apktool b app-debug -o hack.apk
ponovo kreiramo apk datoteku iz nasih izmjenjenih podataka. Ako pokušamo ovu datoteku samo prebaciti na uređaj i instalirati dobit ćemo upozorenje "The package appears to be corrupt.". Razlog tome je što aplikaciju nismo bitovno alignali niti potpisali, pa ćemo to napraviti pomoću zipalign
i apksigner
koji su dio Android SDK (može ih se pronaći u build tools).
Prvo ćemo bitovno poravnati aplikaciju naredbom ./zipalign -v -p 4 /Users/faleksic/Downloads/vbox/hacked.apk /Users/faleksic/Downloads/vbox/hacked-aligned.apk
. Sljedeći korak je generirati ključ za potpisivanje aplikacije: keytool -genkey -v -keystore test-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias test-key
i na kraju potpisujemo poravnatu aplikaciju ./apksigner sign --ks /Users/faleksic/Downloads/vbox/test-key.jks --out /Users/faleksic/Downloads/vbox/hack-r.apk /Users/faleksic/Downloads/vbox/hacked-aligned.apk
.
Na kraju imamo izmjenjen tekst na aplikaciji:
Primjer 2 - Hello Obfuscated
Možemo vidjeti da nije teško mijenjati nečiji kod niti pogledati kako funkcionira iz gotove aplikacije. Zbog toga programeri koji zarađuju novac osmišljavajući algoritme žele zaštiti svoje intelektualno vlasništvo i ne želie da netko njihov proizvod lako otvori i otkrije kako funkcionira. Osim toga reverzno inženjerstvo programa predstavlja i sigurnosni rizik jer to olakšava napadaču da pronađe moguće slabosti u programu.
Zbog toga je smišljen način otežavanja reverznog inženjerstva koda, a zove se obfuskacija. Obfuskacija je proces pomoću kojeg napravimo nešto čitko teško razumljivio. U svijetu softwarea kod se obfuskira tako da se imena varijabli, metoda i klasa promjene u besmisleni niz znakova tako da je teško naći smisao u kodu, dodaje se kod koji ne radi ništa kako bi zbunio čitatelja, kriptiraju se dijelovi koda, itd. Kod se obuskira pomoću alata koji se zove obfuskator.
Najpoznatiji obfuskatori za Android su ProGuard i DexGuard. Alati su razvijeni od firme Guard Square, razlika je u tome što je ProGuard besplatan alat otvorenog koda dok je DexGuard komercijalni proizvod. Osim toga ProGuard je optimizator Java bajt koda, minimizira, obfuskira i optimizira desktop, Android i ugrađene aplikacije. DexGuard radi sve što i ProGuard. ali je specifično rađen za Android aplikacije. Štiti od statičke i dinamičke analize aplikacije, dodaje mnogo slojeva kriptiranja i obfuskacije i procesira sve komponente aplikacije.
U ovom primjeru ću pokazati jednostavnu aplikaciju koja se sastoji od 2 ekrana. Prvi ekran očekuje unos šifre od korisnika kako bi mu pokazao sljedeći ekran na koje je tajna. Ako korisnik upiše netočnu šifru neće ga propustiti dalje. Aplikacija je namjerno isprogramirana nesigurno i šifra je stavljena u izvorni kod. Pokušat ćemo obfuskirati aplikaciju, te pokušati reveznim inženjerstvom doći do šifre.
Uključivanje obfuskacije nije komplicirano ako koristimo Android studio, samo je potrebno u build.gradle datoteci u modulu app postaviti minifyEnabled na true.
release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' }
proguard-rules.pro je datoteka unutar našeg projekta pomoću koje možemo definirati pravila koja govore ProGuardu koje datoteke sačuvati, a ne maknuti tokom procesa smanjivanja aplikacije. Mi nećemo dodati ništa jer nam je dovoljno ono što je zapisano u getDefaultProguardFile('proguard-android.txt').
Nakon što je sve definirano generiramo release verziju aplikacije, kako to napraviti možete pronaći ovdje.
Nakon svakog builda ProGuard generira dodatne datoteke u app/build/outputs/mapping/release/
među kojima je nama najzanimljiviji mapping.txt u kojem su prijevodi između svih originalnih nazivlja i onih koje je zadao ProGuard. Kada bi imali ovu datoteku za neku obfuskiranu aplikaciju mogli bi lako obrnuti proces. Samo napomena čitatelju ako radi Android aplikacije da ne izgubi ovu datoteku jer kasnije neće moći debuggirati logove, odnosno biti će jako teško.
Recimo u našem slučaju da mi ne posjedujemo tu datoteku i da idemo na isti način kao u prvom primjeru disassemblati aplikaciju. Ponovno smo iskoristili apktool, ali ovog puta na debug veziji i release (debug nije obfuskirana, release je). Gledajući osnovnu strukturu obe verzije se sastoje od istih direktorija, ali neke datoteke se zovu drugačije jer su im imena obfuskirana i na release verziji imamo puno manje release smali datoteka jer ProGuard minificira kod odnosno briše ono što nije skroz potrebno. U obe verzije se nalaze MainActivity.smali
datoteka koja sadrži metode iz glavne aktivnosti.
Gledajući datoteke možemo primjetiti da se u obfuskiranom dijelu koda metoda zove a, ne piše naziv ulaznog parametra, ali ostalo je relativno slično i ono što je najbitnije da se hardkodirani tekst koji je šifra vidljiv čak i kad je aplikacija obfuskirana pomoću ProGuarda. Mi naravo ne bi odmah znali da je to šifra, ali možemo primjetiti da je to string koji ne bi očekivali u aplikaciji i poziva se metoda equals koja provjerava jednakost stringova. Ove činjenice nam mogu biti dovoljan povod za testiranje ove šifre. Isto tako smo i mogli zamjeniti tu vrijednost za prazan string i mogli bi ući u tajnu aktivnost bey ikakvog ulaza!
Primjer 3 - Analiza Erste aplikacije
U zadnjem primjeru ću pokazati kako dohvatiti aplikaciju i saznati osnovne informacije o njoj statičkom analizom. Za primjer ću koristiti Erste Android aplikaciju koju već imam instaliranu na mobitelu. Aplikaciju ću dohvatiti na računalo pomoću naredbe
adb pull /data/app/com.infinum.erste-2/base.apk ~/Desktop/erste.apk
. Nakon izvršenja naredbe na računalu se nalazi .apk datoteka koju pomoću apktoola disassemblamo: apktool d erste.apk
. Sad imamo erste datoteku koju možemo analizirati.
Prvo ćemo početi s promatranjem AndroidManifest.xml datoteke, možemo vidjeti da aplikacija traži preko 20 dozvola, koristi google mape, crashlytics, neka vlastita 3 servisa i ima preko 100 aktivnosti što nam govori da je aplikacija stvarno velika. Otvaranjem smali datoteke možemo vidjeti da su neke datoteke nazvane samo jednim slovom po čemu možemo zaključiti da je aplikacija obfuskirana. Promatrajući imena datoteka koje nisu obfuskirane možemo prepoznati poznate biblioteke poput microblink, firebase, gson, itd. Pomoću ovih analizamožemo pretpostaviti koje funkcije može obavljati aplikacija i što se vjerojatno u njoj nalazi. Ovakva statička analiza se često koristi za provjeru da li neka aplikacija sadrži maliciozni kod.
Cilj je bio i promjeniti nešto unutar aplikacije, ali to bi zahtjevalo puno veću analizu zbog količine koda kojeg sadrži.
Literatura
- David Griffiths (2017.) How Android Apps are Built and Run, Dostupno 29.01.2018 https://github.com/dogriffiths/HeadFirstAndroid/wiki/How-Android-Apps-are-Built-and-Run
- Tim Strayyere, Jon Sawyer, Caleb Fenton (2015.) Offensive and defensive android reverse engineering, Dostupno 29.01.2018 https://github.com/rednaga/training/tree/master/DEFCON23
- Wikipedia (2018.) Android application package, Dostupno 29.01.2018 https://en.wikipedia.org/wiki/Android_application_package
- Wikipedia (2017.) Android Runtime, Dostupno 29.01.2018 https://en.wikipedia.org/wiki/Android_Runtime
- Wikipedia (2018.) Dalvik (software), Dostupno 29.01.2018 https://en.wikipedia.org/wiki/Dalvik_(software)
- Pau Oliva Fora (2014.) Beginners Guide to Reverse Engineering Android Apps, Dostupno 29.01.2018 https://www.rsaconference.com/writable/presentations/file_upload/stu-w02b-beginners-guide-to-reverse-engineering-android-apps.pdf
- GuardSquare (2017.) DexGuard vs. ProGuard, Dostupno 29.01.2018 https://www.guardsquare.com/en/blog/dexguard-vs-proguard
- Android Developers (2017.) Shrink Your Code and Resources, Dostupno 29.01.2018 https://developer.android.com/studio/build/shrink-code.html