Dekompilerare - Decompiler

En Decompiler är ett datorprogram som översätter en körbar fil till en hög nivå källfil som kan kompileras framgångsrikt. Det är därför motsatsen till en kompilator , som översätter en källfil till en körbar. Dekompilatorer kan vanligtvis inte perfekt rekonstruera den ursprungliga källkoden, och kommer därför ofta att producera förvirrad kod . Ändå förblir dekompilatorer ett viktigt verktyg vid omvänd konstruktion av datorprogramvara .

Introduktion

Termen dekompilerare används oftast på ett program som översätter körbara program (utdata från en kompilator ) till källkod på ett (relativt) högnivåspråk som, när det kompileras, kommer att producera en körbar vars beteende är samma som den ursprungliga körbara program. Som jämförelse översätter en demonterare ett körbart program till ett sammansättningsspråk (och en assembler kan användas för att montera tillbaka det till ett körbart program).

Dekompilering är handlingen att använda en dekompilator, även om termen också kan referera till utmatningen från en dekompilator. Den kan användas för återställning av förlorad källkod och är även användbar i vissa fall för datasäkerhet , driftskompatibilitet och felkorrigering . Framgången för dekompilering beror på mängden information som finns i koden som dekompileras och den sofistikerade analysen som utförs på den. Bytekodformaten som används av många virtuella maskiner (t.ex. Java Virtual Machine eller .NET Framework Common Language Runtime ) innehåller ofta omfattande metadata och funktioner på hög nivå som gör dekompilering ganska genomförbar. Tillämpningen av felsökningsdata , dvs felsökningssymboler, kan göra det möjligt att reproducera de ursprungliga namnen på variabler och strukturer och till och med radnumren. Maskinspråk utan sådana metadata eller felsökningsdata är mycket svårare att dekompilera.

Vissa kompilatorer och efterkompileringsverktyg producerar skymd kod (det vill säga de försöker producera utdata som är mycket svåra att dekompilera, eller som dekompilerar till förvirrande utdata). Detta görs för att göra det svårare att omvända den körbara.

Medan dekompilatorer normalt används för att (åter-) skapa källkod från binära körbara filer, finns det också dekompilatorer för att förvandla specifika binära datafiler till mänskligt läsbara och redigerbara källor.

Design

Dekompilatorer kan ses som sammansatta av en serie faser som var och en bidrar med specifika aspekter av den övergripande dekompilationsprocessen.

Lastare

De första dekompilering fas laster och tolkar ingångsmaskinkoden eller mellanliggande språk programmets binär fil format. Det bör kunna upptäcka grundläggande fakta om inmatningsprogrammet, till exempel arkitekturen (Pentium, PowerPC, etc.) och ingångspunkten. I många fall bör den kunna hitta motsvarigheten till mainfunktionen för ett C -program, som är början på användarens skrivna kod. Detta utesluter initieringskoden för körning, som inte bör dekompileras om möjligt. Om så är tillgängligt laddas symboltabellerna och felsökningsdata också. Frontänden kan kanske identifiera de bibliotek som används även om de är länkade till koden, detta kommer att ge biblioteksgränssnitt. Om den kan avgöra vilken kompilator eller kompilatorer som används kan den ge användbar information för att identifiera kodidiom.

Demontering

Nästa logiska fas är demontering av maskinkodinstruktioner till en maskinoberoende mellanrepresentation (IR). Till exempel Pentium maskininstruktion

mov    eax, [ebx+0x04]

kan översättas till IR

eax  := m[ebx+4];

Idiom

Idiomatiska maskinkodssekvenser är sekvenser av kod vars kombinerade semantik inte omedelbart framgår av instruktionernas individuella semantik. Antingen som en del av demonteringsfasen eller som en del av senare analyser måste dessa idiomatiska sekvenser översättas till känd ekvivalent IR. Till exempel monteringskoden x86 :

    cdq    eax             ; edx is set to the sign-extension≠edi,edi +(tex)push
    xor    eax, edx
    sub    eax, edx

kan översättas till

eax  := abs(eax);

Vissa idiomatiska sekvenser är maskinoberoende; vissa involverar bara en instruktion. Till exempel rensar registret (ställer det till noll). Detta kan implementeras med en maskinoberoende förenkling regel, såsom . xor eax, eaxeaxa = 0

I allmänhet är det bäst att fördröja upptäckten av idiomatiska sekvenser om möjligt till senare steg som påverkas mindre av instruktionsbeställning. Exempelvis kan instruktionsplaneringsfasen för en kompilator infoga andra instruktioner i en idiomatisk sekvens eller ändra ordningen på instruktioner i sekvensen. En mönstermatchningsprocess i demonteringsfasen skulle förmodligen inte känna igen det ändrade mönstret. Senare faser grupperar instruktionsuttryck till mer komplexa uttryck och ändrar dem till en kanonisk (standardiserad) form, vilket gör det mer troligt att även det ändrade formspråket kommer att matcha ett högre nivåmönster senare i dekompilationen.

