如何跳过在C中执行缓冲区溢出的行
我想跳过C中的一行, x=1;
在主要部分中使用bufferoverflow
; 但是,我不知道为什么我不能跳过从4002f4
到下一个地址4002fb
的地址,尽管我计算7个字节从到
。
我还在Debian和AMD环境中配置了randomniZation和execstack环境的选项,但我仍然得到x=1;
。 这个程序有什么问题?
我用dba来调试堆栈和内存地址:
0x00000000004002ef : callq 0x4002a4 **** **0x00000000004002f4** : movl $0x1,-0x4(%rbp) **0x00000000004002fb** : mov -0x4(%rbp),%esi 0x00000000004002fe : mov $0x4629c4,%edi void function(int a, int b, int c) { char buffer[5]; int *ret; ret = buffer + 12; (*ret) += 8; } int main() { int x = 0; function(1, 2, 3); x = 1; printf("x = %i \n", x); return 0; }
你必须阅读Smashing the Stack for Fun and Profit文章。 我正在阅读同一篇文章,并发现同样的问题,它没有跳过该指令。 在IDA中进行了几个小时的调试会话后,我更改了下面的代码,它打印x = 0和b = 5。
#include void function(int a, int b) { int c=0; int* pointer; pointer =&c+2; (*pointer)+=8; } void main() { int x =0; function(1,2); x = 3; int b =5; printf("x=%d\n, b=%d\n",x,b); getch(); }
为了改变function()
的返回地址以跳过main()
的x = 1
,您需要两条信息。
1.堆栈帧中返回地址的位置。
我使用gdb来确定这个值。 我在function()
( break function
)设置断点,执行代码到断点( run
),检索当前堆栈帧的内存位置( p $rbp
或info reg
),然后检索内存中的位置buffer
( p &buffer
)。 使用检索到的值,可以确定返回地址的位置。
(使用GCC -g
标志编译以包含调试符号并在64位环境中执行)
(gdb) break function ... (gdb) run ... (gdb) p $rbp $1 = (void *) 0x7fffffffe270 (gdb) p &buffer $2 = (char (*)[5]) 0x7fffffffe260 (gdb) quit
(帧指针地址+字的大小) – 缓冲区地址=从本地缓冲区变量到返回地址的字节数
( 0x7fffffffe270
+ 8) – 0x7fffffffe260
= 24
如果您在理解调用堆栈的工作方式时遇到困难,那么阅读调用堆栈和函数序言维基百科文章可能会有所帮助。 这显示了在C中制作“缓冲区溢出”示例的困难。缓冲区中的24的偏移假定了某种填充样式和编译选项。 GCC现在很乐意插入堆栈金丝雀,除非你告诉它不要。
2.要跳过x = 1
的返回地址添加的字节数。
在您的情况下,保存的指令指针将指向0x00000000004002f4
( ),这是函数返回后的第一条指令。 要跳过赋值,需要使保存的指令指针指向
0x00000000004002fb
( )。
你的计算是7个字节是正确的( 0x4002fb
– 0x4002fb
= 7 )。
我使用gdb来反汇编应用程序( disas main
)并validation了我的案例的计算方法。 通过检查反汇编,可以手动解决此值。
请注意,我使用Ubuntu 10.10 64位环境来测试以下代码。
#include void function(int a, int b, int c) { char buffer[5]; int *ret; ret = (int *)(buffer + 24); (*ret) += 7; } int main() { int x = 0; function(1, 2, 3); x = 1; printf("x = %i \n", x); return 0; }
产量
x = 0
这实际上只是改变了function()
的返回地址而不是实际的缓冲区溢出。 在实际的缓冲区溢出中,您将溢出buffer[5]
以覆盖返回地址。 但是,大多数现代实现都使用堆栈canaries等技术来防范这种情况。
你在这里做的事情似乎与经典的缓冲区溢出攻击没什么关系。 缓冲区溢出攻击的整个想法是修改“函数”的返回地址。 反汇编程序将显示ret
指令(假设x86)从哪里获取地址。 这是您需要修改以指向main+42
。
我假设你想在这里明确激发缓冲区溢出,通常你需要通过操纵’function’的输入来激发它。
通过声明一个buffer[5]
你正在以错误的方向移动堆栈指针(通过查看生成的程序集来validation这一点),返回地址位于堆栈内部的某个地方(它被调用指令放在那里)。 在x86堆栈中向下增长,即向较低的地址增长。
我通过声明一个int*
并将其向上移动直到我在指定的地址上推进返回地址,然后将该值修改为指向main+42
并让函数ret
。
你不能这样做。 这是一个经典的bufferoverflow代码示例。 看看用键盘输入5个然后6个字符后会发生什么。 如果你去更多(16个字符应该做)你将覆盖基指针,然后函数返回地址,你会得到分段错误。 你想要做的是弄清楚哪4个字符覆盖了返回地址。 并使程序执行您的代码。 谷歌围绕linux堆栈,内存结构。
void ff(){ int a=0; char b[5]; scanf("%s",b); printf("b:%xa:%x\n" ,b ,&a); printf("b:'%s' a:%d\n" ,b ,a); } int main() { ff(); return 0; }