linux64 溢出,linux – 64位驻留缓冲区溢出?

我正在研究一些与安全有关的事情,现在我正在玩我自己的堆栈.我正在做的事应该非常简单,我甚至都没有尝试执行堆栈,只是为了表明我可以控制64位系统上的指令指针.我已经关闭了所有我能够使用它的保护机制(NX-bit,ASLR,也用-fno-stack-protector -z execstack编译).

我没有那么多64位汇编的经验,花了一些时间搜索和试验自己后,我想知道是否有人可以解释我正在经历的问题.

我有一个程序(下面的源代码),它只是将一个字符串复制到一个没有边界检查的堆栈驻留缓冲区.但是,当我用一系列0x41覆盖时,我希望看到RIP设置为0x4141414141414141,而我发现我的RBP设置为此值.我确实遇到了分段错误,但是在执行RET指令时RIP不会更新为此(非法)值,即使RSP设置为合法值也是如此.我甚至在GDB中验证了在RET指令之前有可读存储器在RSP处包含一系列0x41.

我的印象是LEAVE指令:

MOV(E)SP,(E)BP

POP(E)BP

但是在64位上,“LEAVEQ”指令似乎(类似):

MOV RBP,QWORD PTR [RSP]

我认为这只是通过在执行该指令之前和之后观察所有寄存器的内容来实现的. LEAVEQ似乎只是RET指令的上下文相关名称(GDB的反汇编程序给它),因为它仍然只是一个0xC9.

RET指令似乎与RBP寄存器有关,可能是解除引用它?我认为RET确实(类似):

MOV RIP,QWORD PTR [RSP]

但是就像我提到的那样,似乎取消引用RBP,我认为这样做是因为当没有其他寄存器似乎包含非法值时,我得到了分段错误.

该计划的源代码:

#include

#include

int vuln_function(int argc,char *argv[])

{

char buffer[512];

for(int i = 0; i < 512; i++) {

buffer[i] = 0x42;

}

printf("The buffer is at %p\n",buffer);

if(argc > 1) {

strcpy(buffer,argv[1]);

}

return 0;

}

int main(int argc,char *argv[])

{

vuln_function(argc,argv);

return 0;

}

for循环只是用0x42填充缓冲区的合法部分,这使得在溢出之前很容易在调试器中看到它.

调试会话的摘录如下:

(gdb) disas vulnerable

Dump of assembler code for function vulnerable:

0x000000000040056c : push rbp

0x000000000040056d : mov rbp,rsp

0x0000000000400570 : sub rsp,0x220

0x0000000000400577 : mov DWORD PTR [rbp-0x214],edi

0x000000000040057d : mov QWORD PTR [rbp-0x220],rsi

0x0000000000400584 : mov DWORD PTR [rbp-0x4],0x0

0x000000000040058b : jmp 0x40059e

0x000000000040058d : mov eax,DWORD PTR [rbp-0x4]

0x0000000000400590 : cdqe

0x0000000000400592 : mov BYTE PTR [rbp+rax*1-0x210],0x42

0x000000000040059a : add DWORD PTR [rbp-0x4],0x1

0x000000000040059e : cmp DWORD PTR [rbp-0x4],0x1ff

0x00000000004005a5 : jle 0x40058d

0x00000000004005a7 : lea rax,[rbp-0x210]

0x00000000004005ae : mov rsi,rax

0x00000000004005b1 : mov edi,0x40070c

0x00000000004005b6 : mov eax,0x0

0x00000000004005bb : call 0x4003d8

0x00000000004005c0 : cmp DWORD PTR [rbp-0x214],0x1

0x00000000004005c7 : jle 0x4005e9

0x00000000004005c9 : mov rax,QWORD PTR [rbp-0x220]

0x00000000004005d0 : add rax,0x8

0x00000000004005d4 : mov rdx,QWORD PTR [rax]

0x00000000004005d7 : lea rax,[rbp-0x210]

0x00000000004005de : mov rsi,rdx

0x00000000004005e1 : mov rdi,rax