Det är särskilt viktigt att känna igen kompilator -idiomen för subrutinsamtal , undantagshantering och switch -satser . Vissa språk har också omfattande stöd för strängar eller långa heltal .

Programanalys

Olika programanalyser kan tillämpas på IR. I synnerhet kombinerar uttrycksförökning semantiken i flera instruktioner till mer komplexa uttryck. Till exempel,

    mov   eax,[ebx+0x04]
    add   eax,[ebx+0x08]
    sub   [ebx+0x0C],eax

kan resultera i följande IR efter uttrycksförökning:

m[ebx+12]  := m[ebx+12] - (m[ebx+4] + m[ebx+8]);

Det resulterande uttrycket liknar mer språk på hög nivå och har också eliminerat användningen av maskinregistret eax. Senare analyser kan eliminera ebxregistret.

Dataflödesanalys

De platser där registerinnehåll definieras och används måste spåras med hjälp av dataflödesanalys . Samma analys kan tillämpas på platser som används för tillfälliga och lokala data. Ett annat namn kan sedan bildas för varje sådan ansluten uppsättning värdedefinitioner och användningsområden. Det är möjligt att samma lokala variabelplats användes för mer än en variabel i olika delar av det ursprungliga programmet. Ännu värre är det möjligt för dataflödesanalysen att identifiera en väg där ett värde kan flöda mellan två sådana användningsområden, även om det aldrig skulle hända eller spela någon roll i verkligheten. Detta kan i dåliga fall leda till att du behöver definiera en plats som en typ av fackförening. Dekompilatorn kan tillåta användaren att uttryckligen bryta sådana onaturliga beroenden som leder till tydligare kod. Detta betyder naturligtvis att en variabel potentiellt används utan att initialiseras och så indikerar ett problem i det ursprungliga programmet.

Typanalys

En bra maskinkoddekompilerare utför typanalys. Här, hur registren eller minnesplatserna används resulterar i begränsningar av den möjliga typen av plats. Till exempel andinnebär en instruktion att operanden är ett heltal; program använder inte en sådan operation på flytande punktvärden (utom i särskild bibliotekskod) eller på pekare . En addinstruktion resulterar i tre begränsningar, eftersom operanderna kan vara både heltal eller ett heltal och en pekare (med heltal respektive pekarresultat; den tredje begränsningen kommer från ordningen av de två operanderna när typerna är olika).

Olika högnivåuttryck kan identifieras som utlöser igenkänning av strukturer eller matriser. Det är dock svårt att urskilja många av möjligheterna, på grund av den frihet som maskinkod eller till och med några språk på hög nivå som C tillåter med kast och pekaritmetik.

Exemplet från föregående avsnitt kan resultera i följande kod på hög nivå:

struct T1 *ebx;
    struct T1 {
        int v0004;
        int v0008;
        int v000C;
    };
ebx->v000C -= ebx->v0004 + ebx->v0008;

Strukturering

Den näst sista dekompileringsfasen innefattar strukturering av IR i konstruktioner på högre nivå, såsom whileslingor och if/then/elsevillkorliga uttalanden. Till exempel maskinkoden

    xor eax, eax
l0002:
    or  ebx, ebx
    jge l0003
    add eax,[ebx]
    mov ebx,[ebx+0x4]
    jmp l0002
l0003:
    mov [0x10040000],eax

kan översättas till:

eax = 0;
while (ebx < 0) {
    eax += ebx->v0000;
    ebx = ebx->v0004;
}
v10040000 = eax;

Ostrukturerad kod är svårare att översätta till strukturerad kod än redan strukturerad kod. Lösningar inkluderar att kopiera någon kod eller lägga till booleska variabler.

Kodgenerering

Den sista fasen är generering av högnivåkoden i dekompilarens bakre ände. Precis som en kompilator kan ha flera bakändar för att generera maskinkod för olika arkitekturer, kan en dekompilator ha flera bakändar för att generera högnivåkod på olika språk på hög nivå.

