Layered obfuscation: en taksonomi af programmel obfuscation teknikker til lagdelt sikkerhed

dette afsnit undersøger obfuscation teknikker til specifikke kodeelementer. Dette lag dækker de fleste af publikationerne i programformørkelsesområdet. Som vist i Fig. 4, i henhold til hvilke elementer en obfuscation teknik mål, vi deler denne kategori i fem underkategorier: obfuscating layouts, obfuscating kontrol, obfuscating dataobfuscating funktioner, og obfuscating klasser.

Fig. 4
figur4

obfuscation teknikker af kode-element lag

Obfuscating layout

Layout obfuscation krypterer layoutet af koder eller instruktioner, mens den oprindelige syntaks holdes intakt. I dette afsnit beskrives fire layout tilsløring strategier: meningsløse klassifikatorer, stripning overflødige symboler, adskille relaterede koder, og junk koder.

meningsløse identifikatorer

denne tilgang er også kendt som leksikalsk tilsløring, der omdanner meningsfulde identifikatorer til meningsløse. For de fleste programmeringssprog kræves vedtagelse af meningsfulde og ensartede navngivningsregler (f.eks. ungarsk Notation (Simonyi 1999)) som en god programmeringspraksis. Selvom sådanne navne er angivet i kildekoder, vil nogle forblive i det frigivne program som standard. For eksempel opbevares navnene på globale variabler og funktioner i C/C++ i binære filer, og alle navne på Java er reserveret i bytekoder. Fordi sådanne meningsfulde navne kan lette kontradiktorisk programanalyse, bør vi scramble dem. For at gøre de tilslørede identifikatorer mere forvirrende foreslog Chan og Yang (2004) bevidst at anvende de samme navne på objekter af forskellige typer eller inden for forskellige domæner. Sådanne tilgange er blevet vedtaget af ProGuard (2016) som en standard formørkelse ordning for Java-programmer.

stripning af overflødige symboler

denne strategi fjerner overflødige symbolske oplysninger fra frigivne programmer, såsom fejlfindingsoplysninger for de fleste propgrammer (lav 1998). Desuden er der andre overflødige symboler for bestemte formater af programmer. For eksempel indeholder ELF-filer symboltabeller, der registrerer par af identifikatorer og adresser. Ved vedtagelse af standard kompileringsindstillinger til kompilering af C/C++ – programmer, såsom brug af LLVM (Lattner og Adve 2004), indeholder de genererede binære filer sådanne symboltabeller. For at fjerne sådanne overflødige oplysninger kan udviklere anvende strimmelværktøjet. Et andet eksempel med overflødige oplysninger er Android smali-koder. Som standard indeholder de genererede smali-koder oplysninger, der er startet med .line og .kilde, som kan fjernes til formørkelse formål (Dalla Preda og Maggi 2017).

adskillelse af relaterede koder

et program er lettere at læse, hvis dets logisk relaterede koder også er fysisk tæt (Collberg et al. 1997). Derfor kan adskillelse af relaterede koder eller instruktioner øge vanskelighederne ved læsning. Det gælder både kildekoder (f.eks. omordningsvariabler (lav 1998)) og samlingskoder (f. eks. omordningsinstruktioner (2002)). I praksis er det en populær tilgang at anvende ubetingede Spring til at omskrive et program for at opnå dette. For eksempel kan udviklere blande samlingskoderne og derefter anvende goto til at rekonstruere den oprindelige kontrolstrøm (You and Yim 2010). Denne tilgang er populær for monteringskoder og Java bytekoder med tilgængeligheden af GOTO-instruktioner (Dalla Preda og Maggi 2017).

Junk koder

denne strategi tilføjer junk instruktioner, som ikke er funktionelle. For binære filer kan vi tilføje instruktioner uden brug (NOP eller 0h00) (dalla Preda og Maggi 2017; Marcelli et al. 2018). Desuden kan vi også tilføje junk metoder, såsom at tilføje hedengangne metoder i Android smali koder (Dalla Preda og Maggi 2017). Junk koder kan typisk ændre signaturerne af koderne, og derfor undslippe statisk mønstergenkendelse.

