Layered obfuscation: en taksonomi av programvare obfuscation teknikker for lagdelt sikkerhet

denne delen undersøker obfuscation teknikker for spesifikke kodeelementer. Dette laget dekker de fleste publikasjonene i software obfuscation-omradet. Som vist I Fig. 4, i henhold til hvilke elementer en obfuscation teknikk mål, vi dele denne kategorien i fem underkategorier: obfuscating oppsett, obfuscating kontroller, obfuscating dataobfuscating funksjoner, og obfuscating klasser.

Fig. 4
figur4

obfuscation teknikker av kode-element lag

Obfuscating layout

layout obfuscation forvrenger oppsettet av koder eller instruksjoner mens du holder den opprinnelige syntaksen intakt. Denne delen omhandler fire layout obfuscation strategier: meningsløse klassifikatorer, stripping overflødige symboler, skille relaterte koder, og useriøs koder.

Meningsløse identifikatorer

denne tilnærmingen er også kjent som leksikalsk obfuscation som forvandler meningsfulle identifikatorer til meningsløse. For de fleste programmeringsspråk er det nødvendig å vedta meningsfulle og ensartede navneregler (f.eks. ungarsk Notasjon (Simonyi 1999)) som en god programmeringspraksis. Selv om slike navn er spesifisert i kildekoder, vil noen forbli i den utgitte programvaren som standard. For eksempel holdes navnene på globale variabler og funksjoner I C/C++ i binærfiler, og Alle Navn På Java er reservert i bytekoder. Fordi slike meningsfulle navn kan lette adversarial programanalyse, bør vi kryptere dem. For å gjøre de obfuscated identifikatorene mer forvirrende, foreslo Chan And Yang (2004) å bevisst bruke de samme navnene for objekter av forskjellige typer eller innenfor forskjellige domener. Slike tilnærminger har blitt vedtatt Av ProGuard (2016) som en standard obfuscation ordning For Java-programmer.

Fjerner overflødige symboler

denne strategien fjerner overflødig symbolsk informasjon fra utgitt programvare, for eksempel feilsøkingsinformasjonen for de Fleste programmer (Lav 1998). Dessuten er det andre redundante symboler for bestemte formater av programmer. ELF-filer inneholder for eksempel symboltabeller som registrerer parene med identifikatorer og adresser. Når du bruker standard kompileringsalternativer For å kompilere c / C++ – programmer, for eksempel VED BRUK AV LLVM (Lattner and Adve 2004), inneholder de genererte binærfilene slike symboltabeller. For å fjerne slik overflødig informasjon, kan utviklere bruke strip-verktøyet Til Linux. Et annet eksempel med redundant informasjon Er Android smali-koder. Som standard inneholder de genererte smali-kodene informasjon som startet med .line og .kilde, som kan fjernes for obfuscation formål (Dalla Preda And Maggi 2017).

Separering av relaterte koder

et program er lettere å lese hvis dets logisk relaterte koder også er fysisk nært (Collberg et al. 1997). Derfor kan separering av relaterte koder eller instruksjoner øke vanskeligheter med å lese. Den gjelder for både kildekoder (f. eks. omorganisering av variabler (Lav 1998)) og monteringskoder (f.eks. omorganisering av instruksjoner (Wroblewski 2002)). I praksis er bruk av ubetingede hopp for å omskrive et program en populær tilnærming for å oppnå dette. For eksempel kan utviklere blande monteringskoder og deretter ansette goto for å rekonstruere den opprinnelige kontrollflyten (Du Og Yim 2010). Denne tilnærmingen er populær for monteringskoder og Java bytekoder med tilgjengeligheten av goto-instruksjoner(Dalla Preda And Maggi 2017).

Søppelkoder

denne strategien legger til søppelinstruksjoner som ikke fungerer. For binærfiler kan vi legge til no-operation instruksjoner (NOP eller 0X00) (Dalla Preda and Maggi 2017; Marcelli et al. 2018). Dessuten kan vi også legge til søppelmetoder, for eksempel å legge til nedlagte metoder I Android smali-koder(Dalla Preda And Maggi 2017). Søppelkodene kan vanligvis endre signaturene til kodene, og dermed unnslippe statisk mønstergjenkjenning.

