2012-04-26 6 views
12

Ich laufe Linux auf i386: x86_64.Ich habe ein Stück c-Code geschrieben und ich habe es zerlegt sowie die Register gelesen, um zu verstehen, wie das Programm in Assembly funktioniert. Unten ist mein c Programm, das ich geschrieben habe.Warum hat diese Speicheradresse einen zufälligen Wert?

#include <unistd.h> 
#include <string.h> 
#include <stdio.h> 

char *string_in = "Did not work"; 

int test(char *this){ 
    char sum_buf[6]; 
    strncpy(sum_buf,this,32); 
    return 0; 
} 

int hello(){ 
    printf("hello man"); 
    string_in = "If this triggered, it means our shell code is working\n"; 
    while(1){ 
     printf("Worked!"); 
    } 
    return 0; 
} 

int main(int argc, void **argv){ 
    test("\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x28\x06\x06\x40\x00\x00\x00\x00\x00");//6f 73 
    printf("My string is %s",string_in); 
    return 0; 
} 

Das Stück meines Codes, das ich untersucht habe, ist die Testfunktion. Wenn ich die Ausgabe meiner Testfunktion zerlegen ich ...

0x00000000004005b4 <+0>:   push %rbp 
    0x00000000004005b5 <+1>:   mov %rsp,%rbp 
    0x00000000004005b8 <+4>:   sub $0x20,%rsp 
    0x00000000004005bc <+8>:   mov %rdi,-0x18(%rbp) 
    0x00000000004005c0 <+12>:  mov %fs:0x28,%rax 
=> 0x00000000004005c9 <+21>:  mov %rax,-0x8(%rbp) 
    0x00000000004005cd <+25>:  xor %eax,%eax 
    0x00000000004005cf <+27>:  mov -0x18(%rbp),%rcx 
    0x00000000004005d3 <+31>:  lea -0x10(%rbp),%rax 
    0x00000000004005d7 <+35>:  mov $0x20,%edx 
    0x00000000004005dc <+40>:  mov %rcx,%rsi 
    0x00000000004005df <+43>:  mov %rax,%rdi 
    0x00000000004005e2 <+46>:  callq 0x400490 <[email protected]> 
    0x00000000004005e7 <+51>:  mov $0x0,%eax 
    0x00000000004005ec <+56>:  mov -0x8(%rbp),%rdx 
    0x00000000004005f0 <+60>:  xor %fs:0x28,%rdx 
    0x00000000004005f9 <+69>:  je  0x400600 <test+76> 
    0x00000000004005fb <+71>:  callq 0x4004a0 <[email protected]> 
    0x0000000000400600 <+76>:  leaveq 
    0x0000000000400601 <+77>:  retq 

Jetzt liegt mein Interesse an der Linie < 12>. Aus meinem Verständnis heraus erklärt dieser Befehl dem Computer, die ersten 28 Bits des Segmentregisters %fs zu nehmen und in %rax meinen Akkumulator zu legen. Was mich stört, ist, dass vor und nach der Ausführung dieser Zeile das Register %fs über p/x $fs gelesen wird, das einen Wert von Null (sogar während des gesamten Programms) anzeigt und somit %rax Null sein muss. Jedoch zeigt %rax keine Null nach der Ausführung der Anweisung. Tatsächlich ergibt sich eine Zufallszahl. Diese Zufallszahl wird dann 8 Bytes vor %rbp platziert (da es Little Endian ist) und dann erneut überprüft, falls es einen Puffer über den Fluss gibt, der diesen Speicherplatz überschrieben hat.

Was ich gerne wissen würde ist, was mov %fs:0x28,%rax wirklich macht. Habe ich es richtig verstanden? Warum lese ich Null für %fs wenn in p/x $fs und wie lese ich den richtigen Wert?

Antwort

28

Auf x86_64 wird die segmentierte Adressierung nicht mehr verwendet, aber die Register FS und GS können als Basiszeigeradressen verwendet werden, um auf spezielle Betriebssystem-Datenstrukturen zuzugreifen. Was Sie also sehen, ist ein Wert, der mit einem Offset von dem im Register FS enthaltenen Wert geladen wird, und keine Bit-Manipulation des Inhalts des FS Registers.

Speziell was passiert, ist, dass FS:0x28 auf Linux speichert einen speziellen Sentinel-Stack-Guard-Wert, und der Code führt eine Stack-Guard-Prüfung. Wenn Sie zum Beispiel weiter in Ihren Code schauen, sehen Sie, dass der Wert FS:0x28 auf dem Stapel gespeichert ist, und dann wird der Inhalt des Stapels abgerufen und eine XOR wird mit dem ursprünglichen Wert unter FS:0x28 ausgeführt. Wenn die zwei Werte gleich sind, was bedeutet, dass das Null-Bit gesetzt wurde, weil zwei gleiche Werte zu einem Null-Wert führen, springen wir zur Routine test, sonst springen wir zu einer speziellen Funktion, die anzeigt dass der Stapel irgendwie beschädigt war und der auf dem Stapel gespeicherte Sentinel-Wert geändert wurde.

+0

Ok, das macht Sinn. Wie kann ich diese Adresse und den Wert mit gdb lesen? –

+2

Der einfachste Weg ist, den Inhalt des RAX-Registers direkt nach dem MOV-Vorgang zu betrachten. – Jason

1

Mit Blick auf http://www.imada.sdu.dk/Courses/DM18/Litteratur/IntelnATT.htm, ich denke, %fs:28 ist eigentlich ein Offset von 28 Bytes von der Adresse in %fs. Also denke ich, dass es eine volle Registergröße von Ort %fs + 28 in% rax lädt.

+3

Dies ist einfach nicht korrekt. 'fs' ist kein" normales "Register. Es ist ein Segmentregister. Im geschützten Modus ist 'fs' ein * Selektor * in der GDT. Es gibt versteckte "Basis" - und "Limit" -Register, die mit ihm verbunden sind und die Sie nicht sehen können. Also 'fs: 0x28' ist wirklich' [hidden_fs_base + 0x28] '. –