¿Como decodificar x86 opcodes apropiadamente?

Iniciado por harry_the_blogger, 1 Octubre 2014, 02:45 AM

0 Miembros y 1 Visitante están viendo este tema.

harry_the_blogger

Hola, estoy trabajando en un desensamblador para x86 (32 bits). Ya logré hacer la parte que identifica los prefijos. Pero tengo un problema, ahora estoy en la parte de decodificar los opcodes.

Al principio, he estado pensando en esta idea: Mantener una tabla con los opcodes, y comparar el byte actual contra la tabla. Y según cada caso, tomar la decision apropiada: Por ejemplo leer si hay un byte mod r/m, si tiene SIB, leer desplazamiento e immediate value (haciendo esto tras leer primero el mod r/m).

Y si no coincide con ningun opcode, declarar el byte desde donde se empezó a analizar la instruccion como basura.

Pero, he estado pensando que esa idea puede ser muy ineficiente, ¿será que alguien tiene una mejor manera de hacerlo?

Por ejemplo: En vez de comparar cada byte con una tabla, usar algun principio de generalizacion, por ejemplo: Si los primeros cuatro bits son 1001, es un mov. Luego analizar y determinar el mov especifico correspondiente (mover 8bits/16bits/32bits). ¿Será que es posible? ¿O esa es solo la unica manera?

PD: Ya he estado leyendo algunos sources de programas open source, pero muchos usan esa estrategia, al menos en apariencia. Los estoy leyendo a fondo.

Gracias de antemano.
Vista mi blog es enriquemesa.blogspot.com

cpu2

Como en mi anterior mensaje, te invito a leer los manuales en mi caso los de AMD Vol 3, en el punto 1.3 Opcodes pag 16, hay lo tienes todo bien explicado.

Por ejemplo te voy hacer un ejemplo bien facil, tomando los valores y las explicaciones del manual.

Código (asm) [Seleccionar]
movl %esi, %esp  ;  0x89, 0xf4

El opcode la referencia de mov seria 0x89, el valor 0xf4 es ModRM.

0xf4 == 11110100   >> 100 es el encode de esp, 110 el de esi y por ultimo el 11 es el ModRM.mod, no se explicartelo muy bien, lo tienes en la pag 21-22, se que tiene que ver algo con r/m.

Ese ejemplo es basico claro que si haces direcciones mas complejas tienes los SIB byte, pero eso mejor que lo leas, si tiens alguna duda o problema comentalo.

Un saludo.

ctap07b9

No queda otra que entender los PDFs que INTEL o AMD presentan en su web.
Es un tema complicado por el arrastre que los procesadores x86 han acumulado.
Muchos prefijos a entender.

Por ejemplo lo primero que interpreta la CPU es la instrucción que
suelen saltar de 8 en 8 unidades , dependiendo del valor :

0000 add [bx+si],al
0800 or [bx+si],al
1000 adc [bx+si],al
1800 sbb [bx+si],al
2000 and [bx+si],al
2800 sub [bx+si],al
3000 xor [bx+si],al
3800 cmp [bx+si],al
40 inc ax


esos 8 espacios permiten decidir operandos o no.
Pero no todo es tan predecible, hay muchas variaciones.
y prefijos como los nuevos 0F que designan instrucciones modernas.

Animo y que no decaiga el interés.


harry_the_blogger

Bueno, gracias a todos por su ayuda. Ya llevo unos días leyendo los PDFs de Intel. (AMD no me agrada mucho. XD.). Pero aún no consigo encontrar algún patrón en las instrucciones que me evite hacer una comparacion directa con una tabla de opcodes.

Pero al menos con lo que me han dicho ya tengo una orientacion. (Eso de que van de 8 en 8 no lo sabía. XD). Bueno, aunque mi desensamblador no tiene porque reconocer todos los opcodes, al menos quiero que reconozca las instrucciones más usadas (Operaciones aritmetica, Saltos condicionales, Manejo de pila, Mover datos, entre otras de uso común).