fordi layout obfuscation ikke tukle med den opprinnelige kodesyntaksen, er det mindre utsatt for kompatibilitetsproblemer eller feil. Derfor er slike teknikker de mest favoritt i praksis. Videre kan teknikker for meningsløse identifikatorer og stripping overflødige symboler redusere størrelsen på programmer, noe som ytterligere gjør dem attraktive (ProGuard 2016). Imidlertid er styrken av layoutens forvirring begrenset. Den har lovende motstandskraft mot deobfuscation angrep fordi noen transformasjoner er enveis, som ikke kan reverseres. Noen layoutinformasjon kan imidlertid knapt endres, for eksempel metodeidentifikatorene Fra Java SDK. Slik gjenværende informasjon er viktig for motstandere å gjenopprette den obfuscated informasjonen. For eksempel, Bichsel et al. (2016) forsøkte å deobfuscated ProGuard-obfuscated apps, og de klarte å gjenopprette rundt 80% navn.

Obfuscating controls

denne typen obfuscation teknikker forvandler kontrollene av koder for å øke programmets kompleksitet. Det kan oppnås via falske kontrollstrømmer, probabilistiske kontrollstrømmer, dispatcher-baserte kontroller og implisitte kontroller.

Falske kontrollstrømmer

Falske kontrollstrømmer refererer til kontrollstrømmer som er bevisst lagt til et program, men vil aldri bli utført. Det kan øke kompleksiteten i et program, f. eks. mccabe complexity (McCabe 1976) eller Harrison metrics (Harrison Og Magel 1981). For Eksempel beregnes mccabe kompleksitet (McCabe 1976) som antall kanter på en kontrollflyt-graf minus antall noder, og deretter pluss to ganger av de tilkoblede komponentene. For å øke McCabe-kompleksiteten kan Vi enten introdusere nye kanter eller legge til både nye kanter og noder til en tilkoblet komponent.

for å garantere unreachability av falske kontrollstrømmer, Collberg et al. (1997) foreslått bruk av ugjennomsiktige predikater. De definerte ugjennomsiktig predict som predikatet hvis utfall er kjent under obfuscation tid, men er vanskelig å utlede ved statisk programanalyse. Generelt kan et ugjennomsiktig predikat være konstant sant (PT), konstant falskt (PF) eller kontekstavhengig (P?). Det er tre metoder for å lage ugjennomsiktige predikater: numeriske ordninger, programmeringsordninger og kontekstuelle ordninger.

Numeriske Ordninger

Numeriske ordninger komponere ugjennomsiktige predikater med matematiske uttrykk. For eksempel er 7×2-1≠y2 konstant sant for alle heltall x og y. Vi kan direkte bruke slike ugjennomsiktige predikater for å introdusere falske kontrollstrømmer. Figur 5a viser et eksempel, hvor det ugjennomsiktige predikatet garanterer at den falske kontrollflyten (dvs.den andre grenen) ikke vil bli utført. Imidlertid vil angripere ha større sjanser til å oppdage dem hvis vi bruker de samme ugjennomsiktige predikatene ofte i et uklar program. Arboit (2002) foreslo derfor å generere en familie av slike ugjennomsiktige predikater automatisk, slik at en obfuscator kan velge et unikt ugjennomsiktig predikat hver gang.

Fig. 5
figur5

Kontroll-flow obfuscation med ugjennomsiktige predikater

en annen matematisk tilnærming med høyere sikkerhet er å bruke kryptofunksjoner, for eksempel hash-funksjon \(\mathcal {H}\) (Sharif et al. 2008), og homomorf kryptering (Zhu Og Thomborson 2005). For eksempel kan vi erstatte et predikat x = = c med \(\mathcal {H} (x)==c_{hash}\) for å skjule løsningen av x for denne ligningen. Merk at en slik tilnærming er vanligvis ansatt av malware å unngå dynamisk programanalyse. Vi kan også benytte kryptofunksjoner for å kryptere ligninger som ikke kan tilfredsstilles. Imidlertid medfører slike ugjennomsiktige predikater mye overhead.