0x00000000004005e4 : call 0x4003f8

0x00000000004005e9 : mov eax,0x0

0x00000000004005ee : leave

0x00000000004005ef : ret

我在调用strcpy()之前就断开了,但在缓冲区填充了0x42之后.

(gdb) break *0x00000000004005e1

该程序以650 0x41为参数执行,这应该足以覆盖堆栈上的返回地址.

(gdb) run `perl -e 'print "A"x650'`

我在内存中搜索返回地址0x00400610(我从查看main的反汇编中找到).

(gdb) find $rsp, +1024, 0x00400610

0x7fffffffda98

1 pattern found.

我用x / 200x检查内存并得到一个很好的概述,由于它的大小,我在这里省略了,但是我可以清楚地看到0x42表示缓冲区的合法大小和返回地址.

0x7fffffffda90: 0xffffdab0 0x00007fff 0x00400610 0x00000000

strcpy()之后的新断点:

(gdb) break *0x00000000004005e9

(gdb) set disassemble-next-line on

(gdb) si

19 }

=> 0x00000000004005ee : c9 leave

0x00000000004005ef : c3 ret

(gdb) i r

rax 0x0 0

rbx 0x0 0

rcx 0x4141414141414141 4702111234474983745

rdx 0x414141 4276545

rsi 0x7fffffffe17a 140737488347514

rdi 0x7fffffffdb00 140737488345856

rbp 0x7fffffffda90 0x7fffffffda90

rsp 0x7fffffffd870 0x7fffffffd870

r8 0x1 1

r9 0x270 624

r10 0x6 6

r11 0x7ffff7b9fff0 140737349550064

r12 0x400410 4195344

r13 0x7fffffffdb90 140737488346000

r14 0x0 0

r15 0x0 0

rip 0x4005ee 0x4005ee

0x00000000004005ee : c9 leave

=> 0x00000000004005ef : c3 ret

(gdb) i r

rax 0x0 0

rbx 0x0 0

rcx 0x4141414141414141 4702111234474983745

rdx 0x414141 4276545

rsi 0x7fffffffe17a 140737488347514

rdi 0x7fffffffdb00 140737488345856

rbp 0x4141414141414141 0x4141414141414141

rsp 0x7fffffffda98 0x7fffffffda98

r8 0x1 1

r9 0x270 624

r10 0x6 6

r11 0x7ffff7b9fff0 140737349550064

r12 0x400410 4195344

r13 0x7fffffffdb90 140737488346000

r14 0x0 0

r15 0x0 0

rip 0x4005ef 0x4005ef

(gdb) si

Program received signal SIGSEGV, Segmentation fault.

0x00000000004005ee : c9 leave

=> 0x00000000004005ef : c3 ret

(gdb) i r

rax 0x0 0

rbx 0x0 0

rcx 0x4141414141414141 4702111234474983745

rdx 0x414141 4276545

rsi 0x7fffffffe17a 140737488347514

rdi 0x7fffffffdb00 140737488345856

rbp 0x4141414141414141 0x4141414141414141

rsp 0x7fffffffda98 0x7fffffffda98

r8 0x1 1

r9 0x270 624

r10 0x6 6

r11 0x7ffff7b9fff0 140737349550064

r12 0x400410 4195344

r13 0x7fffffffdb90 140737488346000

r14 0x0 0

r15 0x0 0

rip 0x4005ef 0x4005ef

我验证返回地址已被覆盖,我应该看到RIP设置为此地址:

(gdb) x/4x 0x7fffffffda90

0x7fffffffda90: 0x41414141 0x41414141 0x41414141 0x41414141

(gdb) x/4x $rsp

0x7fffffffda98: 0x41414141 0x41414141 0x41414141 0x41414141

RIP显然是:

rip 0x4005ef 0x4005ef

为什么RIP没有像我期待的那样更新? LEAVEQ和RETQ在64位上真正做了什么?总之,我在这里缺少什么?我在编译时试图省略编译器参数,看它是否有任何区别,它似乎没有任何区别.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值