2012-04-26 9 views
12

私はi386:x86_64上でlinuxを実行しています。私はCコードを書いています。また、逆アセンブルしてレジスタを読み込んで、プログラムがアセンブリでどのように動作するかを理解しています。以下は、私が書いた私のCプログラムです。このメモリアドレスがランダムな値を持つのはなぜですか?

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

私が調べてきたコードの一部がテスト機能です。私は私が手出力私のテスト機能を分解すると...

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 

は今、私の関心は、ライン< 12にあります>。私の理解から、命令はセグメントレジスタの最初の28ビットを取るようにコンピュータに指示しています%fs%rax私のアキュムレータに配置します。私が気になるのは、この行を実行する前後に、%fsを介してp/x $fsを読み込みます。この値は0(プログラム全体でさえ)の値を示し、したがって%raxはゼロでなければなりません。しかし、%raxは、命令が実行された後にゼロを示さない。実際には結果は乱数です。この乱数は%rbpの前に8バイト配置され(リトルエンディアンであるため)、その領域を上書きしたバッファオーバーフローがある場合に再度チェックされます。

私が知りたいのは、mov %fs:0x28,%raxが実際にやっていることです。私はそれを正しく理解しましたか? p/x $fsにある%fsの値が0で、正しい値を読み取る方法は何ですか?

答えて

28

x86_64では、セグメント化されたアドレシングは使用されなくなりましたが、FSととレジスタの両方は、特殊なオペレーティングシステムのデータ構造にアクセスするためにベースポインタアドレスとして使用できます。だからあなたが見ているのは、FSレジスタに保持されている値からのオフセットでロードされた値であり、FSレジスタの内容のビット操作ではありません。

具体的に何が起こっているのですか?LinuxのFS:0x28に特別なセンチネルスタックガード値が保存されていて、コードがスタックガードチェックを実行していますか?たとえば、コードをさらに見ると、FS:0x28の値がスタックに格納され、次にスタックの内容が呼び出され、元の値FS:0x28XORが実行されることがわかります。 2つの値が等しい場合は、同じ値の2つがゼロ値になるので0ビットが設定されていることを意味します。そうでない場合は、testルーチンにジャンプします。そうでない場合は、スタックが何らかの形で破損し、スタックに保存されているセンチネル値が変更されたことを示します。

+0

これは意味があります。このアドレスと値をgdbでどのように読み取ることができますか? –

+2

最も簡単な方法は、MOV操作の直後にRAXレジスタの内容を調べることです。 – Jason

1

%fs:28は、実際には%fsのアドレスから28バイトのオフセットであると思います。だから私はそれが%raxに場所%fs + 28から完全なレジスタサイズをロードしていると思います。

+3

これは正しくありません。 fsは通常のレジスタではありません。これはセグメントレジスタです。保護モードでは、 'fs'はGDTの* selector *です。表示されない隠れた「ベース」と「リミット」レジスタが関連付けられています。だから 'fs:0x28'は本当に' [hidden_​​fs_base + 0x28] 'です。 –