2009-05-29 11 views
59

Mi interessa scrivere un dissomigiatore x86 come progetto educativo.Come scrivere un disassemblatore?

L'unica vera risorsa che ho trovato è Spiral Space, "How to write a disassembler". Mentre questo dà una bella descrizione di alto livello dei vari componenti di un disassemblatore, sono interessato ad alcune risorse più dettagliate. Ho anche dato una rapida occhiata al codice sorgente NASM's ma questo è un po 'un peso massimo da cui imparare.

Mi rendo conto che una delle maggiori sfide di questo progetto è il set di istruzioni x86 piuttosto grande che dovrò gestire. Mi interessano anche la struttura di base, i collegamenti di base del disassemblatore, ecc.

Qualcuno può indicarmi risorse dettagliate sulla scrittura di un disassemblatore x86?

+0

Non una risposta, ma la risposta in http://stackoverflow.com/questions/82432/is-learning-assembly-language-worth-the-effort è anche una buona lettura per coloro che stanno iniziando. – claws

risposta

59

Date un'occhiata al section 17.2 del 80386 Programmer's Reference Manual. Un disassemblatore è in realtà solo un glorioso finite-state machine. I passaggi in smontaggio sono:

  1. Verificare se il byte corrente è un byte di prefisso di istruzioni (F3, F2 o F0); se è così, allora hai un prefisso REP/REPE/REPNE/LOCK. Avanzamento al byte successivo.
  2. Verificare se il byte corrente è un byte di dimensione indirizzo (67). In tal caso, decodificare gli indirizzi nel resto dell'istruzione in modalità a 16 bit se attualmente in modalità a 32 bit o decodificare gli indirizzi in modalità a 32 bit se attualmente in modalità a 16 bit
  3. Verificare se il byte corrente è un byte di dimensione dell'operando (66).In tal caso, decodificare gli operandi immediati in modalità a 16 bit se attualmente in modalità a 32 bit o decodificare operandi immediati in modalità a 32 bit se correntemente in modalità 16 bit
  4. Verificare se il byte corrente è un byte di esclusione del segmento (2E, 36, 3E, 26, 64 o 65). In tal caso, utilizzare il registro dei segmenti corrispondente per gli indirizzi di decodifica anziché il registro dei segmenti predefinito.
  5. Il byte successivo è l'opcode. Se l'opcode è 0F, allora è un opcode esteso e legge il byte successivo come opcode esteso.
  6. A seconda del codice operativo particolare, leggere e decodificare un byte Mod R/M, un byte SIB (Scale Index Base), uno spostamento (0, 1, 2 o 4 byte) e/o un valore immediato (0, 1, 2 o 4 byte). Le dimensioni di questi campi dipendono dall'opcode, dall'override della dimensione dell'indirizzo e dalle dimensioni dell'operando sostituite precedentemente decodificate.

Il codice operativo indica l'operazione eseguita. Gli argomenti dell'opcode possono essere decodificati dai valori di Mod R/M, SIB, spostamento e valore immediato. Ci sono molte possibilità e molti casi speciali, a causa della natura complessa di x86. Vedi i link sopra per una spiegazione più approfondita.

+2

Il manuale Intel dice che "I gruppi da 1 a 4 possono essere collocati in qualsiasi ordine relativo l'uno all'altro", quindi i passaggi 1-4 potrebbero non essere in questo ordine – szx

+2

questo è vero per x86 ma non per x86_64 o set di istruzioni moderne come AVX/AVX2 –

6

Iniziare con un piccolo programma che è stato assemblato e che fornisce sia il codice generato che le istruzioni. Fatti un riferimento con lo instruction architecture e lavora a mano con il codice generato con il riferimento dell'architettura. Scoprirai che le istruzioni hanno una struttura molto stereotipata di inst op op op con un numero variabile di operandi. Tutto ciò che devi fare è tradurre la rappresentazione esadecimale o ottale del codice in modo che corrisponda alle istruzioni; un po 'di gioco lo rivelerà.

Quel processo, automatizzato, è il cuore di un disassemblatore. Idealmente, probabilmente vorrai costruire un n array di strutture di istruzioni internamente (o esternamente, se il programma è veramente grande). È quindi possibile tradurre tale matrice nelle istruzioni in formato assembler.

4

È necessaria una tabella di codici op da cui caricare.

La base dati di ricerca fondamentale è un trie, tuttavia una tabella andrà bene se non ci si preoccupa molto della velocità.

Per ottenere il tipo di codice di base, inizia con la corrispondenza sul tavolo.

Esistono alcuni modi per decodificare gli argomenti del registro; tuttavia, ci sono abbastanza casi speciali da richiedere l'implementazione della maggior parte di essi individualmente.

Dal momento che questo è educativo, dai un'occhiata a ndisasm.

21

Si consiglia di verificare alcuni disassemblatori open source, preferibilmente distorm e in particolare "disOps (Istruzioni SetBase di dati)" (ctrl + individuarlo nella pagina).

La documentazione è piena di informazioni sugose su opcode e istruzioni.

preventivo dahttps://code.google.com/p/distorm/wiki/x86_x64_Machine_Code

80x86 Instruction:

Un'istruzione 80x86 è diviso in un numero di elementi:

  1. prefissi istruzioni, influisce sul comportamento del operazione delle istruzioni.
  2. Prefisso obbligatorio utilizzato come byte opcode per le istruzioni SSE.
  3. I byte di codice, possono essere uno o più byte (fino a 3 byte interi).
  4. Il byte ModR/M è facoltativo e talvolta può contenere una parte del codice operativo stesso.
  5. Il byte SIB è facoltativo e rappresenta i moduli di memoria indiretta della memoria complessa .
  6. Il dislocamento è facoltativo ed è un valore di una dimensione variabile di byte (byte, word, long) e utilizzato come offset .
  7. Immediato è facoltativo e viene utilizzato come valore di numero generale creato da una dimensione variabile di byte (byte, word, long).

Il formato presenta come segue:

/-------------------------------------------------------------------------------------------------------------------------------------------\ 
|*Prefixes | *Mandatory Prefix | *REX Prefix | Opcode Bytes | *ModR/M | *SIB | *Displacement (1,2 or 4 bytes) | *Immediate (1,2 or 4 bytes) | 
\-------------------------------------------------------------------------------------------------------------------------------------------/ 
* means the element is optional. 

Le strutture di dati e le fasi di decodifica sono spiegati in https://code.google.com/p/distorm/wiki/diStorm_Internals

RDA:

Fasi decodifica

  1. [prefissi]
  2. [Fetch Opcode]
  3. [Filtro Opcode]
  4. [estratto Operando (s)]
  5. [formattazione di testo]
  6. [Hex Dump]
  7. [Istruzione decodificata]

Ogni passaggio è spiegato anche.


I collegamenti originali sono conservati per ragioni storiche:

http://code.google.com/p/distorm/wiki/x86_x64_Machine_Code e http://code.google.com/p/distorm/wiki/diStorm_Internals

+1

+1 questi sono alcuni ottimi puntatori – claws

+2

"Il prefisso obbligatorio opzionale" sembra divertente. – Calmarius

+0

Sembra che i link siano morti – szx

2

Acquista objdump fonti: è un ottimo strumento, contiene molte tabelle opcode e le sue fonti possono fornire una buona base per creare il tuo disassemblatore.