fordi layout tilsløring ikke manipulere med den oprindelige kode syntaks, det er mindre tilbøjelige til kompatibilitetsproblemer eller fejl. Derfor er sådanne teknikker de mest foretrukne i praksis. Desuden kan teknikkerne til meningsløse identifikatorer og stripping af overflødige symboler reducere størrelsen af programmer, hvilket yderligere gør dem attraktive (ProGuard 2016). Imidlertid er styrken af layoutforklumpningen begrænset. Det har lovende modstandsdygtighed over for deobfuscationsangreb, fordi nogle transformationer er envejs, som ikke kan vendes. Imidlertid kan nogle layoutoplysninger næppe ændres, såsom metodeidentifikatorerne fra Java SDK. Sådanne resterende oplysninger er afgørende for modstandere at genvinde de tilslørede oplysninger. For eksempel Bichsel et al. (2016) forsøgte at deobfuscated ProGuard-tilslørede apps, og de med succes genvundet omkring 80% Navne.

Obfuscating controls

denne type obfuscation teknikker transformerer kontrol af koder for at øge programmets kompleksitet. Det kan opnås via falske kontrolstrømme, probabilistiske kontrolstrømme, dispatcher-baserede kontroller og implicitte kontroller.

falske kontrolstrømme

falske kontrolstrømme henviser til kontrolstrømmene, der bevidst føjes til et program, men som aldrig vil blive udført. Det kan øge kompleksiteten af et program, f. eks., i McCabe kompleksitet (McCabe 1976) eller Harrison metrics (Harrison og Magel 1981). For eksempel beregnes McCabe-kompleksitet (McCabe 1976) som antallet af kanter på en kontrolstrømsgraf minus antallet af noder og derefter plus to gange af de tilsluttede komponenter. For at øge McCabe-kompleksiteten kan vi enten introducere nye kanter eller tilføje både nye kanter og noder til en tilsluttet komponent.

for at garantere utilgængeligheden af falske kontrolstrømme, Collberg et al. (1997) foreslog at anvende uigennemsigtige prædikater. De definerede uigennemsigtig forudsigelse som prædikatet, hvis resultat er kendt under tilsløringstid, men er vanskeligt at udlede ved statisk programanalyse. Generelt kan et uigennemsigtigt prædikat være konstant sandt (PT), konstant falsk (PF) eller kontekstafhængig (P?). Der er tre metoder til at skabe uigennemsigtige prædikater: numeriske ordninger, programmeringsordninger og kontekstuelle ordninger.

numeriske ordninger

numeriske ordninger komponerer uigennemsigtige prædikater med matematiske udtryk. For eksempel er 7H2−1 liter Y2 konstant sandt for alle heltal h og y. Vi kan direkte anvende sådanne uigennemsigtige prædikater til at indføre falske kontrolstrømme. Figur 5a viser et eksempel, hvor det uigennemsigtige prædikat garanterer, at den falske kontrolstrøm (dvs.den anden gren) ikke udføres. Imidlertid ville angribere have større chancer for at opdage dem, hvis vi ofte anvender de samme uigennemsigtige prædikater i et forvirret program. Arboit (2002) foreslog derfor automatisk at generere en familie af sådanne uigennemsigtige prædikater, således at en tilslører kan vælge et unikt uigennemsigtigt prædikat hver gang.

Fig. 5
figur5

formørkelse af Kontrolstrømmen med uigennemsigtige prædikater

en anden matematisk tilgang med højere sikkerhed er at anvende kryptofunktioner, såsom hash-funktion \(\mathcal {H}\) (Sharif et al. 2008) og homomorf kryptering (2005). For eksempel kan vi erstatte et prædikat med \(\mathcal {H}(H)==c_{hash}\) for at skjule løsningen af H for denne ligning. Bemærk, at en sådan tilgang generelt anvendes til at undgå dynamisk programanalyse. Vi kan også anvende kryptofunktioner til at kryptere ligninger, som ikke kan opfyldes. Sådanne uigennemsigtige prædikater pådrager sig imidlertid meget overhead.

