CSAPP lab2 二进制拆弹 binary bombs phase_2

 

给出对应于7个阶段的7篇博客

phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.html
phase_2  https://www.cnblogs.com/wkfvawl/p/10636214.html
phase_3  https://www.cnblogs.com/wkfvawl/p/10651205.html
phase_4  https://www.cnblogs.com/wkfvawl/p/10672680.html
phase_5  https://www.cnblogs.com/wkfvawl/p/10703941.html
phase_6  https://www.cnblogs.com/wkfvawl/p/10742405.html
secret_phase  https://www.cnblogs.com/wkfvawl/p/10745307.html

 phase_2

phase_2要求输入包含6个整数的字符串。phase_2函数从中读取6个整数,并判断其正确性,如果不正确,则炸弹爆炸。phase_2主要考察学生对C语言循环的机器级表示的掌握程度。

观察框架源文件bomb.c:

从上可以看出:

1、首先调用了read_line()函数,用于输入炸弹秘钥,输入放置在char* input中。

2、调用phase_2函数,输入参数即为input,可以初步判断,phase_2函数将输入的input字符串作为参数。

因此下一步的主要任务是从asm.txt中查找在哪个地方调用了readline函数以及phase_2函数。

1.1 寻找并分析调用phase_2函数的代码

打开asm.txt,寻找phase_2函数。

 

phase_1类似分析:

1、当前栈的位置存放的是read_line函数读入的一串输入;

2、phase_2的函数入口地址为0x8048c24

此时的函数栈为:

1.2 phase_2函数分析

寻找8048c24,或者继续寻找phase_2,可以寻找到phase_2函数,如下图所示:

 

分析上面的代码:

1390 ~ 392行:进行一些压栈,并扩展了函数栈帧。

2、第394-395行:lea 0x18(%esp) %eaxmov %eax 4(%esp),将esp + 18指向的栈的内容的地址放置到esp+4指向的地方。简单的说,当前esp + 4指针指向的空间的内容为esp + 18。(实际上,根据后面的分析,可以知道esp + 4的内容,放的是num[0]的地址esp + 18

3、第396行:将0x40(%esp)的内容放置到esp指向的栈。0x40(%esp)里面的内容实际上就是input字符串首地址。

4、第397行:调用了read_six_numbers函数(顾名思义,从字符串中解析出六个整数),可以猜测实际上第394行到第396行,是在为read_six_numbers函数准备参数。

5、在调用read_six_numbers之前,函数栈帧为:

 

7、上图所示的函数栈帧中,从esp + 18 ~ esp+2c,共6个栈空间,标记为保存6个整数,实际上从当前的地方并不能完全看出来,可以有些猜测,到后来阅读read_six_numbers时,证实了当前的猜测是正确的。

8、依据以上的分析,read_six_numbers函数的定义:void read_six_numbers(char* input, num);其中第二个参数,是num数组的地址。在后面,会剖析read_six_numbers函数,来证实以上的猜测,下面的分析以以上的栈帧图为基础。

9、第399行:cmp $0x1, 0x18(%esp)0x18(%esp)中是num[0],该语句判断num[0]是否应等于1,如果等,则跳转到phase_2 + 0x3e(第400行),如果不等,则call explode_bomb(第401行),从此处,可以猜测:num[0]  = 1

10、第412行(8048c62phase_2 + 0x3e)),将0x1c + esp --> ebx寄存器,即将num[1]的地址送入到ebx寄存器,第413行,将0x30 + esp -->esi0x30(%esp)num[5]上面的栈空间,将该栈空间的地址送入到esi

11、第415行:跳转到8048c4b(即第403行)。

12、第403行:将-0x4%ebx)的内容送入到eax-0x4%ebx)的内容实际上指的是0x18(%esp),也即num[0]送入到eax

13、第404行:eax = eax + eax,即: 2 * num[0];

14、第405行:比较ebx所指的地址的内容和eax的内容,据前面分析,当前ebx的内容即为num[1]的地址。

15、第406行:如果相等,则跳转到8048c59

16、第408行(8048c59):ebx += 4,当前ebxnum[1]的地址,加4之后,正好是num[2]的地址。

17、第409行:ebxesinum[5]之上的地址)比较,如果不等则跳转到8048c4b(第403行),继续从前面第11继续开始。如果相等,则跳转到8048c6c(第415行),退出函数。实际上如果ebxesi相等,说明前面已经处理完了num[5],也即处理完了第6个数。如果不等,则说明num[5]没有处理,继续循环。

18、总结前面的分析,以上显然是一个循环表示的机器级表示的处理过程,从上面的分析来看:

1num[0] = 1;

2num[i] = 2 * num[i-1]。(i > 0

因此,phase_2炸弹秘钥应该是:1 2 4 8 16 32。

以上所有的分析是建立在六个输入数字是放置在esp + 0x18开始的地址中的前提下的。为确认这一个问题,下面对read_six_numbers函数进行详细分析。

1.3 read_six_numbers分析

根据前面分析,read_six_numbers的入口地址为80493da,如下图所示:

 

1、第996行:扩展栈帧,增加了44

 2、第997行:将0x34%esp)的内容送到eax0x34%esp)的内容正好是num[0]的地址,也即num的首地址,也即eax内容为num[0]的地址。(参见后面的栈帧图)

3、第998行:将eax + 0x14地址(即为eax + 0x14送到edxeax+0x14正好是num[5]的地址。(参见后面栈帧图)

4、第999行:将edx的内容送到esp + 0x1c的地方,即将num[5]的地址送到esp+0x1c的地方;

5、第1000~1008行:

1num[4]地址,送到esp + 0x18

2num[3]地址,送到esp + 0x14

3num[2]地址,送到esp + 0x10

4num[1]地址,送到esp + 0xc

6、第1009行:num[0]地址,送到esp + 8

7、第1010行:0x804a725送入到esp + 4的地方

8、第1011/1012行:0x30%esp)内容送入到esp0x30%esp)内容为input输入首地址。

9、第1013行:调用scanf函数,用于从input中读入6个整数。可以认为前面都是在为scanf函数调用准备参数,包括第1009行,0x804a67d实际上是指向一个字符串的首地址,这个字符串为“%d %d %d %d %d %d”(这点将在后面分析),因此,我们可以判断scanf的函数定义/使用为:scanf(input, "%d %d %d %d %d %d", &num[0],  &num[1], &num[2], &num[3], &num[4], &num[5],); 返回的是读取的整数的个数。

10、此时的栈帧为:

 

11、第1014行:eax的值与5比较,eax应该是scanf函数返回的输入数字的个数;

12、第1015行:如果大于5,则函数正确返回;

13、第1016行:如果小于等于5,则引爆炸弹。

14.为了查看0x804a67d地址的内容,可以使用objdump --start-address= 0x804a67d -s  bomb命令查看,如下图所示:

 

原文地址:https://www.cnblogs.com/wkfvawl/p/10636214.html