Únete al equipo de Lobera

Consulta nuestra Política de Privacidad sobre el tratamiento de esta información.

Contacta con el equipo de Lobera

Consulta nuestra Política de Privacidad sobre el tratamiento de esta información.

Fundamentos de reversing / 03

Funciones, switch y bucles

LoberaFormación técnica03 / 33 · 11 min de lectura · Linux · x86 / x86-64
03 / 33

Seguir llamadas, distinguir ramas de un switch e identificar el retorno de un bucle.

Consultar conceptos ↗
Notas de la práctica

El vídeo utiliza positive, un ejemplo adicional del laboratorio 3. La práctica manual de esta página utiliza switch-fruit; ambos están disponibles en las descargas.

En el capítulo anterior de este curso completo de radare2, recorrimos la estructura básica de un binario ejecutable escrito en C, prestando atención a las llamadas a funciones de entrada/salida como printf y a estructuras de datos básicas como las variables. También se revisaron las medidas básicas de control del flujo de ejecución mediante sentencias if-else. Hoy profundizamos un poco más en esas medidas de control de flujo: presentaremos el uso básico de la sentencia switch-case, declararemos y usaremos funciones propias y, al final, analizaremos bucles con while y for.

Funciones y condiciones

Refrescamos algunos conceptos anteriores con el siguiente código, que declara una función para detectar números positivos y salta a ella.

C
#include <stdio.h>

func2(){
int num;
printf("Enter a number: ");
scanf("%d", &num);

if(num>0) printf("The number is positive.\n");

getchar();
}

main(){

func2();
getchar();

}

Podemos compilar ese código y abrirlo en radare2.

CÓDIGO / SALIDA
[0x000006a0]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[0x000006a0]> 

Tras ejecutar el afl inicial, esta vez detectamos un par de lugares interesantes. Tenemos el sym.main como de costumbre, pero también el sym.func2, que no parece tan habitual y merece atención. Otras pistas iniciales son la presencia de scanf, getchar, printf o puts, que dan una idea general de lo que hace el programa.

CÓDIGO / SALIDA
[0x000006a0]> afl
0x00000000    3 72   -> 73   sym.imp.__libc_start_main
0x00000618    3 23           sym._init
0x00000640    1 6            sym.imp.puts
0x00000650    1 6            sym.imp.__stack_chk_fail
0x00000660    1 6            sym.imp.printf
0x00000670    1 6            sym.imp.getchar
0x00000680    1 6            sym.imp.__isoc99_scanf
0x00000690    1 6            sub.__cxa_finalize_248_690
0x000006a0    1 43           entry0
0x000006d0    4 50   -> 40   sym.deregister_tm_clones
0x00000710    4 66   -> 57   sym.register_tm_clones
0x00000760    4 49           sym.__do_global_dtors_aux
0x000007a0    1 10           entry1.init
0x000007aa    5 111          sym.func2
0x00000819    1 26           sym.main
0x00000840    4 101          sym.__libc_csu_init
0x000008b0    1 2            sym.__libc_csu_fini
0x000008b4    1 9            sym._fini
[0x000006a0]> 

Empezamos saltando a la función main:

ENSAMBLADOR / REFERENCIA
[0x000006a0]> s main
[0x00000819]> pdb
            ;-- main:
/ (fcn) sym.main 26
|   sym.main ();
|              ; DATA XREF from 0x000006bd (entry0)
|           0x00000819      55             push rbp
|           0x0000081a      4889e5         mov rbp, rsp
|           0x0000081d      b800000000     mov eax, 0
|           0x00000822      e883ffffff     call sym.func2
|           0x00000827      e844feffff     call sym.imp.getchar        ; int getchar(void)
|           0x0000082c      b800000000     mov eax, 0
|           0x00000831      5d             pop rbp
\           0x00000832      c3             ret
[0x00000819]> 

Esta función main realiza las operaciones básicas de alineación de pila y salta rápidamente a la función func2; tras ejecutarla, llama a getchar(), probablemente para mantener la ventana abierta (si el programa se ejecuta, por ejemplo, en Windows).

En este caso nos centraremos en la función func2.

ENSAMBLADOR / REFERENCIA
[0x00000819]> s sym.func2
[0x000007aa]> pdf
/ (fcn) sym.func2 111
|   sym.func2 ();
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x00000822 (sym.main)
|           0x000007aa      55             push rbp
|           0x000007ab      4889e5         mov rbp, rsp
|           0x000007ae      4883ec10       sub rsp, 0x10
|           0x000007b2      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=0x1a08 ; '('
|           0x000007bb      488945f8       mov qword [local_8h], rax
|           0x000007bf      31c0           xor eax, eax
|           0x000007c1      488d3dfc0000.  lea rdi, qword str.Enter_a_number: ; 0x8c4 ; "Enter a number: "
|           0x000007c8      b800000000     mov eax, 0
|           0x000007cd      e88efeffff     call sym.imp.printf         ; int printf(const char *format)
|           0x000007d2      488d45f4       lea rax, qword [local_ch]
|           0x000007d6      4889c6         mov rsi, rax
|           0x000007d9      488d3df50000.  lea rdi, qword [0x000008d5] ; "%d"
|           0x000007e0      b800000000     mov eax, 0
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x000007ea      8b45f4         mov eax, dword [local_ch]
|           0x000007ed      85c0           test eax, eax
|       ,=< 0x000007ef      7e0c           jle 0x7fd
|       |   0x000007f1      488d3de00000.  lea rdi, qword str.The_number_is_positive. ; 0x8d8 ; "The number is positive."
|       |   0x000007f8      e843feffff     call sym.imp.puts           ; int puts(const char *s)
|       |      ; JMP XREF from 0x000007ef (sym.func2)
|       `-> 0x000007fd      e86efeffff     call sym.imp.getchar        ; int getchar(void)
|           0x00000802      90             nop
|           0x00000803      488b55f8       mov rdx, qword [local_8h]
|           0x00000807      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x00000810      7405           je 0x817
|       |   0x00000812      e839feffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       |      ; JMP XREF from 0x00000810 (sym.func2)
|       `-> 0x00000817      c9             leave
\           0x00000818      c3             ret
[0x000007aa]> 
ENSAMBLADOR / REFERENCIA
|           0x000007ae      4883ec10       sub rsp, 0x10
|           0x000007b2      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=0x1a08 ; '('
|           0x000007bb      488945f8       mov qword [local_8h], rax
|           0x000007bf      31c0           xor eax, eax

