CSAPP lab2 二进制拆弹 binary bombs phase_6(施工中。。。。。。)

phase_6

phase_6要求输入6个1~6的数,这6个数不能重复。phase_6根据用户的输入,将某个链表按照用户的输入的值(进行某种计算后)进行排序,如果最终能排成升序,则解题成功。

phase_6主要考察学生对C语言指针、链表以及结构的机器级表示的掌握程度。

观察框架源文件bomb.c:

 

从上可以看出:

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

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

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

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

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

 

phase_1类似分析:

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

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

此时的函数栈为:

1.2 phase_6分析

asm.txt中继续寻找phase_6,或者寻找8048f12,找到phase_6函数入口:

 

1612-619行:初始化函数栈帧,然后调用read_six_numbers函数。调用之后,从input中读取了6个数num[0]  ~  num[5]read_siz_numbers函数分析参见前面),位于esp+0x10  ~esp+0x24,此时函数栈帧如下图所示:

 

2620行:0  --> esi

3621-625行:判断(esp + esi*4 + 0x10)是否小于等于6以及大于等于1,如果不满足,则引爆炸弹(625行);也即输入的数(esi=0时,为num[0]esi=1时,为num[1]......)应该大于等于1,同时小于等于6。注意,比较时,先将该值减1622行),然后与5进行比较(623行),比较时用的jbe无符号整数比较),也即,如果该输入值小于1,减1之后变成一个很大的无符号数(负数),肯定是大于5的。因此这几行就实现了判断num[esi] >=1 && num[esi] <=6。(这应该是编译器做的优化

4626行:esi += 1

5627-628行:esi6进行比较,如果等于,则意味着6个数已经比较完毕,跳转到0x8048f64<phase_6 + 0x52>

6629行:如果628行没有跳转,也即6个数还没有判断完毕,则继续执行,将esi赋值给ebx

7630-633行:判断num[ebx]是否与num[esi-1]相等,如果相等,则引爆炸弹;

8634-636行:ebx+=1,然后判断ebx是否小于等于5,如果是,则跳转到0x8048f4b,即630行,也即跳转到第7步。

9637行:跳转到8048f48,进行num[esi]的比较(注意num[esi]在第626行加1

10、综合以上分析,可以判断出以上代码的作用是:

1)判断每个输入的数应小于等于6,大于等于1

2num[i]不等于它的后续的每个数;

3)也即输入的6个数,应是1/2/3/4/5/6,但顺序不一定。

使用类c语言描述:

for (i = 0; i < 6; i++) {

if ((num[i] < 1) || (num[i] > 6))

explode_bomb();

for (j = i + 1; j < 6; j++) {

if (num[i] == num[j])

explode_bomb();

}

    }

11638行(0x8048f64<phase_6+0x52>):如果第5步判断所有的数都判断完毕,则说明所有的输入的数都符合条件,则从此处继续执行。

12638-646行:对于每个输入的数,用7减去该值,即:

for(i = 0; i < 6; i ++){

num[i] = 7- num[i];

}

13647-648行:ebx = 0;然后跳转到0x8048fa2<phase_6+0x90>

14659行(0x8048fa2<phase_6+0x90>- 662行:ebx保存到esimov %ebx, %esi),将esp + ebx*4 + 0x10的内容(当ebx=0时,为num[0],当ebx=1时,为num[1]......)与1相比较,如果 esp + ebx*4 + 0x10 <= 1661行),则跳转到0x8048f91<phase_6+0x7f>662行),否则继续执行663行。(根据前面分析,ecx为输入的num的值,仅且仅当ecx=1时,执行这个662行跳转语句,跳转到0x8048f91<phase_6+0x7f>)。

15654行(0x8048f91<phase_6+0x7f>):当ecx=1时,会执行该条语句,将0x8048f91送入到edx。查看0x804c174地址的内容(objdump --start-address=0x804c174  -s bomb):

16655行:将edx内容送入到esp + esi*4 + 0x28

17656-658行:ebx += 1,然后与6相比较,如果等于6,则跳转到0x8048fb9