for at komponere uigennemsigtige konstanter, der er resistente over for statisk analyse, Moser et al. (2007) foreslog at anvende 3-SAT-problemer, som er NP-hårde. Dette er muligt, fordi man kan have effektive algoritmer til at komponere sådanne hårde problemer (Selman et al. 1996). For eksempel demonstrerede Tiella og Ceccato (2017), hvordan man komponerer sådanne uigennemsigtige prædikater med K-klikproblemer.

for at komponere uigennemsigtige konstanter, der er resistente over for dynamisk analyse, Vang et al. (2011) foreslog at komponere uigennemsigtige prædikater med en form for uløste formodninger, der løber i mange gange. Fordi sløjfer er udfordrende for dynamisk analyse, bør tilgangen i naturen være modstandsdygtig over for dynamisk analyse. Eksempler på sådanne formodninger omfatter Collats formodninger, 5h+1 formodninger, Mattheus formodninger. Figur 5b viser, hvordan man anvender Collats-formodninger til at indføre falske kontrolstrømme. Uanset hvordan vi initialiserer, afsluttes programmet med 1, og originalCodes () kan altid udføres.

Programmeringsordninger

da kontradiktorisk programanalyse er en stor trussel mod uigennemsigtige prædikater, kan vi anvende udfordrende programanalyseproblemer til at komponere uigennemsigtige prædikater. Collberg et al. foreslået to klassiske problemer, pointer analyse og samtidige programmer.

generelt henviser pointeranalyse til at bestemme, om to pointere kan eller kan pege på den samme adresse. Nogle pointeranalyseproblemer kan være NP-hårde for statisk analyse eller endda ubeslutsomme (Landi og Ryder 1991). En anden fordel er, at markøroperationer er meget effektive under udførelsen. Derfor kan udviklere komponere elastiske og effektive uigennemsigtige prognoser med veldesignede pointeranalyseproblemer, såsom vedligeholdelse af pointers til nogle objekter med dynamiske datastrukturer (Collberg et al. 1998a).

samtidige programmer eller parallelle programmer er et andet udfordrende problem. Generelt har en parallel region af N-udsagn n! forskellige måder at udføre. Udførelsen bestemmes ikke kun af programmet, men også af runtime-status for en værtscomputer. Collberg et al. (1998a) foreslog at anvende samtidige programmer for at forbedre den pegerbaserede tilgang ved samtidig at opdatere pegerne. Majumdar og Thomborson (2006) foreslog at anvende distribuerede parallelle programmer til at komponere uigennemsigtige prædikater.

desuden komponerer nogle tilgange uigennemsigtige prædikater med programmeringstricks, såsom at udnytte undtagelseshåndteringsmekanismer. For eksempel foreslog Parra (2008) at bruge try-catch-mekanismen til at komponere uigennemsigtige prædikater til.net og Java. Undtagelseshændelserne inkluderer opdeling med nul, null pointer, indeks uden for rækkevidde eller endda særlige undtagelser fra udstyr (Chen et al. 2009). Det originale program semantik kan opnås via skræddersyede undtagelseshåndteringsordninger. Sådanne uigennemsigtige prædikater har imidlertid intet sikkerhedsgrundlag, og de er sårbare over for avancerede håndlavede angreb.

kontekstuelle ordninger

kontekstuelle ordninger kan anvendes til at komponere variant uigennemsigtige prædikater(dvs. {P?}). Prædikaterne skal have nogle deterministiske egenskaber, således at de kan anvendes til at tilsløre programmer. For eksempel, Drape og et al. (2009) foreslået at komponere sådanne uigennemsigtige prædikater, som er uforanderlige under en kontekstuel begrænsning, f. eks. er det uigennemsigtige prædikat Mod3==1 konstant sandt, hvis mod3:1?+ + 3. Palsberg et al. (2000) foreslog dynamiske uigennemsigtige prædikater, som inkluderer en sekvens af korrelerede prædikater. Evalueringsresultatet for hvert prædikat kan variere i hvert løb. Men så længe prædikaterne er korrelerede, er programadfærden deterministisk. Figur 5c viser et eksempel på dynamiske uigennemsigtige prædikater. Uanset hvordan vi initialiserer *p og *S, svarer programmet til y=S + 3, S=S + 3.