for å komponere ugjennomsiktige konstanter som er resistente mot statisk analyse, Moser Et al. (2007) foreslo ansette 3-SAT problemer, som ER NP-hard. Dette er mulig fordi man kan ha effektive algoritmer for å komponere slike vanskelige problemer (Selman et al. 1996). For Eksempel viste Tiella and Ceccato (2017) hvordan man komponerer slike ugjennomsiktige predikater med k-clique-problemer.

For å komponere ugjennomsiktige konstanter som er resistente mot dynamisk analyse, Wang et al. (2011) foreslått å komponere ugjennomsiktige predikater med en form for uløste formodninger som loop for mange ganger. Fordi looper er utfordrende for dynamisk analyse, bør tilnærmingen i naturen være motstandsdyktig mot dynamisk analyse. Eksempler på slike formodninger inkluderer Collatz formodning, 5x + 1 formodning, Matthews formodning. Figur 5b demonstrerer hvordan man bruker Collatz-formodning for å introdusere falske kontrollstrømmer. Uansett hvordan vi initialiserer x, avsluttes programmet med x=1, og originalCodes() kan alltid utføres.

Programmeringsordninger

fordi motstridende programanalyse er en stor trussel mot ugjennomsiktige predikater, kan vi bruke utfordrende programanalyseproblemer for å komponere ugjennomsiktige predikater. Collberg et al. foreslått to klassiske problemer, pekeranalyse og samtidige programmer.

