Layered obfuscation: a taxonomy of software obfuscation techniques for layered security

detta avsnitt undersöker fördunklingsteknikerna för specifika kodelement. Detta lager täcker de flesta av publikationerna i software obfuscation area. Såsom visas i Fig. 4, enligt vilka element en obfuscationsteknik riktar sig till, delar vi upp denna kategori i fem underkategorier: obfuscating layouter, obfuscating kontroller, obfuscating dataobfuscating funktioner och obfuscating klasser.

Fig. 4
figur4

obfuscation tekniker för kod-element lager

Obfuscating layout

Layout obfuscation förvränger layouten av koder eller instruktioner samtidigt som den ursprungliga syntaxen intakt. I det här avsnittet diskuteras fyra layoutfördunklingsstrategier: meningslösa klassificerare, strippning av överflödiga symboler, separering av relaterade koder och skräpkoder.

meningslösa identifierare

detta tillvägagångssätt är också känt som lexical obfuscation som omvandlar meningsfulla identifierare till meningslösa. För de flesta programmeringsspråk krävs att man antar meningsfulla och enhetliga namnregler (t.ex. Ungersk Notation (Simonyi 1999)) som en bra programmeringspraxis. Även om sådana namn anges i källkoder, skulle vissa förbli i den släppta programvaran som standard. Till exempel hålls namnen på globala variabler och funktioner i C/C++ i binärer, och alla namn på Java är reserverade i bytekoder. Eftersom sådana meningsfulla namn kan underlätta kontradiktorisk programanalys, bör vi förvränga dem. För att göra de förvirrade identifierarna mer förvirrande föreslog Chan och Yang (2004) att medvetet använda samma namn för objekt av olika slag eller inom olika domäner. Sådana tillvägagångssätt har antagits av ProGuard (2016) som ett standardfördunklingsschema för Java-program.

Stripping redundanta symboler

denna strategi remsor redundant symbolisk information från släppt programvara, såsom felsökningsinformation för de flesta program (låg 1998). Dessutom finns det andra redundanta symboler för vissa programformat. Till exempel innehåller ELF-filer symboltabeller som registrerar par av identifierare och adresser. När du antar standardkompileringsalternativ för att kompilera C / C++ – program, till exempel att använda LLVM (Lattner och Adve 2004), innehåller de genererade binärerna sådana symboltabeller. För att ta bort sådan överflödig information kan utvecklare använda strip-verktyget i Linux. Ett annat exempel med överflödig information är Android smali-koder. Som standard innehåller de genererade smali-koderna information som startas med .linje och .källa, som kan tas bort för obfuscation ändamål (Dalla Preda och Maggi 2017).

separera relaterade koder

ett program är lättare att läsa om dess logiskt relaterade koder också är fysiskt nära (Collberg et al. 1997). Därför kan separering av relaterade koder eller instruktioner öka läsningssvårigheterna. Den är tillämplig på både källkoder (t.ex. omordningsvariabler (låg 1998)) och monteringskoder (t. ex. omordningsinstruktioner (Wroblewski 2002)). I praktiken är det populärt att använda ovillkorliga hopp för att skriva om ett program för att uppnå detta. Till exempel kan utvecklare blanda monteringskoderna och sedan använda goto för att rekonstruera det ursprungliga kontrollflödet (You and Yim 2010). Detta tillvägagångssätt är populärt för monteringskoder och Java bytekoder med tillgången på Goto-instruktioner (Dalla Preda och Maggi 2017).

Skräpkoder

denna strategi lägger till skräpinstruktioner som inte fungerar. För binärer kan vi lägga till instruktioner utan Drift (NOP eller 0x00) (Dalla Preda och Maggi 2017; Marcelli et al. 2018). Dessutom kan vi också lägga till skräpmetoder, till exempel att lägga till nedlagda metoder i Android smali-koder (Dalla Preda och Maggi 2017). Skräpkoderna kan vanligtvis ändra kodernas signaturer och därför undvika statisk mönsterigenkänning.

