pueden - hashtag instagram para conseguir seguidores
¿Cómo desmontar la función principal de una aplicación despojada? (4)
¿Qué tal si hacemos info files
para obtener la lista de secciones (con direcciones) y vamos desde allí?
Ejemplo:
gdb) info files
Symbols from "/home/bob/tmp/t".
Local exec file:
`/home/bob/tmp/t'', file type elf64-x86-64.
Entry point: 0x400490
0x0000000000400270 - 0x000000000040028c is .interp
0x000000000040028c - 0x00000000004002ac is .note.ABI-tag
....
0x0000000000400448 - 0x0000000000400460 is .init
....
El desmonte .init
:
(gdb) disas 0x0000000000400448,0x0000000000400460
Dump of assembler code from 0x400448 to 0x400460:
0x0000000000400448: sub $0x8,%rsp
0x000000000040044c: callq 0x4004bc
0x0000000000400451: callq 0x400550
0x0000000000400456: callq 0x400650
0x000000000040045b: add $0x8,%rsp
0x000000000040045f: retq
Luego adelante y desarme el resto.
Si yo fuera tú, y tuviera la misma versión de GCC con la que se construyó tu ejecutable, examinaría la secuencia de funciones llamadas en un ejecutable ficticio sin pelar. La secuencia de llamadas es probablemente similar en la mayoría de los casos habituales, por lo que podría ayudarlo a analizar la secuencia de inicio hasta su main
por comparación. Sin embargo, las optimizaciones probablemente vendrán en el camino.
Si su binario está eliminado y optimizado, main
podría no existir como una "entidad" en el binario; lo más probable es que no pueda ser mucho mejor que este tipo de procedimiento.
Digamos que compilé la aplicación a continuación y quité sus símbolos.
#include <stdio.h>
int main()
{
printf("Hello/n");
}
Procedimiento de construcción:
gcc -o hello hello.c
strip --strip-unneeded hello
Si la aplicación no se eliminó, desmontar la función principal sería fácil. Sin embargo, no tengo idea de cómo desmontar la función principal de una aplicación despojada.
(gdb) disas main
No symbol table is loaded. Use the "file" command.
(gdb) info line main
Function "main" not defined.
¿Cómo podría hacerlo? ¿Es posible?
Notas : esto debe hacerse solo con GDB. Olvídate de objdump . Supongamos que no tengo acceso al código.
Un ejemplo paso a paso sería muy apreciado.
Hay una nueva herramienta gratuita llamada Unstrip from the paradyn project (revelación completa: trabajo en este proyecto) que reescribirá el programa binario, agregará información de símbolos y recuperará todas (o casi todas) las funciones en los binarios de Elf despojados Para ti, con gran exactitud. No identificará la función principal como "principal", pero la encontrará, y puede aplicar la heurística que ya mencionó anteriormente para averiguar qué función es la principal.
http://www.paradyn.org/html/tools/unstrip.html
Lo siento, esto no es una solución sólo para gdb.
IIRC, x/i <location>
es tu amigo. Por supuesto, tienes que averiguar en qué lugar quieres desarmarte.
Ok, aquí una gran edición de mi respuesta anterior. Creo que encontré un camino ahora.
Usted (todavía :) tiene este problema específico:
(gdb) disas main
No symbol table is loaded. Use the "file" command.
Ahora, si compilas el código (agregué un return 0
al final), obtendrás con gcc -S
:
pushq %rbp
movq %rsp, %rbp
movl $.LC0, %edi
call puts
movl $0, %eax
leave
ret
Ahora, puedes ver que tu binario te da información:
A rayas:
(gdb) info files
Symbols from "/home/beco/Documents/fontes/cpp/teste//distrip".
Local exec file:
`/home/beco/Documents/fontes/cpp/teste//distrip'', file type elf64-x86-64.
Entry point: 0x400440
0x0000000000400238 - 0x0000000000400254 is .interp
...
0x00000000004003a8 - 0x00000000004003c0 is .rela.dyn
0x00000000004003c0 - 0x00000000004003f0 is .rela.plt
0x00000000004003f0 - 0x0000000000400408 is .init
0x0000000000400408 - 0x0000000000400438 is .plt
0x0000000000400440 - 0x0000000000400618 is .text
...
0x0000000000601010 - 0x0000000000601020 is .data
0x0000000000601020 - 0x0000000000601030 is .bss
La entrada más importante aquí es .text
. Es un nombre común para el inicio del código de un ensamblado, y de nuestra explicación de la página principal, por su tamaño, puede ver que incluye main. Si lo desmonta, verá una llamada a __libc_start_main. Lo más importante es que está desarmando un buen punto de entrada que es código real (no es engañoso cambiar DATA a CODE).
disas 0x0000000000400440,0x0000000000400618
Dump of assembler code from 0x400440 to 0x400618:
0x0000000000400440: xor %ebp,%ebp
0x0000000000400442: mov %rdx,%r9
0x0000000000400445: pop %rsi
0x0000000000400446: mov %rsp,%rdx
0x0000000000400449: and $0xfffffffffffffff0,%rsp
0x000000000040044d: push %rax
0x000000000040044e: push %rsp
0x000000000040044f: mov $0x400540,%r8
0x0000000000400456: mov $0x400550,%rcx
0x000000000040045d: mov $0x400524,%rdi
0x0000000000400464: callq 0x400428 <__libc_start_main@plt>
0x0000000000400469: hlt
...
0x000000000040046c: sub $0x8,%rsp
...
0x0000000000400482: retq
0x0000000000400483: nop
...
0x0000000000400490: push %rbp
..
0x00000000004004f2: leaveq
0x00000000004004f3: retq
0x00000000004004f4: data32 data32 nopw %cs:0x0(%rax,%rax,1)
...
0x000000000040051d: leaveq
0x000000000040051e: jmpq *%rax
...
0x0000000000400520: leaveq
0x0000000000400521: retq
0x0000000000400522: nop
0x0000000000400523: nop
0x0000000000400524: push %rbp
0x0000000000400525: mov %rsp,%rbp
0x0000000000400528: mov $0x40062c,%edi
0x000000000040052d: callq 0x400418 <puts@plt>
0x0000000000400532: mov $0x0,%eax
0x0000000000400537: leaveq
0x0000000000400538: retq
La llamada a __libc_start_main obtiene como primer argumento un puntero a main (). Por lo tanto, el último argumento en la pila justo antes de la llamada es su dirección principal ().
0x000000000040045d: mov $0x400524,%rdi
0x0000000000400464: callq 0x400428 <__libc_start_main@plt>
Aquí está 0x400524 (como ya sabemos). Ahora establece un punto de interrupción e intente esto:
(gdb) break *0x400524
Breakpoint 1 at 0x400524
(gdb) run
Starting program: /home/beco/Documents/fontes/cpp/teste//disassembly/d2
Breakpoint 1, 0x0000000000400524 in main ()
(gdb) n
Single stepping until exit from function main,
which has no line number information.
hello 1
__libc_start_main (main=<value optimized out>, argc=<value optimized out>, ubp_av=<value optimized out>,
init=<value optimized out>, fini=<value optimized out>, rtld_fini=<value optimized out>,
stack_end=0x7fffffffdc38) at libc-start.c:258
258 libc-start.c: No such file or directory.
in libc-start.c
(gdb) n
Program exited normally.
(gdb)
Ahora puedes desmontarlo usando:
(gdb) disas 0x0000000000400524,0x0000000000400600
Dump of assembler code from 0x400524 to 0x400600:
0x0000000000400524: push %rbp
0x0000000000400525: mov %rsp,%rbp
0x0000000000400528: sub $0x10,%rsp
0x000000000040052c: movl $0x1,-0x4(%rbp)
0x0000000000400533: mov $0x40064c,%eax
0x0000000000400538: mov -0x4(%rbp),%edx
0x000000000040053b: mov %edx,%esi
0x000000000040053d: mov %rax,%rdi
0x0000000000400540: mov $0x0,%eax
0x0000000000400545: callq 0x400418 <printf@plt>
0x000000000040054a: mov $0x0,%eax
0x000000000040054f: leaveq
0x0000000000400550: retq
0x0000000000400551: nop
0x0000000000400552: nop
0x0000000000400553: nop
0x0000000000400554: nop
0x0000000000400555: nop
...
Esta es principalmente la solución.
Por cierto, este es un código diferente, para ver si funciona. Es por eso que el montaje anterior es un poco diferente. El código de arriba es de este archivo c:
#include <stdio.h>
int main(void)
{
int i=1;
printf("hello %d/n", i);
return 0;
}
¡Pero!
Si esto no funciona, entonces todavía tienes algunos consejos:
Debería estar buscando establecer puntos de interrupción al comienzo de todas las funciones a partir de ahora. Están justo antes de un ret
o se leave
. El primer punto de entrada es el propio .text
. Este es el inicio de la asamblea, pero no el principal.
El problema es que no siempre un punto de interrupción permitirá que su programa se ejecute. Como este en el .text
:
(gdb) break *0x0000000000400440
Breakpoint 2 at 0x400440
(gdb) run
Starting program: /home/beco/Documents/fontes/cpp/teste//disassembly/d2
Breakpoint 2, 0x0000000000400440 in _start ()
(gdb) n
Single stepping until exit from function _start,
which has no line number information.
0x0000000000400428 in __libc_start_main@plt ()
(gdb) n
Single stepping until exit from function __libc_start_main@plt,
which has no line number information.
0x0000000000400408 in ?? ()
(gdb) n
Cannot find bounds of current function
Por lo tanto, debe seguir intentando hasta que encuentre su camino, estableciendo puntos de interrupción en:
0x400440
0x40046c
0x400490
0x4004f4
0x40051e
0x400524
De la otra respuesta, debemos mantener esta información:
En la versión no rayada del archivo, vemos:
(gdb) disas main
Dump of assembler code for function main:
0x0000000000400524 <+0>: push %rbp
0x0000000000400525 <+1>: mov %rsp,%rbp
0x0000000000400528 <+4>: mov $0x40062c,%edi
0x000000000040052d <+9>: callq 0x400418 <puts@plt>
0x0000000000400532 <+14>: mov $0x0,%eax
0x0000000000400537 <+19>: leaveq
0x0000000000400538 <+20>: retq
End of assembler dump.
Ahora sabemos que el principal está en 0x0000000000400524,0x0000000000400539
. Si usamos el mismo desplazamiento para ver el binario con bandas obtenemos los mismos resultados:
(gdb) disas 0x0000000000400524,0x0000000000400539
Dump of assembler code from 0x400524 to 0x400539:
0x0000000000400524: push %rbp
0x0000000000400525: mov %rsp,%rbp
0x0000000000400528: mov $0x40062c,%edi
0x000000000040052d: callq 0x400418 <puts@plt>
0x0000000000400532: mov $0x0,%eax
0x0000000000400537: leaveq
0x0000000000400538: retq
End of assembler dump.
Por lo tanto, a menos que pueda obtener alguna sugerencia donde comienza el inicio (como usar otro código con símbolos), otra forma es si puede tener alguna información acerca de las primeras instrucciones de ensamblaje, así puede desensamblar en lugares específicos y ver si coinciden. Si no tiene acceso al código, puede leer la definición ELF para comprender cuántas secciones deben aparecer en el código y probar una dirección calculada. Aún así, ¡necesitas información sobre secciones en el código!
Eso es un trabajo duro, mi amigo! ¡Buena suerte!
Beco