RareGaZz nº19:(rgz_09):27/03/2002 << Back To RareGaZz nº 19
: :.:..:...:....:.....:......:.......:........:.........:..........: : : :::: : : : : 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 <stdio.h> 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 <main>: push %ebp 0x80483d1 <main+1>: mov %esp,%ebp 0x80483d3 <main+3>: mov 0xc(%ebp),%eax 0x80483d6 <main+6>: add $0x4,%eax 0x80483d9 <main+9>: mov (%eax),%edx 0x80483db <main+11>: push %edx 0x80483dc <main+12>: push $0x8048450 0x80483e1 <main+17>: call 0x8048308 <printf> 0x80483e6 <main+22>: add $0x8,%esp 0x80483e9 <main+25>: xor %eax,%eax 0x80483eb <main+27>: jmp 0x80483f0 <main+32> 0x80483ed <main+29>: lea 0x0(%esi),%esi 0x80483f0 <main+32>: leave 0x80483f1 <main+33>: ret 0x80483f2 <main+34>: 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 <main>: push %ebp Coloca en la pila el contenido de ebp, o Puntero Base. 0x80483d1 <main+1>: 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 <main+3>: 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 <main+9>: 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 <main+6>: 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 <main+9>: 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 <main+11>: 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 <main+12>: 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 <main+17>: call 0x8048308 <printf> 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 <main+22>: 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 <main+25>: xor %eax,%eax XOReamos el registro, con esa instruccion logica se 'limpia' el registro eax. (gdb) p/x $eax $19 = 0x0 0x80483eb <main+27>: jmp 0x80483f0 <main+32> Un salto incondicional a: 0x80483f0 <main+32>: 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 <main+33>: ret ret, no significa mas que retornar el control al programa que llamo a este. (gdb) stepi 0x400329cb in __libc_start_main (main=0x80483d0 <main>, 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 <stdio.h> 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 <main>: push %ebp 0x8048399 <main+1>: mov %esp,%ebp 0x804839b <main+3>: mov $0x7,%eax 0x80483a0 <main+8>: mov $0xb,%ebx 0x80483a5 <main+13>: add %eax,%ebx 0x80483a7 <main+15>: nop 0x80483a8 <main+16>: sub %ebx,%eax 0x80483aa <main+18>: nop 0x80483ab <main+19>: xor %eax,%eax 0x80483ad <main+21>: inc %eax 0x80483ae <main+22>: xor %ebx,%ebx 0x80483b0 <main+24>: int $0x80 0x80483b2 <main+26>: leave 0x80483b3 <main+27>: 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 <main>: push %ebp Coloca el contenido %ebp en la pila. Decrementandose esp en 4 unidades. esp 0xbffffb68 -1073743000 ebp 0xbffffb88 -1073742968 0x8048399 <main+1>: mov %esp,%ebp Evidente. Ustedes ya lo saben. ;) (gdb) x/x $esp 0xbffffb68: 0xbffffb88 (gdb) x/x $ebp 0xbffffb68: 0xbffffb88 0x804839b <main+3>: mov $0x7,%eax Mueve 0x7 al registro eax. (gdb) p/x $eax $1 = 0x7 (gdb) stepi 0x80483a5 in main () 0x80483a0 <main+8>: mov $0xb,%ebx Mueve 0xb al registro ebx (gdb) p/x $ebx $2 = 0xb 0x80483a5 <main+13>: 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 <main+15>: nop nop = no operacion. ;) Se entendio?. 0x80483a8 <main+16>: sub %ebx,%eax Operacion resta 'Sub'. No necesita mayor explicacion. 0x80483aa <main+18>: 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 <main+19>: xor %eax,%eax (gdb) p/x $eax $3 = 0x0 0x80483ad <main+21>: inc %eax (gdb) p/x $eax $4 = 0x1 0x80483ae <main+22>: xor %ebx,%ebx (gdb) p/x $ebx $6 = 0x0 0x80483b0 <main+24>: int $0x80 0x80483b2 <main+26>: leave 0x80483b3 <main+27>: 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 <main>: 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 <main+16>: 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 <main+18>: 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 <stdio.h> 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 <main>: push %ebp | %ebp | <-- esp |----------| | | 0x8048431 <main+1>: mov %esp,%ebp | %ebp | <-- esp <-- ebp |----------| | | 0x8048433 <main+3>: push $0x8048520 | $0x08048520 | <-- $esp = 0xbffffb54 |-------------| | %ebp | <-- $ebp = 0xbffffb58 |-------------| | | 0x8048438 <main+8>: call 0x8048330 <puts> 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 <main+13>: add $0x4,%esp | $0x08048520 | <-- $esp = 0xbffffb54 --. |-------------| | | %ebp | <-- $ebp = 0xbffffb58 = $esp |-------------| | | 0x8048440 <main+16>: push $0x8049658 0x8048445 <main+21>: push $0x804965c 0x804844a <main+26>: push $0x804853f | $0x0804853f | <-- $esp = 0xbffffb4c |-------------| | $0x0804965c | <-- $esp = 0xbffffb50 |-------------| | $0x08049658 | <-- $esp = 0xbffffb54 |-------------| | %ebp | <-- $ebp = 0xbffffb58 |-------------| | | 0x804844f <main+31>: call 0x8048340 <scanf> 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 <main+36>: 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 <main+39>: mov 0x8049658,%eax 0x804845c <main+44>: push %eax 0x804845d <main+45>: mov 0x804965c,%eax 0x8048462 <main+50>: 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 <main+51>: call 0x8048480 <el_mayor> Desemsamblemos la funcion 'el mayor', y analizemos: (gdb) disass el_mayor Dump of assembler code for function el_mayor: 0x8048480 <el_mayor>: push %ebp 0x8048481 <el_mayor+1>: 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 <el_mayor+3>: 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 <el_mayor+6>: 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 <el_mayor+9>: jle 0x8048494 <el_mayor+20> 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 <el_mayor+11>: mov 0x8(%ebp),%edx 0x804848e <el_mayor+14>: mov %edx,%eax 0x8048490 <el_mayor+16>: jmp 0x80484a0 <el_mayor+32> 0x8048492 <el_mayor+18>: jmp 0x80484a0 <el_mayor+32> 0x8048494 <el_mayor+20>: mov 0xc(%ebp),%edx 0x8048497 <el_mayor+23>: mov %edx,%eax Se mueve el '3' al registro %edx y de alli al registro %eax. (gdb) p/x $eax $3 = 0x3 0x8048499 <el_mayor+25>: jmp 0x80484a0 <el_mayor+32> Y eso es todo!. Sale de la funcion. 0x804849b <el_mayor+27>: nop 0x804849c <el_mayor+28>: lea 0x0(%esi,1),%esi 0x80484a0 <el_mayor+32>: leave 0x80484a1 <el_mayor+33>: 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 <main+56>: add $0x8,%esp Evidente no?... Ademas ya tengo ganas de irme a dormir. ;) | %ebp | <-- $ebp = 0xbffffb58 |-------------| | | 0x804846b <main+59>: 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 <main+61>: push %eax | 0x00000003 | <-- $esp = 0xbffffb54 |-------------| | %ebp | <-- $ebp = 0xbffffb58 |-------------| | | 0x804846e <main+62>: 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 <main+67>: call 0x8048370 <printf> 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 <main+72>: add $0x8,%esp | %ebp | <-- $ebp = 0xbffffb58 = $esp |-------------| | | 0x804847b <main+75>: leave 0x804847c <main+76>: 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::>>