eftersom layout obfuscation inte manipulerar med den ursprungliga kodsyntaxen är den mindre benägen för kompatibilitetsproblem eller buggar. Därför är sådana tekniker de mest favorit i praktiken. Dessutom kan teknikerna för meningslösa identifierare och strippande överflödiga symboler minska storleken på programmen, vilket ytterligare gör dem attraktiva (ProGuard 2016). Emellertid är styrkan i layouten obfuscation begränsad. Det har lovande motståndskraft mot deobfuscationattacker eftersom vissa omvandlingar är enkelriktade, som inte kan vändas. Viss layoutinformation kan dock knappast ändras, till exempel metodidentifierarna från Java SDK. Sådan kvarstående information är nödvändig för motståndare att återvinna förvrängd information. Till exempel Bichsel et al. (2016) försökte deobfuscated ProGuard-obfuscated apps, och de lyckades återhämta sig runt 80% namn.

Obfuscating kontroller

denna typ av obfuscation tekniker omvandlar kontrollerna av koder för att öka programmets komplexitet. Det kan uppnås via falska kontrollflöden, probabilistiska kontrollflöden, avsändarbaserade kontroller och implicita kontroller.

falska kontrollflöden

falska kontrollflöden hänvisar till de kontrollflöden som medvetet läggs till i ett program men aldrig kommer att köras. Det kan öka komplexiteten i ett program, t. ex., i McCabe komplexitet (McCabe 1976) eller Harrison metrics (Harrison och Magel 1981). Exempelvis beräknas McCabe complexity (McCabe 1976) som antalet kanter på ett kontrollflödesdiagram minus antalet noder och sedan plus två gånger av de anslutna komponenterna. För att öka McCabe-komplexiteten kan vi antingen introducera nya kanter eller lägga till både nya kanter och noder till en ansluten komponent.

för att garantera att falska kontrollflöden inte kan nås, Collberg et al. (1997) föreslog att man använde ogenomskinliga predikat. De definierade ogenomskinlig förutsäga som predikatet vars resultat är känt under obfuscationstid men är svårt att härleda genom statisk programanalys. I allmänhet kan ett ogenomskinligt predikat vara ständigt sant (PT), ständigt falskt (PF) eller kontextberoende (P?). Det finns tre metoder för att skapa ogenomskinliga predikat: numeriska system, programmeringsscheman och kontextuella system.

numeriska scheman

numeriska scheman komponerar ogenomskinliga predikat med matematiska uttryck. Till exempel är 7×2-1 x2-1 x2 alltid sant för alla heltal x och y. Vi kan direkt använda sådana ogenomskinliga predikat för att införa falska kontrollflöden. Figur 5a visar ett exempel där det ogenomskinliga predikatet garanterar att det falska kontrollflödet (dvs. else-grenen) inte kommer att utföras. Men angripare skulle ha större chanser att upptäcka dem om vi använder samma ogenomskinliga predikat ofta i ett obfuscated program. Arboit (2002) föreslog därför att generera en familj av sådana ogenomskinliga predikat automatiskt, så att en obfuscator kan välja ett unikt ogenomskinligt predikat varje gång.

Fig. 5
figur5

kontrollflöde obfuscation med ogenomskinliga predikat

ett annat matematiskt tillvägagångssätt med högre säkerhet är att använda kryptofunktioner, såsom hashfunktion \(\mathcal {H}\) (Sharif et al. 2008) och homomorf kryptering (Zhu och Thomborson 2005). Till exempel kan vi ersätta ett predikat x==c med \(\mathcal {H}(x)==c_{hash}\) för att dölja lösningen av x för denna ekvation. Observera att ett sådant tillvägagångssätt i allmänhet används av skadlig kod för att undvika dynamisk programanalys. Vi kan också använda kryptofunktioner för att kryptera ekvationer som inte kan uppfyllas. Sådana ogenomskinliga predikat medför emellertid mycket omkostnader.

