2013-01-21 123 views
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); 
} 

在这个程序中,我想要做的是重写返回寄存器地址,以便程序的结果是打印“0”而不是“1”作为值主要的x。如何找出ret地址

但我没有找到返回地址 - 缓冲区的偏移量和返回地址的偏移量。

下面是汇编语言的一部分:

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 <[email protected]> 
0x000000000040058f <+65>: leaveq 
0x0000000000400590 <+66>: retq 

也就是说为什么我选择“20”和“7”。但它不起作用,它说“段错误”。我尝试过很多数字,有时会出现“总线错误”。我不知道为什么以及如何找到它。我在64位机器上运行它。

回答

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 

注意,最后两个指令(POP和RET)从弹出堆栈两个项目,RBP的一个/保存的值和返回地址。

这意味着该返回地址是正确的堆栈上的保存RBP值以上时,或在RBP + 8.

但你在RBP修改存储器+ 11

此外,你”只修改4个字节,而所有指针都是64位(8字节)。

所以,你需要改变

int *ret; 

unsigned long long *ret; 

然后纠正不变,所以你修改地址RBP + 8 8个字节,而不是在RBP + 11或RBP +别的东西。

火箭科学?不。只要阅读代码,一步一步,看看会发生什么,并思考。你有调试器!

+0

非常感谢!我还有更多关于它的问题。什么是movl%rax,-8(%rbp)和movl -8(%rbp),%rax是做什么的? – stcheng

+0

在rxp-8的rax和内存之间移动4个字节。阅读装配教程和CPU手册。' –

0

如果你正在使用Visual Studio,考虑调用编译器内在

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

function main() 
{ 
    void* ret = _ReturnAddress(); 
    printf("%p", ret); 
}