modstanden af falske kontrolstrømme afhænger for det meste af sikkerheden ved uigennemsigtige prædikater. En ideel sikkerhedsegenskab for uigennemsigtige prædikater er, at de kræver værste tilfælde eksponentiel tid til at bryde, men kun polynomisk tid til at konstruere. Bemærk, at nogle uigennemsigtige prædikater er designet med sådanne sikkerhedsproblemer, men kan implementeres med fejl. For eksempel 3-SAT-problemerne foreslået af ogiso et al. (2003) er baseret på trivielle problemindstillinger, som let kan forenkles. Hvis sådanne uigennemsigtige prædikater implementeres korrekt, ville de være lovende at være modstandsdygtige.

probabilistiske kontrolstrømme

falske kontrolstrømme kan gøre problemer med statisk programanalyse. De er dog sårbare over for dynamisk programanalyse, fordi de falske kontrolstrømme er inaktive. Ideen om probabilistiske kontrolstrømme vedtager en anden strategi for at tackle truslen. 2016). Det introducerer replikationer af kontrolstrømme med samme semantik, men forskellige syntaks. Når du modtager den samme indgang flere gange, kan programmet opføre sig forskelligt for forskellige udførelsestider. Teknikken er også nyttig til bekæmpelse af sidekanalangreb (Crane et al. 2015).

Bemærk, at strategien for probabilistiske kontrolstrømme ligner falske kontrolstrømme med kontekstuelle uigennemsigtige prædikater. Men de er forskellige i naturen, da kontekstuelle uigennemsigtige prædikater introducerer døde stier, selvom de ikke introducerer junk-koder.

Dispatcher-baserede kontroller

en dispatcher-baseret kontrol bestemmer de næste blokke af koder, der skal udføres under runtime. Sådanne kontroller er afgørende for kontrolstrømsforbinding, fordi de kan skjule de oprindelige kontrolstrømme mod statisk programanalyse.

en større afsenderbaseret tilsløringsmetode er udfladning af kontrolstrømmen, som omdanner dybdekoder til lavvandede med mere kompleksitet. Et al. (2000) for det første foreslog den tilgang. Figur 6 viser et eksempel fra deres papir, der omdanner et stykke loop til en anden form med omskifter. For at realisere en sådan transformation er det første skridt at omdanne koden til en tilsvarende repræsentation med If-then-goto-udsagn som vist i Fig. 6; derefter ændrer de goto-udsagnene med Skift-case-udsagn som vist i Fig. 6. På denne måde realiseres det originale programsemantik implicit ved at kontrollere datastrømmen for omskiftervariablen. Fordi eksekveringsrækkefølgen for kodeblokke bestemmes af variablen dynamisk, kan man ikke kende kontrolstrømmene uden at udføre programmet. Cappaert og Preneel (2010) formaliserede udfladning af kontrolstrøm som anvendelse af en afsenderknude (f.eks. skifte), der styrer den næste kodeblok, der skal udføres; efter udførelse af en blok overføres kontrol tilbage til afsenderknudepunktet. Desuden er der flere forbedringer til kodestrømsfladning. For eksempel for at forbedre modstanden mod statisk programanalyse på omskiftervariablen, Vang et al. (2001) foreslået at indføre pointer analyse problemer. For yderligere at komplicere programmet, Chov et al. (2001) foreslået at tilføje falske kodeblokke.

Fig. 6
figur6

kontrol-strømning udfladning tilgang foreslået af Vang et al. (2000)