Como ya conocemos el programa, sabemos que lo primero que hace en este punto es declarar la variable number, que almacenará la entrada del usuario. Las dos primeras líneas pueden resultar algo confusas y, en mi opinión, no están directamente relacionadas con el algoritmo que analizamos. La primera línea aparece en muchas funciones y básicamente reserva espacio en la pila para operar con variables y estructuras de datos; como aquí usamos variables, el programa «reserva» ese espacio. Profundizaremos en esto más adelante; por ahora es suficiente con entenderlo así.

A continuación encontramos esta instrucción mov rax, qword que va un poco más allá del alcance de este artículo. La inserta el compilador gcc y su función es establecer una comprobación de guarda de pila contra posibles vulnerabilidades de desbordamiento de búfer. Si miramos el código con más detalle, vemos una operación XOR en la misma ubicación al final de la rutina. En términos generales, el programa comprobará si la pila ha sido corrompida; si es así, lanzará __stack_chk_fail para gestionar el problema de forma segura. El mecanismo completo está explicado en detalle en https://stackoverflow.com/questions/10325713/why-does-this-memory-address-fs0x28-fs0x28-have-a-random-value. La variable etiquetada como local_8h se usará para almacenar ese «canario».

Continuamos.

ENSAMBLADOR / REFERENCIA
|           0x000007c1      488d3dfc0000.  lea rdi, qword str.Enter_a_number: ; 0x8c4 ; "Enter a number: "
|           0x000007c8      b800000000     mov eax, 0
|           0x000007cd      e88efeffff     call sym.imp.printf         ; int printf(const char *format)
|           0x000007d2      488d45f4       lea rax, qword [local_ch]
|           0x000007d6      4889c6         mov rsi, rax
|           0x000007d9      488d3df50000.  lea rdi, qword [0x000008d5] ; "%d"
|           0x000007e0      b800000000     mov eax, 0
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf

Como ya sabemos, en sistemas x64 los parámetros o argumentos se pasan a las funciones a través de los registros, así que con lea rdi, "enter a number" cargamos la dirección efectiva donde está almacenada la cadena "Enter a number:" en memoria en el registro. No pasamos la cadena completa a la función, sino una referencia a su ubicación. Este concepto es importante: si la función modifica la cadena, el valor original también se modificará. Si en cambio pasáramos una copia, las operaciones realizadas dentro de la función sobre esa copia no afectarían al original.

Vemos que la cadena a imprimir se pasa por referencia como parámetro a la función printf y que el registro eax se pone a cero. En la ABI x86_64, si una función (como printf) tiene argumentos variables, se espera que AL (parte de EAX) contenga el número de registros vectoriales (SSE, AVX) usados para pasar argumentos a esa función; en nuestro caso ese número es cero. Más información al respecto en https://www.codingame.com/playgrounds/283/sse-avx-vectorization/what-is-sse-and-avx

Una vez pasados esos parámetros, se llama a la función printf, que debería mostrar la cadena por la salida estándar (pantalla).

Justo después de la llamada, vemos que la etiqueta local_ch se carga primero en rax y luego en rsi. local_ch referencia un espacio reservado, habitualmente una variable; en este caso representa la variable number. Después vemos que "%d" también se carga como parámetro, en este caso en rdi, eax se pone a cero y el programa llama a la función scanf.

En términos generales, ese bloque de código equivale a printf("Enter a number:") seguido de scanf("%d", &number).

La siguiente fase del programa corresponde a la comprobación del valor introducido por el usuario.

ENSAMBLADOR / REFERENCIA
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x000007ea      8b45f4         mov eax, dword [local_ch]
|           0x000007ed      85c0           test eax, eax
|       ,=< 0x000007ef      7e0c           jle 0x7fd
|       |   0x000007f1      488d3de00000.  lea rdi, qword str.The_number_is_positive. ; 0x8d8 ; "The number is positive."
|       |   0x000007f8      e843feffff     call sym.imp.puts           ; int puts(const char *s)
|       |      ; JMP XREF from 0x000007ef (sym.func2)
|       `-> 0x000007fd      e86efeffff     call sym.imp.getchar        ; int getchar(void)

Tras scanf, la entrada del usuario (local_ch) se mueve a eax y, como vimos antes, se realiza una comparación con 0 mediante test y jle. Si la entrada es >0, la ejecución continúa para imprimir "The number is positive"; si esa condición no se cumple, el programa salta directamente al final del bloque.

A partir de aquí podemos dar por concluido este análisis sencillo y pasar a un paso adicional.

Añadimos ahora una condición else para ver cómo la gestiona el compilador.

C
#include <stdio.h>

func2(){
int num;
printf("Enter a number: ");
scanf("%d", &num);

if(num>0) printf("The number is positive.\n");
else printf("The number is negative.\n");
getchar();
}

main(){

func2();
getchar();

}

Podemos ir directamente al punto relevante.

ENSAMBLADOR / REFERENCIA
|           0x000007e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x000007ea      8b45f4         mov eax, dword [local_ch]
|           0x000007ed      85c0           test eax, eax
|       ,=< 0x000007ef      7e0e           jle 0x7ff
|       |   0x000007f1      488d3df00000.  lea rdi, qword str.The_number_is_positive. ; 0x8e8 ; "The number is positive."
|       |   0x000007f8      e843feffff     call sym.imp.puts           ; int puts(const char *s)
|      ,==< 0x000007fd      eb0c           jmp 0x80b
|      ||      ; JMP XREF from 0x000007ef (sym.func2)
|      |`-> 0x000007ff      488d3dfa0000.  lea rdi, qword str.The_number_is_negative. ; 0x900 ; "The number is negative."
|      |    0x00000806      e835feffff     call sym.imp.puts           ; int puts(const char *s)
|      |       ; JMP XREF from 0x000007fd (sym.func2)
|      `--> 0x0000080b      e860feffff     call sym.imp.getchar        ; int getchar(void)

La rama else se ejecuta para cero y para los valores negativos. El mensaje del ejemplo los describe como negativos, aunque cero pertenece también a esa ruta. Al analizar una condición, comprueba sus instrucciones además de la cadena que imprime.

Dentro de radare2 existe una forma más clara de inspeccionar bifurcaciones de código como la que acabamos de ver. Si escribes VV dentro de una función podrás inspeccionarla visualmente.

ENSAMBLADOR / REFERENCIA
                                                   | test eax, eax                              |                                                   
                                                   | jle 0x7ff;[gc]                             |                                                   
                                                   `--------------------------------------------'                                                   
                                                           | |                                                                                      
                                                           | '--------------------.                                                                 
                              .----------------------------'                      |                                                                 
                              |                                                   |                                                                 
                              |                                                   |                                                                 
                      .------------------------------------------------.    .------------------------------------------------.                      
                      |  0x7f1 ;[gg]                                   |    |  0x7ff ;[gc]                                   |                      
                      |   ; 0x8e8                                      |    |      ; JMP XREF from 0x000007ef (sym.func2)    |                      
                      |   ; "The number is positive."                  |    |   ; 0x900                                      |                      
                      | lea rdi, qword str.The_number_is_positive.     |    |   ; "The number is negative."                  |                      
                      | call sym.imp.puts;[ge]                         |    | lea rdi, qword str.The_number_is_negative.     |                      
                      | jmp 0x80b;[gf]                                 |    | call sym.imp.puts;[ge]                         |                      
                      `------------------------------------------------'    `------------------------------------------------'                      
                          |                                                     |                                                                   
                          '----------------------------.                        |                                                                   
                                                       .------------------------'                                                                   
                                                       |                                                                                            
                                                       |                                                                                            
                                                   .---------------------------------------------.                          

