2009-05-29 17 views
59

Estoy interesado en escribir un disquete x86 como un proyecto educativo.¿Cómo escribir un desensamblador?

El único recurso real que he encontrado es Spiral Space, "How to write a disassembler". Si bien esto ofrece una buena descripción de alto nivel de los diversos componentes de un desensamblador, me interesan algunos recursos más detallados. También he echado un vistazo rápido al código fuente NASM's, pero este es un poco pesado para aprender.

Me doy cuenta de que uno de los principales desafíos de este proyecto es el conjunto de instrucciones x86 bastante grande que voy a tener que manejar. También me interesan la estructura básica, los enlaces desensambladores básicos, etc.

¿Alguien me puede indicar algún recurso detallado sobre cómo escribir un desensamblador x86?

+0

No es una respuesta, pero la respuesta en http://stackoverflow.com/questions/82432/is-learning-assembly-language-worth-the-effort también es una buena lectura para aquellos que están comenzando. – claws

Respuesta

59

Tome un vistazo a section 17.2 del 80386 Programmer's Reference Manual. Un desensamblador es solo una glorificada finite-state machine. Los pasos de desmontaje son:

  1. Comprobar si el byte actual es un byte de prefijo de instrucción (F3, F2 o F0); si es así, entonces tiene un prefijo REP/REPE/REPNE/LOCK. Avance al siguiente byte.
  2. Compruebe si el byte actual es un byte de tamaño de dirección (67). Si es así, decodifique direcciones en el resto de la instrucción en modo de 16 bits si actualmente está en modo de 32 bits, o decodifique direcciones en modo de 32 bits si actualmente está en modo de 16 bits
  3. Compruebe si el byte actual está un byte de tamaño de operando (66).Si es así, decodifique operandos inmediatos en modo de 16 bits si actualmente está en modo de 32 bits, o decodifique operandos inmediatos en modo de 32 bits si actualmente está en modo de 16 bits
  4. Compruebe si el byte actual es un byte de sobreescritura de segmento (2E, 36, 3E, 26, 64, o 65). De ser así, utilice el registro de segmento correspondiente para decodificar direcciones en lugar del registro de segmento predeterminado.
  5. El siguiente byte es el código de operación. Si el código de operación es 0F, entonces es un código de operación extendido, y lee el siguiente byte como el código de operación extendido.
  6. Según el código de operación particular, lea y decodifique un byte Mod R/M, un byte Base de índice de escala (SIB), un desplazamiento (0, 1, 2 o 4 bytes) y/o un valor inmediato (0, 1, 2 o 4 bytes). Los tamaños de estos campos dependen del código de operación, la anulación del tamaño de la dirección y las anulaciones del tamaño del operando previamente decodificadas.

El código de operación le indica la operación que se está realizando. Los argumentos del código de operación se pueden decodificar a partir de los valores de Mod R/M, SIB, desplazamiento y valor inmediato. Hay muchas posibilidades y muchos casos especiales, debido a la naturaleza compleja de x86. Vea los enlaces de arriba para una explicación más completa.

+2

El manual de Intel dice 'Los grupos del 1 al 4 pueden colocarse en cualquier orden relativos entre sí', por lo que los pasos 1-4 pueden no estar en ese orden – szx

+2

esto es cierto para x86 pero no para x86_64 o conjuntos de instrucciones modernos como AVX/AVX2 –

6

Comience con un programa pequeño que se haya ensamblado y que le proporcione el código generado y las instrucciones. Obtenga una referencia con el instruction architecture, y resuelva a mano algunos de los códigos generados con la referencia de arquitectura. Encontrará que las instrucciones tienen una estructura muy estereotípica de inst op op op con un número variable de operandos. Todo lo que necesita hacer es traducir la representación hexadecimal o octal del código para que coincida con las instrucciones; un poco de juego lo revelará.

Ese proceso, automatizado, es el núcleo de un desensamblador. Idealmente, es probable que desee construir una serie de estructuras de instrucciones internamente (o externamente, si el programa es realmente grande). A continuación, puede traducir esa matriz a las instrucciones en formato de ensamblador.

4

Necesita una tabla de códigos de operación para cargar.

La estructura de búsqueda fundamental es un trie, sin embargo, una tabla funcionará bastante bien si no le importa demasiado la velocidad.

Para obtener el tipo de código de operación base, comienza con el partido en la mesa.

Existen algunas maneras básicas de decodificar los argumentos de registro; sin embargo, hay suficientes casos especiales para requerir la implementación de la mayoría de ellos individualmente.

Dado que esto es educativo, eche un vistazo a ndisasm.

21

Recomendaría echar un vistazo a algunos desensambladores de código abierto, preferiblemente distorm y especialmente "disOps (Instructions Sets DataBase)" (ctrl + encuéntrelo en la página).

La documentación en sí está llena de información jugosa sobre los códigos de operación e instrucciones.

Presupuesto dehttps://code.google.com/p/distorm/wiki/x86_x64_Machine_Code

80x86 Instrucción:

Una instrucción de 80x86 se divide en un número de elementos:

  1. prefijos instrucción, afecta el comportamiento de la instrucción operación.
  2. Prefijo obligatorio utilizado como byte de código de operación para las instrucciones de SSE.
  3. Los bytes del código de operación, podrían ser de uno o más bytes (hasta 3 bytes completos).
  4. ModR/M byte es opcional y, a veces, podría contener una parte del código de operación en sí.
  5. El byte SIB es opcional y representa formas complejas de direccionamiento indirecto de memoria .
  6. El desplazamiento es opcional y tiene un valor de un tamaño variable de bytes (byte, palabra, largo) y se usa como un offset .
  7. Inmediato es opcional y se utiliza como un valor de número general construido desde un tamaño variable de bytes (byte, palabra, largo).

El formato es el siguiente:

/-------------------------------------------------------------------------------------------------------------------------------------------\ 
|*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. 

Las estructuras de datos y fases de decodificación se explican en https://code.google.com/p/distorm/wiki/diStorm_Internals

Cita:

Fases de decodificación

  1. [Prefijos]
  2. [Fetch Opcode]
  3. [Filtro Opcode]
  4. [Extracto Operando (s)]
  5. [Formato de texto]
  6. [Dump Hex]
  7. [Instrucción decodificada]

Cada paso se explica también.


Los enlaces originales se mantienen por razones históricas:

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

+1

+1 estos son algunos indicadores excelentes – claws

+2

"Prefijo obligatorio opcional" parece gracioso. – Calmarius

+0

Parece que los enlaces están muertos – szx

2

Pedido objdump fuentes - es una gran herramienta, contiene muchas tablas de códigos de operación y sus fuentes pueden proporcionar una buena base para hacer su propio desensamblador.

Cuestiones relacionadas