för att komponera ogenomskinliga konstanter som är resistenta mot statisk analys, Moser et al. (2007) föreslog att man använde 3-SAT-problem, som är NP-hårda. Detta är möjligt eftersom man kan ha effektiva algoritmer för att komponera sådana hårda problem (Selman et al. 1996). Till exempel visade Tiella och Ceccato (2017) hur man komponerar sådana ogenomskinliga predikat med k-clique-problem.

för att komponera ogenomskinliga konstanter som är resistenta mot dynamisk analys, Wang et al. (2011) föreslog att komponera ogenomskinliga predikat med en form av olösta gissningar som slingrar många gånger. Eftersom loopar är utmanande för dynamisk analys bör tillvägagångssättet i naturen vara motståndskraftigt mot dynamisk analys. Exempel på sådana gissningar inkluderar Collatz gissningar, 5x + 1 gissningar, Matthews gissningar. Figur 5b visar hur man använder Collatz-gissningar för att införa falska kontrollflöden. Oavsett hur vi initierar x, avslutas programmet med x=1, och originalCodes() kan alltid köras.

Programmeringsscheman

eftersom kontradiktorisk programanalys är ett stort hot mot ogenomskinliga predikat kan vi använda utmanande programanalysproblem för att komponera ogenomskinliga predikat. Collberg et al. föreslog två klassiska problem, pekaranalys och samtidiga program.

i allmänhet hänvisar pekaranalys till att bestämma om två pekare kan eller kan peka på samma adress. Vissa pekaranalysproblem kan vara NP-svåra för statisk analys eller till och med obeslutbar (Landi och Ryder 1991). En annan fördel är att pekaroperationer är mycket effektiva under körning. Därför kan utvecklare komponera fjädrande och effektiva ogenomskinliga förutspår med väl utformade pekaranalysproblem, som att upprätthålla pekare till vissa objekt med dynamiska datastrukturer (Collberg et al. 1998a).

samtidiga program eller parallella program är en annan utmanande fråga. I allmänhet har en parallell region med n-uttalanden n! olika sätt att utföra. Exekveringen bestäms inte bara av programmet utan också av körtidsstatusen för en värddator. Collberg et al. (1998A) föreslog att använda samtidiga program för att förbättra den pekarbaserade metoden genom att samtidigt uppdatera pekarna. Majumdar och Thomborson (2006) föreslog att använda distribuerade parallella program för att komponera ogenomskinliga predikat.

dessutom komponerar vissa tillvägagångssätt ogenomskinliga predikat med programmeringstrick, såsom att utnyttja undantagshanteringsmekanismer. Till exempel föreslog Dolz och Parra (2008) att använda try-catch-mekanismen för att komponera ogenomskinliga predikat för.net och Java. Undantagshändelserna inkluderar division med noll, null pekare, index utanför intervallet eller till och med särskilda hårdvaruundantag (Chen et al. 2009). Det ursprungliga programmet semantik kan uppnås via skräddarsydda undantagshanteringsscheman. Sådana ogenomskinliga predikat har emellertid ingen säkerhetsbas, och de är sårbara för avancerade handgjorda attacker.

kontextuella system

kontextuella system kan användas för att komponera variant opaka predikat(dvs {P?}). Predikaten bör ha vissa deterministiska egenskaper så att de kan användas för att dölja program. Till exempel drapering och et al. (2009) föreslog att komponera sådana ogenomskinliga predikat som är invarianta under en kontextuell begränsning, t. ex. det ogenomskinliga predikatet x mod3==1 är ständigt sant om x mod3:1?x++: x=x + 3. Palsberg et al. (2000) föreslagna dynamiska ogenomskinliga predikat, som inkluderar en sekvens av korrelerade predikat. Utvärderingsresultatet för varje predikat kan variera i varje körning. Men så länge predikaterna är korrelerade är programbeteendet deterministiskt. Figur 5c visar ett exempel på dynamiska ogenomskinliga predikat. Oavsett hur vi initierar *p och * q, motsvarar programmet y=x+3,x=y+3.

