2012-09-27 6 views
7

バッファオーバーフローの脆弱性の基礎を見て、スタックの動作を理解しようとしました。そのためには、戻り値のアドレスをある値に変更する簡単なプログラムを作成したかったのです。誰も私が最初の引数からオフセットを得るためにベースポインタのサイズを計算するのを助けることができますか?次のようにスタック上のリターンアドレスを変更する

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

生成されたアセンブラコードが見える:

.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 

FOOフレームセグメントの関連部分は次のようになります

を[チャーRET] [ベースポインタ] [リターンアドレス]

私は1バイトだけの最初の位置を持っています。 http://insecure.org/stf/smashstack.htmlに記載されているように、ベースポインタまたはワードのサイズに1バイトだけ進んでいますか?そして、どのようにしてベースポインタのサイズを知ることができますか?

答えて

0

RBPとRSPのレジスタは64ビットなので、64ビットアーキテクチャを使用しているようです。 ptrchar*と宣言した場合は、スタックを移動するために8回インクリメントする必要があります。 代わりにuint64_t *と宣言できます。このデータ型は通常<stdint.h>で利用できます。

ただし、スタックフレームの定義は、ターゲットアーキテクチャやコンパイラの動作や最適化によっても異なります。あなたが実験しているなら、それはいいですね。

1

バニラCでこれを行うことはできませんが、コンパイラがスタックフレームをレイアウトする方法を制御することはできません。

x86-64の場合、返信先アドレスは%rbp + 8である必要があります。いくつかのインラインアセンブリを使用して(gcc構文):

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

同様に設定することができます。

コンパイラが%rbpを設定するかどうかわからないので、それは少しスケッチです。 YMMV。

+1

+1再コンパイルでは常に変数を移動できます。 OPのテストのためだけに、この特定のケースでは、戻りアドレスは '&ret + 17'にあります。これは、その関数のローカル変数が変更されない限りは変更されません。 – ughoavgfhw

+0

オフセットは17です。あなたはそれをどう決めたのか説明できますか? – fliX

+0

@fliXアセンブリでは、 'leaq -9(%rbp)、%rax'命令がスタック上のアドレスを取得しています。それはバイト整列であり、唯一のアドレス計算であるため、 'ret'があるはずです。その9を取って、前の64ビットのベースポインタに8を加えれば、17が得られます。 – ughoavgfhw

1

あなたのベースポインタはたぶんポインタなので、サイズはsizeof(int *)です。 しかし、変数retと基本ポインタの間には別の値もあります。 私はそのレジスタ(eax?)の値を想定します。

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

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

それはポインタのサイズを持っていると仮定すると変更されたリターンの目標(他の命令セットのために異なる可能性があります):あなたは無限ループをしたい場合、これは、次のようなものをもたらすことになります。それをデクリメントすることにより、復帰目標は、発呼点の前の点に設定され、fooとなります。

関連する問題