Bueno, si alguna problema sucede, me imagino que puedo contar con ustedes. ¿o no? XD.

Gracias a todos por sus respuestas.
Vista mi blog es enriquemesa.blogspot.com

cpu2

Claro si tienes algun problema comentalo, pero como te dije antes y ahora en las pag y capitulo que cite esta todo bien detallado.

Si no te gustan los AMD lee los Intel, aun que sera practicamente igual.

Un saludo.

ctap07b9

Si, los 8 espacios de operandos son por los 8 registros
de proposito general ordenados con numeros de menor
a mayo AX,CX,DX,BX,SP,BP,SI,DI
( numeros hexadecimales)
40 inc ax
41 inc cx
42 inc dx
43 inc bx
44 inc sp
45 inc bp
46 inc si
47 inc di

si empiezas con 40+8 entonces es la instruccion "dec"
si empiezas con 50 entonces es la instruccion "push"
si empiezas con 50+8 entonces es la instruccion "pop"

Pero no todo es tan rigido, por ejemplo.

0000 add [bx+si],al
0001 add [bx+di],al
0002 add [bp+si],al
0003 add [bp+di],al
0004 add [si],al
0005 add [di],al
0006 add [0x0000],al
0007 add [bx],al

No es lineal con los registros de proposito general.

Creo que las tablas son totalmente necesarias.
Además hay que determinar si la CPU opera en 16 o 32 bits
porque los prefijos querrán decir una cosa u otra.

harry_the_blogger

Hola gracias a todos por sus respuestas. Por favor, ¿será que me pueden decir un método que sea rápido y eficiente para reconocer opcodes de un byte? He estado tratando que saber si ciertos bits están activados, y en ese caso reconocer si es un opcode valido (en ese caso se podría generalizar la instrucción push, descartando e ignorando los tres últimos bits).

He estado intentando con hacer un AND entre una máscara de bits (que selecciona cuáles bits debe considerar) y el byte a verificar si es opcode, y luego comparar el resultado con el opcode general.

Por ahora me ha dado algunos resultados, pero seguiré probandolo. ¿Será que alguien me puede decir si estoy procediendo de la mejor forma? ¿O habrá otra mejor?

Ah, reconocer opcodes de 16 bits no me interesa mucho, estoy centrado en 32 bits. Realmente solo quiero hacer un desensamblador que me reconozca las instrucciones de 32 bits, al menos las más usadas.

Gracias a todos de antemano.
Vista mi blog es enriquemesa.blogspot.com

cpu2

De momento no se me ocurre otro metodo que ir comparando con mascaras y cosas asi.

Sobre la instruccion push, no solo gasta un byte depende de el parametro que pases, ya sea registro, valor o offset.

Si pasas un registro este se torna en 1 byte, que es 0101 0000, el primer bloque de 4 bits hace referencia al 5 y el otro al registro, te leiste lo que te dije sobre ModRM?

El ejemplo que te puse seria push eax ya que eax vale 0 en ModRM, un push ebx seria 0x53 0101 0011, ya que ebx es 11 en ModRM.

Como te dije no se me pasa otra cosa por la cabeza que no sea como bien dijiste con mascaras de bit y todo eso.

Un saludo.

harry_the_blogger

Gracias cpu2 por tu ayuda. Bueno, si al parecer no hay otro método, seguiré por esa linea. Ah, en cuanto al byte Mod R/M ya he leído algo, específicamente he aprendido que puede estar embebido dentro del opcode en algunas ocasiones.

E incluso, (si no me equivoco), una parte del opcode, puede ir dentro del mod R/M. Bueno, seguiré leyendo más sobre x86 en los manuales de Intel.

Gracias por clarificarme de nuevo las cosas sobre Mod R/M.
Vista mi blog es enriquemesa.blogspot.com

cpu2

De nada, si tienes algun problema no dudes en comentarlo.

Un saludo.