SWITCH CASE

El switch-case plantea un escenario más avanzado que el if-else. La sentencia if-else funciona bien cuando hay dos o tres caminos posibles para redirigir el flujo de ejecución, pero si hay muchos casos distintos conviene usar algo más avanzado como switch-case.

Trabajaremos con el siguiente fragmento de código.

C
#include <stdio.h>

func2(){
printf("Enter a key and then press enter: ");
char key;
scanf("%c",&key);

switch(key){
case ' ':
        printf("Space. \n");
        break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0': printf("Digit.\n");
    break;
    default: printf("Neither space nor digit.\n");
}

}

main(){
    func2();
    getchar();
}

El código es bastante sencillo; como de costumbre, toda la lógica ocurre dentro de la función func2. El programa lee un carácter de la entrada estándar y lo pasa a la sentencia switch. El valor del carácter se evaluará contra cada uno de los case. Si el carácter introducido es un espacio, se llamará a printf y la comprobación terminará con la instrucción break, lo que significa que el programa saltará directamente fuera del bloque switch.

Los casos correspondientes a los caracteres 0 a 9 comparten el mismo bloque. Basta con que el carácter coincida con uno de ellos para imprimir Digit.. Los valores que no coinciden con el espacio ni con un dígito llegan a default.

Ahora que sabemos qué hace este programa, vamos directamente a radare2. Esta vez analizaremos el binario tanto de forma estática como dinámica.

