assembly x86 gdb reverse-engineering

assembly - Bomba Binaria-Fase 4



x86 gdb (1)

Estoy teniendo un momento muy difícil para rastrear el código de ensamblaje para la siguiente bomba binaria (una asignación de la escuela donde se debe desactivar una bomba, esta bomba contiene 6 fases, todas tienen 1 entrada correcta para pasar a la siguiente fase). Actualmente estoy en fase_4 y tiene una función recursiva llamada func4. He identificado que la entrada es "% d% d", que es dos enteros. Sin embargo, no puedo entender qué func4 está haciendo, incluso después de obtener la información en todos los registros a lo largo de cada paso.

Fase_4:

(gdb) disas Dump of assembler code for function phase_4: => 0x08048e24 <+0>: sub $0x2c,%esp 0x08048e27 <+3>: lea 0x1c(%esp),%eax 0x08048e2b <+7>: mov %eax,0xc(%esp) 0x08048e2f <+11>: lea 0x18(%esp),%eax 0x08048e33 <+15>: mov %eax,0x8(%esp) 0x08048e37 <+19>: movl $0x804a7f1,0x4(%esp) 0x08048e3f <+27>: mov 0x30(%esp),%eax 0x08048e43 <+31>: mov %eax,(%esp) 0x08048e46 <+34>: call 0x80488d0 <__isoc99_sscanf@plt> 0x08048e4b <+39>: cmp $0x2,%eax 0x08048e4e <+42>: jne 0x8048e5d <phase_4+57> 0x08048e50 <+44>: mov 0x18(%esp),%eax 0x08048e54 <+48>: test %eax,%eax 0x08048e56 <+50>: js 0x8048e5d <phase_4+57> 0x08048e58 <+52>: cmp $0xe,%eax 0x08048e5b <+55>: jle 0x8048e62 <phase_4+62> 0x08048e5d <+57>: call 0x8049470 <explode_bomb> 0x08048e62 <+62>: movl $0xe,0x8(%esp) 0x08048e6a <+70>: movl $0x0,0x4(%esp) 0x08048e72 <+78>: mov 0x18(%esp),%eax 0x08048e76 <+82>: mov %eax,(%esp) 0x08048e79 <+85>: call 0x8048dbb <func4> 0x08048e7e <+90>: cmp $0x25,%eax 0x08048e81 <+93>: jne 0x8048e8a <phase_4+102> 0x08048e83 <+95>: cmpl $0x25,0x1c(%esp) 0x08048e88 <+100>: je 0x8048e8f <phase_4+107> 0x08048e8a <+102>: call 0x8049470 <explode_bomb> 0x08048e8f <+107>: add $0x2c,%esp 0x08048e92 <+110>: ret End of assembler dump.

func4:

Breakpoint 2, 0x08048dbb in func4 () (gdb) disas Dump of assembler code for function func4: => 0x08048dbb <+0>: sub $0x1c,%esp 0x08048dbe <+3>: mov %ebx,0x14(%esp) 0x08048dc2 <+7>: mov %esi,0x18(%esp) 0x08048dc6 <+11>: mov 0x20(%esp),%eax 0x08048dca <+15>: mov 0x24(%esp),%edx 0x08048dce <+19>: mov 0x28(%esp),%esi 0x08048dd2 <+23>: mov %esi,%ecx 0x08048dd4 <+25>: sub %edx,%ecx 0x08048dd6 <+27>: mov %ecx,%ebx 0x08048dd8 <+29>: shr $0x1f,%ebx 0x08048ddb <+32>: add %ebx,%ecx 0x08048ddd <+34>: sar %ecx 0x08048ddf <+36>: lea (%ecx,%edx,1),%ebx 0x08048de2 <+39>: cmp %eax,%ebx 0x08048de4 <+41>: jle 0x8048dfd <func4+66> 0x08048de6 <+43>: lea -0x1(%ebx),%ecx 0x08048de9 <+46>: mov %ecx,0x8(%esp) 0x08048ded <+50>: mov %edx,0x4(%esp) 0x08048df1 <+54>: mov %eax,(%esp) 0x08048df4 <+57>: call 0x8048dbb <func4> 0x08048df9 <+62>: add %eax,%ebx 0x08048dfb <+64>: jmp 0x8048e16 <func4+91> 0x08048dfd <+66>: cmp %eax,%ebx 0x08048dff <+68>: jge 0x8048e16 <func4+91> 0x08048e01 <+70>: mov %esi,0x8(%esp) 0x08048e05 <+74>: lea 0x1(%ebx),%edx 0x08048e08 <+77>: mov %edx,0x4(%esp) 0x08048e0c <+81>: mov %eax,(%esp) 0x08048e0f <+84>: call 0x8048dbb <func4> 0x08048e14 <+89>: add %eax,%ebx 0x08048e16 <+91>: mov %ebx,%eax 0x08048e18 <+93>: mov 0x14(%esp),%ebx 0x08048e1c <+97>: mov 0x18(%esp),%esi 0x08048e20 <+101>: add $0x1c,%esp 0x08048e23 <+104>: ret End of assembler dump.


Espero que sea obvio que phase4 está comprobando que el primer número está en el rango 0 .. 14 inclusive (ver líneas +44 .. +57 ) Luego invoca func4 con tres argumentos: el primer número ingresado, 0 y 14 (líneas +62 .. +85 ). A continuación, verifica que el valor de retorno sea 0x25 (37 decimal) en la línea +90 y que el segundo número ingresado sea también 37 (línea +95 )

Pasemos a func4 . Llamaré a los tres argumentos x , low y high . Inicialmente no sabes lo que son, por supuesto. Líneas +23 .. +34 calcular (high - low) / 2 . El feo lío se debe a que el compilador genera código para manejar números negativos con truncamiento a cero. Sin embargo, no veremos ningún número negativo. La línea +36 es simplemente una adición elegante, así que en ebx ahora tenemos low + (high - low) / 2 que también se conoce como el promedio de los dos números. El código luego compara este promedio con el número x que se ha proporcionado como primer argumento. Las líneas +43 .. +62 se ejecutan si x < average e invocan func4(x, low, average - 1) y agregan el valor devuelto al promedio. De forma similar, las líneas +70 .. +89 se ejecutan si x > average y calcula el average + func4(x, average + 1, high) . Si x == average , solo se devuelve el promedio.

Básicamente está haciendo una búsqueda binaria y sumando las suposiciones a medida que avanzan. Dado que el intervalo tiene 15 elementos, necesitará como máximo 4 suposiciones. La primera conjetura va a ser 7 , por lo que para obtener el resultado requerido de 37 necesitamos 30 más. Tenemos como máximo 3 intentos más y todas las suposiciones serán menos de 7 o más de 7. Dado que 7 * 3 = 21 y que no pueden darnos 30 significa que el número debe ser mayor que 7. Segundo intento es así va a ser (8 + 14) / 2 = 11 , por lo que nuestra suma 18 con 19 más por recorrer. Si el número era superior a 11 significaría que rebasamos el objetivo, por lo que el número debe ser mayor que 7 y menor que 11. El tercer intento es por lo tanto (8 + 10) / 2 = 9 que eleva la suma a 27 con 10 más para ve y solo una adivinanza, entonces eso significa que el número es 10 .

TL; DR: la entrada correcta debe ser 10 y 37