18、如果不等于6,则继续执行659行,对于本文,即跳转到第14步,前面分析了ecx=1的情况,如果ecx不等于1,则应继续执行663-665

19663-665行:eax赋值为1edx赋值为0x804c174,跳转到8048f85 <phase_6+0x73>。(第649行)

20649-652行:这是一个循环。判断ecxnum[ebx])是否等于eax,如果不是,则将edx + 8的内容送入到edx,然后继续判断, edx +8的内容应该是指向的是一个地址。如果相等,则跳转到8048f96 <phase_6+0x84>(从第655行继续执行)

21、根据前面的分析,13~20步的代码,是根据处理后的num值(参见第10步分析),将相关信息压栈(从esp+28开始压栈):(注意:IA32是小端方式)

1)当num[i] == 1时,将0x804c174压入到esp + 0x28 + i * 4

2)当num[i] == 2时,将0x 804c180压入到esp + 0x28 + i * 4

3)当num[i] == 3时,将0x 804c18c压入到esp + 0x28 + i * 4

4)当num[i] == 4时,将0x804c198压入到esp + 0x28 + i * 4

5)当num[i] == 5时,将0x 804c1a4压入到esp + 0x28 + i * 4

6)当num[i] == 6时,将0x 804c1b0压入到esp + 0x28 + i * 4

7)观察压入栈的内容,每个内容地址实际上是指向12字节的一段数据,该数据的末尾又是指向一个地址,因此,可以判断0x804c174开始的地方指向的是一个链表(但这些链表的存空间是连续分配的),每个节点包括12个字节,其中最后一个是指向下一个的指针,猜测每个节点的定义:

struct node {

int d1;//尚不清楚含义,以4个字节的int暂替

int d2;//尚不清楚含义,以4个字节的int暂替

struct node* next;

}

6个节点,分别为:

node1 = {0x216, 0x01, 0x804c180}; (&node1 = 0x804c174)

node2 = {0x2a4, 0x02, 0x804c18c }; (&node2 = 0x804c180)

node3 = {0x2f9, 0x03, 0x804c198}; (&node3 = 0x804c18c)

node4 = {0x220, 0x04, 0x804c1a4}; (&node4 = 0x804c198)

node5 = {0x22c, 0x05, 0x804c1b0}; (&node5 = 0x804c1a4)

node6 = {0x10b, 0x06, 0}; (&node6 = 0x804c1b0)

链接关系为:

node1   -->   node2   -->   node3   -->   node4   -->   node5   -->   node6   -->   0

8)假设当前6num的值为6/5/4/3/2/1,则经过6次循环后,函数栈帧如下图所示。

 

注:后面分析,均假设6num的值为6/5/4/3/2/1

22、以上操作结束,则跳转到0x8048fb9 <phase_6+0xa7>(第666行,参见第17步分析)。

236668048fb9 <phase_6+0xa7>- 676行:

1666行:0x28%esp)的内容(num[0]这个值指向的节点的地址)  --> ebx

2667行:esp+0x2c  --> eaxesp+0x2c这个地址的内容为num[1]这个值对应的节点的地址

3668行:esp + 0x40 --> esiesp + 0x40,根据后面的分析,这个值是作为“哨兵”,防止访问越界

4669行:ebx --> ecx(此时ebx以及ecx都是num[0]这个值指向的节点的地址)

5670行:eax所指向的地址的内容(num[1]这个值对应的节点的地址)--> edx

6671行:将edx的内容赋值给8%ecx)的地址,注意,此时ecxnum[0]指向的节点的地址,8%ecx)正好是num[0]这个值所对应的next,即node6.next = &node5

7672行:eax += 4,即eax所指向的地址的内容变成了num[2]所指向的节点的地址;

8673行:将eaxesi(哨兵)相比较,如果等于,则说明循环结束,跳转到8048fd7 <phase_6+0xc5>,如果不是,继续执行。

9675行:edx --> ecx:根据前面分析,edxnum[1]值指向的节点的地址。