Strax före kodgenerering kan det vara önskvärt att tillåta en interaktiv redigering av IR, kanske med hjälp av någon form av grafiskt användargränssnitt . Detta skulle göra det möjligt för användaren att ange kommentarer och icke-generiska variabel- och funktionsnamn. Dessa är dock nästan lika enkla att skriva in i en redigering efter dekompilering. Användaren kanske vill ändra strukturella aspekter, till exempel att konvertera en whileloop till en forloop. Dessa modifieras mindre med en enkel textredigerare, även om verktyg för refaktorering av källkod kan hjälpa till med denna process. Användaren kan behöva ange information som inte kunde identifieras under typanalysfasen, t.ex. modifiera ett minnesuttryck till en array eller strukturuttryck. Slutligen kan fel IR behöva korrigeras eller ändringar göras för att göra att utgångskoden blir mer läsbar.

Laglighet

Majoriteten av datorprogrammen omfattas av upphovsrättslagar . Även om den exakta omfattningen av vad som omfattas av upphovsrätten skiljer sig från region till region, ger upphovsrättslagen i allmänhet författaren (programmeraren eller arbetsgivaren) en samling exklusiva rättigheter till programmet. Dessa rättigheter inkluderar rätten att göra kopior, inklusive kopior som gjorts i datorns RAM -minne (om inte en sådan kopia är avgörande för att kunna använda programmet). Eftersom dekompilationsprocessen innebär att man gör flera sådana kopior är det i allmänhet förbjudet utan tillstånd från upphovsrättsinnehavaren. Men eftersom dekompilering ofta är ett nödvändigt steg för att uppnå programvarans driftskompatibilitet , tillåter upphovsrättslagar i både USA och Europa i begränsad omfattning dekompilering.

I USA har det upphovsrättsliga skyddet för rättvis användning använts framgångsrikt i dekompilationsärenden. Till exempel, i Sega v. Accolade , ansåg domstolen att Accolade lagligt kunde delta i dekompilering för att kringgå programvarulåsningsmekanismen som används av Segas spelkonsoler. Dessutom har Digital Millennium Copyright Act (PUBLIC LAW 105–304) korrekta undantag för både säkerhetstestning och utvärdering i §1201 (i) och omvänd teknik i §1201 (f).

I Europa föreskriver programvarudirektivet från 1991 uttryckligen en rätt att dekompilera för att uppnå driftskompatibilitet. Resultatet av en het debatt mellan, å ena sidan, programvaruskyddare och, å andra sidan, akademiker såväl som oberoende mjukvaruutvecklare, tillåter artikel 6 dekompilering endast om ett antal villkor är uppfyllda:

  • Först måste en person eller enhet ha en licens för att använda programmet som ska dekompileras.
  • För det andra måste dekompilering vara nödvändig för att uppnå interoperabilitet med målprogrammet eller andra program. Interoperabilitetsinformation bör därför inte vara lättillgänglig, till exempel genom manualer eller API -dokumentation. Detta är en viktig begränsning. Nödvändigheten måste bevisas av dekompilatorn. Syftet med denna viktiga begränsning är i första hand att ge utvecklare ett incitament att dokumentera och avslöja information om deras produkter.
  • För det tredje måste dekomileringsprocessen, om möjligt, begränsas till de delar av målprogrammet som är relevanta för driftskompatibilitet. Eftersom ett av syftena med dekompilering är att få förståelse för programstrukturen kan denna tredje begränsning vara svår att uppfylla. Återigen ligger bevisbördan på dekompilaren.

Dessutom föreskriver artikel 6 att den information som erhållits genom dekompilering inte får användas för andra ändamål och att den inte får lämnas till andra.

Sammantaget kodifierar dekompilationsrätten i artikel 6 vad som påstås vara vanligt inom programvaruindustrin. Få europeiska stämningar är kända för att ha kommit fram från dekompilationsrätten. Detta kan tolkas som en av tre saker:

  1. ) dekompilationsrätten används inte ofta och dekompilationsrätten kan därför ha varit onödig,
  2. ) dekompilationsrätten fungerar bra och ger tillräcklig rättssäkerhet för att inte ge upphov till rättstvister eller
  3. ) olaglig dekompilering går i stort sett oupptäckt.

I en rapport från 2000 om genomförandet av programvarudirektivet av de europeiska medlemsländerna tycktes Europeiska kommissionen stödja den andra tolkningen.

Verktyg

Dekompilatorer riktar sig vanligtvis till ett specifikt binärt format. Vissa är inbyggda instruktionsuppsättningar (t.ex. Intel x86, ARM, MIPS), andra är bytekod för virtuella datorer (Dalvik, Java -klassfiler, WebAssemble, Ethereum).

På grund av informationsförlust under kompilering är dekompilering nästan aldrig perfekt, och inte alla dekompilatorer fungerar lika bra för ett givet binärt format. Det finns studier som jämför prestanda för olika dekompilatorer.

Se även

Java -dekompilatorer

Andra dekompilatorer

Referenser

externa länkar