2011-11-13 37 views
5

Me gustaría saber si alguien puede ayudarme con un problema que tengo cuando estudio una de las diapositivas de clase de montaje introductorio que estoy tomando en la escuela. El problema que tengo es que no entiendo el ensamblaje, sino cómo se ordena el código fuente de C en función del ensamblaje. Publicaré el fragmento del que estoy hablando y tal vez quede más claro de lo que estoy hablando.Ingeniería inversa C-código fuente del ensamblaje

C Fuente dado:

int arith(int x, int y, int z) 
{ 
    int t1 = x+y; 
    int t2 = z+t1; 
    int t3 = x+4; 
    int t4 = y * 48; 
    int t5 = t3 + t4; 
    int rval = t2 * t5; 
    return rval; 
} 

montaje que se indica:

arith: 
pushl %ebp 
movl %esp,%ebp 

movl 8(%ebp),%eax 
movl 12(%ebp),%edx 
leal (%edx,%eax),%ecx 
leal (%edx,%edx,2),%edx 
sall $4,%edx 
addl 16(%ebp),%ecx 
leal 4(%edx,%eax),%eax 
imull %ecx,%eax 

movl %ebp,%esp 
popl %ebp 
ret 

Estoy confundido en cuanto a cómo se supone que debo ser capaz de discernir, por ejemplo, que la adición de z + t1 (z + x + y) aparece en la segunda línea (en la fuente) cuando en el ensamblaje viene después de y * 48 en el código de ensamblaje o, por ejemplo, x + 4 es la tercera línea cuando en el ensamblaje ni siquiera está en línea, es tipo de mezclado con la última declaración leal. Tiene sentido para mí cuando tengo la fuente pero se supone que puedo reproducir la fuente para una prueba y entiendo que el compilador optimice las cosas, pero si alguien tiene una forma de pensar en la ingeniería inversa que podría ayudarme Les agradecería mucho si pudieran guiarme a través de su proceso de pensamiento.

Gracias.

+0

¿Qué nivel está utilizando cuando se compila la optimización? Si desea una conversión línea por línea, use -O0; de lo contrario, deberá tener en cuenta las optimizaciones. – Maz

+1

... y revertir el código optimizado al orden de enunciado C original no es del todo posible. –

+0

¿Estás seguro de que esta es una compilación ingenua? Parece que está algo optimizado. Escriba la expresión algebraica que calcula la función y vea si puede descubrir eso. –

Respuesta

9

He desglosado el desmontaje para que usted muestre cómo se produjo el ensamblaje a partir de la fuente C.

8(%ebp) = x, 12(%ebp) = y, 16(%ebp) = z

arith: 

Crear el marco de pila:

pushl %ebp 
movl %esp,%ebp 


Mover x en eax, y en edx:

movl 8(%ebp),%eax 
movl 12(%ebp),%edx 


t1 = x + y. leal (Load dirección efectiva) agregará edx y eax, y t1 estará en ecx:

leal (%edx,%eax),%ecx 


int t4 = y * 48; en dos pasos a continuación, se multiplica por 3, y luego por 16. t4 al final habrá en edx :

Multiplica edx por 2 y agrega edx al resultado, es decir. edx = edx * 3:

leal (%edx,%edx,2),%edx 

Shift left 4 bits, es decir. multiplicar por 16:

sall $4,%edx 


int t2 = z+t1;. ecx inicialmente sostiene t1, z es en 16(%ebp), al final de la instrucción ecx será la celebración de t2:

addl 16(%ebp),%ecx 


int t5 = t3 + t4;. t3 era simplemente x + 4, y en lugar de calcular y almacenar t3, la expresión t3 se coloca en línea. Esta instrucción esencial es (x+4) + t4, que es lo mismo que t3 + t4. Agrega edx ( t4) y eax ( x), y agrega 4 como un desplazamiento para lograr ese resultado.

leal 4(%edx,%eax),%eax 

int rval = t2 * t5; bastante sencilla éste; ecx representa t2 y eax representa t5. El valor devuelto se transfiere a la persona que llama a través del eax.

imull %ecx,%eax 


Destruye el marco de pila y restaurar esp y ebp:

movl %ebp,%esp 
popl %ebp 


retorno de la rutina:

ret 


De este ejemplo se puede ver que el resultado t es lo mismo, pero la estructura es un poco diferente. Lo más probable es que este código haya sido compilado con algún tipo de optimización o alguien lo haya escrito así para demostrar un punto.

Como han dicho otros, no se puede volver exactamente a la fuente desde el desmontaje. Depende de la interpretación de la persona que lee el ensamblaje para obtener un código C equivalente.


Para ayudar con el montaje de aprender y comprender el desmontaje de sus programas de C, puede hacer lo siguiente en Linux:

Compilar con información de depuración (-g), que incrustar la fuente:

gcc -c -g arith.c 

