Introdução
GDB, ou o GNU Debugger, é o debugger padrão de sistemas Linux desenvolvidos pelo GNU. Ele tem sido distribuidos para diversos sistemas e é suportado por diversas linguagens, como C, C++, Objective-C, FORTRAN, Java e muitas outras.
GDB nos oferece facilidades em rastreamento, como breakpoints ou stacked traces, que permitem intervir na execução do programa. Ele também nos permite, por exemplo, manipular as variáveis da aplicação ou chamar funções independentes da execução do programa.
Nós usamos o GNU Debugger para ver o arquivo binário criado no nível de Assembly. Uma vez que executamos o binário com o GDB, nós podemos observar o funcionamento do programa principal.
student@nix-bow:~$ gdb -q bow32
Reading symbols from bow...(no debugging symbols found)...done.
(gdb) disassemble main
Dump of assembler code for function main:
0x00000582 <+0>: lea 0x4(%esp),%ecx
0x00000586 <+4>: and $0xfffffff0,%esp
0x00000589 <+7>: pushl -0x4(%ecx)
0x0000058c <+10>: push %ebp
0x0000058d <+11>: mov %esp,%ebp
0x0000058f <+13>: push %ebx
0x00000590 <+14>: push %ecx
0x00000591 <+15>: call 0x450 <__x86.get_pc_thunk.bx>
0x00000596 <+20>: add $0x1a3e,%ebx
0x0000059c <+26>: mov %ecx,%eax
0x0000059e <+28>: mov 0x4(%eax),%eax
0x000005a1 <+31>: add $0x4,%eax
0x000005a4 <+34>: mov (%eax),%eax
0x000005a6 <+36>: sub $0xc,%esp
0x000005a9 <+39>: push %eax
0x000005aa <+40>: call 0x54d <bowfunc>
0x000005af <+45>: add $0x10,%esp
0x000005b2 <+48>: sub $0xc,%esp
0x000005b5 <+51>: lea -0x1974(%ebx),%eax
0x000005bb <+57>: push %eax
0x000005bc <+58>: call 0x3e0 <puts@plt>
0x000005c1 <+63>: add $0x10,%esp
0x000005c4 <+66>: mov $0x1,%eax
0x000005c9 <+71>: lea -0x8(%ebp),%esp
0x000005cc <+74>: pop %ecx
0x000005cd <+75>: pop %ebx
0x000005ce <+76>: pop %ebp
0x000005cf <+77>: lea -0x4(%ecx),%esp
0x000005d2 <+80>: ret
End of assembler dump.
Na primeira coluna, os números hexadecimais representam os endereços de memória. Os números com +
na frente mostram os pulos de endereço na memória em bytes, usamos pela respectiva instrução. A seguir, podemos ver as instruções em Assembly com registradores e operadores de sufixo. A sintaxe utilizada é AT&T
, que são reconhecidos pelos símbolos %
e $
.
Endereço de memória | Pulo de endereço | Instrução de Assembly | Sufixo de operações |
---|---|---|---|
0x00000582 | <+0>: | lea | 0x4(%esp),%ecx |
0x00000586 | <+4>: | and | $0xfffffff0,%esp |
A Linguagem Intel faz a representação em Assembly mais fácil de ser lida e interpretada, e pode ser representada pelo seguinte comando no GDB:
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
0x00000582 <+0>: lea ecx,[esp+0x4]
0x00000586 <+4>: and esp,0xfffffff0
0x00000589 <+7>: push DWORD PTR [ecx-0x4]
0x0000058c <+10>: push ebp
0x0000058d <+11>: mov ebp,esp
0x0000058f <+13>: push ebx
0x00000590 <+14>: push ecx
0x00000591 <+15>: call 0x450 <__x86.get_pc_thunk.bx>
0x00000596 <+20>: add ebx,0x1a3e
0x0000059c <+26>: mov eax,ecx
0x0000059e <+28>: mov eax,DWORD PTR [eax+0x4]
<SNIP>
Para definir nosso
0x6a70@0x6a70:~$ echo 'set disassembly-flavor intel' > ~/.gdbinit
Verificando como o arquivo foi compilado
Para isso, pode-se utilizar o comando
file <file> | tr "," "\n"