2009 foreslog en kontrol-strøm udfladning mekanisme til at håndtere specifikke C++ syntaks, såsom try-catch, mens-do, fortsætte. Mekanismen er baseret på abstrakt syntaks træ og anvender et fast mønster af layout. For hver blok kode, der skal tilsløres, konstruerer den et stykke tid i den ydre sløjfe og en omskifterhusforbindelse inde i løkken. Omskifterkasseforbindelsen implementerer det originale programsemantik, og omskiftervariablen anvendes også til at afslutte den ydre sløjfe. Cappaert og Preneel (2010) fandt ud af, at mekanismerne kan være sårbare over for lokal analyse, dvs.at omskiftervariablen straks tildeles således, at modstandere kan udlede den næste blok, der skal udføres, ved kun at se på en nuværende blok. De foreslog en styrket tilgang med flere tricks, såsom at anvende referencetildeling (f.eks. svar=Svar+1) i stedet for direkte tildeling (f. eks. svar=3), erstatte opgaven via if-else med et ensartet opgaveudtryk og anvende envejsfunktioner til beregning af efterfølgeren til en grundlæggende blok.

udover udfladning af kontrolstrømmen er der adskillige andre afsendelsesbaserede tilsløringsundersøgelser (f.eks. (Linn og Debray 2003; Ge et al. 2005; Chang et al. 2010; Schmidt og Katsenbeisser 2011)). Linn og Debray (2003) foreslog at tilsløre binære filer med grenfunktioner, der styrer udførelsen baseret på stakoplysningerne. Tilsvarende, Jang et al. (2010) foreslog at anvende filialfunktioner til at tilsløre objektorienterede programmer, der definerer en samlet metode påkaldelsesstil med en objektpool. For at forbedre sikkerheden ved sådanne mekanismer, Ge et al. (2005) foreslog at skjule kontroloplysningerne i en anden selvstændig proces og anvende kommunikation mellem processer. (2011) foreslog at anvende diversificerede kodeblokke, der implementerer den samme semantik.

Dispatcher-baseret tilsløring er modstandsdygtig over for statisk analyse, fordi den skjuler styringsgrafen for et program. Det er dog sårbart over for dynamisk programanalyse eller hybridmetoder. For eksempel udupa et al. (2005) foreslog en hybrid tilgang til at afsløre de skjulte kontrolstrømme med både statisk analyse og dynamisk analyse.

implicitte kontroller

denne strategi konverterer eksplicitte kontrolinstruktioner til implicitte. Det kan forhindre omvendte ingeniører i at adressere de korrekte kontrolstrømme. Jmp og jne) med en kombination af mov og andre instruktioner, der implementerer den samme kontrolsemantik (Balachandran og Emmanuel 2011).

Bemærk, at alle eksisterende styrestrøms-tilsløringsmetoder fokuserer på syntaktisk niveau transformation, mens beskyttelse på semantisk niveau sjældent er blevet diskuteret. Selvom de muligvis viser en vis modstandsdygtighed over for angreb, forbliver deres tilsløringseffektivitet med hensyn til semantisk beskyttelse uklar.

Obfuscating data

nuværende data obfuscation teknikker fokuserer på almindelige datatyper, såsom heltal, strenge og arrays. Vi kan transformere data via opdeling, sammenlægning, procedurisering, kodning osv.

data opdeling/sammenlægning

data opdeling distribuerer oplysninger om en variabel i flere nye variabler. For eksempel kan en boolsk variabel opdeles i to boolske variabler, og udførelse af logiske operationer på dem kan få den oprindelige værdi.

Datafletning aggregerer på den anden side flere variabler i en variabel. Collberg et al. (1998b) demonstrerede et eksempel, der fusionerer to 32-bit heltal i et 64-bit heltal. Ertaul og Venkatesh (2005) foreslog en anden metode, der pakker flere variabler i et rum med diskrete logaritmer.

data procedurering

data procedurering erstatter statiske data med procedure opkald. Collberg et al. (1998b) foreslog at erstatte strenge med en funktion, der kan producere alle strenge ved at specificere paticular parameterværdier. Drape og et al. (2004) foreslået at kode numeriske data med to inverse funktioner f og g. For at tildele en værdi v til en variabel i, tildeler vi den til en injiceret variabel j som j=f(v). For at bruge jeg påberåber vi g (j) i stedet.

datakodning

