0
void function(int a, int b, int c)
{    char buffer[1];
     int *ret;
     ret = buffer + 20;
     (*ret) += 7;
}
void main()
{    int x;
    x = 0;
    function(1, 2, 3);
    x = 1;
    printf("%d\n", x);
}

このプログラムでやりたいことは、プログラムの結果がメインの x の値として "1" ではなく "0" を出力するように、リターン レジスタ アドレスを書き換えることです。

しかし、リターン アドレス (バッファーのオフセットとリターン アドレスのオフセット) を見つけることができませんでした。

アセンブリ言語の一部を次に示します。

function:
.LFB0:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movl    %edi, -20(%rbp)
movl    %esi, -24(%rbp)
movl    %edx, -28(%rbp)
leaq    -9(%rbp), %rax
addq    $20, %rax
movq    %rax, -8(%rbp)
movq    -8(%rbp), %rax
movl    (%rax), %eax
leal    7(%rax), %edx
movq    -8(%rbp), %rax
movl    %edx, (%rax)
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc

と、

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    $0, -4(%rbp)
movl    $3, %edx
movl    $2, %esi
movl    $1, %edi
call    function
movl    $1, -4(%rbp)
movl    $.LC0, %eax
movl    -4(%rbp), %edx
movl    %edx, %esi
movq    %rax, %rdi
movl    $0, %eax
call    printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

コンパイル後、gdb を使用して戻りアドレスを調べます

   0x000000000040054e <+0>: push   %rbp
0x000000000040054f <+1>:    mov    %rsp,%rbp
0x0000000000400552 <+4>:    sub    $0x10,%rsp
0x0000000000400556 <+8>:    movl   $0x0,-0x4(%rbp)
0x000000000040055d <+15>:   mov    $0x3,%edx
0x0000000000400562 <+20>:   mov    $0x2,%esi
0x0000000000400567 <+25>:   mov    $0x1,%edi
0x000000000040056c <+30>:   callq  0x400524 <function>
0x0000000000400571 <+35>:   movl   $0x1,-0x4(%rbp)
0x0000000000400578 <+42>:   mov    $0x400694,%eax
0x000000000040057d <+47>:   mov    -0x4(%rbp),%edx
0x0000000000400580 <+50>:   mov    %edx,%esi
0x0000000000400582 <+52>:   mov    %rax,%rdi
0x0000000000400585 <+55>:   mov    $0x0,%eax
0x000000000040058a <+60>:   callq  0x400418 <printf@plt>
0x000000000040058f <+65>:   leaveq 
0x0000000000400590 <+66>:   retq   

だから「20」と「7」を選んでいます。しかし、「セグメントエラー」と表示されてうまくいきません。私は多くの番号を試しましたが、「バスエラー」になることがあります。なぜ、どのようにしてそれを見つけることができるのかわかりません。64ビットマシンで実行しています。

4

2 に答える 2

1

私のコメントでコードを見てください:

function:
pushq   %rbp ; save rbp on the stack
movq    %rsp, %rbp ; rbp = address of previous value of rbp on the stack
movl    %edi, -20(%rbp)
movl    %esi, -24(%rbp)
movl    %edx, -28(%rbp)
leaq    -9(%rbp), %rax ; rax = address of buffer = rbp - 9
addq    $20, %rax ; rax = buffer + 20 = rbp - 9 + 20 = rbp + 11
movq    %rax, -8(%rbp)
movq    -8(%rbp), %rax
movl    (%rax), %eax ; read 4 bytes from address rbp + 11, WHY 4? 8-byte pointers!
leal    7(%rax), %edx ; add 7 to those
movq    -8(%rbp), %rax
movl    %edx, (%rax) ; store the sub back
popq    %rbp ; restore rbp from the stack
ret ; return

最後の 2 つの命令 (pop と ret) は、スタックから 2 つの項目 (以前の/保存された rbp の値と戻りアドレス) をポップすることに注意してください。

これは、戻りアドレスがスタックに保存された rbp 値のすぐ上、または rbp + 8 であることを意味します。

しかし、rbp + 11 でメモリを変更しています。

さらに、すべてのポインターが 64 ビット (8 バイト) であるのに対し、4 バイトのみを変更しています。

だから、あなたは変える必要があります

int *ret;

unsigned long long *ret;

次に、定数を修正します。したがって、rbp + 11 または rbp + 何かではなく、アドレス rbp + 8 で 8 バイトを変更します。

ロケット科学?いいえ。コードを読んで、ステップ実行して、何が起こるか見て、考えてみてください。あなたはデバッガを手に入れました!

于 2013-01-21T06:41:36.613 に答える
0

Visual Studio を使用している場合は、コンパイラ組み込み関数の呼び出しを検討してください

void* _ReturnAddress(void);
#pragma intrinsic(_ReturnAddress);

function main()
{
    void* ret = _ReturnAddress();
    printf("%p", ret);
}
于 2013-01-22T06:05:08.370 に答える