CÓDIGO / SALIDA
red@blue:~/c/chapter3$ radare2 -d case
Process with PID 7901 started...
= attach 7901 7901
bin.baddr 0x560c23c18000
Using 0x560c23c18000
asm.bits 64
[0x7ff6a3b32090]> aaa
[ WARNING : block size exceeding max block size at 0x560c23e18fe0
[+] Try changing it with e anal.bb.maxsize
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
= attach 7901 7901
7901
[0x7ff6a3b32090]> 

Iniciamos el programa con el flag -d para poder depurarlo. Lo que queremos analizar está dentro de la función func2, así que empezamos por ahí.

ENSAMBLADOR / REFERENCIA
[0x7ff6a3b32090]> s sym.func2
[0x560c23c187aa]> pdf
/ (fcn) sym.func2 154
|   sym.func2 ();
|           ; var int local_9h @ rbp-0x9
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x560c23c1884d (sym.main)
|           0x560c23c187aa      55             push rbp
|           0x560c23c187ab      4889e5         mov rbp, rsp
|           0x560c23c187ae      4883ec10       sub rsp, 0x10
|           0x560c23c187b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x560c23c187bb      488945f8       mov qword [local_8h], rax
|           0x560c23c187bf      31c0           xor eax, eax
|           0x560c23c187c1      488d3d200100.  lea rdi, qword str.Enter_a_key_and_then_press_enter: ; 0x560c23c188e8 ; "Enter a key and then press enter: "
|           0x560c23c187c8      b800000000     mov eax, 0
|           0x560c23c187cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x560c23c187d2      488d45f7       lea rax, qword [local_9h]
|           0x560c23c187d6      4889c6         mov rsi, rax
|           0x560c23c187d9      488d3d2b0100.  lea rdi, qword [0x560c23c1890b] ; "%c"
|           0x560c23c187e0      b800000000     mov eax, 0
|           0x560c23c187e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x560c23c187ea      0fb645f7       movzx eax, byte [local_9h]
|           0x560c23c187ee      0fbec0         movsx eax, al
|           0x560c23c187f1      83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
|       |   0x560c23c187f6      83f820         cmp eax, 0x20           ; 32
|      ,==< 0x560c23c187f9      7c26           jl 0x560c23c18821
|      ||   0x560c23c187fb      83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813
|    |||`-> 0x560c23c18805      488d3d020100.  lea rdi, qword str.Space. ; 0x560c23c1890e ; "Space. "
|    |||    0x560c23c1880c      e82ffeffff     call sym.imp.puts       ; int puts(const char *s)
|    |||,=< 0x560c23c18811      eb1a           jmp 0x560c23c1882d
|    ||||      ; JMP XREF from 0x560c23c18803 (sym.func2)
|    `----> 0x560c23c18813      488d3dfc0000.  lea rdi, qword str.Digit. ; 0x560c23c18916 ; "Digit."
|     |||   0x560c23c1881a      e821feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x560c23c1881f      eb0c           jmp 0x560c23c1882d
|    |``--> 0x560c23c18821      488d3df50000.  lea rdi, qword str.Neither_space_nor_digit. ; 0x560c23c1891d ; "Neither space nor digit."
|    |  |   0x560c23c18828      e813feffff     call sym.imp.puts       ; int puts(const char *s)
|    |  |      ; JMP XREF from 0x560c23c18811 (sym.func2)
|    |  |      ; JMP XREF from 0x560c23c1881f (sym.func2)
|    `--`-> 0x560c23c1882d      90             nop
|           0x560c23c1882e      488b55f8       mov rdx, qword [local_8h]
|           0x560c23c18832      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x560c23c1883b      7405           je 0x560c23c18842
|       |   0x560c23c1883d      e80efeffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x560c23c18842      c9             leave
\           0x560c23c18843      c3             ret
[0x560c23c187aa]> 

Este bloque comienza con el mecanismo de protección de pila que presentamos antes. El control del flujo de ejecución aquí es algo más complejo. Esto ocurrirá a veces en CTFs o en proyectos reales de reversing. Hay dos soluciones habituales: saltar al modo visual o buscar cadenas o llamadas a funciones interesantes. Aquí podemos identificar fácilmente las cadenas Space, Digit y Neither space or digit, lo que dice mucho y básicamente resuelve el problema, ya que identificamos tres casos principales.

La lógica comienza con el scanf.

ENSAMBLADOR / REFERENCIA
|           0x560c23c187d2      488d45f7       lea rax, qword [local_9h]
|           0x560c23c187d6      4889c6         mov rsi, rax
|           0x560c23c187d9      488d3d2b0100.  lea rdi, qword [0x560c23c1890b] ; "%c"
|           0x560c23c187e0      b800000000     mov eax, 0
|           0x560c23c187e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x560c23c187ea      0fb645f7       movzx eax, byte [local_9h]
|           0x560c23c187ee      0fbec0         movsx eax, al
|           0x560c23c187f1      83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805

En este ejemplo, empezamos pasando un par de parámetros a la función scanf a través de los registros: con %c indicamos que vamos a leer un carácter, y local_9h será la ubicación de ese carácter en memoria.

Tras leer el valor del carácter desde la entrada del usuario, el programa lo prepara para compararlo con el conjunto de caracteres del primer case. Como un char en C ocupa un (1) byte, el programa solo necesita el contenido de AL (la parte baja de RAX/EAX). Tras cargar el valor, el programa lo compara con 0x20, que representa el valor de 'espacio' según la tabla ASCII.

ENSAMBLADOR / REFERENCIA
|           0x560c23c187f1      83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
|       |   0x560c23c187f6      83f820         cmp eax, 0x20           ; 32
|      ,==< 0x560c23c187f9      7c26           jl 0x560c23c18821
|      ||   0x560c23c187fb      83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813
|    |||`-> 0x560c23c18805      488d3d020100.  lea rdi, qword str.Space. ; 0x560c23c1890e ; "Space. "
|    |||    0x560c23c1880c      e82ffeffff     call sym.imp.puts       ; int puts(const char *s)
|    |||,=< 0x560c23c18811      eb1a           jmp 0x560c23c1882d
|    ||||      ; JMP XREF from 0x560c23c18803 (sym.func2)
|    `----> 0x560c23c18813      488d3dfc0000.  lea rdi, qword str.Digit. ; 0x560c23c18916 ; "Digit."
|     |||   0x560c23c1881a      e821feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x560c23c1881f      eb0c           jmp 0x560c23c1882d
|    |``--> 0x560c23c18821      488d3df50000.  lea rdi, qword str.Neither_space_nor_digit. ; 0x560c23c1891d ; "Neither space nor digit."
|    |  |   0x560c23c18828      e813feffff     call sym.imp.puts       ; int puts(const char *s)
|    |  |      ; JMP XREF from 0x560c23c18811 (sym.func2)
|    |  |      ; JMP XREF from 0x560c23c1881f (sym.func2)
|    `--`-> 0x560c23c1882d      90             nop

Si el contenido de eax (la entrada) es igual al valor de espacio, el programa saltará a 0x560c23c18805, donde imprimirá "space"; tras imprimirlo, saldrá del bloque saltando directamente a 0x560c23c1882d (nop).

Lo interesante viene justo después. Tras esta primera comparación, si la condición de salto no se cumple, el programa vuelve a comparar el valor con un espacio, esta vez comprobando si el valor es MENOR QUE un espacio; si lo es, salta a "Neither space nor digit" y sale del bloque. ¿Por qué? Es un truco sencillo que usan los compiladores para comprobar si un carácter está fuera de un rango. Como se puede verificar, todos los dígitos entre 0 y 9 tienen valores superiores a 0x20 en la tabla ASCII, por lo que cualquier valor por debajo de 0x20 no puede ser un dígito.

A continuación se realiza la última comparación:

ENSAMBLADOR / REFERENCIA
|      ||   0x560c23c187fb      83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813

La resta eax - 0x30 transforma los códigos ASCII de 0 a 9 en el intervalo de 0 a 9. La comparación posterior con 9 y el salto ja descartan los valores que, interpretados sin signo, quedan por encima del intervalo. En este listado, ja conduce a Neither space nor digit; la otra ruta alcanza Digit..

Examinemos esto con más detalle depurando el programa e introduciendo un dígito.

Empezamos estableciendo un par de breakpoints.

CÓDIGO / SALIDA
[0x560c23c187aa]> db 0x560c23c187f1
[0x560c23c187aa]> db 0x560c23c187fb
[0x560c23c187aa]> 

Lanzamos la ejecución hasta el primer breakpoint.

ENSAMBLADOR / REFERENCIA
[0x560c23c187aa]> dc
Enter a key and then press enter: 5
hit breakpoint at: 560c23c187f1
[0x560c23c187f1]> pdb
/ (fcn) sym.func2 154
|   sym.func2 ();
|           ; var int local_9h @ rbp-0x9
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x560c23c1884d (sym.main)
|           0x560c23c187aa      55             push rbp
|           0x560c23c187ab      4889e5         mov rbp, rsp
|           0x560c23c187ae      4883ec10       sub rsp, 0x10
|           0x560c23c187b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x560c23c187bb      488945f8       mov qword [local_8h], rax
|           0x560c23c187bf      31c0           xor eax, eax
|           0x560c23c187c1      488d3d200100.  lea rdi, qword str.Enter_a_key_and_then_press_enter: ; 0x560c23c188e8 ; "Enter a key and then press enter: "
|           0x560c23c187c8      b800000000     mov eax, 0
|           0x560c23c187cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x560c23c187d2      488d45f7       lea rax, qword [local_9h]
|           0x560c23c187d6      4889c6         mov rsi, rax
|           0x560c23c187d9      488d3d2b0100.  lea rdi, qword [0x560c23c1890b] ; "%c"
|           0x560c23c187e0      b800000000     mov eax, 0z
|           0x560c23c187e5      e896feffff     call sym.imp.__isoc99_scanf
|           0x560c23c187ea      0fb645f7       movzx eax, byte [local_9h]
|           0x560c23c187ee      0fbec0         movsx eax, al
|           ;-- rip:
|           0x560c23c187f1 b    83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
[0x560c23c187f1]> 

El valor '5' se ha cargado en la variable y, por tanto, ahora está en AL (RAX).

ENSAMBLADOR / REFERENCIA
|           0x560c23c187f1 b    83f820         cmp eax, 0x20           ; 32
|       ,=< 0x560c23c187f4      740f           je 0x560c23c18805
[0x560c23c187f1]> dr
rax = 0x00000035

rax = 0x35, que corresponde a '5' en ASCII. '5' se comparará con ' ' y, como no son el mismo número, el flag zero permanecerá a cero.

CÓDIGO / SALIDA
[0x560c23c187f1]> ds
[0x560c23c187f1]> dr 1
cf = 0x00000000
pf = 0x00000000
af = 0x00000000
zf = 0x00000000
sf = 0x00000000
tf = 0x00000000
if = 0x00000001
df = 0x00000000
of = 0x00000000
[0x560c23c187f1]> 

El flujo de ejecución continuará y llegaremos a la otra instrucción cmp.

cmp funciona así con los flags:

CÓDIGO / SALIDA
Assume result = op1 - op2

CF - 1 if unsigned op2 > unsigned op1
OF - 1 if sign bit of OP1 != sign bit of result
SF - 1 if MSB (aka sign bit) of result = 1
ZF - 1 if Result = 0 (i.e. op1=op2)
AF - 1 if Carry in the low nibble of result
PF - 1 if Parity of Least significant byte is even

Para el carácter 5, la comparación con el espacio no toma la rama de igualdad. Después se calcula 0x35 - 0x30 = 5. Los flags que interesan a cada salto son los producidos por su comparación; no se puede asumir que todos estén a cero.

ENSAMBLADOR / REFERENCIA
      ||   0x560c23c187fb b    83e830         sub eax, 0x30           ; '0'
|      ||   0x560c23c187fe      83f809         cmp eax, 9              ; 9
|     ,===< 0x560c23c18801      771e           ja 0x560c23c18821
|     |||   ;-- rip:
|    ,====< 0x560c23c18803      eb0e           jmp 0x560c23c18813
|    |||`-> 0x560c23c18805      488d3d020100.  lea rdi, qword str.Space. ; 0x560c23c1890e ; "Space. "

El valor se comparará entonces con 9, por lo que los flags quedarán así:

CÓDIGO / SALIDA
[0x560c23c187f1]> dr 1
cf = 0x00000001
pf = 0x00000001
af = 0x00000001
zf = 0x00000000
sf = 0x00000001
tf = 0x00000000
if = 0x00000001
df = 0x00000000
of = 0x00000000
[0x560c23c187f1]> 

ja requiere CF=0 y ZF=0. Al comparar 5 con 9 aparece CF=1, como muestra el registro anterior, así que ese salto no se toma. La ejecución continúa por el jmp que conduce al bloque Digit..

ENSAMBLADOR / REFERENCIA
|    ||||      ; JMP XREF from 0x560c23c18803 (sym.func2)
|    `----> 0x560c23c18813      488d3dfc0000.  lea rdi, qword str.Digit. ; 0x560c23c18916 ; "Digit."
|     |||   0x560c23c1881a      e821feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x560c23c1881f      eb0c           jmp 0x560c23c1882d

En este punto ya sabemos cómo va a terminar el programa, así que pasamos a nuestro último ejemplo de switch-case.

C
#include <stdio.h>

func2(){
printf("Enter a key and then press enter: ");
int val;

printf("Select a fruit: \n");
printf("1: Apple\n");
printf("2: Orange\n");
printf("3: Banana\n");
printf("4: Pear\n");

scanf("%d",&val);

switch(val){
case 1:
        printf("Apple. \n");
        break;
case 2:
        printf("Orange. \n");
        break;
case 3:
        printf("Banana. \n");
        break;
case 4:
        printf("Pear. \n");
        break;

default: printf("Nothing selected.\n");
}

}

main(){
func2();
getchar();
}

Podemos inspeccionarlo fácilmente con el modo visual de radare2.

ENSAMBLADOR / REFERENCIA
                                                                                                       | cmp eax, 2                                               |                                               
                                                                                                        | je 0x854;[gd]                                            |                                               
                                                                                                        `----------------------------------------------------------'                                               
                                                                                                                | |                                                                                                
                                                                                                                | '--------------------------------------------.                                                   
                                                                                         .----------------------'                                              |                                                   
                                                                                         |                                                                     |                                                   
                                                                                         |                                                                     |                                                   
                                                                                 .--------------------.                                                  .---------------------------------------------.           
                                                                                 |  0x82e ;[gg]       |                                                  |  0x854 ;[gd]                                |           
                                                                                 | cmp eax, 2         |                                                  |      ; JMP XREF from 0x0000082c (sym.func2) |           
                                                                                 | jg 0x83a;[gf]      |                                                  |   ; 0x9ac                                   |           
                                                                                 `--------------------'                                                  |   ; "Orange. "                              |           
                                                                                         | |                                                             | lea rdi, qword str.Orange.                  |           
                                                                                         | |                                                             | call sym.imp.puts;[gb]                      |           
                                                                                         | |                                                             | jmp 0x88a;[gp]                              |           
                                                                                         | |                                                             `---------------------------------------------'           
                                                                                         | |                                                                 |                                                     
                                                                                         | '---------------------.                                           |                                                     
                                                   .-------------------------------------'                       |                                           |                                                     
                                                   |                                                             |                                           '------------------.                                  
                                                   |                                                             |                                                              |                                  
                                                   |                                                             |                                                              |                                  
                                           .--------------------.                                          .---------------------------------------------.                      |                                  
                                           |  0x833 ;[gi]       |                                          |  0x83a ;[gf]                                |                      |                                  
                                           | cmp eax, 1         |                                          |      ; JMP XREF from 0x00000831 (sym.func2) |                      |                                  
                                           | je 0x846;[gh]      |                                          | cmp eax, 3                                  |                      |                                  
                                           `--------------------'                                          | je 0x862;[gl]                               |                      |                                  
                                                   | |                                                     `---------------------------------------------'                      |                                  
                                                   | |                                                             | |                                                          |                                  
                                                   | '--.                                                          | |                                                          |                                  
                                .------------------'    |                                                          | |                                                          |                                  
                                |                       |                                                          | '--------------.                                           |                                  
                                |                       |                                                   .------'                |                                           |                                  
                                |                       |                                                   |                       |                                           |                                  
                                |                       |                                                   |                       |                                           |                                  
                        .--------------------.    .---------------------------------------------.   .--------------------.    .---------------------------------------------.   |                                  
                        |  0x838 ;[gk]       |    |  0x846 ;[gh]                                |   |  0x83f ;[gn]       |    |  0x862 ;[gl]                                |   |                                  
                        | jmp 0x87e;[gj]     |    |      ; JMP XREF from 0x00000836 (sym.func2) |   | cmp eax, 4         |    |      ; JMP XREF from 0x0000083d (sym.func2) |   |                                  
                        `--------------------'    |   ; 0x9a4                                   |   | je 0x870;[gm]      |    |   ; 0x9b5                                   |   |                                  
                            |                     |   ; "Apple. "                               |   `--------------------'    |   ; "Banana. "                              |   |                                  
                            |                     | lea rdi, qword str.Apple.                   |           | |               | lea rdi, qword str.Banana.                  |   |                                  
                            |                     | call sym.imp.puts;[gb]                      |           | |               | call sym.imp.puts;[gb]                      |   |                                  
                            |                     | jmp 0x88a;[gp]                              |           | |               | jmp 0x88a;[gp]                              |   |                                  
                            |                     `---------------------------------------------'           | |               `---------------------------------------------'   | 

El programa lee la elección del usuario y compara el valor con cada una de las opciones; si no coincide, sigue comparando hasta evaluar todas las condiciones. Deberías poder hacer el análisis por tu cuenta sin mayor dificultad.

Bucles While y For

Los bucles repiten un bloque mientras se cumpla una condición. Tanto while como for pueden producir comparaciones y saltos hacia un bloque anterior. Para reconstruirlos hay que localizar la inicialización, la condición y la actualización; el número de iteraciones depende de esos elementos.

C
#include <stdio.h>

func2(){
int num;

printf("Enter a num, (exit with 0):");

scanf("%d", &num);

while(num != 0){

        if(num > 0) printf("Positive num\n");
        else printf("Negative num\n");

        printf("Enter another num (exit with 0):");

        scanf("%d", &num);

}

}

main(){

func2();
getchar();

}

Dentro de radare2, la función func2 tendrá este aspecto:

ENSAMBLADOR / REFERENCIA
[0x7fba89d68090]> s sym.func2
[0x55b75b1ba7aa]> pdf
/ (fcn) sym.func2 170
|   sym.func2 ();
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x55b75b1ba85d (sym.main)
|           0x55b75b1ba7aa      55             push rbp
|           0x55b75b1ba7ab      4889e5         mov rbp, rsp
|           0x55b75b1ba7ae      4883ec10       sub rsp, 0x10
|           0x55b75b1ba7b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x55b75b1ba7bb      488945f8       mov qword [local_8h], rax
|           0x55b75b1ba7bf      31c0           xor eax, eax
|           0x55b75b1ba7c1      488d3d300100.  lea rdi, qword str.Enter_a_num___exit_with_0_: ; 0x55b75b1ba8f8 ; "Enter a num, (exit with 0):"
|           0x55b75b1ba7c8      b800000000     mov eax, 0
|           0x55b75b1ba7cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x55b75b1ba7d2      488d45f4       lea rax, qword [local_ch]
|           0x55b75b1ba7d6      4889c6         mov rsi, rax
|           0x55b75b1ba7d9      488d3d340100.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|           0x55b75b1ba7e0      b800000000     mov eax, 0
|           0x55b75b1ba7e5      e896feffff     call sym.imp.__isoc99_scanf
|       ,=< 0x55b75b1ba7ea      eb4a           jmp 0x55b75b1ba836
|      .--> 0x55b75b1ba7ec      8b45f4         mov eax, dword [local_ch]
|      :|   0x55b75b1ba7ef      85c0           test eax, eax
|     ,===< 0x55b75b1ba7f1      7e0e           jle 0x55b75b1ba801
|     |:|   0x55b75b1ba7f3      488d3d1d0100.  lea rdi, qword str.Positive_num ; 0x55b75b1ba917 ; "Positive num"
|     |:|   0x55b75b1ba7fa      e841feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x55b75b1ba7ff      eb0c           jmp 0x55b75b1ba80d
|    |`---> 0x55b75b1ba801      488d3d1c0100.  lea rdi, qword str.Negative_num ; 0x55b75b1ba924 ; "Negative num"
|    | :|   0x55b75b1ba808      e833feffff     call sym.imp.puts       ; int puts(const char *s)
|    | :|      ; JMP XREF from 0x55b75b1ba7ff (sym.func2)
|    `----> 0x55b75b1ba80d      488d3d240100.  lea rdi, qword str.Enter_another_num__exit_with_0_: ; 0x55b75b1ba938 ; "Enter another num (exit with 0):"
|      :|   0x55b75b1ba814      b800000000     mov eax, 0
|      :|   0x55b75b1ba819      e842feffff     call sym.imp.printf     ; int printf(const char *format)
|      :|   0x55b75b1ba81e      488d45f4       lea rax, qword [local_ch]
|      :|   0x55b75b1ba822      4889c6         mov rsi, rax
|      :|   0x55b75b1ba825      488d3de80000.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|      :|   0x55b75b1ba82c      b800000000     mov eax, 0
|      :|   0x55b75b1ba831      e84afeffff     call sym.imp.__isoc99_scanf
|      :|      ; JMP XREF from 0x55b75b1ba7ea (sym.func2)
|      :`-> 0x55b75b1ba836      8b45f4         mov eax, dword [local_ch]
|      :    0x55b75b1ba839      85c0           test eax, eax
|      `==< 0x55b75b1ba83b      75af           jne 0x55b75b1ba7ec
|           0x55b75b1ba83d      90             nop
|           0x55b75b1ba83e      488b55f8       mov rdx, qword [local_8h]
|           0x55b75b1ba842      644833142528.  xor rdx, qword fs:[0x28]
|       ,=< 0x55b75b1ba84b      7405           je 0x55b75b1ba852
|       |   0x55b75b1ba84d      e8fefdffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       `-> 0x55b75b1ba852      c9             leave
\           0x55b75b1ba853      c3             ret
[0x55b75b1ba7aa]> 

La lógica del programa aquí es bastante similar a los bloques if-else anteriores, en términos generales. La diferencia es que vemos una flecha que vuelve hacia arriba desde la instrucción ubicada en 0x55b75b1ba83b, lo que captura la esencia de un bucle. Vamos paso a paso.

En primer lugar, el programa reserva espacio y, como se puede ver, usa un par de variables: una relacionada con el guarda de pila y la otra probablemente con la entrada del usuario.

ENSAMBLADOR / REFERENCIA
|           ; var int local_ch @ rbp-0xc
|           ; var int local_8h @ rbp-0x8
|              ; CALL XREF from 0x55b75b1ba85d (sym.main)
|           0x55b75b1ba7aa      55             push rbp
|           0x55b75b1ba7ab      4889e5         mov rbp, rsp
|           0x55b75b1ba7ae      4883ec10       sub rsp, 0x10
|           0x55b75b1ba7b2      64488b042528.  mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
|           0x55b75b1ba7bb      488945f8       mov qword [local_8h], rax
|           0x55b75b1ba7bf      31c0           xor eax, eax

El programa solicita la entrada del usuario y la almacena en la variable local_ch.

ENSAMBLADOR / REFERENCIA
|           0x55b75b1ba7cd      e88efeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x55b75b1ba7d2      488d45f4       lea rax, qword [local_ch]
|           0x55b75b1ba7d6      4889c6         mov rsi, rax
|           0x55b75b1ba7d9      488d3d340100.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|           0x55b75b1ba7e0      b800000000     mov eax, 0
|           0x55b75b1ba7e5      e896feffff     call sym.imp.__isoc99_scanf

Podemos incluso renombrar esa variable para que todo sea más legible.

CÓDIGO / SALIDA
[0x55b75b1ba7aa]> afvn local_ch input
[0x55b75b1ba7aa]> afvn
local_8h
input
[0x55b75b1ba7aa]> 

Continuamos. Justo después del scanf, realizamos un salto al final de este bloque de código, así:

ENSAMBLADOR / REFERENCIA
|       ,=< 0x55b75b1ba7ea      eb4a           jmp 0x55b75b1ba836
|      .--> 0x55b75b1ba7ec      8b45f4         mov eax, dword [input]

Y si recordamos, la condición de salida del bucle era que el valor de entrada fuera igual a cero, así que en este caso, si la entrada no es cero, jne se activará y nos llevará de vuelta al inicio del bucle.

ENSAMBLADOR / REFERENCIA
|      :`-> 0x55b75b1ba836      8b45f4         mov eax, dword [input]
|      :    0x55b75b1ba839      85c0           test eax, eax
|      `==< 0x55b75b1ba83b      75af           jne 0x55b75b1ba7ec

El siguiente bloque de código ya lo conocemos.

ENSAMBLADOR / REFERENCIA
|      .--> 0x55b75b1ba7ec      8b45f4         mov eax, dword [input]
|      :|   0x55b75b1ba7ef      85c0           test eax, eax
|     ,===< 0x55b75b1ba7f1      7e0e           jle 0x55b75b1ba801
|     |:|   0x55b75b1ba7f3      488d3d1d0100.  lea rdi, qword str.Positive_num ; 0x55b75b1ba917 ; "Positive num"
|     |:|   0x55b75b1ba7fa      e841feffff     call sym.imp.puts       ; int puts(const char *s)
|    ,====< 0x55b75b1ba7ff      eb0c           jmp 0x55b75b1ba80d
|    |`---> 0x55b75b1ba801      488d3d1c0100.  lea rdi, qword str.Negative_num ; 0x55b75b1ba924 ; "Negative num"
|    | :|   0x55b75b1ba808      e833feffff     call sym.imp.puts       ; int puts(const char *s)
|    | :|      ; JMP XREF from 0x55b75b1ba7ff (sym.func2)

El programa comprueba si la entrada es positiva o negativa mediante test y el salto correspondiente.

Justo después, vuelve a pedir una entrada y comprueba la condición de salida al final del bloque.

ENSAMBLADOR / REFERENCIA
|    `----> 0x55b75b1ba80d      488d3d240100.  lea rdi, qword str.Enter_another_num__exit_with_0_: ; 0x55b75b1ba938 ; "Enter another num (exit with 0):"
|      :|   0x55b75b1ba814      b800000000     mov eax, 0
|      :|   0x55b75b1ba819      e842feffff     call sym.imp.printf     ; int printf(const char *format)
|      :|   0x55b75b1ba81e      488d45f4       lea rax, qword [input]
|      :|   0x55b75b1ba822      4889c6         mov rsi, rax
|      :|   0x55b75b1ba825      488d3de80000.  lea rdi, qword [0x55b75b1ba914] ; "%d"
|      :|   0x55b75b1ba82c      b800000000     mov eax, 0
|      :|   0x55b75b1ba831      e84afeffff     call sym.imp.__isoc99_scanf
|      :|      ; JMP XREF from 0x55b75b1ba7ea (sym.func2)
|      :`-> 0x55b75b1ba836      8b45f4         mov eax, dword [input]
|      :    0x55b75b1ba839      85c0           test eax, eax
|      `==< 0x55b75b1ba83b      75af           jne 0x55b75b1ba7ec

La otra forma habitual de crear bucles es mediante for, como se puede ver aquí:

C
#include <stdio.h>


func2(){

int counter = 0;

for(counter=1; counter <=10; counter++){

        printf("%d ", counter);

}

}

main(){

        func2();
        getchar();

}

Y el desensamblado en radare2 tendrá este aspecto:

ENSAMBLADOR / REFERENCIA
[0x0000068a]> pdf
/ (fcn) sym.func2 59
|   sym.func2 ();
|           ; var int input @ rbp-0x4
|              ; CALL XREF from 0x000006ce (sym.main)
|           0x0000068a      55             push rbp
|           0x0000068b      4889e5         mov rbp, rsp
|           0x0000068e      4883ec10       sub rsp, 0x10
|           0x00000692      c745fc000000.  mov dword [input], 0
|           0x00000699      c745fc010000.  mov dword [input], 1
|       ,=< 0x000006a0      eb1a           jmp 0x6bc
|       |      ; JMP XREF from 0x000006c0 (sym.func2)
|      .--> 0x000006a2      8b45fc         mov eax, dword [input]
|      :|   0x000006a5      89c6           mov esi, eax
|      :|   0x000006a7      488d3db60000.  lea rdi, qword [0x00000764] ; "%d "
|      :|   0x000006ae      b800000000     mov eax, 0
|      :|   0x000006b3      e898feffff     call sym.imp.printf         ; int printf(const char *format)
|      :|   0x000006b8      8345fc01       add dword [input], 1
|      :|      ; JMP XREF from 0x000006a0 (sym.func2)
|      :`-> 0x000006bc      837dfc0a       cmp dword [input], 0xa      ; [0xa:4]=0
|      `==< 0x000006c0      7ee0           jle 0x6a2
|           0x000006c2      90             nop
|           0x000006c3      c9             leave
\           0x000006c4      c3             ret
[0x0000068a]> 

En esta compilación no aparece un canario en la función. La instrumentación depende de las opciones del compilador y de las características de cada función; la presencia de una entrada del usuario, por sí sola, no determina que se inserte una protección de pila.

El programa empieza poniendo el valor a cero (counter = 0) y luego lo pone a 1 para iniciar el for.

ENSAMBLADOR / REFERENCIA
|           0x00000692      c745fc000000.  mov dword [input], 0
|           0x00000699      c745fc010000.  mov dword [input], 1
|       ,=< 0x000006a0      eb1a           jmp 0x6bc

A continuación compara el valor con 10 (0xA).

ENSAMBLADOR / REFERENCIA
|      :`-> 0x000006bc      837dfc0a       cmp dword [input], 0xa      ; [0xa:4]=0
|      `==< 0x000006c0      7ee0           jle 0x6a2

Como el valor es menor, vuelve al inicio para ejecutar el código del bucle.

El código básicamente imprime el valor de la variable y le suma 1.

ENSAMBLADOR / REFERENCIA
|      .--> 0x000006a2      8b45fc         mov eax, dword [input]
|      :|   0x000006a5      89c6           mov esi, eax
|      :|   0x000006a7      488d3db60000.  lea rdi, qword [0x00000764] ; "%d "
|      :|   0x000006ae      b800000000     mov eax, 0
|      :|   0x000006b3      e898feffff     call sym.imp.printf         ; int printf(const char *format)
|      :|   0x000006b8      8345fc01       add dword [input], 1

En la siguiente lección empezaremos a explorar estructuras de datos como arrays y matrices.

Sesión en vídeo / Companion0:38

Interpretar un condicional.

Hermes localiza la condición del ejemplo positive. La comprobación manual permite seguir el salto y las dos rutas dentro de func2.

Captura de una sesión real de Hermes y radare2. Sin audio; esperas abreviadas.Descargar vídeo ↙
Leer la explicación y los comandos

La consulta

En positive, localiza la comparación del entero con cero y el salto condicional. ¿Qué valores siguen cada rama? Apoya la explicación en el desensamblado.

Qué muestra la sesión

El ejemplo lee un entero y muestra un mensaje cuando es positivo. Hermes identifica la función func2 y examina la secuencia que decide si se imprime el mensaje.

En esta compilación, test eax, eax actualiza los flags a partir del entero. El salto jle de 0x4011fb conduce a 0x401207 cuando el valor es cero o negativo. Para un valor positivo, la ejecución llega a puts en 0x401202.

La consola muestra esa misma secuencia y permite comprobar el destino del salto. El vídeo recoge el análisis del flujo; no ejecuta pruebas con distintas entradas.

Comprobación desde la consola

En ./coursectl console, muestra la función que contiene el condicional:

CÓDIGO / SALIDA
pdf @ sym.func2

La segunda grabación utiliza positive, otro ejemplo del laboratorio 3. Puedes abrirlo con ./coursectl chat 3 --example positive. Las direcciones pueden variar al recompilar. Descargar el fuente del vídeo ↙ · Preparar el companion ↗

Práctica manual

Vuelve al binario

Seguir llamadas, distinguir ramas de un switch e identificar el retorno de un bucle. Identifica al menos una instrucción y el dato que utiliza; conserva la compilación con la que lo has observado.

Fuente de esta práctica: switch-fruit.c ↓.

Descargar los 13 ejemplos de fundamentos ↙

Puedes utilizar la consola del companion sin conversar con el agente, o abrir el ejecutable en tu desensamblador habitual.

TERMINAL / COMPANION
./coursectl start 3 --example switch-fruit
./coursectl console

Cierra la sesión anterior con ./coursectl stop antes de cambiar de laboratorio. Ver preparación.

Comprobación personal

Antes de continuar

Explica con tus palabras la operación estudiada y señala las instrucciones o capturas que la justifican. Distingue los datos observados de los nombres y tipos que has reconstruido.

El progreso se guarda en este navegador. Puedes recorrer las lecciones en cualquier orden.

Código original: AGPLv3. Textos y diagramas originales: CC BY-SA 4.0. Reutilización y alcance ↗.

Lobera

Conceptos de reversing

Consulta una definición y continúa donde estabas.