: :.:..:...:....:.....:......:.......:........:.........:..........: : : ::::
: : : :
RGZ_09 Playing GDB Debug ReYDeS
: :.:..:...:....:.....:......:.......:........:.........:..........: : : ::::
* Presentacion.
* Conocimientose Previos.
* Intro al GDB.
* Utilizando el GDB.
* Despedida.
* Presentacion:
He visto muy buen material en espa~ol; tratando temas de buffer overflows,
exploits y demas. Pero me percate de la existencia de un 'vacio'; para alguien
NO muy entendido en temas sobre la composicion interna de Computadores, como
administracion de memoria, funcionamiento de pila y demas.
Ello me incentivo a escribir este texto; en el cual fusiono los conceptos
antes mencionados, con la utilizacion del GDB. Y de esta manera acercar a las
personas que desean empezar a tratar estos temas, tan complejos como
interesantes.
* Conocimientos Previos:
+ Pues, un poco de Arquitectura de Computadores y especificamente de la
familia INTEL :) x86, no os caeria nada mal.
+ Un poco de programacion en C; solo un poco. ;). Y algo de conocimientos de
ASM y si fuese ASM AT&T, mucho mejor.
+ Entendimiento de los numero binarios, hexadecimales y octales, seria
recomendable; punteros, direcciones de memoria, organizacion de la pila.
:O)
* Intro al GDB: (GNU debugger)
Para aquellos que no se llevan bien con el english; el GDB en su concepto
mas comun, puede denominarse un depurador. Si alguna vez se encontraron con
algun 'core' en un sistema *NIX, provocado por alguna causa; sea esta un
codigo C erroneo, o algun binario en su sistema que NO soporto un simple y
tipico: `perl -e 'print "A" x 2048'`. Con la ayuda del GDB nos sera posible
profundizar en estos temas tanto como deseemos; teniendo como limite,
nuestras propias capacidades.
Un depurador como el GDB, nos permite inmiscuirnos en un 'programa' que
deseamos depurar y conocer su funcionamiento a profundidad.
Con gdb podemos hacer los siguiente: (Extraido del INFO gdb)
+ Iniciar un programa, especificar algo que pueda afectar su comportamiento.
+ Hacer que el programa pare sobre condiciones especificas.
+ Examinar que ocurre, cuando el programa es detenido.
+ Cambiar cosas en el programa, asi se puede experimentar corrigiendo los
efectos de un bug y ir aprendiendo sobre otro.
* Utilizando el GDB:
Intentare explicar y explayarme en los anteriores acapites y en aquellos
temas que crea necesario para el entendimiento del presente escrito; y vas a
quedar tan conforme, como se quedaria un puber ante una clase de sexologia.
Empiezo con un simple programa en C.
/*
parametro.c
*/
#include
main ( int argc, char *argv[] )
{
printf ("El parametro ingresado : %s\n", argv[1]);
return 0;
}
Como podeis apreciar, es un simple codigo en C, al cual le ingresais un
parametro y lo unico que hace es mostrar un mensaje, seguido del parametro
ingresado.
Compilamos: cc -g -o parametro parametro.c
Os preguntais, para que el -g?. Simple, este parametro le indica al
compilador; en este caso el gcc; genere informacion para depuracion. Ahora
invocamos al gdb.
[root@amaranta mas-C]# gdb binario
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) disass main
Dump of assembler code for function main:
0x80483d0 : push %ebp
0x80483d1 : mov %esp,%ebp
0x80483d3 : mov 0xc(%ebp),%eax
0x80483d6 : add $0x4,%eax
0x80483d9 : mov (%eax),%edx
0x80483db : push %edx
0x80483dc : push $0x8048450
0x80483e1 : call 0x8048308
0x80483e6 : add $0x8,%esp
0x80483e9 : xor %eax,%eax
0x80483eb : jmp 0x80483f0
0x80483ed : lea 0x0(%esi),%esi
0x80483f0 : leave
0x80483f1 : ret
0x80483f2 : nop
y mas... nop's =)
Lo anterior no es mas que nuestro programa en C, 'desensamblado' por el gdb y
mostrado en codigo asm. Voy a explicar algunas lineas; utilizando para este
proposito al gdb; lo cual dilucidara o confundira mas a los 'novatos'. ;)
Con lo siguiente, os dareis cuenta de lo util e interesante que puede ser la
utilizacion de un debugger:
Le indicamos al gdb la utilizacion de un 'argumento' en nuestro programa.
(gdb) set args RareGaZz
(gdb) show args
Argument list to give program being debugged when it is started is "RareGaZz".
(gdb) run
Starting program: /root/aaaaaaa/bbbbb/parametro RareGaZz
Un 'breakpoint' no es mas que un punto en el cual un programa se detiene;
claro esta, especificandolo al gdb. Podemos especificar diversos modos para
indicarle al gdb los breakpoints y watchpoints.
(gdb) break *0x80483d1
Breakpoint 1 at 0x80483d1: file parametro.c, line 4.
Breakpoint 1, 0x80483d1 in main (argc=134513616, argv=0x2) at parametro.c:4
4 {
80483d0 : push %ebp
Coloca en la pila el contenido de ebp, o Puntero Base.
0x80483d1 : mov %esp,%ebp
Mueve el contenido del registro esp a ebp; con esto; ebp 'apunta' a donde
apuntaba esp. Lo anterior se realiza por la siguiente razon:
La pila utiliza 2 punteros; el 'sp' generalmente apunta a la cima de la pila;
y el 'bp', utilizado como un mecanismo BASE, para referenciar a contenidos en
la pila basandose en Offsets a partir de este.
En general, los procedimientos al ser invocados, realizan la anterior accion
en la pila, y luego reservan espacio para las variables locales.
0x80483d3 : mov 0xc(%ebp),%eax
0xc posiciones siguientes a ebp, lo cual se almacena en el registro eax.
(gdb) x/w $ebp
0xbffffb48: 0xbffffb68
(gdb) x/w $ebp+0xc
0xbffffb54: 0xbffffb94
Ahora examino 0xbffffb94.
(gdb) x/s 0xbffffb94
0xbffffb94: 0xbffffc89
(gdb) x/2s 0xbffffc89
0xbffffc89: "/root/aaaaaaa/bbbbb/parametro"
0xbffffca7: "RareGaZz"
Para los despistados como yo :P, 0xbfffc89 contiene el nombre del programa;
el path completo; y 0xbffffca7, contiene el 'parametro' en este caso la
palabra RareGaZz.
Osea: argv[0] Nombre del programa; argv[1] Parametro.
Entonces eax, queda de la siguiente manera:
(gdb) x/x $eax
0xbffffb94: 0xbffffc89
(gdb) stepi
0x80483d9 5 printf ("El paramentro ingresado : %s\n", argv[1]);
(gdb) x/x $pc
0x80483d9 : 0x6852108b
pc, es el contador del programa, indica la siguiente instruccion a
ejecutarse. Examinemos nuevamente eax luego de haberse ejecutado la siguiente
instruccion maquina:
0x80483d6 : add $0x4,%eax
Mas que simple, Suma 0x4 al contenido del registro eax, y que es lo que
almacenaba eax?, pues 0xbffffb94, pues ahora debe tener 4 unidades mas.
(gdb) x/x $eax
0xbffffb98: 0xbffffca7
Y esto en espa~ol, dice lo siguiente:
(gdb) x/s *0xbffffb98
0xbffffca7: "RareGaZz"
eax almacena la direccion en la cual se ubica la cadena 'RareGaZz'.
Bingo!... ;)
Ahora bien:
0x80483d9 : mov (%eax),%edx
Nada mas que mover la direccion almacenada en eax hacia edx.
(gdb) x/x $eax
0xbffffb98: 0xbffffca7
(gdb) x/s $edx
0xbffffca7: "RareGaZz"
0x80483db : push %edx
Coloca en la pila edx, con lo cual; el registro sp, debe de haberse
decrementado 4 unidades, examinemos eso:
(gdb) x/x $esp
0xbffffb44: 0xbffffca7
Correcto!. ya que anteriormente esp tenia su direccion igual al de ebp. Con
esto; se almacena en la pila la direccion en donde se ubica 'RareGaZz'.
(gdb) x/x $ebp
0xbffffb48: 0xbffffb68
0x80483dc : push $0x8048450
Nuevamente se hace uso de la pila esta vez almacenado la direccion que se
detalla en la instruccion; decrementandose nuevamente el sp 4 unidades.
(gdb) info registers
Info registers, os muestra casi todos los registros, pero solo pasteo los que
me interesa.
...
esp 0xbffffb40 -1073743040
ebp 0xbffffb48 -1073743032
...
Ahora bien, sp esta apuntando a 0xbffffb40, la cual almacena la direccion
anteriormente colocada, con la instruccion push.
(gdb) x/s *0xbffffb40
0x8048450 <_IO_stdin_used+4>: "El paramentro ingresado : %s\n"
(gdb) stepi
0x8048308 in printf () at printf.c:26
(gdb) p/x $pc
$12 = 0x8048308
Con esto, se intuye que se ejecuto la siguiente instruccion:
0x80483e1 : call 0x8048308
Que no es mas que la llamada a la funcion printf. Si deseasemos seguir la
depuracion dentro de dicha funcion; usese stepi.
Y bueno, despues de estar 'vagando' varios segundos, entre funciones y
funciones; las cuales son llamadas por nuestro simple programa y llegar a
visualizar esto: Os dejo como tarea averiguarlo. :)
0x40001840 in ?? () from /lib/ld-linux.so.2
(gdb)
_dl_lookup_versioned_symbol (undef_name=0x400143c8 "", ref=0x40023590,
symbol_scope=0x1a, reference_name=0xbffffca7 "RareGaZz",
version=0x80483d0, reloc_type=-1073742952) at dl-lookup.c:185
185 dl-lookup.c: No such file or directory.
Ya es momento de salir de las subrutinas y volver al programa.
(gdb) finish
Run till exit from #0 0x400325b4 in ?? () from /lib/libc.so.6
El paramentro ingresado : RareGaZz
0x80483e6 in main (argc=2, argv=0xbffffb94) at parametro.c:5
5 printf ("El paramentro ingresado : %s\n", argv[1]);
(gdb) p/x $pc
$16 = 0x80483e6
0x80483e6 : add $0x8,%esp
Con esto se retoma el antiguo valor que tenia esp, recuerdan los 2
decrementos realizados?. Pues con esta instruccion vuelve esp a su posicion.
(gdb) x $esp
0xbffffb48: 0xbffffb68
0x80483e9 : xor %eax,%eax
XOReamos el registro, con esa instruccion logica se 'limpia' el registro eax.
(gdb) p/x $eax
$19 = 0x0
0x80483eb : jmp 0x80483f0
Un salto incondicional a:
0x80483f0 : leave
(gdb) p/x $pc
$20 = 0x80483f0
(gdb) stepi
0x80483f1 in main (argc=134513616, argv=0x2) at parametro.c:8
8 }
(gdb) p/x $pc
$21 = 0x80483f1
0x80483f1 : ret
ret, no significa mas que retornar el control al programa que llamo a este.
(gdb) stepi
0x400329cb in __libc_start_main (main=0x80483d0 , argc=2,
argv=0xbffffb94, init=0x8048298 <_init>, fini=0x804842c <_fini>,
rtld_fini=0x4000ae60 <_dl_fini>, stack_end=0xbffffb8c)
at ../sysdeps/generic/libc-start.c:92
Si os fijais en lo anterior, veran numeros hexadecimales 'conocidos', y
utilizados en el presente escrito. Tienen como buen ejercicio el dilucidar su
significado.
Program exited normally.
Creo que hasta el momento, ya habre confundido bastante a algunos. Sigamos
con algo que reordene mis 'neuronas' y las vuestras tambien. =X
Ahora tocamos algo mas simple.
/*
asm1.c
*/
#include
main()
{
__asm__("
movl $0x7, %eax
movl $0xb, %ebx
addl %eax, %ebx
nop
subl %ebx, %eax
nop
xorl %eax, %eax
incl %eax
xorl %ebx, %ebx
int $0x80
");
}
Simplemente es la suma del contenido de 2 registros y sigue con la operacion
resta aplicados a los mismo registros. Compilamos y desensamblamos.
Dump of assembler code for function main:
0x8048398 : push %ebp
0x8048399 : mov %esp,%ebp
0x804839b : mov $0x7,%eax
0x80483a0 : mov $0xb,%ebx
0x80483a5 : add %eax,%ebx
0x80483a7 : nop
0x80483a8 : sub %ebx,%eax
0x80483aa : nop
0x80483ab : xor %eax,%eax
0x80483ad : inc %eax
0x80483ae : xor %ebx,%ebx
0x80483b0 : int $0x80
0x80483b2 : leave
0x80483b3 : ret
(gdb) break *0x8048398
Breakpoint 1 at 0x8048398
(gdb) info registers
eax 0x401093f8 1074828280
ecx 0x8048398 134513560
edx 0x4010b098 1074835608
ebx 0x4010a1ec 1074831852
esp 0xbffffb6c -1073742996
ebp 0xbffffb88 -1073742968
...
0x8048398 : push %ebp
Coloca el contenido %ebp en la pila. Decrementandose esp en 4 unidades.
esp 0xbffffb68 -1073743000
ebp 0xbffffb88 -1073742968
0x8048399 : mov %esp,%ebp
Evidente. Ustedes ya lo saben. ;)
(gdb) x/x $esp
0xbffffb68: 0xbffffb88
(gdb) x/x $ebp
0xbffffb68: 0xbffffb88
0x804839b : mov $0x7,%eax
Mueve 0x7 al registro eax.
(gdb) p/x $eax
$1 = 0x7
(gdb) stepi
0x80483a5 in main ()
0x80483a0 : mov $0xb,%ebx
Mueve 0xb al registro ebx
(gdb) p/x $ebx
$2 = 0xb
0x80483a5 : add %eax,%ebx
Suma 'add' el contenido del registro eax, con el contenido del registro ebx;
almacendandose el resultado de esta operacion en el registro eax. Dicho en
buen eZpa~ol:
0x7 + 0xb = 0x12 (en hexadecimal claro esta)
0x80483a7 : nop
nop = no operacion. ;) Se entendio?.
0x80483a8 : sub %ebx,%eax
Operacion resta 'Sub'. No necesita mayor explicacion.
0x80483aa : nop
Idem. Porque utilzo nop?... pues porque me gusta identar mis codigos. :)
Lo siguiente es la rutina a seguir para 'terminar' la ejecucion del programa
de manera 'natural'
0x80483ab : xor %eax,%eax
(gdb) p/x $eax
$3 = 0x0
0x80483ad : inc %eax
(gdb) p/x $eax
$4 = 0x1
0x80483ae : xor %ebx,%ebx
(gdb) p/x $ebx
$6 = 0x0
0x80483b0 : int $0x80
0x80483b2 : leave
0x80483b3 : ret
End of assembler dump.
(gdb) finish
Run till exit from #0 0x80483b3 in main ()
Program exited normally.
Para los que habeis utilizado alguna vez el 'debug' en MS-DOS, estareis
acostumbrados a utilizar la interrupcion 21. En linux esto difiere, y se hace
de la siguiente manera:
En linux se utilizan llamadas al sistema o 'systems calls' con int $0x80. El
numero de 'system call' se almacena en eax; para este caso en particular se
almaceno eax con el valor 0x1. Y si la 'system call' tuviese mas parametros,
estos se almacenan en los registros restantes; es por ello que se almacena en
ebx 0x0, debido a que NO hay necesidad de mas parametros.
Ahora 'juguemos' un poco con este programa y el gdb.
Coloquese un breakpoint en la primera instruccion maquina del programa (esto
lo haceis vosotros). Y ahora ejecutese el programa.
Modifico el registro eax, utilizo para este proposito el comando 'set':
(gdb) set $eax=2002
(gdb) p/t $eax
$9 = 11111010010
Cuidado aqui, NO es lo mismo colocar 2002 que 02002. OJO!. 11111010010 es la
representacion binaria de 2002.
(gdb) set $ebx=10
(gdb) p/d $ebx
$15 = 10
Idem con 10.
(gdb) info registers
eax 0x7d2 2002
ecx 0x8048398 134513560
edx 0x4010b098 1074835608
ebx 0xa 10
Con esto manipulamos los registros y podemos continuar la ejecucion de
nuestro programa. Ahora bien, NO quiero que se ejecute la instruccion add, y
quiero que directamente se vaya a la direccion 0x80483a8; la cual contiene la
operacion resta.
Simple, modifico el $ip. Reiniciamos el programa, con un breakpoint en la
primera linea, y luego en una direccion siguiente a la antes mencionada; de
esta manera se 'comprobara' la NO realizacion de 'add'.
(gdb) run
The program being debugged has been started already.
(gdb) break *0x8048398
Breakpoint 1 at 0x8048398: file asm1.c, line 4.
(gdb) break *0x80483aa
Breakpoint 2 at 0x80483aa: file asm1.c, line 5.
(gdb) display/i $pc
Una manera de visualizar un valor; del cual necesitamos conocer su
comportamiento con frecuencia; es utilizando el comando 'display' del gdb.
Starting program: /root/aaaaaaa/bbbbbbb/ccccc/asm1
Breakpoint 1, main () at asm1.c:4
4 {
2: x/i $eip 0x8048398 : push %ebp
Ya tenemos nuestros 2 puntos y ahora NO deseo que la SUMA se realize, y deseo
saltar a la RESTA; pudiese utilizar un JMP, salto incondicional o uno
condicional, pero estoy haciendo esto con fines educativos. :O)
Modificamos entonces el valor de eip.
(gdb) set $eip=0x80483a7
(gdb) stepi
0x80483a8 5 __asm__("
1: x/i $eip 0x80483a8 : sub %ebx,%eax
Revisamos con el stepi. Y se continua con la ejecucion de programa, luego de
realizada la resta, se debe detener en el segundo NOP.
(gdb) step
Breakpoint 2, 0x80483aa in main () at asm1.c:5
5 __asm__("
1: x/i $eip 0x80483aa : nop
Correcto, ahora se comprueba el valor actual de eax, el cual debe almacenar
el resultado de la 'resta'.
(gdb) info registers
eax 0x7c8 1992
ecx 0x8048398 134513560
edx 0x4010b098 1074835608
ebx 0xa 10
(gdb) step
Program exited normally.
Todo bien hasta el momento, nos ha sido posible manipular datos en memoria y
tambien el comportamiento del programa con comandos inherentes del gdb.
Como acapite final, examinemos mas incisivamente el comportamiento de la pila
en el siguiente 'basico' programa. Con esto disipare algunas dudas que
pudiesen haberse producido.
/*
c2.c (El mayor de 2 numero diferentes)
*/
#include
int a, b;
int el_mayor (int, int);
main ()
{
puts("Ingresa 2 numeros desiguales: ");
scanf("%d%d", &a, &b);
printf("El mayor es: %d\n", el_mayor(a, b));
}
int el_mayor(int a, int b)
{
if (a > b)
return a;
else
return b;
}
Empezamos... Poner especial atencion!. Aqui voy a recordar muchisimo a mi
profesor de Arquitectura de Computadores. Y esos dibujos estilo expresionista
con que imitaba a la memoria y la pila. :O)
(gdb) disass main
Dump of assembler code for function main:
0x8048430 : push %ebp
| %ebp | <-- esp
|----------|
| |
0x8048431 : mov %esp,%ebp
| %ebp | <-- esp <-- ebp
|----------|
| |
0x8048433 : push $0x8048520
| $0x08048520 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x8048438 : call 0x8048330
Siempre que hay un salto a una funcion o similares, se almacena la direccion
de retorno en la pila; aqui se observa que la direccion de la siguiente
instruccion '0x804843d' es almacenada en la pila; pues se hace una llamada
incondicional a puts.
| 0x0804843d | <-- $esp = 0xbffffb50
|-------------|
| $0x08048520 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x804843d : add $0x4,%esp
| $0x08048520 | <-- $esp = 0xbffffb54 --.
|-------------| |
| %ebp | <-- $ebp = 0xbffffb58 = $esp
|-------------|
| |
0x8048440 : push $0x8049658
0x8048445 : push $0x804965c
0x804844a : push $0x804853f
| $0x0804853f | <-- $esp = 0xbffffb4c
|-------------|
| $0x0804965c | <-- $esp = 0xbffffb50
|-------------|
| $0x08049658 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x804844f : call 0x8048340
Nuevamente se realiza una llamada a la funcion scanf, por ende se almacena la
direccion siguiente a ejecutarse en la pila; es la ultima vez que lo repito;
espero quede claro.
| 0x08048454 | <-- $esp = 0xbffffb48
|-------------|
| $0x0804853f | <-- $esp = 0xbffffb4c
|-------------|
| $0x0804965c | <-- $esp = 0xbffffb50
|-------------|
| $0x08049658 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x8048454 : add $0xc,%esp
Con esta instruccion se ubica esp en la posicion que indica el grafico.
| 0x08048454 | <-- $esp = 0xbffffb48 -. (Posicion anterior)
|-------------| |
| $0x0804853f | |
|-------------| |
| $0x0804965c | |
|-------------| |
| $0x08049658 | <-- $esp = 0xbffffb54 = <-- $esp (Posicion actual)
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x8048457 : mov 0x8049658,%eax
0x804845c : push %eax
0x804845d : mov 0x804965c,%eax
0x8048462 : push %eax
Lo que se realiza con estas instrucciones, es almacenar los numero que he
pasado al programa; en este caso 3 y 2. Como se aprecia estos 2 valores se
almacenan primero en eax y luego son colocados en la pila.
(gdb) x 0x8049658
0x8049658 < b>: 0x00000003
(gdb) x 0x804965c
0x804965c < a>: 0x00000002
Ahora veamos la pila:
| 0x00000002 | <-- $esp = 0xbfffff50
|-------------|
| 0x00000003 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x8048463 : call 0x8048480
Desemsamblemos la funcion 'el mayor', y analizemos:
(gdb) disass el_mayor
Dump of assembler code for function el_mayor:
0x8048480 : push %ebp
0x8048481 : mov %esp,%ebp
Despues de las anteriores 2 instrucciones, la situacion queda asi:
| 0xbffffb58 | <-- $esp = 0xbffffb48 <-- $ebp = 0xbffffb48 (Actual)
|-------------| |
| 0x8048468 | <-- $esp = 0xbffffb4c |
|-------------| |
| 0x00000002 | <-- $esp = 0xbfffff50 |
|-------------| |
| 0x00000003 | <-- $esp = 0xbffffb54 |
|-------------| |
| %ebp | <-- $ebp = 0xbffffb58 -' (Posicion anterior)
|-------------|
| |
0x8048483 : mov 0x8(%ebp),%eax
Aqui se almacena el numero '2' en el registro eax. Si aprecian el grafico
anterior verificareis lo que os digo: 0x8(%ebp) = 0xbfffff50
(gdb) p/x $eax
$2 = 0x2
0x8048486 : cmp 0xc(%ebp),%eax
Siguiendo el grafico y el mecanismo empleado para localizar el anterior
numero; localizareis el actual; y este se CoMPara con el almacenado en %eax.
0x8048489 : jle 0x8048494
Si es menor o igual se hace un salto condicional a: 0x8048494. Y como de
hecho es asi, el salto se realiza a dicha direccion.
Ahora bien, si esto no hubiese ocurrido de esta manera, las siguientes
instrucciones; que lo unico que hacen, es mover el numero '2' hasta el
registro %eax, y luego saltar incondicionalmente a 0x80484a0; hubiesen tenido
que ser ejecutadas.
0x804848b : mov 0x8(%ebp),%edx
0x804848e : mov %edx,%eax
0x8048490 : jmp 0x80484a0
0x8048492 : jmp 0x80484a0
0x8048494 : mov 0xc(%ebp),%edx
0x8048497 : mov %edx,%eax
Se mueve el '3' al registro %edx y de alli al registro %eax.
(gdb) p/x $eax
$3 = 0x3
0x8048499 : jmp 0x80484a0
Y eso es todo!. Sale de la funcion.
0x804849b : nop
0x804849c : lea 0x0(%esi,1),%esi
0x80484a0 : leave
0x80484a1 : ret
End of assembler dump.
Retorna a la funcion llamante 'main'. Veamos la situacion actual de la pila:
| 0x00000002 | <-- $esp = 0xbfffff50
|-------------|
| 0x00000003 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x8048468 : add $0x8,%esp
Evidente no?... Ademas ya tengo ganas de irme a dormir. ;)
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x804846b : mov %eax,%eax
Y que hay en eax?... haber...
(gdb) p/x $eax
$4 = 0x3
Ah!!... El numero mayor que nuestro programa tan 'cordialmente' calculo para
nosotros.
0x804846d : push %eax
| 0x00000003 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
0x804846e : push $0x8048544
| 0x08048544 | <-- $esp = 0xbffffb50
|-------------|
| 0x00000003 | <-- $esp = 0xbffffb54
|-------------|
| %ebp | <-- $ebp = 0xbffffb58
|-------------|
| |
Un resumen menos visual de la pila.
(gdb) x/3x $esp
0xbffffb50: 0x08048544 0x00000003 0xbffffb78
0x8048473 : call 0x8048370
Y bueno, para abreviar este ya extenso texto, se hace una llamada a printf,
con los registos requeridos por este; con lo cual se visualizara por pantalla
el tan 'buscado' numero '3' de nuestro ejemplo. Pueden verificar esto vosotros
mismos. YO ya estoy durmiendo con mi ojo izquierdo.
0x8048478 : add $0x8,%esp
| %ebp | <-- $ebp = 0xbffffb58 = $esp
|-------------|
| |
0x804847b : leave
0x804847c : ret
End of assembler dump.
Y se acabo!!!...
* Despedida:
Para aquellos que no son muy doctos o que aun estan confusos respecto a estos
temas; los incentivo a practicar, investigar, leer y un largo etc. Es la unica
manera de aprender y lograr TODO lo que uno desea.
Si encuentran algun error, inconcordancia, falacia, o si teneis algun
comentario, pregunta o sugerencia; con gusto os atendere personalmente si me
escriben a mi e-mail.
Solo tengo lucides, para agradecer a todos vosotros la atencion prestada.
E**** dice:
la maquina.. tu pc... no es tu vida
vas a vivir.. con tu pc toda la vida
acaso?
(gdb) quit
<<::RareGaZz::>>
|