10676行:跳转到8048fc7 <phase_6+0xb5>,即第670行,可以转到上面第5)步继续执行,注意,此时edxnum[1]值指向的节点地址,eax的内容为num[2]值指向的节点的地址,即node5.next = &node4

11)如此循环,最后的结果是:

node6.next = &node5node5.next = &node4node4.next = &node3node3.next = &node2node2.next = &node1

12)如果以上都做完,跳转到8048fd7 <phase_6+0xc5>677行)继续。

13677行:将0赋值给8%edx)指向的地址,此时edxnode1的地址,即将node1.next=0

14)显然,以上步骤,根据num的值重新构成了一个链表,此时的链接关系变成了:

node6   -->   node5   -->   node4   -->   node3   -->   node2   -->   node1   -->   0。(注:以上分析均是基于6num的值为6/5/4/3/2/1

24678 - 行:

1678行:5 --> esi

2679行:将ebx+8这个地址的内容送给eax,注意, ebxnode6的地址,ebx+8node6->next这个值的地址,这个地址的内容即为node5的地址。也即eax的内容为node5的地址。

3680行:将eax指向的地址的内容赋值为eax,也即eax的内容为node5.d1

4681行:将node5.d1ebx指向的地址的内容相比较;(显然,此处是整数的比较,因此,也可以判断struct node中第一个元素应该是int),此时ebx的内容为node6的地址,node6的地址的内容为node6.d1,即node5.d1node6.d1相比较。

5682-683行:如果node6.d1 >= node5.d1,则跳转到8048ff1 <phase_6+0xdf>,否则引爆炸弹

6684行:ebx的内容变为其指向的节点的next,即ebx=node6-next,指向了node5

7685行:esi-= 1

8686行:如果esi不为0,则跳转到679行,按以上的2)继续分析,应注意,此处ebx的值为node5的地址了。

9)显然,此时会判断node5.d1是否大于等于node4.d1,如果是,则继续,如果不是,则引爆炸弹

10)后续会依次判断node4.d1是否大于等于node3.d1node3.d1是否大于等于node2.d1......,综合起来,就是判断按照num值排序之后的节点是否降序排列,如果是,则解题成功,如果不是,则引爆炸弹。

1.3 phase_6结果分析

根据以上分析,phase_6的功能:

1phase_6定义了一个包含6个节点的链表,每个节点中包含两个整型(d1d2),以及指向下一个节点的指针;6个节点依次的链接顺序为node1->node2->node3->node4->node5->node6

2)要求用户输入6个数,这6个数应为1~6,而且不能重;为便于以后说明,假设这6个数为1/2/3/4/5/6

3phase_6对这6个数进行处理:num[i] = 7 -num[i];处理后的num变为6/5/4/3/2/1

4)按照num[i]的值重新排列链表,此时链表变为:

node6->node5->node4->node3->node2->node1

5)判断以上链表是否降序排列(按分量d1),如果是,则拆弹成功,否则,引爆炸弹。

也即phase_6会给出一个链表,链表中的节点的d1分量含有一个整数值,需要用户输入一个序列号,分别用7去减得到新的序列号,按照这个顺序重新排列链表中的节点,如果链表是按照降序排列,则输入的这个序列号是正确的。

对于前面的炸弹,其初始化的节点值为:

node1 = {0x216, 0x01, 0x804c180}; (&node1 = 0x804c174)

node2 = {0x2a4, 0x02, 0x804c18c }; (&node2 = 0x804c180)

node3 = {0x2f9, 0x03, 0x804c198}; (&node3 = 0x804c18c)

node4 = {0x220, 0x04, 0x804c1a4}; (&node4 = 0x804c198)

node5 = {0x22c, 0x05, 0x804c1b0}; (&node5 = 0x804c1a4)

node6 = {0x10b, 0x06, 0}; (&node6 = 0x804c1b0)

显然,使得这个链表按降序排列的序列是:3 2 5 4 1 6,这个序列是用7减出来的,因此,输入的序列号应为:4 5 2 3 6 1,此即为本关答案。

猜你喜欢

转载自www.cnblogs.com/wkfvawl/p/10742405.html