datakodning koder data med matematiske funktioner eller cifre. Ertaul og Venkatesh (2005) foreslog at kode strenge med Affine cifre (f.eks. Caser cipher) og anvende diskrete logaritmer til at pakke ord. Fukushima et al. (2008) foreslog at kode de klare tal med eksklusive eller operationer og derefter dekryptere beregningsresultatet før output. Kovacheva (2013) foreslog at kryptere strenge med RC4-krypteringen og derefter dekryptere dem under runtime.

array transformation

Array er en mest almindeligt anvendte datastruktur. At tilsløre arrays, Collberg et al. (1998b) diskuterede flere transformationer, såsom opdeling af et array i flere subarrays, sammenlægning af flere arrays i et array, foldning af et array for at øge dets dimension eller udfladning af et array for at reducere dimensionen. Ertaul og Venkatesh (2005) foreslog at omdanne array-indekserne med sammensatte funktioner. Et al. (2006); (2007) foreslog at anvende homomorf kryptering til array transformation, herunder indeksændring, foldning og smigrende. For eksempel kan vi blande elementerne i et array med i Kurt m mod n, hvor jeg er det originale indeks, n er størrelsen på det originale array, og m og n er relativt primære.

Obfuscating methods

Method inline/outline

en metode er en uafhængig procedure, der kan kaldes af andre instruktioner i programmet. Metode inline erstatter det oprindelige procedureopkald med selve funktionskroppen. Metodeoversigt fungerer på den modsatte måde, som udtrækker en sekvens af instruktioner og abstraherer en metode. De er gode virksomheder, der kan tilsløre den oprindelige abstraktion af procedurer (Collberg et al. 1997).

Metodeklon

hvis en metode er stærkt påberåbt, kan vi oprette replikationer af metoden og tilfældigt kalde en af dem. For at forvirre kontradiktorisk fortolkning skal hver version af replikationen være unik på en eller anden måde, såsom ved at vedtage forskellige tilsløringstransformationer (Collberg et al. 1997) eller forskellige underskrifter (Ertaul og Venkatesh 2004).

Metodeaggregering/spredning

ideen ligner dataforklaring. Vi kan samle irrelevante metoder i en metode eller sprede en metode i flere metoder (Collberg et al. 1997; lav 1998).

metode fuldmagt

denne tilgang skaber fuldmægtige metoder til at forvirre reverse engineering. For eksempel kan vi oprette fuldmagter som offentlige statiske metoder med randomiserede identifikatorer. Der kan være flere forskellige fuldmagter for den samme metode (Dalla Preda og Maggi 2017). Fremgangsmåden er yderst nyttig, når metodesignaturerne ikke kan ændres (Protsenko og Muller 2013).

Tilslørende klasser

Tilslørende klasser deler nogle lignende ideer med tilslørende metoder, såsom opdeling og klon (Collberg et al. 1998b). Da klasse kun findes i objektorienterede programmeringssprog, såsom JAVA og.net, diskuterer vi dem som en unik kategori. Nedenfor præsenterer vi de vigtigste strategier for tilslørende klasser.

droppe modifikatorer

objektorienterede programmer indeholder modifikatorer (f. eks., offentlig, privat) for at begrænse adgangen til klasser og medlemmer af klasser. Droppe modifikatorer fjerner sådanne begrænsninger og gøre alle medlemmer offentlige (Protsenko og Muller 2013). Denne tilgang kan lette implementeringen af andre klasseforvævningsmetoder.

opdeling/Koalescering klasse

ideen om koalescering/opdeling er at tilsløre udviklernes hensigt, når de designer klasserne (Sosonkin et al. 2003). Når vi samler klasser, kan vi overføre lokale variabler eller lokale instruktionsgrupper til en anden klasse (Fukushima et al. 2003).

klassehierarki udfladning

Interface er et kraftfuldt værktøj til objektorienterede programmer. I lighed med metode fuldmagt kan vi oprette fuldmagter til klasser med grænseflader (Sosonkin et al. 2003). En mere potent måde er imidlertid at bryde det oprindelige arveforhold mellem klasser med grænseflader. Ved at lade hver node af et undertræ i klassehierarkiet implementere den samme grænseflade, kan vi flade hierarkiet (Foket et al. 2012).

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.