motståndet hos falska kontrollflöden beror mest på säkerheten hos ogenomskinliga predikat. En idealisk säkerhetsegenskap för ogenomskinliga predikat är att de kräver värsta fall exponentiell tid att bryta men bara polynom tid att konstruera. Observera att vissa ogenomskinliga predikat är utformade med sådana säkerhetsproblem men kan implementeras med brister. Till exempel de 3-SAT-problem som föreslagits av Ogiso et al. (2003) är baserade på triviala probleminställningar som lätt kan förenklas. Om sådana ogenomskinliga predikat implementeras korrekt, skulle de lova att vara motståndskraftiga.

probabilistiska kontrollflöden

falska kontrollflöden kan göra problem med statisk programanalys. De är dock sårbara för dynamisk programanalys eftersom de falska kontrollflödena är inaktiva. Tanken om probabilistiska kontrollflöden antar en annan strategi för att hantera hotet (Pawlowski et al. 2016). Det introducerar replikeringar av kontrollflöden med samma semantik men olika syntax. När du tar emot samma ingång flera gånger kan programmet uppträda annorlunda för olika exekveringstider. Tekniken är också användbar för att bekämpa sidokanalattacker (Crane et al. 2015).

Observera att strategin för probabilistiska kontrollflöden liknar falska kontrollflöden med kontextuella ogenomskinliga predikat. Men de är olika i naturen eftersom kontextuella ogenomskinliga predikat introducerar döda vägar, även om de inte introducerar skräpkoder.

Dispatcher-baserade kontroller

en dispatcher-baserad kontroll bestämmer nästa block av koder som ska köras under körning. Sådana kontroller är väsentliga för kontrollflödesfördunkling eftersom de kan dölja de ursprungliga kontrollflödena mot statisk programanalys.

en stor avsändare-baserade fördunkling tillvägagångssätt är kontroll-flöde plattas, som omvandlar koder djup i grunda sådana med mer komplexitet. Wang et al. (2000) för det första föreslog metoden. Figur 6 visar ett exempel från deras papper som förvandlar en While loop till en annan form med switch-case. För att förverkliga en sådan omvandling är det första steget att omvandla koden till en ekvivalent representation med if-then-goto-uttalanden som visas i Fig. 6; sedan ändrar de goto-uttalandena med switch-case-uttalanden som visas i Fig. 6. På detta sätt realiseras den ursprungliga programsemantiken implicit genom att styra dataflödet för omkopplingsvariabeln. Eftersom exekveringsordningen för kodblock bestäms av variabeln dynamiskt kan man inte känna till kontrollflödena utan att köra programmet. Cappaert och Preneel (2010) formaliserade kontrollflödesplattning som att använda en avsändarnod (t.ex. switch) som styr nästa kodblock som ska köras; efter att ha kört ett block överförs kontrollen tillbaka till avsändarnoden. Dessutom finns det flera förbättringar av kodflödesplattning. Till exempel, för att förbättra motståndet mot statisk programanalys på omkopplingsvariabeln, Wang et al. (2001) föreslog att införa pekaranalysproblem. För att ytterligare komplicera programmet, Chow et al. (2001) föreslog att lägga till falska kodblock.

Fig. 6
figur6

Kontrollflödesplattningsmetod föreslagen av Wang et al. (2000)

l 2009 föreslog en kontrollflödesplattningsmekanism för att hantera specifik C++ – syntax, som try-catch, while-do, continue. Mekanismen är baserad på abstrakt syntaxträd och använder ett fast layoutmönster. För varje block av kod för att fördunkla, konstruerar det ett tag uttalande i den yttre slingan och en switch-fall förening inuti slingan. Switch-case-föreningen implementerar den ursprungliga programsemantiken, och switch-variabeln används också för att avsluta den yttre slingan. Cappaert och Preneel (2010) fann att mekanismerna kan vara sårbara för lokal analys, dvs omkopplingsvariabeln tilldelas omedelbart så att motståndare kan härleda nästa block att utföra genom att bara titta på ett aktuellt block. De föreslog ett förstärkt tillvägagångssätt med flera knep, såsom att använda referensuppgift (t.ex. swVar=swVar+1) istället för direkt tilldelning (t. ex. swVar=3), ersätta uppgiften via if-else med ett enhetligt uppdragsuttryck och använda envägsfunktioner vid beräkning av efterföljaren till ett grundblock.

