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ビットマシンで実行しています。