Si está en una máquina de 64 bits, puede indicarle al compilador que cree un binario de 32 bits con la bandera -m32 (lo hice para el ejemplo a continuación).


Uso objdump para volcar el archivo objeto con que es intercalada fuente:

objdump -d -S arith.o 

-d = desmontaje, -S = fuente de visualización. Puede agregar -M intel-mnemonic para usar la sintaxis de Intel ASM si lo prefiere a través de la sintaxis T de AT & que utiliza su ejemplo.

Salida:

arith.o:  file format elf32-i386 


Disassembly of section .text: 

00000000 <arith>: 
int arith(int x, int y, int z) 
{ 
    0: 55      push %ebp 
    1: 89 e5     mov %esp,%ebp 
    3: 83 ec 20    sub $0x20,%esp 
    int t1 = x+y; 
    6: 8b 45 0c    mov 0xc(%ebp),%eax 
    9: 8b 55 08    mov 0x8(%ebp),%edx 
    c: 01 d0     add %edx,%eax 
    e: 89 45 fc    mov %eax,-0x4(%ebp) 
    int t2 = z+t1; 
    11: 8b 45 fc    mov -0x4(%ebp),%eax 
    14: 8b 55 10    mov 0x10(%ebp),%edx 
    17: 01 d0     add %edx,%eax 
    19: 89 45 f8    mov %eax,-0x8(%ebp) 
    int t3 = x+4; 
    1c: 8b 45 08    mov 0x8(%ebp),%eax 
    1f: 83 c0 04    add $0x4,%eax 
    22: 89 45 f4    mov %eax,-0xc(%ebp) 
    int t4 = y * 48; 
    25: 8b 55 0c    mov 0xc(%ebp),%edx 
    28: 89 d0     mov %edx,%eax 
    2a: 01 c0     add %eax,%eax 
    2c: 01 d0     add %edx,%eax 
    2e: c1 e0 04    shl $0x4,%eax 
    31: 89 45 f0    mov %eax,-0x10(%ebp) 
    int t5 = t3 + t4; 
    34: 8b 45 f0    mov -0x10(%ebp),%eax 
    37: 8b 55 f4    mov -0xc(%ebp),%edx 
    3a: 01 d0     add %edx,%eax 
    3c: 89 45 ec    mov %eax,-0x14(%ebp) 
    int rval = t2 * t5; 
    3f: 8b 45 f8    mov -0x8(%ebp),%eax 
    42: 0f af 45 ec    imul -0x14(%ebp),%eax 
    46: 89 45 e8    mov %eax,-0x18(%ebp) 
    return rval; 
    49: 8b 45 e8    mov -0x18(%ebp),%eax 
} 
    4c: c9      leave 
    4d: c3      ret 

Como se puede ver, sin optimizaciones del compilador produce un binario más grande que el ejemplo que tiene. Puede jugar con eso y agregar un indicador de optimización del compilador al compilar (es decir, -O1, -O2, -O3). Cuanto más alto sea el nivel de optimización, más abstracto parecerá el desensamblaje.

Por ejemplo, con sólo el nivel 1 de optimización (gcc -c -g -O1 -m32 arith.c1), el código de montaje producido es mucho más corto:

00000000 <arith>: 
int arith(int x, int y, int z) 
{ 
    0: 8b 4c 24 04    mov 0x4(%esp),%ecx 
    4: 8b 54 24 08    mov 0x8(%esp),%edx 
    int t1 = x+y; 
    8: 8d 04 11    lea (%ecx,%edx,1),%eax 
    int t2 = z+t1; 
    b: 03 44 24 0c    add 0xc(%esp),%eax 
    int t3 = x+4; 
    int t4 = y * 48; 
    f: 8d 14 52    lea (%edx,%edx,2),%edx 
    12: c1 e2 04    shl $0x4,%edx 
    int t5 = t3 + t4; 
    15: 8d 54 11 04    lea 0x4(%ecx,%edx,1),%edx 
    int rval = t2 * t5; 
    19: 0f af c2    imul %edx,%eax 
    return rval; 
} 
    1c: c3      ret 
6

No puede reproducir la fuente original, solo puede reproducir una fuente equivalente.

En su caso, el cálculo para t2 puede aparecer en cualquier lugar después t1 y antes retval.

La fuente incluso podría haber sido una sola expresión:

return (x+y+z) * ((x+4) + (y * 48)); 
1

Decompilation no es perfectamente alcanzable: hay una cierta pérdida de conocimiento cuando se va desde el código fuente (donde comenta & nombres que dieron una pista del original la intención del programador) al código máquina binario (donde las instrucciones deben ser ejecutadas por el procesador).

5

Cuando la ingeniería inversa, no le importa el código fuente original línea por línea, le importa lo que hace. Un efecto secundario es que ve lo que hace el código, no lo que el programador pretendía que hiciera el código.

Cuestiones relacionadas