2012-09-27 17 views
7

Ich habe die Grundlagen der Pufferüberlauf-Schwachstellen untersucht und versucht zu verstehen, wie der Stack funktioniert. Dazu wollte ich ein einfaches Programm schreiben, das die Adresse der Rückadresse auf einen Wert ändert. Kann mir jemand helfen, die Größe des Basiszeigers herauszufinden, um den Offset vom ersten Argument zu bekommen?Ändern der Absenderadresse auf dem Stack

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; 
} 

Der erzeugte Assembler-Code sieht wie folgt aus:

.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 

Der relevante Teil des foo Rahmensegments sollte wie folgt aussehen:

[char ret] [Basiszeiger] [Rückkehradresse ]

Ich habe die Position der ersten, die nur 1 Byte groß ist. Ist es nur 1 Byte weiter als der Basiszeiger oder die Größe eines Wortes wie in http://insecure.org/stf/smashstack.html erwähnt? Und wie kann ich die Größe des Basiszeigers kennenlernen?

Antwort

0

Es scheint, dass Sie eine 64-Bit-Architektur verwenden, da RBP- und RSP-Register 64 Bit lang sind. Wenn Sie ptr als char* deklarieren, müssen Sie es 8 mal inkrementieren, um durch den Stapel zu gehen. Stattdessen können Sie es als uint64_t * deklarieren. Dieser Datentyp ist normalerweise in <stdint.h> verfügbar.

Die Stapelrahmendefinition variiert jedoch abhängig von der Zielarchitektur und sogar vom Compilerverhalten und den Optimierungen. Es ist in Ordnung, wenn Sie experimentieren.

1

Sie werden nicht in der Lage sein, dies in Vanille C zu tun, Sie haben keine Kontrolle darüber, wie der Compiler den Stack-Frame auslegt.

In x86-64 sollte die Absenderadresse %rbp + 8 sein. Sie könnten eine Inline-Assembly verwenden, um das zu erhalten (gcc-Syntax):

Ähnlich für die Einstellung.

Auch das ist ein bisschen skizzenhaft, da Sie nicht wissen, ob der Compiler %rbp oder nicht einrichten wird. YMMV.

+1

+1 Eine Neukompilierung könnte immer Variablen verschieben. Nur für das Testen des OPs ist in diesem speziellen Fall die Rückkehradresse bei & ret + 17, was sich kaum ändern wird, solange sich die lokalen Variablen in dieser Funktion nicht ändern. – ughoavgfhw

+0

Offset von 17 arbeitet. Kannst du erklären, wie du es bestimmt hast? – fliX

+0

@fliX In der Assembly erhält die Anweisung 'leaq -9 (% rbp),% rax 'eine Adresse auf dem Stack. Da es Byte-ausgerichtet ist und die einzige Adressenberechnung ist, muss dies "ret" sein. Nimmt man diese 9 und addiert 8 für den vorherigen 64-Bit-Basiszeiger, erhält man 17. – ughoavgfhw

1

Ihr Basiszeiger ist höchstwahrscheinlich nur ein Zeiger, also hat er die Größe sizeof (int *). Aber es gibt auch einen anderen Wert zwischen Ihrer Variablen ret und dem Basiszeiger. Ich würde seinen Wert eines Registers annehmen (eax?). Dies würde wie die folgenden zu etwas führen, wenn Sie eine Endlosschleife wollen:

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

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

Das Renditeziel, das modifiziert wird vorausgesetzt, es ist die Größe eines Zeigers hat (könnte auch für andere Befehlssätze unterscheiden). Durch Dekrementieren wird das Rückkehrziel auf einen Punkt vor dem Aufrufpunkt foo gesetzt.

Verwandte Themen