förutom kontrollflödesplattning finns det flera andra avsändarbaserade obfuscationsundersökningar (t.ex. (Linn och Debray 2003; Ge et al. 2005; Zhang et al. 2010; Schrittwieser och Katzenbeisser 2011)). Linn och Debray (2003) föreslog att dölja binärer med filialfunktioner som styr körningen baserat på stackinformationen. Liknande, Zhang et al. (2010) föreslog att använda filialfunktioner för att dölja objektorienterade program, som definierar en enhetlig metod anropsstil med en objektpool. För att förbättra säkerheten för sådana mekanismer, Ge et al. (2005) föreslog att dölja kontrollinformationen i en annan fristående process och använda kommunikation mellan processer. Schrittwieser och Katzenbeisser (2011) föreslog att använda diversifierade kodblock som implementerar samma semantik.

Dispatcher-baserad obfuscation är resistent mot statisk analys eftersom den döljer styrflödesdiagrammet för ett program. Det är dock sårbart för dynamisk programanalys eller hybridmetoder. Till exempel Udupa et al. (2005) föreslog en hybridmetod för att avslöja de dolda kontrollflödena med både statisk analys och dynamisk analys.

implicita kontroller

denna strategi konverterar explicita kontrollinstruktioner till implicita. Det kan hindra omvända ingenjörer från att ta itu med rätt kontrollflöden. Till exempel kan vi ersätta kontrollinstruktionerna för monteringskoder (t.ex. jmp och jne) med en kombination av mov och andra instruktioner som implementerar samma kontrollsemantik (Balachandran och Emmanuel 2011).

Observera att alla befintliga styrflödesfördunklingsmetoder fokuserar på syntaktisk nivåomvandling, medan semantisk nivåskydd sällan har diskuterats. Även om de kan visa viss motståndskraft mot attacker, är deras obfuscationseffektivitet när det gäller semantiskt skydd fortfarande oklart.

Obfuscating data

nuvarande data obfuscation tekniker fokuserar på vanliga datatyper, såsom heltal, strängar och arrayer. Vi kan omvandla data via delning, sammanslagning, procedurering, kodning etc.

datadelning/sammanslagning

datadelning distribuerar informationen om en variabel i flera nya variabler. Till exempel kan en boolesk variabel delas upp i två booleska variabler, och att utföra logiska operationer på dem kan få det ursprungliga värdet.

datasammanslagning, å andra sidan, aggregerar flera variabler i en variabel. Collberg et al. (1998b) visade ett exempel som sammanfogar två 32-bitars heltal i ett 64-bitars heltal. Ertaul och Venkatesh (2005) föreslog en annan metod som packar flera variabler i ett utrymme med diskreta logaritmer.

dataprocedurering

dataprocedurering ersätter statiska data med proceduranrop. Collberg et al. (1998b) föreslog att ersätta strängar med en funktion som kan producera alla strängar genom att ange paticular parametervärden. Drapera och et al. (2004) föreslog att koda numeriska data med två inversa funktioner f och g. För att tilldela ett värde v till en variabel i tilldelar vi den till en injicerad variabel j som j=f(v). För att använda i, åberopar vi g (j) istället.

datakodning

datakodning kodar data med matematiska funktioner eller chiffer. Ertaul och Venkatesh (2005) föreslog att koda strängar med Affine ciphers (t.ex. Caser cipher) och använda diskreta logaritmer för att packa ord. Fukushima et al. (2008) föreslog att koda de tydliga siffrorna med exklusiva eller operationer och sedan dekryptera beräkningsresultatet före utgången. Kovacheva (2013) föreslog att kryptera strängar med RC4-chifferet och sedan dekryptera dem under körning.