generelt refererer pekeranalyse til å avgjøre om to pekere kan eller kan peke til samme adresse. Noen pekeranalyseproblemer kan VÆRE np-harde for statisk analyse eller til og med undecidable (Landi and Ryder 1991). En annen fordel er at pekeroperasjoner er svært effektive under utførelse. Derfor kan utviklere komponere motstandsdyktige og effektive ugjennomsiktige spår med godt utformede pekeranalyseproblemer, for eksempel å opprettholde pekere til noen objekter med dynamiske datastrukturer (Collberg et al. (1998a).

Samtidige programmer eller parallelle programmer er et annet utfordrende problem. Generelt har en parallell region med n uttalelser n! ulike måter å utføre. Utførelsen bestemmes ikke bare av programmet, men også av kjøretidsstatusen til en vertsdatamaskin. Collberg et al. (1998a) foreslått å ansette samtidige programmer for å forbedre pekerbasert tilnærming ved samtidig oppdatering av pekerne. Majumdar Og Thomborson (2006) foreslo å bruke distribuerte parallelle programmer for å komponere ugjennomsiktige predikater.

dessuten komponerer noen tilnærminger ugjennomsiktige predikater med programmeringstriks, for eksempel å utnytte unntakshåndteringsmekanismer. For Eksempel Foreslo Dolz and Parra (2008) å bruke try-catch-mekanismen til å komponere ugjennomsiktige predikater for.Net og Java. Unntakshendelsene inkluderer divisjon med null, nullpeker, indeks utenfor rekkevidde, eller til og med spesielle maskinvareunntak (Chen et al. 2009). Den opprinnelige programmet semantikk kan oppnås via skreddersydde unntak håndtering ordninger. Imidlertid har slike ugjennomsiktige predikater ingen sikkerhetsgrunnlag, og de er sårbare for avanserte håndlagde angrep.

Kontekstuelle Ordninger

Kontekstuelle ordninger kan brukes til å komponere variant ugjennomsiktige predikater (dvs. {p?}). Predikatene bør ha noen deterministiske egenskaper slik at de kan brukes til å forvirre programmer. For Eksempel, Drape og et al. (2009) foreslått å komponere slike ugjennomsiktige predikater som er invariante under en kontekstuell begrensning, for eksempel er det ugjennomsiktige predikatet x mod3==1 konstant sant hvis x mod3:1?x++: x = x+3. Palsberg et al. (2000) foreslått dynamiske ugjennomsiktige predikater, som inkluderer en sekvens av korrelerte predikater. Evalueringsresultatet for hvert predikat kan variere i hvert løp. Men så lenge predikatene er korrelerte, er programadferden deterministisk. Figur 5c viser et eksempel på dynamiske ugjennomsiktige predikater. Uansett hvordan vi initialiserer * p og * q, svarer programmet til y=x + 3, x=y + 3.

motstanden til falske kontrollstrømmer er for det meste avhengig av sikkerheten til ugjennomsiktige predikater. En ideell sikkerhetsegenskap for ugjennomsiktige predikater er at de krever verste eksponentiell tid for å bryte, men bare polynomisk tid å konstruere. Merk at noen ugjennomsiktige predikater er utformet med slike sikkerhetsproblemer, men kan implementeres med feil. For eksempel, 3-LØR problemer foreslått Av Ogiso et al. (2003) er basert på trivielle probleminnstillinger som lett kan forenkles. Hvis slike ugjennomsiktige predikater implementeres riktig, ville de være lovende å være motstandsdyktige.

Probabilistiske kontrollstrømmer

Falske kontrollstrømmer kan gjøre problemer med statisk programanalyse. De er imidlertid sårbare for dynamisk programanalyse fordi de falske kontrollstrømmene er inaktive. Ideen om probabilistiske kontrollstrømmer vedtar en annen strategi for å takle trusselen (Pawlowski et al. 2016). Det introduserer replikasjoner av kontrollstrømmer med samme semantikk, men forskjellig syntaks. Når du mottar samme inngang flere ganger, kan programmet oppføre seg annerledes for ulike utførelsestider. Teknikken er også nyttig for å bekjempe sidekanalangrep (Crane et al. 2015).

Merk at strategien for probabilistiske kontrollstrømmer ligner på falske kontrollstrømmer med kontekstuelle ugjennomsiktige predikater. Men de er forskjellige i naturen som kontekstuelle ugjennomsiktige predikater introduserer døde baner, selv om de ikke introduserer søppelkoder.

Dispatcher-baserte kontroller

en dispatcher-basert kontroll bestemmer de neste kodene som skal kjøres under kjøring. Slike kontroller er avgjørende for kontroll-flow obfuscation fordi de kan skjule den opprinnelige kontrollen flyter mot statisk programanalyse.

en stor dispatcher-basert obfuscation tilnærming er kontroll-flow flattning, som forvandler koder av dybde i grunne seg med mer kompleksitet. Wang et al. (2000) for det første foreslo tilnærmingen. Figur 6 viser et eksempel fra deres papir som forvandler en stund sløyfe i en annen form med bryterhuset. For å realisere en slik transformasjon, er det første trinnet å forvandle koden til en ekvivalent representasjon med if-then-goto-setninger som vist I Fig. 6; deretter endrer de goto-setningene med switch-case-setninger som vist I Fig. 6. På denne måten realiseres den opprinnelige programsemantikken implisitt ved å kontrollere datastrømmen til brytervariabelen. Fordi utførelsesrekkefølgen av kodeblokker bestemmes av variabelen dynamisk, kan man ikke kjenne kontrollstrømmene uten å utføre programmet. Cappaert og Preneel (2010) formalisert kontrollflyt flattning som å ansette en dispatcher node (f. eks. bryter) som styrer neste kodeblokk som skal utføres; etter å ha utført en blokk, overføres kontrollen tilbake til dispatcher node. Dessuten er det flere forbedringer i kodestrømflattning. For eksempel, for å øke motstanden mot statisk programanalyse på brytervariabelen, Wang et al. (2001) foreslått å innføre pekeranalyseproblemer. For ytterligere å komplisere programmet, Chow et al. (2001) foreslått å legge til falske kodeblokker.

Fig. 6
figur6

Kontroll-flow flattening tilnærming foreslått Av Wang et al. (2000)

Lá Og Kyss (2009) foreslo en flattningsmekanisme for kontrollflyt for å håndtere spesifikk C++ – syntaks, for eksempel try-catch, while-do, continue. Mekanismen er basert på abstrakt syntaks treet og benytter et fast mønster av layout. For hver blokk med kode for å forvirre, konstruerer den en stund setning i den ytre sløyfen og en bryterhusforbindelse inne i sløyfen. Switch-case-forbindelsen implementerer den opprinnelige programsemantikken, og brytervariabelen brukes også til å avslutte den ytre sløyfen. Cappaert og Preneel (2010) fant at mekanismene kan være sårbare for lokal analyse, det vil si at brytervariabelen umiddelbart tildeles slik at motstandere kan utlede neste blokk å utføre ved bare å se på en nåværende blokk. De foreslo en styrket tilnærming med flere triks, for eksempel å bruke referanseoppgave (f. eks. swVar=swVar+1) i stedet for direkte oppdrag (f. eks. swVar=3), erstatte oppdraget via if-else med et enhetlig oppdragsuttrykk, og bruke enveisfunksjoner ved beregning av etterfølgeren til en grunnblokk.

Foruten kontrollflyt flattning, er det flere andre dispatcher-baserte obfuscation undersøkelser (f.eks (Linn and Debray 2003; Ge et al. 2005; Zhang et al. 2010; Schrittwieser Og Katzenbeisser 2011)). Linn And Debray (2003) foreslo å forvirre binærfiler med grenfunksjoner som styrer utførelsen basert på stakkinformasjonen. Tilsvarende, Zhang et al. (2010) foreslått å benytte grenfunksjoner for å forvirre objektorienterte programmer, som definerer en enhetlig metode påkallingsstil med et objektbasseng. For å forbedre sikkerheten til slike mekanismer, Ge et al. (2005) foreslått å skjule kontrollinformasjonen i en annen frittstående prosess og ansette interprosesskommunikasjon. Schrittwieser Og Katzenbeisser (2011) foreslo å ansette diversifiserte kodeblokker som implementerer samme semantikk.

Dispatcher-basert obfuscation er motstandsdyktig mot statisk analyse fordi den skjuler kontrollflyt grafen til et program. Det er imidlertid sårbart for dynamisk programanalyse eller hybrid tilnærminger. For eksempel, Udupa et al. (2005) foreslo en hybrid tilnærming for å avsløre de skjulte kontrollstrømmene med både statisk analyse og dynamisk analyse.

Implisitte kontroller

denne strategien konverterer eksplisitte kontrollinstruksjoner til implisitte kontroller. Det kan hindre omvendte ingeniører fra å adressere de riktige kontrollstrømmene. For eksempel kan vi erstatte kontrollinstruksjonene til monteringskoder (f. eks. jmp og jne) med en kombinasjon av mov og andre instruksjoner som implementerer samme kontrollsemantikk (Balachandran and Emmanuel 2011).

Merk at alle eksisterende kontroll-flow obfuscation tilnærminger fokuserer på syntaktisk nivå transformasjon, mens den semantiske nivå beskyttelse har sjelden blitt diskutert. Selv om de kan vise noe motstandskraft mot angrep, er deres obfuscation effektivitet angående semantisk beskyttelse fortsatt uklart.

Obfuscating data

Present data obfuscation teknikker fokuserer på vanlige datatyper, for eksempel heltall, strenger og matriser. Vi kan transformere data via splitting, sammenslåing, prosedyre, koding, etc.

data splitting / sammenslåing

data splitting distribuerer informasjon om en variabel i flere nye variabler. For eksempel kan en boolsk variabel deles i to boolske variabler, og å utføre logiske operasjoner på dem kan få den opprinnelige verdien.

data sammenslåing, derimot, samler flere variabler i en variabel. Collberg et al. (1998b) viste et eksempel som fusjonerer to 32-biters heltall i ett 64-biters heltall. Ertaul And Venkatesh (2005) foreslo en annen metode som pakker flere variabler i ett rom med diskrete logaritmer.

dataprosedyrering

dataprosedyrering erstatter statiske data med prosedyrekall. Collberg et al. (1998b) foreslått å erstatte strenger med en funksjon som kan produsere alle strenger ved å angi patikulære parameterverdier. Drape et al. (2004) foreslått å kode numeriske data med to inverse funksjoner f og g. For å tilordne en verdi v til en variabel i, tilordner vi den til en injisert variabel j som j=f (v). For å bruke i, påkaller vi g (j) i stedet.

datakoding

datakoding koder data med matematiske funksjoner eller koder. Ertaul And Venkatesh (2005) foreslo å kode strenger med Affine ciphers (f. Eks. Fukushima et al. (2008) foreslått å kode de klare tallene med eksklusive eller operasjoner og deretter dekryptere beregningsresultatet før utgang. Kovacheva (2013) foreslo å kryptere strenger MED RC4-krypteringen og deretter dekryptere dem under kjøretid.

Matrisetransformasjon

Matrise er en mest brukte datastruktur. Til obfuscate arrays, Collberg et al. (1998b) diskuterte flere transformasjoner, for eksempel å splitte en matrise i flere subarrays, slå sammen flere arrays i en matrise, brette en matrise for å øke dimensjonen, eller flate en matrise for å redusere dimensjonen. Ertaul Og Venkatesh (2005) foreslo å transformere matriseindeksene med sammensatte funksjoner. Zhu et al. (2006); Zhu (2007) foreslo å bruke homomorphic kryptering for array transformasjon, inkludert indeksendring, folding og smigrende. For eksempel kan vi blande elementene i en matrise med i∗m mod n, hvor jeg er den opprinnelige indeksen, n er størrelsen på den opprinnelige matrisen, og m og n er relativt primære.

Obfuscating metoder

Metode inline / outline

en metode er en uavhengig prosedyre som kan kalles av andre instruksjoner i programmet. Metode inline erstatter det opprinnelige prosedyreanropet med selve funksjonsorganet. Method outline opererer på motsatt måte som trekker ut en sekvens av instruksjoner og abstracts en metode. De er gode selskaper som kan forvirre den opprinnelige abstraksjonen av prosedyrer (Collberg et al. 1997).

Method clone

hvis en metode er tungt påberopt, kan vi lage replikasjoner av metoden og tilfeldig ringe en av dem. For å forvirre motstridende tolkning, bør hver versjon av replikasjonen være unik på en eller annen måte ,for eksempel ved å vedta forskjellige obfuscation transformasjoner (Collberg et al. 1997) eller forskjellige signaturer (Ertaul and Venkatesh 2004).

metode aggregering / spredning

ideen ligner data obfuscation. Vi kan samle irrelevante metoder i en metode eller spre en metode i flere metoder (Collberg et al. 1997; Lav 1998).

metodeproxy

denne tilnærmingen oppretter proxy-metoder for å forvirre omvendt utvikling. For eksempel kan vi opprette proxyer som offentlige statiske metoder med randomiserte identifikatorer. Det kan være flere forskjellige proxyer for samme metode(Dalla Preda And Maggi 2017). Tilnærmingen er svært nyttig når metoden signaturer ikke kan endres(Protsenko and Muller 2013).

Obfuscating klasser

Obfuscating klasser deler noen lignende ideer med obfuscating metoder, som splitting og klone (Collberg et al. (1998b). Men siden klassen bare eksisterer i objektorienterte programmeringsspråk, SOM JAVA OG. NET, diskuterer vi dem som en unik kategori. Nedenfor presenterer vi de viktigste strategiene for obfuscating klasser.

Droppe modifikatorer

Objektorienterte programmer inneholder modifikatorer (f. eks. offentlig, privat) for å begrense tilgangen til klasser og medlemmer av klasser. Dropping av modifikatorer fjerner slike restriksjoner og gjør alle medlemmer offentlige(Protsenko and Muller 2013). Denne tilnærmingen kan lette gjennomføringen av andre klasse obfuscation metoder.

Splitting/Coalescing class

ideen om coalescing / splitting er å obfuscate hensikten med utviklere når design klassene (Sosonkin et al. 2003). Når vi samler klasser, kan vi overføre lokale variabler eller lokale instruksjonsgrupper til en annen klasse (Fukushima et al. 2003).

klassehierarki flattning

Grensesnitt er et kraftig verktøy for objektorienterte programmer. I likhet med metode proxy, kan vi lage fullmakter for klasser med grensesnitt (Sosonkin et al. 2003). En mer potent måte er imidlertid å bryte det opprinnelige arvforholdet mellom klasser med grensesnitt. Ved å la hver node av et undertre i klassehierarkiet implementere det samme grensesnittet, kan vi flate hierarkiet (Foket et al. 2012).

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.