2012-09-27 20 views
7

Analicé los aspectos básicos de las vulnerabilidades de desbordamiento del búfer e intenté comprender cómo funciona la pila. Para eso quería escribir un programa simple que cambia la dirección de la dirección de retorno a algún valor. ¿Alguien puede ayudarme a calcular el tamaño del puntero base para obtener el desplazamiento del primer argumento?Modificar dirección de devolución en la pila

void foo(void) 
{ 
    char ret; 
    char *ptr; 

    ptr = &ret; //add some offset value here 
    *ptr = 0x00; 
} 

int main(int argc, char **argv) 
{ 
    foo(); 

    return 1; 
} 

El código ensamblador generado se ve de la siguiente manera:

.file "test.c" 
    .text 
    .globl foo 
    .type foo, @function 
foo: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    leaq -9(%rbp), %rax 
    movq %rax, -8(%rbp) 
    movq -8(%rbp), %rax 
    movb $0, (%rax) 
    popq %rbp 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size foo, .-foo 
    .globl main 
    .type main, @function 
main: 
.LFB1: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    subq $16, %rsp 
    movl %edi, -4(%rbp) 
    movq %rsi, -16(%rbp) 
    call foo 
    movl $1, %eax 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE1: 
    .size main, .-main 
    .ident "GCC: (GNU) 4.7.1 20120721 (prerelease)" 
    .section .note.GNU-stack,"",@progbits 

La parte pertinente del segmento de marco foo debería tener este aspecto:

[Char RET] [puntero de base] [dirección de retorno ]

Tengo la posición de la primera que tiene solo 1 byte de tamaño. ¿Está solo a 1 byte del puntero base o del tamaño de una palabra como se menciona en http://insecure.org/stf/smashstack.html? ¿Y cómo puedo saber el tamaño del puntero base?

Respuesta

0

Parece que está utilizando una arquitectura de 64 bits, ya que los registros RBP y RSP tienen una longitud de 64 bits. Si declara ptr como char*, tendrá que incrementarlo 8 veces para moverse a través de la pila. En cambio, puede declararlo como uint64_t *. Este tipo de datos normalmente está disponible en <stdint.h>.

Sin embargo, la definición del marco de pila varía según la arquitectura de destino e incluso en el comportamiento y las optimizaciones del compilador. Está bien si estás experimentando, sin embargo.

1

No vas a poder hacer esto en vainilla C, no tienes control de cómo el compilador establece el marco de pila.

En x86-64, la dirección de retorno debe estar en %rbp + 8. Puede usar un ensamblaje en línea para obtener esa sintaxis (gcc):

uint64_t returnaddr; 
asm("mov 8(%%rbp),%0" : "=r"(returnaddr) : :); 

De forma similar para configurarlo.

Incluso eso es un poco incompleto, ya que no se sabe si el compilador va a configurar %rbp o no. YMMV.

+1

+1 Una recompilación siempre podría mover variables. Solo para las pruebas de OP, en este caso particular, la dirección de retorno está en '& ret + 17', que es poco probable que cambie siempre que las variables locales en esa función no cambien. – ughoavgfhw

+0

compensación de 17 obras. ¿Puedes explicar cómo lo determinaste? – fliX

+0

@fliX En el conjunto, la instrucción 'leaq -9 (% rbp),% rax' recibe una dirección en la pila. Como está alineado por bytes, y es el único cálculo de dirección, debe ser donde 'ret' es. Tomando ese 9 y agregando 8 para el puntero base anterior de 64 bits, obtienes 17. – ughoavgfhw

1

Su basepointer es muy probablemente solo un puntero, por lo que tiene el tamaño sizeof (int *). Pero también hay otro valor entre su variable ret y el puntero base. Supongo que su valor de un registro (eax?). Esto conduciría a algo como lo siguiente, si quieres un bucle sin fin:

void foo(void) 
{ 
    char ret; 
    char *ptr; 

    ptr = (char*)(&ret) + (sizeof(ret) + 2*sizeof(int*)) ; 
    *(int*)ptr -= 0x0c; 
} 

El objetivo de rentabilidad que se modifica suponiendo que tiene el tamaño de un puntero (podría diferir para otros conjuntos de instrucciones). Al decrementarlo, el objetivo de retorno se establece en un punto anterior al punto de llamada foo.

Cuestiones relacionadas