Array transformation

Array är en vanligast förekommande datastruktur. För att dölja matriser, Collberg et al. (1998b) diskuterade flera transformationer, såsom att dela en array i flera subarrays, slå samman flera arrayer i en array, vika en array för att öka dess dimension eller platta en array för att minska dimensionen. Ertaul och Venkatesh (2005) föreslog att omvandla matrisindexen med kompositfunktioner. Zhu et al. (2006); Zhu (2007) föreslog att använda homomorf kryptering för matristransformation, inklusive indexändring, vikning och smickrande. Till exempel kan vi blanda elementen i en array med i Macau m mod n, där jag är det ursprungliga indexet, n är storleken på den ursprungliga arrayen, och m och n är relativt prime.

Obfuscating metoder

metod inline/outline

en metod är en oberoende procedur som kan anropas av andra instruktioner i programmet. Metod inline ersätter det ursprungliga proceduranropet med själva funktionskroppen. Metod kontur fungerar på motsatt sätt som extraherar en sekvens av instruktioner och abstraherar en metod. De är bra företag som kan dölja den ursprungliga abstraktionen av förfaranden (Collberg et al. 1997).

Metodklon

om en metod är starkt åberopad kan vi skapa replikationer av metoden och slumpmässigt ringa en av dem. För att förvirra motsatt tolkning bör varje version av replikationen vara unik på något sätt, till exempel genom att anta olika fördunklingstransformationer (Collberg et al. 1997) eller olika signaturer (Ertaul och Venkatesh 2004).

metod aggregering/spridning

tanken liknar data obfuscation. Vi kan samla irrelevanta metoder i en metod eller sprida en metod i flera metoder (Collberg et al. 1997; låg 1998).

metod proxy

detta tillvägagångssätt skapar proxymetoder för att förvirra omvänd teknik. Till exempel kan vi skapa proxyservrar som offentliga statiska metoder med randomiserade identifierare. Det kan finnas flera olika proxies för samma metod (Dalla Preda och Maggi 2017). Tillvägagångssättet är mycket användbart när metoden signaturer inte kan ändras (Protsenko och Muller 2013).

Obfuscating klasser

Obfuscating klasser delar några liknande tankar med obfuscating metoder, såsom splittring och klon (Collberg et al. 1998b). Men eftersom klassen bara finns i objektorienterade programmeringsspråk, som JAVA och.Net, diskuterar vi dem som en unik kategori. Nedan presenterar vi de viktigaste strategierna för obfuscating klasser.

släppa modifierare

objektorienterade program innehåller modifierare (t. ex., offentliga, privata) för att begränsa tillgången till klasser och medlemmar i klasserna. Att släppa modifierare tar bort sådana begränsningar och gör alla medlemmar offentliga (Protsenko and Muller 2013). Detta tillvägagångssätt kan underlätta genomförandet av andra klassfördunstningsmetoder.

Splitting/Coalescing class

tanken med coalescing/splitting är att fördunkla utvecklarens avsikt när de utformar klasserna (Sosonkin et al. 2003). När vi samlar klasser kan vi överföra lokala variabler eller lokala instruktionsgrupper till en annan klass (Fukushima et al. 2003).

klasshierarki plattning

gränssnitt är ett kraftfullt verktyg för objektorienterade program. I likhet med metodproxy kan vi skapa proxyservrar för klasser med gränssnitt (Sosonkin et al. 2003). Ett mer kraftfullt sätt är dock att bryta det ursprungliga arvsförhållandet mellan klasser med gränssnitt. Genom att låta varje nod i ett underträd i klasshierarkin implementera samma gränssnitt kan vi platta hierarkin (Foket et al. 2012).

Lämna ett svar

Din e-postadress kommer inte publiceras.