zaštita mobilnih aplikacija od zlonamjernih...
TRANSCRIPT
SVEUCILIŠTE U ZAGREBUFAKULTET ELEKTROTEHNIKE I RACUNARSTVA
DIPLOMSKI RAD br. 709
Zaštita mobilnih aplikacija odzlonamjernih izmjena
Viktor Berger
Zagreb, lipanj 2014.
iii
SADRŽAJ
1. Uvod 1
2. Arhitektura operacijskog sustava Android 22.1. Programski jezik i virtualni stroj . . . . . . . . . . . . . . . . . . . . 2
2.2. Proces izgradnje aplikacija . . . . . . . . . . . . . . . . . . . . . . . 3
2.3. Datoteka apk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4. Datoteka dex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.5. Izvodenje aplikacija . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3. Analiza izvornog teksta aplikacija 83.1. Reverzni inženjering Android aplikacija . . . . . . . . . . . . . . . . 8
3.2. Staticka analiza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2.1. Alati dx i dexdump . . . . . . . . . . . . . . . . . . . . . . . 11
3.2.2. Alat dex2jar . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2.3. Alat Dare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2.4. Alati Smali i Baksmali . . . . . . . . . . . . . . . . . . . . . 12
3.2.5. Alat jd-gui . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2.6. Alat Jad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2.7. Radno okruženje dexter . . . . . . . . . . . . . . . . . . . . 15
3.2.8. Radni okvir Apktool . . . . . . . . . . . . . . . . . . . . . . 16
3.2.9. Radni okvir Radare . . . . . . . . . . . . . . . . . . . . . . . 16
3.2.10. Radni okvir Androguard . . . . . . . . . . . . . . . . . . . . 16
3.2.11. Alat IDAPro . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2.12. Santoku Linux . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.3. Dinamicka analiza . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.3.1. Radni okvir DroidBox . . . . . . . . . . . . . . . . . . . . . 17
3.3.2. Servis Andrubis . . . . . . . . . . . . . . . . . . . . . . . . . 17
iv
4. Zaštita izvornog teksta aplikacija 184.1. Dinamicko ucitavanje kriptiranog medukôda . . . . . . . . . . . . . . 18
4.2. Samopromjenjivi izvorni tekst programa . . . . . . . . . . . . . . . . 19
4.3. Arhitektura klijent-poslužitelj . . . . . . . . . . . . . . . . . . . . . . 19
4.4. Detekcija izmjene izvornog teksta programa . . . . . . . . . . . . . . 20
4.5. Stjecanje administratorskih ovlasti u Android sustavu . . . . . . . . . 21
5. Obfusciranje izvornog teksta programa 235.1. Leksicke transformacije . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.2. Transformacije kontrole toka programa . . . . . . . . . . . . . . . . 26
5.2.1. Transformacije grupiranja izvornog teksta programa . . . . . 26
5.2.2. Transformacije redoslijeda . . . . . . . . . . . . . . . . . . . 29
5.2.3. Transformacije izracuna . . . . . . . . . . . . . . . . . . . . 30
5.3. Transformacije podataka . . . . . . . . . . . . . . . . . . . . . . . . 34
5.3.1. Transformacije pohrane i kodiranja . . . . . . . . . . . . . . 34
5.3.2. Transformacije grupiranjem . . . . . . . . . . . . . . . . . . 36
5.3.3. Transformacije redoslijeda . . . . . . . . . . . . . . . . . . . 37
5.4. Alati za obfuskaciju izvornog teksta programa . . . . . . . . . . . . . 37
5.4.1. Alat ProGuard . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.4.2. Alat DexGuard . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.4.3. Alat DashO . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
6. Programski sustav 416.1. Motivacija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.2. Implementacija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.2.1. Komunikacija . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.2.2. Klijentska aplikacija . . . . . . . . . . . . . . . . . . . . . . 43
6.2.3. Poslužiteljska aplikacija . . . . . . . . . . . . . . . . . . . . 45
6.3. Simulacija napada na klijentsku aplikaciju . . . . . . . . . . . . . . . 45
6.4. Ranjivosti sustava . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
7. Zakljucak 53
Literatura 54
v
1. Uvod
Android je popularni operacijski sustav otvorenog kôda u vlasništvu tvrtke Google,
namijenjen izvodenju na pametnim telefonima. U posljednjih nekoliko godina stekao
je veliku popularnost i pokrece milijune mobilnih uredaja diljem svijeta. Posljedicno,
razvilo se i tržište mobilnih aplikacija. Danas ih Google-ov servis za distribuciju apli-
kacija Android Market broji preko milijun. S obzirom na tako veliku korisnicku bazu,
sve se veca pažnja posvecuje sigurnosti Android aplikacija.
Android aplikacije distribuiraju se u obliku relativno jednostavnog medukoda, te
je njihov reverzni inženjering time vrlo jednostavan. S obzirom na to da bi nezavisni
programeri i razne kompanije htjeli sacuvati intelektualno vlasništvo poput posebnih
algoritama i struktura podataka, u interesu im je onemoguciti ili barem otežati reverzni
inženjering. S druge strane, zlonamjerni programeri mogu dekompajlirati aplikaciju,
izmijeniti izvorni tekst programa ili ubaciti zlocudni kôd, ponovno zapakirati aplika-
ciju i distribuirati ju predstavljajuci ju kao originalnu. Korisnik tada koristi zlocudnu
aplikaciju bez da je toga svjestan, što ga cini lakom metom zlonamjernih programera.
Ovaj rad bavi se problematikom zaštite izvornog teksta Android aplikacija. U
njemu su opisane tehike i alati korišteni prilikom reverznog inženjeringa aplikacija, te
metode i alati korišteni za zaštitu izvornog teksta aplikacija. Na kraju je dan prijedlog
i implementacija jednostavnog sustava cija je zadaca detektirati eventualne zlocudne
promjene u klijentskoj Android aplikaciji.
1
2. Arhitektura operacijskog sustavaAndroid
U ovom poglavlju opisana je arhitektura operacijskog sustava Android. Opisano je
okruženje u kojem se izvode aplikacije (eng. runtime environment), sadržaj i arhitek-
tura aplikacija, te proces izgradnje (eng. build) aplikacija.
2.1. Programski jezik i virtualni stroj
Android aplikacije pisane su u programskom jeziku Java. Java je poznata kao platform-
ski neovisan jezik. Njezina platformska neovisnost omogucena je postojanjem tzv.
medukôda (eng. bytecode) i virtualnog stroja (eng. virtual machine) koji taj medukod
izvodi. Za svaki operacijski sustav postoji posebna implementacija virtualnog stroja.
Java program se dakle ne prevodi direktno u izvršni kôd, nego u medukôd izvoden od
strane virtualnih strojeva.
Slika 2.1: Proces prevodenja i izvodenja Java koda u standardnom Java okruženju
Na slici 2.1 prikazan je pojednostavljeni proces prevodenja i izvodenja izvornog
teksta Java programa. U stvarnosti, svaki razred u izvornom tekstu programa tokom
prevodenja zapisan je u jednu .class datoteku. Na primjer, prevodenje .java datoteka
s jednim javnim razredom koji u sebi definira dva staticka unutrašnja razreda i jedan
anonimni razred rezultiralo bi s cetiri .class datoteke.
Operacijski sustav Android koristi posebnu inacicu virtualnog stroja po imenu Dal-
vik. Dalvik je dizajniran na nacin da ucinkovito koristi ogranicene resurse (npr. me-
morija, procesor) mobilnih uredaja. Konvencionalni Java virtualni strojevi bazirani su
2
na stogu, dok je Dalvik virtualni stroj baziran na registrima [19]. Registarska arhitek-
tura zahtijeva manji broj izvedenih naredbi virtualnog stroja. S druge strane izvorni
tekst programa za takvu arhitekturu duži je nego za stogovsku, što za sobom povlaci
zanemarivo duže vrijeme ucitavanja naredbi. Vrijeme izvodenja programa na virtual-
nom stroju registarske arhitekture u prosjeku je 32.3% krace od vremena izvodenja na
virtualnom stroju stogovske arhitekture [19].
Virtualni strojevi interpretiraju medukôd. Kako bi to bilo moguce, medukôd u sebi
mora zadržati veliku kolicinu informacija iz izvornog teksta programa. Zbog toga,
aplikacije pisane za Dalvik podložne su tehnikama analize i izlucivanja izvornog teksta
programa, koje se ponekad svode na svega nekoliko klikova mišem.
Slika 2.2: Proces prevodenja i izvodenja Java koda u Android okruženju
2.2. Proces izgradnje aplikacija
Izrgadnja (eng. build) aplikacija za operacijski sustav Android složen je proces koji
ukljucuje mnogo alata i raznovrsnih datoteka. Proces izgradnje detaljno je prikazan na
slici 2.3.
Androidov alat za pakiranje resursa (eng. Android Asset Packaging Tool)
kompajlira resurse aplikacije i proizvodi datoteku R.java. Alat aidl pretvara sucelja
definirana Androidovim jezikom za definiciju sucelja (eng. Android Interface Defi-
nition Language) u Java sucelja. Sve datoteke s Java izvornim tekstom programa,
R.java datoteka i Java sucelja proizvedena u prošlom koraku predaju se kompajleru
koji proizvodi .class datoteke. Alat dx preuzima nastale .class datoteke, sve ostale
.class datoteke ukljucene u projekt kao i korištene knjižnice koda, te ih prevodi u Dal-
vik medukôd. Rezultat ovog koraka je .dex datoteka. Ta .dex datoteka prosljeduje se
alatu apkbuilder zajedno s resursima kompajliranim u prvom koraku i ostalim resur-
sima. Apkbuilder primljene datoteke pakira u .apk datoteku, koja se potom prosljeduje
programu Jarsigner. Jarsigner potpisuje .apk datoteku privatnim kljucem programera
aplikacije. Postoje dvije vrste kljuceva. Prva vrsta se koristi tokom razvoja aplikacije,
dok se druga koristi za potpisivanje konacne produkcijske verzije aplikacije. Potpisiva-
nje ne povecava sigurnost nego služi kao sredstvo identifikacije programera. Ukoliko
3
Slika 2.3: Potpuni proces izgradnje aplikacija za Android sustav [2]
je aplikacija dovršena i spremna za distribuciju, nakon potpisivanja .apk datoteke, ona
još prolazi kroz alat zipalign koji ju optimizira u smislu zauzeca memorije.
4
2.3. Datoteka apk
Rezultat procesa izgradnje aplikacije jedna je .apk datoteka. Njen sadržaj u grubim je
crtama prikazan na slici 2.4. U suštini, ta datoteka je zip arhiva s promijenjenom eks-
tenzijom iz .zip u .apk. Sadržaj .apk datoteke može varirati od aplikacije do aplikacije,
ali u pravilu sadrži iduce elemente:
META-INF
MANIFEST.MF manifest datoteka koja sadrži popis datoteka aplikacije
i njihove odgovarajuce kriptografske sažetke
CERT.RSA certifikat aplikacije
CERT.SF sadrži popis resursa i sažetke odgovarajucih linija u datoteci
MANIFEST.MF. Koristi se algoritam SHA-1
res sadrži resurse koji ne zahtijevaju prevodenje poput slika i audio zapisa.
classes.dex sadrži medukôd aplikacije koji izvodi Dalvik.
Resources.arsc prevedeni resursi aplikacije
AndroidManifest.xml binarna datoteka u kojoj su navedene sve komponente koje sa-
cinjavaju aplikaciju, kao i dozvole koje aplikacija zahtijeva od sustava kako bi
mogla normalno funkcionirati
Android sustav prilikom instalacije provjerava digitalni potpis aplikacije sadržan u
datoteci CERT.RSA. Sve aplikacije dakle moraju biti potpisane kako bi se mogle ins-
talirati. Iako Android od aplikacije ocekuje certifikat s valjanim digitalnim potpisom,
taj certifikat ne mora biti ovjeren od strane nekog certifikacijskog tijela. Certifikati su
dakle samopotpisani.
Slika 2.4: Sadržaj .apk datoteke [2]
5
2.4. Datoteka dex
Kao što je prikazano u potpoglavlju 2.2, izvorni tekst Java programa normalno se
prevodi u .class datoteke koje se potom prebacuju u .dex datoteku. To je datoteka izvr-
šnog Dalvik medukôda. U usporedbi s .jar arhivom koji sadrži mnogo .class datoteka,
Androidov .apk arhiv sadrži samo datoteku classes.dex s jednakim informacijama. An-
droidova .dex datoteka optimizirana je za uštedu memorije, koja se temelji na principu
dijeljenja podataka izmedu razreda.
Slika 2.5: Struktura .dex datoteke
Na slici 2.5 prikazano je mapiranje .class datoteka u jednu .dex datoteku. Ono se
provodi jednako za sve .class datoteke koje sacinjavaju aplikaciju. Najveca razlika
izmedu ova dva formata, osim u broju sadržanih razreda, je upravljanje konstantama.
Pod konstante se ubrajaju konstantni znakovni nizovi, nazivi varijabli, clanskih vari-
jabli, razreda, sucelja i metoda. Analiza Java .class datoteka pokazala je da veci dio
datoteke zauzima heterogeni bazen konstanti. Android taj problem rješava spremajuci
sve konstante istog tipa za sve razrede u jedan bazen konstanti. Na taj nacin smanjuje
broj ponavljanja konstanti kroz više datoteka efektivno smanjujuci zauzece memorije .
Kao primjer možemo uzeti više metoda koje kao povratni tip imaju niz znakova,
odnosno String. To ce rezultirati ponavljanjem Ljava/lang/String; u potpisima svih
metoda. Android medutim sprema Ljava/lang/String;u bazen konstanti imena razreda,
6
te ga svaka metoda referencira. To rezultira manjim brojem ponavljanja i vecim brojem
referenciranja. Na taj nacin, velicina datoteke se smanjuje i do 50% [19],
2.5. Izvodenje aplikacija
Korisnici mogu preuzeti Android aplikacije sa službenog Google-ovog servisa po
imenu Google Play ili s neke druge internet stranice. Po završetku preuzimanja aplika-
cije s Google Play servisa na mobilni uredaj, automatski se pokrece instalacija aplika-
cije i korisnik nema izravan pristup .apk datoteci. U slucaju preuzimanja aplikacije s
neke druge stranice, korisnik mora prebaciti preuzetu .apk datoteku na pametni telefon
putm USB kabla i potom ju rucno instalirati. Nakon završetka instalacije, aplikacija je
spremna za korištenje.
Android je izgraden povrh jezgre operacijskog sustava Linux. Linux je po-
pularni operacijski sustav otvorenog koda koji je napisao finski racunarski znanstve-
nik Linus Torvalds. On je u pravom smislu rijeci višekorisnicki operacijski sustav i
tu cinjenicu Android iskorištava. Prilikom instalacije, svakoj aplikaciji dodijeljen je
jedinstveni korisnicki identifikator (UID). Prilikom izvodenja, Android svaku aplika-
ciju tretira kao posebnog korisnika. Time se svaka aplikacija izvodi u svom procesu i
izolirana je od ostatka sustava. Pristup resursima uredaja kontroliran je od strane ope-
racijskog sustava i temeljen je na dozvolama definiranim u datoteci Manifest.xml koju
sadrži svaka aplikacija.
Svaka aplikacija izvodi se u vlastitom primjerku virtualnog stroja. Sve aplika-
cije koriste biblioteke koje su dio sustava Android (eng. core libraries). Aplikacije te
datoteke uglavnom citaju i ne pišu u njih. Pokretanje novog virtualnog stroja prilicno
je sporo i ne radi se za svaku aplikaciju posebno. Prilikom paljenja uredaja stvara se
proces Zygote u kojem se pokrece virtualni uredaj i inicijaliziraju se Androidove knjiž-
nice kôda. Pretpostavka je da mnogo aplikacija koristi mnogo Android datoteka. Svaki
put kada korisnik želi pokrenuti novu aplikaciju, sustav stvara proces dijete iz procesa
Zygote. Tako se otklanja potreba za opetovanom inicijalizacijom novih instanci virtu-
alnih strojeva i knjižnica koda, te se minimizira vrijeme pokretanja aplikacije.
7
3. Analiza izvornog teksta aplikacija
Središnji cilj analize izvornog koda aplikacija otkrivanje je nacina na koji one rade.
Analiza se dijeli na staticku i dinamicku, ovisno o kontekstu njenog provodenja. Za
provodenje staticke analize program nije potrebno izvoditi. Potreban je samo pristup
.apk datoteci. S jedne strane moguce je analizirati datoteku Manifest.xml koja sadrži
meta podatke poput komponenata aplikacije, dozvola i slicno. S druge strane dekom-
pajliranjem iz medukoda moguce je u vecoj ili manjoj meri povratiti izvorni tekst pro-
grama. Prilikom staticke analize izvornog teksta programa prikupljaju se informacije
poput identifikatora varijabli i metoda, znakovnih konstanti, te se dobiva uvid u struk-
turu programa. Staticka analiza medutim zakazuje ako u aplikaciji postoji dinamicko
ucitavanje kôda ili ako su djelovi kôda enkriptirani. U takvim situacijama koristi se
dinamicka analiza.
Dinamicka analiza, za razliku od staticke, zahtijeva izvodenje programa i pruža in-
formacije dostupne samo u trenutku izvodenja, nevidljive iz perspektive staticke ana-
lize. Prilikom dimanicke analize program se pokrece u kontroliranom okruženju i bi-
lježi se njegova interakcija s mrežom ili sustavom. U tom slucaju moguce je analizirati
dinamicko ucitavanje kôda i dekriptirati enkriptirane dijelove kôda. Kako bi se dobio
potpun uvid u nacin na koji aplikacija funkcionira, najcešce je potrebno koristiti oba
pristupa analizi.
3.1. Reverzni inženjering Android aplikacija
Opcenito govoreci, izraz reverzni inženjering primjenjiv je na sva podrucja djelovanja
inženjera, a odnosi se na shvacanje procesa rada zatvorenog sustava (crne kutije). U
kontekstu Android sustava, reverzni inženjering odnosi se na analizu i shvacanje izvor-
nog teksta programa ili mnemonika dobivenih izlucivanjem iz arhive aplikacije(.apk
datoteka). Mnemonici su tekstualna i ljudima shvatljiva reprezentacija medukôda (eng.
bytecode). Zahvaljujuci relativno malom i jednostavnom skupu naredbi koje inter-
pretira Androidov virtualni stroj, reverzni inženjering je relativno jednostavan. Mne-
8
monici se dobivaju tzv. disasembliranjem Dalvikovog medukôda pohranjenog u cla-
sses.dex datoteci unutar arhive aplikacije. Disasembliranje je proces koji niz bajtova
pretvara u niz ekvivalentnih mnemonika.
U primjeni su dvije tehnike disasembliranja [26]:
• linearni prolazak (eng. linear sweep)
• rekurzivni obilazak (eng. recursive traversal)
Algoritam linearnog prolaska
Algoritam linearnog prolaska najjednostavniji je moguci pristup disasembliranju
koda. Kao što samo ime govori, on prolazi kroz medukôd pokušavajuci slijedno pre-
voditi naredbe. Problem kod takvog pristupa je što ne može razlikovati bajtove naredbi
od proizvoljno ubacenih bajtova ili bajtova podataka. Taj pristup ima velikih problema
s uvjetnim i bezuvjetnim skokovima.
Algorithm 1 Algoritam linearnog prolaska
proc linear_sweep(addr) ≡begin
while (start_addr <= addr < end_addr)
I := decode instr at addr;
addr+ = length(I).end
Algoritam rekurzivnog obilaska
Algoritam rekurzivnog obilaska rješava problem skokova, odnosno kontrolnog grana-
nja toka programa. Prilikom rada, on u obzir uzima sve moguce ishode evaluacije
naredbe kontrole toka. Na taj nacin obilazi sav kod koji se može dosegnuti i koji se
izvodi, zaobilazeci dijelove cije bi disasembliranje dovelo do neispravnih rezultata.
Pseudokod tog algoritma prikazan je u nastavku.
9
Algorithm 2 Algoritam rekurzivnog obilaskal
proc recursive_traversal(addr) ≡begin
while (start_addr <= addr < end_addr)
if (addr.visited == true) return;
I := decode instr at addr;
addr.visited = true;
if (I is a branch of function call)
for each target t of I
recursive_traversal(t);
return;
else addr+ = length(I).end
Dekompajliranje
Osim izlucivanja mnemonika, iz medukôda je moguce u vecoj ili manjoj mjeri rekon-
struirati izvorni tekst aplikacije. Taj postupak naziva se dekompajliranje i obrnut je od
procesa kompajliranja.
Slika 3.1: Proces dekompajliranja Adnroid aplikacije
Na slici 3.1 prikazan je proces dekompajliranja na Android platformi. Prvi korak
je promjena formata medukôda iz Dalvikovog u standardni Java medukod. To za po-
sljedicu ima stvaranje .class datoteke za svaki razred iz dosadašnje jedne datoteke cla-
sses.dex kao što je objašnjeno u poglavlju 2. Nakon toga slijedi samo dekompajliranje
koje rezultira nizom .java datoteka koje sadrže izvorni tekst aplikacije. Na prethod-
noj slici ispod strelica navedeni su popularni, cesto korišteni alati za provodenje ovog
postupka. Oni su detaljnije opisani u iducem potpoglavlju.
10
3.2. Staticka analiza
U ovom odjeljku opisani su popularniji alati koji se koriste tokom staticke analize An-
droid aplikacija. Za neke od alata prikazani su i rezultati njihova rada nad jednostav-
nom referentnom aplikacijom, tocnije njenim .class i .dex datotekama, ciji je izvorni
tekst prikazan u nastavku.
package probna.aplikacija;
import android.app.Activity;
import android.os.Bundle;
public class Main extends Activity
{
/** Poziva se prilikom stvaranja aplikacije. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
Izvorni tekst programa 3.1: Izvorni tekst aplikacije
3.2.1. Alati dx i dexdump
Dx je prije spomenuti alat koji prebacuje Javine .class datoteke u .dex datoteku. Uz
odgovarajuce argumente, ovaj alat može ispisati sadržaj .dex datoteke. S druge strane,
dexdump je disasembler koji koristi metodu linearnog prolaska. Iz tog razloga daje
dobre rezultate s kodom koji nije obfusciran, ali odredene modifikacije izvornog teksta
programa mogu dovesti do njegovog neispravnog rada. Oba alata dostupna su kao dio
Android SDK1 i time su cesto korišteni u svrhe reverznog inženjeringa.
1Dostupno na web adresi: http://developer.android.com/sdk/index.html
11
3.2.2. Alat dex2jar
Alat Dex2jar je konverter koji kao ulaz prima .dex datoteku i pretvara ju u odgova-
rajucu .jar arhivu s .class datotekama [23]. Alat se sastoji od nekoliko komponenti
koje omogucuju citanje, manipulaciju i prevodenje .dex/.odex datoteka u druge for-
mate. Ovim alatom je moguce raspakirati aplikaciju, prebaciti medukôd u Jasmin2
format, izmijeniti ga i potom ga ponovno zapakirati te potpisati. Osim toga alat nudi
mogucnost deobfuskacije koda. Medutim, ta radnja za razliku od ostalih nije automa-
tizirana, zbog cinjenice da se obfuskacijom nepovratno gube sve originalne vrijednosti
identifikatora. Ovaj alat cesto je dio vecih sustava za analizu izvornog teksta Android
aplikacija. Isto tako cesto se koristi u kombinaciji s dekompajlerima Java kôda poput
JD-GUI i JAD.
3.2.3. Alat Dare
Kao alternativa alatu dex2jar može se koristiti alat Dare. Alat Dare takoder je nami-
jenjen rekonstrukciji .class datoteka iz .dex datoteke cime se omogucuje daljnje disa-
sembliranje/dekompajliranje pomocu drugih alata. On je mocnija i preciznija zamjena
za stariji alat iste namijene DED3.
3.2.4. Alati Smali i Baksmali
Alat Smali je asembler za dex format koji koristi Adroidov virtualni stroj, Dalvik. Bak-
smali je disasembler istovrsnih datoteka. Njihova imena na islandskom jeziku znace
upravo asembler i disasembler, a odabrana su jer je i sam Dalvik dobio ime po is-
landskom ribarskom selu. Sintaksa koju koristi Smali bazirana je na Jasmin/dedexer
sintaksi. U kombinaciji, ovi alati mogu biti korišteni za dekompilaciju, modifikaciju i
ponovno prevodenje Android aplikacija. Baksmali za pronalaženje instrukcija koristi
metodu rekurzivnog obilaska i cesto je korišten alat pri obrnutom inženjeringu Android
aplikacija, kako samostalno, tako i od strane drugih alata. Jedan od popularnijih alata
koji ga koristi je apktool. Medukôd referentne aplikacije disasembliran pomocu alata
baksmali prikazan je u nastavku.
2Dostupno na web adresi: http://jasmin.sourceforge.net/3Dostupno na web adresi: http://siis.cse.psu.edu/ded/
12
.class public Lprobna/aplikacija/Main;
.super Landroid/app/Activity;
.source "Main.java"
# direct methods
.method public constructor <init>()V
.registers 1
.prologue
.line 6
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
return-void
.end method
# virtual methods
.method public onCreate(Landroid/os/Bundle;)V
.registers 3
.param p1, "savedInstanceState" # Landroid/os/Bundle;
.prologue
.line 12
invoke-super {p0, p1},
Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
.line 13
const/high16 v0, 0x7f030000
invoke-virtual {p0, v0},
Lprobna/aplikacija/Main;->setContentView(I)V
.line 14
return-void
.end method
Izvorni tekst programa 3.2: Rezultat rada disasemblera baksmali
3.2.5. Alat jd-gui
Alat JD-GUI jedan je od najpoznatijih dekompajlera Java kôda. Vrlo je jednostavan
za korištenje i radi na drag-and-drop principu. Prihvaca pojedinacne .class datoteke,
ali i cijele .jar arhive. Prilikom rada s .jar arhivama, u grafickom sucelju rekonstruira
13
originalni projekt, stvarajuci stablastu strukturu paketa. U tom procesu .class datoteke
prevode se natrag u .java datoteke cime cijeli originalni izvorni kod programa postaje
dostupan napadacu. Osim u samostalnoj platformski neovisnoj inacici ovaj alat dos-
tupan je i kao dodatak za razvojno okruženje Eclipse. Rezultat rada alata nad .class
datotekom referentne aplikacije prikazan je na slici 3.2.
Slika 3.2: Izvorni tekst programa dobiven dekompajliranjem .class datoteke pomocu alata JD-
GUI
3.2.6. Alat Jad
Alat Jad je brzi i besplatni dekompajler .class datoteka. Za razliku od JD-GUI de-
kompajlera ovaj alat namijenjen je uporabi u naredbenom retku. Unatoc tome, vrlo je
jednostavan za korištenje. On pruža opciju anotacije izvornog teksta programa odgo-
varajucim dijelovima asemblerskim mnemonicima iz kojih je on potekao. To ga cini
idealnim alatom za shvacanje asemblerskog kôda. Primjer rada alata Jad koji u ko-
mentarima disasembliranog izvornog teksta programa sadrži mnemonike prikazan je u
nastavku.
14
package probna.aplikacija;
import android.app.Activity;
import android.os.Bundle;
public class Main extends Activity
{
public Main()
{
// 0 0:aload_0
// 1 1:invokespecial #1 <Method void Activity()>
// 2 4:return
}
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// 0 0:aload_0
// 1 1:aload_1
// 2 2:invokespecial #2 <Method void
Activity.onCreate(Bundle)>
setContentView(0x7f030000);
// 3 5:aload_0
// 4 6:ldc1 #3 <Int 0x7f030000>
// 5 8:invokevirtual #4 <Method void
setContentView(int)>
// 6 11:return
}
}
Izvorni tekst programa 3.3: Izvorni tekst programa dobiven radom alata Jad
3.2.7. Radno okruženje dexter
Dexter je bogato online okruženje za staticku analizu aplikacija. Osim što uspješno
disasemblira kôd s ubacenim beskorisnim bajtovima, pruža uvid u omjer obfusciranog
i neobfusciranog izvornog teksta programa, dopuštenja aplikacije, gradivne elemente
15
aplikacije, te pregled svih znakovnih nizova korištenih u aplikaciji. Takoder pruža
mogucnost vizualizacije grafa aplikacije.
3.2.8. Radni okvir Apktool
Apktool je mocan alat koji automatizira disasembliranje i ponovnu izgradnju ta paki-
ranje aplikacija. Kao disasembler koristi prije spomenuti Baksmali. Takoder ukljucuje
i Smali debugger cime omogucuje detaljnije proucavanje raspakiranog i disasemblira-
nog kôda. Osim toga, Apktool koristi i alat aapt koji je dio Android SDK. Njegova
namjena je raspakiravanje resursa iz .apk arhive. Apktool može rekonstruirati i origi-
nalni sadržaj binarnih .xml datoteka (Manifest.xml) sadržanih u arhivi.
3.2.9. Radni okvir Radare
Radni okvir(eng. framework) za disasembliranje i analizu aplikacija. Ovaj alat pose-
ban je po tome što osim potpuno automatiziranog disasembliranja pomocu rekurzivnog
obilaska omogucuje i odredivanje pocetne adrese od koje bi disasembliranje trebalo
krenuti. Na taj nacin mogu se zaobici neke zamke koje bi mogle prouzrokovati neis-
pravan rad ostalih alata. Osim toga, alat omogucuje i vizualizaciju aplikacije u obliku
grafova što olakšava proces analize.
3.2.10. Radni okvir Androguard
Androguard je radni okvir napisan programskom jeziku python. On omogucuje rad s
.dex i .odex datotekama, .apk arhivom, binarnim xml datotekama i .arsc datotekama.
Pruža mogucnosti disasembliranja/dekompajliranja aplikacija. Kao glavni dekompaj-
ler koristi DAD (Dalvik dekompajler). DAD direktno iz Dalvikovog medukôda stvara
izvorni Java kod. Osim DAD dekompajlera omogucava integraciju s ostalim dekom-
pajlerima poput JD-GUI, DED i JAD.
3.2.11. Alat IDAPro
IDAPro komercijalni je alat za reverzni inžinjering programa napisanih za razne arhi-
tekture. Podržava i disasembliranje Dalvik medukôda, medutim ne podržava moguc-
nost dekompajliranja. Ovaj alat kao i neki od prije spomenutih omogucava vizualiza-
ciju programa u obliku grafa.
16
3.2.12. Santoku Linux
Santoku je distribucija operacijskog sustava Linux namijenjena mobilnu forenziku,
analizu zlocudnih aplikacija i testiranje sigurnosti aplikacija. Iako je ovaj alat pr-
venstveno namijenjen testiranju za štite aplikacija, on se vrlo lako može primijeniti
za analizu istih. To proizlazi iz cinjenice da ova distribucija dolazi sa vecinom predins-
taliranih prije spomenutih alata za analizu aplikacija iz ovog poglavlja.
3.3. Dinamicka analiza
Broj alata za dinamicku analizu Android aplikacija vrlo je velik te je stoga u nastavku
odabrano i opisano nekoliko poznatijih i cešce korištenih.
3.3.1. Radni okvir DroidBox
DroidBox je niz python skripti koje omogucuju dinamicku analizu Android aplikacija.
Po završetku analize, generiraju izvještaj koji sadrži podatke poput:
• kriptografskih sažetaka analiziranog paketa
• mrežnih podataka
• ulazno-izlaznih operacija
• pokrenutih servisa
• dinamicki ucitanih razreda pomocu razreda DexClassLoader
• kriptografskih operacija obavljenih pomocu Android API-ja i sl.
Osim toga generira ponašajni graf aplikacije koji prikazuje vremenski slijed ope-
racija.
3.3.2. Servis Andrubis
Andrubis je nadogradnja online servisa za analizu zlonamjernih programa namijenje-
nih za operacijski sustav Windows. Osim staticke analize, obavlja i dinamicku analizu
koja izmedu ostalog ukljucuje i pracenje mrežne komunikacije kao i ulazno/izlazne
operacije poput pisanja i citanja u datoteke. Na kraju formira izvještaj s informacijama
prikupljenim tokom staticke i dinamicke analize u HTML i XML formatima. Procje-
njuje u kojoj mjeri je aplikacija zlonamjerna dajuci joj ocjenu na skali od 1 do 10.
17
4. Zaštita izvornog teksta aplikacija
Ovo poglavlje posveceno je tehnikama i alatima razvijenima u svrhu zaštite izvornog
koda od reverznog inženjeringa.
4.1. Dinamicko ucitavanje kriptiranog medukôda
Ideja dinamickog ucitavanja kriptiranog medukôda u aplikaciju vrlo je jednostavna.
Aplikacija se sastoji od dva dijela:
• podsustav za ucitavanje kriptiranog medukôda (eng. stub)
• kriptirani medukôd (kriptirana dex datoteka)
Korisnicka aplikacija obvezno sadrži prvi dio, dok kriptirani datoteka može biti
sadržana u .apk datoteci ili pohranjena na udaljenom serveru. U prvom slucaju takva
datoteka tretira se kao resurs i dohvat je trivijalan, dok je u drugom slucaju tu datoteku
potrebno dohvatiti preko mreže. Prvi korak rada podsustava za ucitavanje kriptirane
datoteke dohvat je kriptirane datoteke. U slucaju da je ista pohranjena na udaljenom
serveru, podsustav ju mora dohvatiti. Android sadrži odgovarajucu knjižnicu kôda1
koja tu radnju cini trivijalnom. Kada je datoteka ucitana, odnosno dohvacena, pod-
sustav ju mora dekriptirati. Ta radnja takoder se može izvršiti uporabom standardne
knjižnice kôda koja je dio Android platforme2. Nakon što je datoteka dekriptirana, ona
se ucitava u proces pomocu reflekcije koristeci mogucnosti razreda DexFile3. Izvrša-
vanjem tog posljednjeg koraka, sada dekriptirana dex datoteka ucitana je u memoriju i
aplikacija je spremna za korištenje.
Ideja ovog pristupa u smislu skrivanja izvornog teksta programa je otežavanje pris-
tupa izvornom obliku dex datoteke. Staticka analiza na ovaj nacin je odgodena. Kako
1Dostupno na web adresi: http://developer.android.com/reference/android/net/
package-summary.html2Dostupno na web adresi: http://developer.android.com/reference/javax/crypto/
package-summary.html3Dostupno na web adresi: http://developer.android.com/reference/dalvik/system/DexFile.html
18
bi napadac mogao izvršiti staticku analizu, prvo mora analizirati podsustav za ucitava-
nje kriptirane datoteke kako bi iz nje mogao izluciti izvorni tekst programa. Kako bi
se njegov posao još više otežao, nad izvornim tekstom sustava za ucitavanje kao i nad
izvornim tekstom programa u kriptiranoj datoteci moguce je primijeniti obfuskacijske
transformacije.
Ovakav pristup djeluje vrlo korisno. Medutim, specificnosti Android sustava cine
sav trud gotovo uzaludnim. Naime, kako bi virtualni stroj Dalvik mogao izvršiti me-
dukôd sadržan u raspakiranoj dex datoteci, sustav ju prvo mora pohraniti u datotecnom
sustavu. Prilikom pohrane, raspakirana datoteka postaje dostupna napadacu i sav trud
postaje uzaludan. Autor clanka [21] kao rješenje ovog problema predlaže preselje-
nje funkcionalnosti podsustava za ucitavanje kriptiranog medukôda na nativnu razinu
i korištenje Javinog sucelja prema nativnom kôdu (eng. Java Native Interface).
4.2. Samopromjenjivi izvorni tekst programa
Izvorni tekst programa koji ima sposobnost promjene tokom izvodenja naziva se samo-
promjenjivi izvorni tekst programa [21]. Takav izvorni tekst programski ima sposob-
nost promjene svog sadržaja tokom izvodenja cime se otežava njegova staticka analiza.
Ova tehnika može se koristiti kao alternativa dinamickom ucitavanju medukôda.
Medukôd korišten od strane virtualnog stroja Dalvik nema naredbe koje omogu-
cuju pristup i izmjenu medukôda koji je u toku izvršavanja. Zbog toga je nemoguce
pisati aplikacije samopromjenjivog programskog kôda izravno u Javi. Unatoc tome,
operacijski sustav Android ipak omogucuje ostvarenje ove tehnike kroz druge meha-
nizme. Jedan nacin je korištenje nativnog kôda koji se izvršava izravno na procesoru
uredaja i ima sposobnost izmjene izvornog teksta same aplikacije. Drugi je nacin ko-
rištenje samopromjenjivog nativnog kôda.
4.3. Arhitektura klijent-poslužitelj
S obzirom da obfuskacija nikada ne može u potpunosti zaštititi izvorni tekst programa
od napada, najbolje rješenje bilo bi izvorni tekst programa držati izvan dosega napa-
daca. Takav pristup zaštiti izvornog teksta programa moguce je ostvariti arhitekturom
klijent-poslužitelj. Ideja je ostvariti tanki klijent koji se daje korisniku. Na taj na-
cin, zlonamjernom programeru dostupan je samo izvorni tekst programa koji ostvaruje
komunikaciju s udaljenim poslužiteljem. U njemu u pravilu nije sadržano ništa inova-
19
tivno, vec samo genericki kôd sucelja i mrežne komunikacije. Sva aplikacijska logika
poput vlasnickih struktura podataka i algoritama smještena je na sigurni server kojem
ne može pristupiti nitko osim programera aplikacije. Na taj nacin, osjetljivi dijelovi
aplikacije nedostupni su i reverzni inženjering je nemoguc.
S druge strane postavlja se problem zlonamjerne izmjene klijentske strane aplika-
cije koju ovaj pristup ne sprijecava. Zlonamjerni programer može promijeniti neku
od funkcionalnosti sucelja, kao i adresu servisa s kojim klijent komunicira u svrhu
krade identiteta, lozinki, privatnih informacija ili financijskih sredstava. Kako bi se to
sprijecilo, takve promjene na neki bi nacin trebalo detektirati.
4.4. Detekcija izmjene izvornog teksta programa
Osim zaštite izvornog teksta programa i otežavanja reverznog inženjeringa u svrhu cu-
vanja algoritama i struktura podataka, programeru aplikacije od velike važnosti može
biti i detekcija zlonamjerne promjene u izvornom tekstu aplikacije.
Zamislimo situaciju u kojoj je programer stvorio mobilnu aplikaciju koja komu-
nicira sa serverom i izvršava vrlo važne transakcije. Napadac može dekompajlirati
aplikaciju, izmijeniti važan dio izvornog teksta programa koji upravlja transakcijama
te ponovno kompajlirati i potpisati aplikaciju. Korisnici koji instaliraju takvu aplika-
ciju na svoj mobilni uredaj nece biti svjesni da koriste zlocudnu inacicu aplikacije i
mogu biti ošteceni. U toj situaciji, programer aplikacije htjet ce razviti mehanizam
koji ce biti u stanju detektirati takve promjene i omoguciti programeru da reagira na
odgovarajuci nacin.
Provjera potpisa aplikacije
Android sustav prilikom instalacije aplikacije na uredaj provjerava digitalni potpis apli-
kacije. Aplikacije koje nisu ispravno potpisane Android odbija instalirati. To znaci da
svaka aplikacija mora biti potpisana privatnim kljucem svojstvenim za programera te
aplikacije. Svaka aplikacija dakle sadrži jedinstven potpis. Sam proces potpisivanja
detaljno je opisan u službenoj dokumentaciji sustava [2]. Potpisu aplikacije može se
pristupiti programski u trenutku izvodenja. Ovo je moguce iskoristiti na dva nacina.
Prvi nacin je da aplikacija korisniku dojavi da je neispravna i odbije nastavak izvodenja
(ili se jednostavno sruši),ako u trenutku izvodenja utvrdi da je njen potpis promijenjen.
Na taj nacin korisniku ce biti jasno da je sigurnost aplikacije kompromitirana. Drugi
slucaj je dojava zlocudne promjene na poslužitelj koji pruža usluge klijentskoj aplika-
20
ciji. Na taj nacin, sustav može odbiti nastavak pružanja usluge.
Provjera sažetka izvornog teksta programa
Drugi nacin detekcije promjene je provjera sažetka izvornog teksta aplikacije za vri-
jeme izvodenja. Sažetak se racuna iz datoteke classes.dex nakon cega ga je moguce
koristiti na isti nacin kao i potpis.
Oba nacina detekcije koda relativno je lako zaobici. U prvom slucaju napadac
može prilikom dinamicke analize izluciti potpis aplikacije i potom ga u izmjenjenoj
aplikaciji hardkodirati. U drugom slucaju napadac može izracunati sažetak izvornog
teksta aplikacije i takoder ga hardkodirati. U oba slucaja, poslužitelju ce stici ispravne
informacije i zlocudna promjena nece biti detektirana.
4.5. Stjecanje administratorskih ovlasti u Android sus-
tavu
Uredaji pogonjeni Android operacijskim sustavom poput tableta i pametnih telefona
dolaze s odredenim restrikcijama korisnickih ovlasti. Tvornicke postavke korisniku
onemogucuju pristup sistemskom dijelu datotecnog sustava, pristup hardware-u kao ni
promjenu/nadogradnju operacijskog sustava. Kako bi se te restrikcije zaobišle, uredaji
se moraju podvrgnuti odredenom postupku popularnog imena Rootanje. Rootanje je
proces koji korisniku uredaja daje administratorske ovlasti nad uredajem zaobilazeci
tvornicke postavke. Ime je preuzeto iz terminologije operacijskog sustava Linux, gdje
je naziv za korisnika s administratorskim ovlastima Root. Postupak stjecanja admi-
nistratorskih ovlasti razlikuje se od uredaja do uredaja, a svodi se na iskorištavanje si-
gurnosnih propusta. Jednom kada je korisnik stekao administratorske ovlasti, on može
brisati aplikacije instalirane od strane proizvodaca, pristupati sistemskim datotekama,
pa cak i instalirati cijeli novi operacijski sustav. Uredaji nad kojima je proveden proces
stjecanja administratorskih ovlasti obicno sadrže aplikaciju koja upravlja tim ovlas-
tima. Na internetu postoji mnogo uputa i alata koji omogucuju automatsko stjecanje
administratorskih ovlasti. Neke od tih aplikacija su Z4Root koja je radili na Android
sustavu verzije 2.2 (Froyo), GingerBreak namijenjena Android 2.3 (Gingerbread) ver-
ziji sustava, te Superboot koja pruža administratorske ovlasti za uredaje pogonjene
Android 4.0 (Ice Cream Sandwich) verzijom sustava.
Ukoliko korisnik nema pristup sustavskom dijelu datotecnog sustava, on ne može
vidjeti .apk arhive aplikacija instaliranih putem Google Marketa. Instalirane aplikacije
21
pohranjene su u datoteci /data/app. Stjecanjem administratorskih ovlasti, ta datoteka
postaje dostupna i korisnik može vrlo lako pristupiti .apk arhivama i prebaciti ih na
racunalo. Time analiza i reverzni inženjering aplikacija postaju moguci.
22
5. Obfusciranje izvornog tekstaprograma
Obfuskacija izvornog teksta programa je postupak primjene niza transformacija koje
rezultiraju teže citljivom, ali funkcionalno jednakom aplikacijom. Drugim rijecima,
funkcionalnost aplikacije koju korisnik vidi ostaje nepromijenjena, dok je izvorni tekst
programa transformiran na nacin da ga je teško shvatiti. Obfuskacija ne može u pot-
punosti zaštititi izvorni tekst programa, nego samo povecava vrijeme i trud koje je
analiticar morao uložiti kako bi shvatio obfuscirani izvorni tekst. Razina sigurnosti
koju obfuskacija pruža aplikaciji ovisi o nekoliko faktora, a to su: složenost primi-
jenjene transformacije, moc dostupnih algoritama deobfusciranja, te racunalna moc
racunala koja izvršava te algoritme [15]. Program koji transformira izvorni tekst pro-
grama na prije opisan nacin naziva se obfuskator. Uzmimo da se obfuskatoru kao ulaz
da program P. Kao izlaz ce se dobiti transformirani program P’. Od njega se može zah-
tijevati da ima identicnu funkcionalnost poput ulaznog programa P’. Medutim, kako
bi se proširio moguci skup transformacija nad programom P, uvjeti se mogu ublažiti.
Od programa P’ ocekuje se da ima jednako vidljivo ponašanje kao i ulazni program P.
Vidljivo ponašanje je definirano kao ponašanje koje opaža korisnik. To ostavlja mjesta
za popratne efekte poput ulazno-izlaznih operacija (pisanje u datoteke, komunikacija
internetom i sl).
Formalna definicija transformacije izvornog teksta programa u okviru obfuskacije
glasi:
Teorem 1. [15]
Neka je P T−→ P ′ transformacija izvornog programa P u ciljni program P’. P T−→ P ′ je
obfuskacijska transformacija, ako je vidljivo ponašanje P i P’ jednako. Preciznije, da
bi P T−→ P ′ bila ispravna obfuskacijska transformacija, moraju biti zadovoljena dva
uvjeta:
23
• Ako se P ne terminira ili završava rad u stanju pogreške, P ’ se može ali ne
mora terminirati
• inace, P’ mora restati s izvodenjem i dati isti izlaz kao i P
U radu [15] ponudeno je nekoliko mjera za procjenu kvalitete obfuskacijskih tran-
sformacija:
• Potentnost: koliko je analiza programa otežana covjeku?
• Elasticnost: koliko je otežana automatizirana deobfuskacija?
• Nevidljivost: koliko se transformirani dio izvornog teksta programa uklapa u
ostatak programa?
• Cijena izvodenja: koliko se povecala složenost izvodenja programa nakon pri-
mjene transformacije?
U nastavku su razmotrene obfuskacijske transformacije izvorog teksta programa.
Ovo je sažetak informacija iz radova [15], [16] i [25]. Transformacije se obicno dijele
na:
• leksicke transformacije 1
• transformacije kontrole toka
• transformacije podataka
• preventivne transformacije
S obzirom na razinu kôda na kojoj se provode, transformacije se mogu podijeliti na:
• Transformacije izvornog teksta programa (Java kôd)
• Transformacije medukôda (Dalvik medukôd)
5.1. Leksicke transformacije
Prva vrsta transformacija koja se razmatra odnosi se na manipulaciju naziva identi-
fikatora u kodu aplikacije. Kao što je vec spomenuto, medukod koji se izvršava na
virtualnim strojevima u sebi nosi mnogo meta-informacija. Izmedu ostaloga, svi iden-
tifikatori o staju u potpunosti ocuvani. U identifikatore se ubrajaju imena paketa, raz-
reda, metoda i varijabli. S obzirom da deskriptivni identifikatori pridonose jasnoci
1U literaturi navodene kao transformacije rasporeda (eng. layout transformations). S obzirom da se
ova vrsta transformacija provodi nad pojedinacnim leksickim jedinkama, odabran je intuitivniji prijevod:
leksicke transformacije.
24
koda (poznato i kao samodokumentirajuci kod), posao osobe koja pokušava shvatiti
izvorni tekst programa time je uvelike olakšan. U nastavku je dan primjer takvog vrlo
jasnog i nezašticenog izvornog teksta programa [1].
private void CalcPayroll (SpecialList employeeGroup) {
while(employeeGroup.HasMore()) {
employee = employeeGroup.GetNext(true);
employee.UpdateSalary();
DistributeCheck(employee);
}
Izvorni tekst programa 5.1: Izvorni Java kôd
Imajuci to na umu, kao jednostavna mjera zaštite namece se zamjena imena iden-
tifikatora proizvoljnim nizovima znakova. Takve zamjene moraju biti dosljedne kako
bi funkcionalnost programa ostala ista. Preimenovanjem identifikatora u besmislene
nizove znakova otežava se citljivost izvornog teksta programa i otežava posao napa-
dacima. Postoje razni pristupi odabiru novih naziva. Alat ProGuard za nove nazive
koristi leksicki poredane nizove znakova: {a, b, c, ..., z, aa, ab, ac, ..., az, ... }. Kôd
transformiran na taj nacin:
private void a (b c) {
while(c.d()) {
e = c.f(true);
e.g();
h(e);
}
Izvorni tekst programa 5.2: Obfuscirani Java kôd (leksicki poredani nizovi znakova)
Neki obfuskatori odabiru nazive koji sadrže kljucne rijeci programskog jezika po-
put private, public, static i sl. Na kraju, alat DashO iskorištava patentirani pristup
obfuskaciji preopterecivanjem operatora kako bi dodatno otežao razumijevanje izvor-
nog teksta programa.
25
private void a(a b) {
while (b.a()) {
a = b.a(true);
a.a();
a(a);
}
}
Izvorni tekst programa 5.3: Obfuscirani Java kôd (preopterecivanje operatora)
Ovakvim transformacijama nepovratno se gube svi originalni nazivi identifikatora,
te ih stoga nazivamo nepovratnima. One su potentne i ne mijenjaju cijenu izvodenja.
5.2. Transformacije kontrole toka programa
Druga vrsta transformacija navedena u [15] svodi se na manipulaciju toka izvršavanja
programa. Autori su napravili dodatnu podjelu ove vrste transformacija s obzirom na
njihov utjecaj na grupiranje i redoslijed naredbi, te izracune u sklopu toka izvršavanja
programa. Transformacije grupiranja razbijaju blokove naredbi koje logicki pripadaju
skupa ili spajaju logicki nepovezane dijelove koda u nove grupacije. Transformacije
redoslijeda, kao što samo ime implicira, mijenjaju redoslijed naredbi na slucajan nacin
(ali pazeci pri tome da funkcionalnost programa ne bude promijenjena). Transforma-
cije izracuna ubacuju nedohvatljivi ili redundantni kod ili provode algoritamske pro-
mjene u izvornom tekstu aplikacije. Transformacije kontrole toka programa u pravilu
povecavaju cijenu izvodenja programa što otvara problem izbora izmedu razine obfu-
skacije izvornog teksta aplikacije i brzine izvodenja aplikacije.
5.2.1. Transformacije grupiranja izvornog teksta programa
S obzirom da je Java programski jezik visoke razine apstrakcije, naredbe koje cine
logicke cjeline grupiraju se u metode. Ideja iza ove skupine transformacija je razbijanje
metoda i rasporedivanja njihovog kôda po ostatku aplikacije. Moguc je i drugi smjer:
stvaranje metoda grupirajuci naredbe koje nisu logicki povezane.
26
Ubacivanje i izlucivanje metode
Ubacivanje tijela metode na mjestima njezina poziva unutar programa poznata je opti-
mizacijska tehnika cesto korištena od strane prevodioca. U kontekstu obfuskacije ona
je korištena kako bi se smanjila razina apstrakcije postignuta korištenjem metoda. Na-
kon što su svi pozivi metode u programu zamijenjeni tijelom te metode, ona se može
obrisati. Pošto ta metoda više ne postoji, logicka grupacija naredbi koju je sadržavala
više nije ocita i razina apstrakcije je smanjena.
Obrnut proces je stvaranje metoda cije tijelo sadrži naredbe koje nisu logicki pove-
zane. To je moguce ostvariti kombiniranjem tijela dvaju metoda koje se pozivaju jedna
za drugom.
Ispreplitanje metoda
Transformacija ispreplitanjem metoda uzima dvije ili više metoda, te ih spaja u jednu
koja obavlja iste zadatke. Cilj postupka je stvoriti metodu sa spojenom listom argu-
menta i isprepletenim naredbama iz metoda koje se spajaju. Cuvanje funkcionalnosti
spojenih metoda ostvaruje se izborom funkcionalnosti proizvedne metode na temelju
novog argumenta ili globalne varijable. Ako se metode uvijek mogu pozivati zajedno,
nema potrebe za dodavanjem novih varijabli kontrole toka. Primjer ispreplitanja me-
toda prikazan je iducim isjeccima izvornih tekstova programa [21].
void showBalance(double customerAmount, int daysOld) {
if(daysOld > 60) {
printDetails(customerAmount * 1.2);
} else {
printDetails(customerAmount);
}
}
void emailInvoice(int customerNumber) {
printBanner();
printItems(customerNumber);
printFooter();
}
Izvorni tekst programa 5.4: Zasebne metode
27
void showBalanceEmailInvoice(double customerAmount, int
daysOld, int customerNumber) {
printBanner();
if(daysOld > 60) {
printItems(customerNumber);
printDetails(customerAmount * 1.2);
} else {
printItems(customerNumber);
printDetails(customerAmount);
}
printFooter();
}
Izvorni tekst programa 5.5: Isprepletene metode
Kloniranje metoda
Kako bi se analiza izvornog teksta programa dodatno otežala, moguce je stvoriti više
metoda iste funkcionalnosti ali razlicitog izgleda (npr. potpis metode). Cilj je zbu-
niti napadaca pozivanjem naizgled razlicitih metoda na raznim mjestima, dok one u
biti daju iste rezultate. Ovo se može ostvariti statickim pozivima metoda unutar samog
kôda ili dinamickim odabirom metode tokom izvodenja. Drugi nacin ostvaruje se stva-
ranjem pomocne metode koja tokom izvodenja na neki nacin odabire jednu od metoda
(od kojih sve daju jednak rezutat) koja ce se izvesti. Kako bi se potpuno iskoristio
potencijal ovog pristupa, preporucljivo je dodatno otežati shvacanje dvaju metoda do-
datnim obfuskacijskim transformacijama.
Transformacije petlji
Temelj ovih transformacija preuzet je iz podrucja optimizacije kôda. Prevodioci pro-
vode neke od transformacija nad petljama kako bi optimizirali efikasnost izvodenja
racunskih operacija. Prva od transformacija stvaranje je dodatnih razina ugnježdenih
petlji radi boljeg keširanja vrijednosti. Takva transformacija prikazana je iducim od-
sjeccima izvornih tekstova programa.
28
for(int i=0; i <= n; i++)
for(int j=0; j <= n;
j++)
a[i][j] = b[j][i];
Izvorni tekst programa 5.6: Pocetna
petlja
for(int k=0; k <= n; k+=32)
for(int l=0; l <= n; l+=32)
for(int i=k; i<=min(k+32,n); i++)
for(int j=l; j<=min(l+32,n); j++)
a[i][j] = b[j][i];
Izvorni tekst programa 5.7: Optimirana petlja
Drugu postupak je razmotavanje petlji. On pokušava na odreden nacin replicirati
tijelo petlje. Iduci blokovi kôda pokazuju jedan takav primjer. U tom primjeru je uzeto
u obzir da je gornja granica petlje djeljiva s 3.
for(int index=0; index <= (threshold-2); index++)
b[i] = a[i] + a[i+1] + a[i+2] ;
Izvorni tekst programa 5.8: Pocetna petlja
for(int index=0; index <= (threshold-5); index += 3) {
b[i] = a[i] + a[i+1] + a[i+2] ;
b[i+1] = a[i+1] + a[i+2] + a[i+3] ;
b[i+2] = a[i+2] + a[i+3] + a[i+4] ;
}
Izvorni tekst programa 5.9: Razmotana petlja
Posljednja tehnika temelji se na cinjenici da se jedna petlja cije tijelo sadrži više
neovisnih naredbi može razdvojiti na više petlji s istim granicama iteriranja.
for(int i=0; i <= n; i++) {
t = a[i];
b[i] = a[i+1] * c;
}
Izvorni tekst programa 5.10: Pocetna
petlja
for(int i=0; i <= n; i++)
t = a[i];
for(int i=0; i <= n; i++)
b[i] = a[i+1] * c;
Izvorni tekst programa 5.11:Razdvojene petlje
5.2.2. Transformacije redoslijeda
Ova podvrsta transformacija kontrole toka izvodenja oslanja se na cinjenicu da progra-
meri logicki smislene programske cjeline drže blizu jedne drugima u kodu. Ovakve
29
transformacije pokušavaju razbiti tu lokalnost nasumicno seleci kôd po programu. Za
neke cjeline poput metoda unutar razreda to je trivijalno. Medutim, prilikom promjene
lokacija naredbi unutar tijela metoda treba paziti kako se ne bi narušio tok podataka.
Ovakve transformacije ne cine kod mnogo opskurnijim, ali su jednosmjerne i nemo-
guce je povratiti originalni raspored. Zbog toga, preporucljivo je uz njih koristiti još
neku od prije navedenih transformacija.
Razmještanje izraza
Ovo je kompleksniji slucaj razmještanja jezicnih konstrukta. Kao što je spomenuto,
premještanje cjelovitih metoda unutar razreda je trivijalno. S druge strane, razmješta-
nje izraza zahtjeva analizu toka podataka u programu kako funkcionalnost programa
ne bi bila narušena. Takvo razmještanje izvornog teksta programa ne otežava analizu
u vecoj mjeri.
Obrtanje petlji
Još jedna transformacija preuzeta iz podrucja optimizacije izvornog teksta programa
korištene od strane prevodioca. Ova transformacija obrce smjer izvodenja petlji od
kraja prema pocetku. Niti ona ne cini izvorni tekst programa mnogo teže shvatljivim.
5.2.3. Transformacije izracuna
Transformacije izracuna djeluju na više razina. Na razini izvornog teksta programa
mogu skrivati stvarni tok izvodenja programa uvodeci nove, ali nebitne naredbe kon-
trole toka i izracune, ili uklanjajuci originalne apstrakcije kontrole toka. Na razini
strojnog kôda mogu uvesti konstrukte koji nemaju odgovarajuce jezicne konstrukte u
izvornom jeziku više apstrakcije (Java izvorni kôd). Takve transformacije uzrokuju
grešku ili loš izlaz prilikom rada dekompajlera.
Proširivanje uvjeta u petljama
Cilj ove transformacije otežavanje je shvacanja petlji uvodenjem kompleksnih uvjeta
u zaglavlje petlje. To se ostvaruje ubacivanjem kompleksnih matematickih operacija
u jedan ili više uvjeta (ovisno o vrsti petlje). U pravilu, takvi bi uvjeti uvijek trebali
davati isti rezultat tako da u stvarnosti ne utjecu na tok izvodenja petlje.
30
Dodavanje redundantnih operatora
Jedna od jednostavnijih transformacija dodavanje je redundantnih operanada u mate-
maticke izraze. Ova tehnika obicno se koristi kod izraza koji daju cjelobrojne rezultate.
Iduci isjecak koda ispisuje na zaslon broj 5.
int a = 1;
int b = 5;
int c;
c = a*b;
System.out.println(c);
Izvorni tekst programa 5.12: Pocetni izvorni tekst programa
Ubacivanjem dodatnih operanada u obliku malih brojeva s pomicnim zarezom ne
narušava se konzistentnost operacije (na kraju se decimalni dio gubi pretvorbom rezul-
tata u cijeli broj), medutim izvorni tekst programa postaje necitkiji.
int a = 1;
int b = 5;
double c;
double x = 0.0004;
double y = 0.00003;
c = a*b + x*y*y + y*x*x;
System.out.println((int)c);
Izvorni tekst programa 5.13: Izvorni tekst programa s ubacenim redundantnim operandima
Paralelizacija izvornog teksta programa
Tehnika inace korištena za ubrzavanje izracuna i reaktivnost aplikacija ovdje je kori-
štena u svrhu obfusciranja izvornog teksta programa. Paralelizacijom izvornog teksta
programa raste broj mogucih tokova izvodenja, cime se povecava njegova kompleks-
nost i otežava analiza. Ovakvu transformaciju moguce je izvesti na dva nacina:
1. Stvaranjem dretve koja ne utjece na rezultat izvodenja
2. Paralelizacijom sekvencijskog kôda
Druga metoda uzima dio koda koji se izvodi sekvencijski i paralelizira ga ne naru-
šavajuci njegov tok izvodenja. Ukoliko u tom dijelu izvornog teksta programa nema
31
podatkovnih ovisnosti, paralelizacija se može provesti u potpunosti. Ako to nije slucaj
i podatkovna ovisnost postoji, stvara se "kvazi-paralelni" izvorni tekst programa. Ta-
kav dio programa dijeli se u dretve, a tok izvodenja programa kontrolira se strukturama
poput semafora i monitora.
Uklanjanje oblikovnih obrazaca i programskih idioma
Oblikovni obrasci su ustaljena i opcenita rješenja ucestalih oblikovnih programskih
problema, dok su programski idiomi kraca, cesto korištena rješenja svojstvena za odre-
deni programski jezik. Takvi ucestali i dobro poznati jezicni konstrukti olakšavaju
održavanje i shvacanje izvornog teksta programa. Kako bi se analiza izvornog tek-
sta programa otežala, ideja je takve konstrukte razbiti. Drugim rijecima, cilj je loša
struktura koda koja bi otežala njegovo razumijevanje. Takav pristup ce otežati analizu
izvornog teksta programa, ali isto tako uciniti kod neefikasnijim i težim za održavanje.
Taj problem se može riješiti automatizacijom postupka uklanjanja oblikovnih obrazaca
i programskih idioma.
Ubacivanje nedokucivog i irelevantnog kôda
Veci broj uvjeta i grananja cini izvorni tekst programa teže shvatljivim. Imajuci to
na umu ovu je tehniku moguce implementirati na više nacina. Prvo uvodimo pojam
neprozirnog predikata (eng. opaque predicate). To je predikat cija je vrijednost a pri-
ori poznata programeru ili obfuskacijskom alatu, dok je s druge strane napadacu vrlo
teško otkriti tu vrijednost [15]. Prvi nacin je ubacivanje neprozirnog predikata s isti-
nitom vrijednošcu u sredinu programskog bloka dijeleci ga na dva dijela. Neprozirni
predikat ovdje je irelevantan s obzirom na to da uvijek ima istinitu vrijednost. Drugi je
nacin ponovna podjela bloka na dva dijela, ali ovaj put predikatom koji poprima razli-
cite vrijednosti tokom faze izvodenja. Prvi dio ostaje nepromijenjen, dok se od drugog
dijela stvaraju dvije verzije obfuscirane razlicitim tehnikama tako da su naizgled raz-
liciti. Posljednji nacin konceptualno je slican prije spomenutom. Proces se ponavlja,
ali u jednu obfusciranu granu namjerno se uvodi greška i neprozirnim predikatom iz-
vodenje se usmjerava u ispravnu obfusciranu granu.
Ubacivanje beskorisnih bajtova
Ova tehnika implementira se na razini medukôda. Za razliku od prethodno spomenutih
tehnika koje su bile usmjerene na zbunjivanje napadaca, cilj ove tehnike je zbunjiva-
nje automatskih alata za analizu koje napadac koristi. Ubacivanje nedokucivog kôda
32
mora zadovoljiti dva uvjeta. Prvi je zbunjivanje strategija disasembliranja medukôda
spomenutih u poglavlju 3.1, dok je drugi uvjet garancija da se ubaceni kôd nikada nece
izvesti. To znaci da takav kôd mora biti smješten unutar bloka cije se izvodenje izbje-
gava bezuvjetnim skokom ili uvjetnim skokom ciji uvjet uvijek ima istinitu vrijednost.
Obfuskacija znakovnih nizova
Odredeni podaci pohranjuju se u obliku znakovnih nizova unutar izvornog teksta apli-
kacija. Takvi podaci mogu sadržavati korisnicka imena i lozinke za pristup bazi po-
dataka ili adrese aplikacijskih programskih sucelja (eng. Application Programming
Interface - API) udaljenih servisa. Kako bi se njihovo izlucivanje otežalo, koristi se
princip slican onome opisanom u poglavlju 4.1 o dinamickom ucitavanju kôda. Za
skrivanje znakovnog niza može se koristiti bilo koja injektivna funkcija F . U izvor-
nom tekstu programa niz znakova S zamjenjuje se rezultatom primjene funkcije F na
njega, odnosno F(S). U praksi to se izvodi na nacin da se tokom faze obfuskacije u
izvornom tekstu programa kriptiraju svi znakovni nizovi, te se ubaci funkcija zadužena
za njihovu dekripciju u fazi izvodenja.
public void connect() {
String api = "http://serv.hr/comm";
String password = "saps164";
connection.connect(api,password);
}
Izvorni tekst programa 5.14: Izvorni tekst programa
public void connect() {
String api =
decrypt("cm87MgYJdFuwwyZieH8gA589Ooy7fVowxdU3ARODLpg=");
String password = decrypt("1wWilncORhC8znee78x4LA==");
connection.connect(api,password);
}
Izvorni tekst programa 5.15: Izvorni tekst programa sa skrivenim znakovnim nizovima
33
5.3. Transformacije podataka
U ovom odjeljku razmatraju se obfuskacijske transformacije nad podacima. Ove tran-
sformacije dijele se na transformacije pohrane i kodiranja, grupiranja (agregacije) i
redoslijeda[15].
5.3.1. Transformacije pohrane i kodiranja
Transformacije pohrane pokušavaju koristiti neintuitivne tipove podataka i metode po-
hrane podataka u varijable. Neke od tih metoda opisane su u nastavku.
Promjena kodiranja
Ovakva transformacija može se provesti na jednostavan nacin. Prilikom pohrane, cje-
lobrojna varijabla i može se izraziti kao i′ = c1 · i+ c2 pri cemu su c1 i c2 konstante. U
iducem primjeru za c1 i c2 odabrani su 7 i 3.
for(int i=1; i<100; i++) {
a[i] = i*2;
}
Izvorni tekst programa 5.16: Pocetni
izvorni tekst programa
for(int i=10; i<703; i+=7) {
a[(i-3)/7] = (i-3)/14;
}
Izvorni tekst programa 5.17:Izmijenjeno kodiranja
Mijenjajuci pohranu podataka na ovako jednostavan nacin, u izvorni tekst pro-
grama se unosi nejasnoca. Isto tako, ovakve izmjene malo povecavaju vrijeme iz-
vodenja.
Razdvajanje varijabli
Razdvajanje varijabli može se obavljati nad logickim varijablama kao i ostalim varija-
blama ogranicenog raspona vijednosti. Logicka varijabla x može se primjerice podije-
liti na dvije varijable a i b cjelobrojnog tipa. Sada se može zadati da je x=true ako su
a=b=0 ili a=b=1, a x=false u slucajevima kada su a=1 i b=0, te a=0 i b=1. Iduca slika
prikazuje lookup tablicu u kojoj su vrijednosti izvorne varijable pridjeljene vrijednos-
tima novostvorenih varijabli.
34
a b x 2a+ b
0 0 True 0
0 1 False 1
1 0 False 2
1 1 True 3
Slika 5.1: Lookup tablica za razdvojene varijable
U posljednjem stupcu prikazan je izraz koji pridjeljuje jedinstvene vrijednosti raz-
licitim kombinacijama varijabli. Pomocu tako dobivenih vrijednosti možemo definirati
tablicu logicke I funkcije.
I[a,b] 0 1 2 3
0 3 0 0 0
1 3 1 2 3
2 0 2 1 3
3 3 0 0 3
Slika 5.2: Lookup tablica logicke I funkcije
Sljedeca dva odsjecka pokazuju izvorni kôd i odgovarajuci kod nad kojim je pro-
vedena transformacija.
boolean x,y,z;
x = true;
y= false;
z = x&y;
Izvorni tekst programa 5.18: Originalni kôd
int x1, x2,y1 ,y2 , z1, z2;
x1 = 0; x2 = 0;
y1 = 0; y2 = 1;
int tmp = I[2*x1 + x2, 2*y1 + y2]; z1 = tmp/2; z2=tmp\%2;
Izvorni tekst programa 5.19: Kôd s razdvojenim varijablama
35
Lookup tablice moguce je stvoriti i pohraniti u statickom kontekstu. Rješenje koje
bi još više otežalo shvacanje izvornog teksta programa dinamicko je stvaranje lookup
tablica koje bi provodio obfuscirani program tokom izvodenja.
5.3.2. Transformacije grupiranjem
Grupiranje podataka u objektno orijentiranoj paradigmi moguce je ostvariti na dva na-
cina: grupiranjem podataka u polja i stvaranjem razreda. Takva logicka organizacija
podataka u cjeline povecava razinu apstrakcije i olakšava shvacanje programa. Pro-
grameru koji pokušava zaštititi svoj izvorni tekst programa u interesu je na neki nacin
sakriti podatkovne strukture. U iduce tri sekcije opisane su neke od tehnika predlože-
nih u [15] kojima se takvo skrivanje postiže.
Spajanje skalarnih varijabli
Više jednostavnih skalarnih varijabli moguce je spojiti u jednu kompleksnu skalarnu
varijablu koja sadrrži sve informacije. Jedan primjer takve transformacije spajanje je
dva 32-bitna cijela broja u jedan 64-bitni cijeli broj pri cemu gornja 32 bita odgovaraju
vrijednosti prve varijable, dok donja 32 bita odgovaraju vrijednosti druge varijable.
Kako bi se tokom programa moglo upravljati takvim varijablama potrebno je razraditi
aritmeticke operacije nad njima. Povecanje kompleksnosti transformacije moguce je
postici korištenjem bitovnih operatora. Osim toga, skalarne varijable takoder je mo-
guce spojiti u polje.
Restrukturiranje polja
Polja su podložna znatnom broju transformacija. Programer koji štiti svoj program
može polje razdijeliti na potpolja, spojiti više polja u jedno te povecati ili smanjiti di-
menzionalnost polja. Jednostavno razdvajanje polja moguce je ostvariti izdvajanjem
njegovih elemenata na temelju parnosti njihovih indeksa. Elementi na parnim po-
zicijama odvajaju se u jedno polje, dok se oni na neparnim odvajaju u drugo polje.
Spajanje polja moguce je obaviti njihovim ispreplitanjem. Smanjenje i povecanje di-
menzionalnosti takoder je jednostavno.
Modificiranje razreda
Osnovna jedinica apstrakcije u objektno orijentiranim jezicima je razred. Razred se
sastoji od stanja (podataka) i operacija koje to stanje mogu mijenjati. U objektno
36
orijentiranoj sintagmi podržan je koncept nasljedivanja. Ideja iza tog koncepta je da
razred može naslijediti funkcionalnost vec definiranog razreda i proširiti ju. Na taj
nacin nastaje hijerarhija nasljedivanja. Razred dijete ima svu funkcionalnost razreda
roditelja kao i novu funkcionalnost. Metode i podaci koje je naslijedio od razreda
roditelja nisu eksplicitno vidljivi u definiciji razreda. Veca dubina stabla nasljedivanja
otežava shvacanje programa. Obfuskacija modifikacijom razreda iskorištava upravo tu
cinjenicu.
Prvi pristup je razdvajanje razreda na više razreda koji zajedno obavljaju jedanku
zadacu i imaju isti razred roditelj. Drugi pristup je stvaranje bespotrebnih razreda
u hijerarhiji nasljedivanja. Na kraju, moguce je "refaktorirati" razrede koji nemaju
nikakvu poveznicu seleci dijelove njihovog kôda u zajednicki razred roditelj. Na taj
nacin unosi se nejasnoca u hijerarhiju nasljedivanja što rezultira otežanim shvacanjem
funkcioniranja programa.
5.3.3. Transformacije redoslijeda
Uobicajen nacin deklaracije varijabli je njihovo grupiranje na pocetku klase ako se radi
o clanskim varijablama ili njihova definicija neposredno prije korištenja. Takav na-
cin pozicioniranja varijabli olakšava održavanje i shvacanje izvornog teksta programa.
Cilj ove vrste transformacija otežavanje je shvacanja izvornog teksta programa pre-
rasporedivanjem definicija varijabli kroz izvorni tekst programa na neintuitivan nacin.
Ogranicenja koja ne bi smjela biti narušena su podatkovna ovisnost i doseg varijabli.
5.4. Alati za obfuskaciju izvornog teksta programa
5.4.1. Alat ProGuard
Obfuskator Java koda koji provodi preimenovanje identifikatora, smanjuje kôd ukla-
njajuci nekorištene razrede, metode, clanske i lokalne varijable, te oznacava nedos-
tupni, "mrtvi" kôd radi lakšeg uklanjanja. Osim toga, provodi optimizaciju medukôda
uklanjajuci nekorištene naredbe. ProGuard dolazi kao dio Android SDK. Rezultat pri-
mjene alata ProGuard na izvorni tekst programa prikazan na slici 5.20, dan je na slici
5.21.
package hr.fer.zemris.berger.securebanking.model;
public class Transaction {
37
private String senderAccount;
private String recipientAccount;
private String amount;
public String getSenderAccount() {
return senderAccount;
}
public void setSenderAccount(String senderAccount) {
this.senderAccount = senderAccount;
}
public String getRecipientAccount() {
return recipientAccount;
}
public void setRecipientAccount(String recipientAccount) {
this.recipientAccount = recipientAccount;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
}
Izvorni tekst programa 5.20: Neobfuscirani izvorni tekst programa
package hr.fer.zemris.berger.securebanking.a;
public class a
{
private String a;
private String b;
38
private String c;
public String a()
{
return this.a;
}
public void a(String paramString)
{
this.a = paramString;
}
public String b()
{
return this.b;
}
public void b(String paramString)
{
this.b = paramString;
}
public String c()
{
return this.c;
}
public void c(String paramString)
{
this.c = paramString;
}
}
Izvorni tekst programa 5.21: Izvorni tekst programa obfusciran alatom ProGuard
39
5.4.2. Alat DexGuard
DexGuard je nasljednik ProGuard-a namijenjen optimizaciji i obfuskaciji Android
aplikacija. Osim osnovne obfuskacije, DexGuard pruža mnoštvo mogucnosti poput
enkripcije znakovnih nizova, cijelih razreda, resursa i nativnog kôda, Java reflekcije
za pristupanje osjetljivim API-jima i sl. Kako bi otežao analizu, DexGuard uklanja
logiranje zaostalo iz faze razvoja aplikacije. Za razliku od ProGuarda, DexGuard je
komercijalni proizvod.
5.4.3. Alat DashO
Još jedan komercijalni alat koji pruža mogucnosti obfuskacije koda preimenovanjem
identifikatora, promjenom toka izvodenja, te enkripcijom znakovnih nizova. Osim
toga pruža mogucnost ugradivanja koda za kontrolu probnih razdoblja aplikacije. Na-
posljetku, ovaj alat pruža mogucnost injekcije kôda za detekciju i dojavu eventualnih
pokušaja reverznog inženjeringa aplikacije.
40
6. Programski sustav
U ovom poglavlju opisan je prakticni dio rada. Dan je pregled detalja arhitekture i
implementacije sustava za detekciju promjena u izvornom tekstu klijentske Android
aplikacije.
6.1. Motivacija
Zamislimo situaciju u kojoj je programer stvorio klijentsku Android aplikaciju za nov-
cane transakcije. Takva aplikacija zahtijeva vrlo visoku razinu sigurnosti u smislu
autentifikacije korisnika i sigurne komunikacije putem mreže. Programer je osigurao
sve standardne sigurnosne zahtjeve, medutim, nije razmišljao o mogucnosti promjene
izvornog teksta aplikacije. Napadac je s druge strane svjestan koliko je lako izmjeniti
izvorni tekst Android aplikacije. On na odreden nacin dobiva pristup izvornom tekstu
aplikacije, hardkodira svoj bankovni racun kao odredišni, te ponovno pakira i potpisuje
aplikaciju. Pošto su funkcionalnost aplikacije i korisnicko sucelje naizgled identicni
originalnoj aplikaciji, korisnik nije svjestan promjena. On obavlja transakciju i novac
odlazi na krivi bankovni racun.
Ocito je da ovakvi napadi oštecuju korisnike u financijskom smislu i srozavaju
ugled kompanije koja je izradila aplikaciju. Kako bi se takvi napadi izbjegli, dan je
prijedlog sustava koji bi napade mogao detektirati. U slucaju detekcije kompanija
može odbiti provodenje transakcije zahtjevane od strane izmijenjene aplikacije.
6.2. Implementacija
Sustav za detekciju promjena u izvornom tekstu aplikacija zasnovana je na arhitekturi
klijent-poslužitelj. Klijentska aplikacija je Android aplikacija za novcane transakcije.
Dio sustava na poslužitelju zadužen je za bilježenje transakcija i podataka o klijentskim
aplikacijama kojima su te transakcije provedene. U stvarnosti, ovaj bi sustav prvo
provjeravao je li aplikacija koja je poslala zahtjev za transakcijom izmijenjena. Tek
41
nakon toga, na temelju rezultata te provjere, sustav bi odlucivao hoce li zatraženu
transakciju provesti ili ne.
6.2.1. Komunikacija
Klijent i poslužitelj komuniciraju preko mreže koristeci HTTPS protokol. Klijent pri-
likom komunikacije sa poslužiteljem utvrduje njegov identitet provjeravajuci njegov
samopotpisani certifikat.
Poruke koje izmjenjuju klijent i poslužitelj su u JSON1 formatu. Primjer jedne
klijentske poruke poslužitelju prikazan je u nastavku.
1 {
2 "sender":"3652563268",
3 "recipient":"6633589247",
4 "amount":"1500",
5 "hash":"6611CC6E318593ABC8242CE604013B9C3B225E24",
6 "signature":"625898345",
7 "deviceID":"b3596bdf9469a2f8",
8 "timestamp":"15.05.2014 23:00:27"
9 }
Izvorni tekst programa 6.1: Klijentska poruka
Prva tri polja odnose se na transakciju. To su polja s brojem racuna platitelja, bro-
jem racuna primatelja te novcanim iznosom koji se prebacuje s jednog na drugi racun.
Druga tri polja odnose se na otisak aplikacije. Otisak definiramo kao skup podataka
svojstvenih verziji aplikacije i uredaju koji ju pokrece. On se sastoji od jedinstvenog
identifikatora uredaja, potpisa aplikacije i sažetka medukôda aplikacije. Posljednje
polje je vrijeme slanja zahtjeva za provedbom transakcije.
Odgovor koji poslužitelj šalje klijentu mnogo je jednostavniji.
1 {
2 "response":"success!"
3 }
Izvorni tekst programa 6.2: Klijentska poruka
Prvo i jedino polje sadrži obavijest o ishodu provedbe transakcije.
1Više informacija o formatu json dostupno je na web adresi: http://json.org/
42
6.2.2. Klijentska aplikacija
Sucelje klijentske aplikacije prikazano je na iducoj slici.
Slika 6.1: Sucelje klijentske Android aplikacije
Ono se sastoji od tri polja za unos teksta i dva gumba. Polja za unos teksta omogu-
cuju unos brojeva racuna platitelja (sender) i primatelja(recipient) te novcanog iznosa
(amount). Gumbi služe za izbor hoce li se transakcija provesti ili ne.
Klijentska aplikacija zanimljiva je po tome što osim osnovne zadace racuna i svoj
otisak. Isjecak izvornog teksta programa koji racuna sažetak aplikacije prikazan je u
nastavku.
public static byte[] getHash(String algorithm, Activity
activity) throws IOException,
NoSuchAlgorithmException {
String name = null;
// load file input stream of this class
String sourceDir =
activity.getApplicationInfo().sourceDir;
43
DexFile dexFile = new DexFile(sourceDir);
name = dexFile.getName();
File file = new File(name);
byte[] buffer = new byte[4096];
MessageDigest md = MessageDigest.getInstance(algorithm);
BufferedInputStream stream = new BufferedInputStream(
new FileInputStream(file));
int len = 0;
while ((len = stream.read(buffer)) != -1) {
md.update(buffer, 0, len);
}
byte[] finalDigest = md.digest(buffer);
stream.close();
return finalDigest;
}
Izvorni tekst programa 6.3: Metoda za dohvacanje sažetka medukôda aplikacije
Kao što je spomenuto u poglavlju 2, medukôd aplikacije pohranjen je u dato-
teci classes.dex. Metoda "getHash" dohvat te datoteke obavlja korištenjem razreda
DexFile. Nakon toga slijedi standardna procedura izracunavanja sažetka sadržaja da-
toteke. Na kraju, metoda vraca sažetak datoteke u obliku niza bajtova.
Druga važna metoda cija je zadaca dohvat potpisa aplikacije prikazana je u nas-
tavku.
public static String getSignature(Activity activity) throws
PackageManager.NameNotFoundException {
PackageManager pm = activity.getPackageManager();
Signature sig =
pm.getPackageInfo(activity.getPackageName(),
PackageManager.GET_SIGNATURES).signatures[0];
return String.valueOf(sig.hashCode());
}
Izvorni tekst programa 6.4: Metoda za dohvacanje potpisa aplikacije
44
6.2.3. Poslužiteljska aplikacija
Poslužiteljska aplikacija zadužena je za bilježenje i kontrolu provedbe transakcija kao
i bilježenje otisaka klijentskih aplikacija koje su poslale zahtjeve. Ona takoder vodi ra-
cuna o verzijama aplikacije koje, naravno, imaju razlicite sažetke. Ti sažetci, zajedno s
potpisima aplikacije, pohranjeni su u bazu podataka. Prilikom svakog klijentskog zah-
tjeva, sažetak i potpis koji su sadržani u tom zahtjevu, usporeduju se sa svim verzijama
pohranjenim u bazi podataka. Ukoliko se sažetak i potpis ne nalaze u bazi podataka,
aplikacija oznacuje odgovarajuci redak zelenom bojom. Ako to nije slucaj, redak se
oznacuje crvenom bojom.
Slika 6.2: Dio sucelja poslužiteljske aplikacije koji prikazuje sve pohranjene otiske aplikacija
Slika 6.3 prikazuje web stranicu na kojoj je moguce upravljati potpisima i sažet-
cima aplikacija vezanim uz njihove verzije.
Poslužiteljski dio aplikacije implementiran je korištenjem tehnologije Java serv-
leta, dok je na sloju perzistencije korištena je tehnologija JPA (eng. Java Persistence
API).
6.3. Simulacija napada na klijentsku aplikaciju
U ovom odjeljku prikazan je primjer napada po obrascu opisanom u 6.1. Dakle, radi
se o ubacivanju hardkodirane vrijednosti bankovnog racuna u klijentsku aplikaciju.
Za izmjenu aplikacije korišten je alat FJ-APKTools2. To je skup shell skripti koje
2Alat je dostupan na web adresi: http://code.google.com/p/fj-apktools/
45
Slika 6.3: Dio sucelja poslužiteljske aplikacije koji prikazuje sve pohranjene verzije aplikacija
služe kao omotac oko prije spomenutog radnog okvira Apktool i alata za potpisivanje
signapk3.
Alat sadrži sljedece datoteke:
final sadrži izmijenjenu i potpisanu verziju aplikacije
original_apk sadrži originalnu aplikaciju
out sadrži izmijenjenu verziju aplikacije
signing sadrži aplikaciju koju treba potpisati
tools sadrži sve korištene alate i skripte
working sadrži raspakiranu i disasembliranu aplikaciju
3Alat je dostupan na web adresi: http://code.google.com/p/signapk/
46
Slika 6.4: Prvi izbornik
Slika 6.5: Drugi izbornik
Slika 6.6: izbornici korištenog alata
Prvi korak u izmjeni izvornog teksta aplikacije, smještanje je njezine .apk datoteke
u direktorij original_apk. Nakon toga odabire se opcija "Decompile apk" što rezultira
47
raspakiravanjem i disasembliranjem aplikacije u novostvoreni direktorij imena "wor-
king". U njemu se nalaze svi resursi i disasemblirani kôd aplikacije u smali formatu.
Dio aplikacije koji je potrebno izmjeniti je datoteka Transaction.smali, odnosno
metoda razreda Transaction koja vraca broj racuna primatelja. Originalni isjecak kôda
u smali formatu dan je u 6.5.
1 .method public getRecipientAccount()Ljava/lang/String;
2 .locals 1
3
4 .prologue
5 .line 29
6 iget-object v0, p0,
Lhr/fer/zemris/berger/securebanking/model/Transaction;
7 ->recipientAccount:Ljava/lang/String;
8
9 return-object v0
10 .end method
Izvorni tekst programa 6.5: Originalna metoda
Prva linija kôda govori da u metodi postoji jedna lokalna varijabla (lokalne varija-
ble oznacuju se kao v1...vn). Šesta linija u varijablu v0 ucitava niz znakova iz privatne
clanske varijable recipientAccount. Na kraju deveta linija vraca vrijednost varijable v0
kao povratnu vrijednost funkcije.
Ovu funkcionalnost vrlo je lako izmijeniti. Umjesto da funkcija vraca vrijednost
varijable recipientAccount koja se puni korisnickm unosom, funkciju je moguce pre-
praviti da vraca hardkodiranu vrijednost. Izmijenjeni kôd dan je u nastavku.
1 .method public getRecipientAccount()Ljava/lang/String;
2 .locals 1
3 .prologue
4 .line 29
5 const-string v0, "1902355830"
6 .local v0, s:Ljava/lang/String;
7 return-object v0
8 .end method
Izvorni tekst programa 6.6: Originalna metoda
48
Druga linija ponovno govori da u metodi postoji jedna lokalna varijabla. Medutim
u petoj i šestoj liniji sada je definirana lokalna varijabla s vrijednošcu racuna napadaca.
Ta vrijednost se u sedmoj liniji vraca kao ispravna vrijednost funkcije.
Iduci korak je ponovno pakiranje aplikacije izborom opcije "compile apk". Sada
se izmijenjena zapakirana aplikacija nalazi u direktoriju out. Nakon što je aplikacija
preseljena u direktorij signing i odabrana opcija "Sign apk file" iz drugog izbornika,
potpisana aplikacija stvara se u direktoriju final. Takvu aplikaciju sada je moguce
podmetnuti korisniku.
Korisnik sada unosi svoj broj racuna (3368584442), odredišni broj racuna (1020653890)
i iznos (300). Podaci o transakciji koje je aplikacija primila prikazani su na iducim sli-
kama:
49
Slika 6.7: Transakcije
Slika 6.8: Otisci
Slika 6.9: Poslužiteljska aplikacija s jednom valjanom transakcijom
Aplikacija je po primitku klijentskog zahtjeva provjerila poslani sažetak aplikacije
u bazi podataka i zakljucila kako takav sažetak postoji te kako aplikacija nije mije-
njana. Pretpostavimo da je korisniku podmetnuta izmijenjena aplikacija. Njeno je
sucelje i ponašanje u potpunosti jednako originalnoj, s iznimkom hardkodiranog broja
racuna (koji korisnik i dalje ne vidi). Korisni unosi jednake podatke i potvrduje tran-
sakciju. Stanje posužiteljske aplikacije nakon primitka tog zahtjeva prikazano je na
iduce dvije slike.
Sustav je ponovio postupak provjere sažetka i zakljucio da se sažetak iz zahtjeva
ne nalazi u bazi podataka. Redak koji odgovara odgovarajucem otisku boji se crvenom
50
Slika 6.10: Transakcije
Slika 6.11: Otisci
Slika 6.12: Poslužiteljska aplikacija s jednom valjanom transakcijom i jednim detektiranim
pokušajem napada
bojom, cime se administratoru sustava daje do znanja da je došlo do napada. Važno je
primjetiti kako je osim sažetka izmijenjen i potpis aplikacije. To proizlazi iz cinjenice
da je aplikacija nakon izmjene morala biti ponovno potpisana, ali sada napadacevim
kljucem, s obzirom na to da napadac nema pristup privatnom kljucu programera apli-
kacije.
51
6.4. Ranjivosti sustava
Jednostavna demonstracija napada na sustav pokazuje kako je sustav u stanju registri-
rati osnovne napada izmjenom izvornog teksta programa u klijentskoj aplikaciji. Dvije
su komponente zaštite. Prva je sažetak medukôda aplikacije, a druga provjera potpisa
aplikacije. Obje provjere malo predaniji napadac može s lakocom zaobici. Sažetak
originalne aplikacije moguce je izracunati prije, pohraniti ga u datoteku, te u svakom
zahtjevu slati taj prije izracunati sažetak. Obilaženje zaštite provjerom potpisa takoder
je trivijalno. Napadac može dinamickom analizom aplikacije doci do originalnog pos-
tpisa i ponoviti prije opisani postupak pohrane i slanja vrijednosti iz datoteke. Unatoc
tome, sustav nije beskoristan i omogucuje detekciju izmijena. Kako bi se povecala
njegova efikasnost, najbolja praksa bila bi koristiti neke od dinamickih tehnika zaštite
izvornog teksta programa iz poglavlja 4 ili obfuskacijskih tehnika iz poglavlja 5.
52
7. Zakljucak
U okviru ovog rada pokazano je da postoji velik broj alata i tehnika namijenjenih ana-
lizi izvornih tekstova Android aplikacija. Objašnjeno je kako je razlog tome relativno
jednostavan medukôd koji se izvršava na Dalvik virtualnom stroju. Osim toga, pro-
izvedeni su i cijeli radni okviri koji cine zlocudnu izmjenu aplikacija relativno jednos-
tavnom. Kao odgovor na to , razvijene su tehnike i alati za zaštitu izvornog teksta pro-
grama. Neke tvrtke posvetile su se razvoju komercijalnih alata s ciljem zaštite izvornih
tekstova programa. Ti alati koriste vec dobro poznate tehnike skrivanja (obfuskacije)
izvornog teksta programa, kao i neke druge tehnike poput enkripcije i dinamickog uci-
tavanja kôda. Postoji stalni razvoj alata za automatsku analizu aplikacija, ali s druge
strane konstantno se razvijaju i tehnike koje ce onemoguciti njihov rad. Programe-
rima kojima je stalo do integriteta njihovih osjetljivih aplikacija posebno su zanimljive
tehnike detekcije izmjene aplikacija koje su takoder ostvarive.
U prakticnom dijelu rada implementiran je sustav koji je u mogucnosti detekti-
rati promjene u medukôdu klijentske aplikacije. Eventualne promjene detektiraju se
provjerom sažetka medukôda i digitalnog potpisa programera. Iako je sustav u mo-
gucnosti detektirati promjene u izvornom tekstu aplikacije, ukoliko se izvorni tekst
aplikacije dodatno ne zaštiti nekom od tehnika obfuskacije, detekciju je vrlo lako za-
obici. Naime, Android aplikacije su klijentske što znaci da sav njihov kôd mora biti
pohranjen na klijentskom uredaju kako bi se mogao izvoditi. Zbog toga je on dostupan
napadacima. Uporni napadac s dovoljno vremena, znanja i uloženog truda gotovo ce
sigurno postici svoj cilj. On ce moci promijeniti izvorni tekst programa na nacin koji
mu odgovara zaobilazeci sve sigurnosne mehanizme ugradene od strane autora aplika-
cije. To znaci da unatoc velikom broju obfuskacijskih tehnika, izvorni tekst programa
nikada nije moguce u potpunosti zaštititi, nego samo otežati napadacev posao.
53
LITERATURA
[1] Java obfuscator DashO. URL https://www.preemptive.com/
products/dasho. datum pristupa: 15.4.2014.
[2] Android SDK | android developers, . URL http://
developer.android.com/sdk/index.html?hl=sk. datum pristupa:
1.4.2014.
[3] Andrubis, . URL http://blog.iseclab.org/2012/06/04/
andrubis-a-tool-for-analyzing-unknown-android-
applications-2/. datum pristupa: 5.5.2014.
[4] Apktool. URL https://code.google.com/p/android-apktool/.
datum pristupa: 2.4.2014.
[5] DexGuard. URL http://www.saikoa.com/dexguard. datum pristupa:
5.5.2014.
[6] droidbox - android application sandbox - google project hosting. URL https:
//code.google.com/p/droidbox/. datum pristupa: 10.4.2014.
[7] IDAPro. URL https://www.hex-rays.com/products/ida/. datum
pristupa: 15.4.2014.
[8] Java decompiler - jd-gui. URL http://jd.benow.ca/. datum pristupa:
15.4.2014.
[9] Mobile sandbox. URL http://mobilesandbox.org/. datum pristupa:
2.4.2014.
[10] Proguard. URL https://www.google.hr/webhp?sourceid=chrome-
instant&ion=1&espv=2&ie=UTF-8#q=proguard. datum pristupa:
5.5.2014.
54
[11] radare. URL http://radare.org/y/. datum pristupa: 30.5.2014.
[12] Santoku-linux. URL https://santoku-linux.com/. datum pristupa:
15.6.2014.
[13] Smali - an assembler/disassembler for android’s dex format. URL https://
code.google.com/p/smali/. datum pristupa: 15.5.2014.
[14] Bluebox. Dexter. URL https://dexter.bluebox.com/. datum pristupa:
2.4.2014.
[15] Christian Collberg, Clark Thomborson, i Douglas Low. A taxonomy of ob-
fuscating transformations. Technical report, Department of Computer Sci-
ence, The University of Auckland, New Zealand, 1997. URL https://
researchspace.auckland.ac.nz/handle/2292/3491. datum pris-
tupa: 8.5.2014.
[16] Christian Collberg, Clark Thomborson, i Douglas Low. Manufacturing cheap,
resilient, and stealthy opaque constructs. stranica 184–196, 1998. URL http:
//dl.acm.org/citation.cfm?id=268962.
[17] Bornstein Dan. Dalvik VM internals - 2008 google I/O session videos
and slides. URL https://sites.google.com/site/io/dalvik-vm-
internals. datum pristupa: 4.5.2014.
[18] Anthony Desnos. Androguard. URL https://code.google.com/p/
androguard/. datum pristupa: 2.4.2014.
[19] David Ehringer. The dalvik virtual machine architecture. Techn. report
(March 2010), 2010. URL http://davidehringer.com/software/
android/TheDalvikVirtualMachine.pdf. datum pristupa: 30.3.2014.
[20] Sudipta Ghosh, MTech Scholar, S. R. Tandan, i Kamlesh Lahre.
Shielding android application against reverse engineering. URL
http://www.ijert.org/browse/volume-2-2013/june-
2013-edition?download=4095%3Ashielding-android-
application-against-reverse-engineering&start=360.
datum pristupa: 8.5.2014.
[21] Godfrey Nolan. Decompiling Android. Apress, Berkeley, 1 edition izdanju, Sr-
panj 2012. ISBN 9781430242482.
55
[22] Gabor Paller. Dedexer. URL http://dedexer.sourceforge.net/. da-
tum pristupa: 3.4.2014.
[23] Panxiaobo. dex2jar. URL https://code.google.com/p/dex2jar/. da-
tum pristupa: 2.4.2014.
[24] SBA Research. Android application obfuscation. Technical report, Lipanj 2013.
[25] Patrick Schulz. Code protection in android. Technical report, University of Bonn,
2012. URL http://net.cs.uni-bonn.de/fileadmin/userupload/
plohmann/2012-Schulz-CodeProtectioninAndroid.pdf. datum
pristupa: 8.4.2014.
[26] Benjamin Schwarz, S. Debray, i G. Andrews. Disassembly of executable code
revisited. U Ninth Working Conference on Reverse Engineering, 2002. Proce-
edings, stranice 45–54, 2002. doi: 10.1109/WCRE.2002.1173063.
[27] Jeff Six. Application security for the Android platform. O’Reilly Media, Sebas-
topol, 2012. ISBN 9781449322281 144932228X 9781449322274 1449322271.
URL http://proquestcombo.safaribooksonline.com/
9781449322250. datum pristupa: 8.5.2014.
56
Zaštita mobilnih aplikacija od zlonamjernih izmjena
Sažetak
Ovaj rad daje uvid u podrucje zaštite izvornog teksta programa mobilnih aplikacija
namijenjenih operacijskom sustavu Android. Drugo poglavlje daje kratak pregled ar-
hitekture i nacina rada operacijskog sustava Android kao i strukture aplikacija koje se
na njemu izvršavaju. Trece poglavlje daje pregled alata i tehnika za analizu i izmjenu
izvornog teksta aplikacija. U cetvrtom poglavlju dan je pregled dinamickih tehnika
zaštite mobilnih aplikacija. Peto poglavlje daje pregled koncepta skrivanja izvornog
teksta programa. Naposljetku, u šestom poglavlju dan je prijedlog i implementacija
sustava za detekciju zlonamjernih izmjena izvornog teksta aplikacija.
Kljucne rijeci: android, dalvik virtualni stroj, skrivanje izvornog teksta programa,
zaštita izvornog teksta programa, detekcija promjene izvornog teksta programa, sta-
ticka analiza, dinamicka analiza, sigurnost, ponovno pakiranje aplikacija, obfuskacija,
provjera tokom izvodenja, otisci
Mobile Application Code Protection
Abstract
This paper gives an insight into the area of protecting the original texts for mobile
application programs intended for the Android operating system. The second chapter
shows a brief overview of the way Android operating system works and its architecture,
as well as the structures of applications executed on it. The third chapter offers an
overview of the tools and techniques for analysis and editing of the application’s source
code. The fourth chapter summarizes the dynamic techniques for the protection of
mobile applications. The concept of hiding the program’s source code is shown in the
fifth chapter. And finally, the sixth chapter offers the suggestion and implementation
of the system for the detection of malicious changes in the application source code.
Keywords: Android, Dalvik, Source code obfuscation, source code protection, tamper
detection, static analysis, dynamic analysis, security, malicious, repackaging, obfusca-
tion, runtime checking, footprints