zoukankan      html  css  js  c++  java
  • CSAPP lab2 二进制拆弹 binary bombs phase_6

    给出对应于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_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_6函数。

     

    phase_1类似分析:

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

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

    此时的函数栈为:

     

    1.2 phase_6分析

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

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

     

    2592行:0  --> esi

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

    4598行:esi += 1

    5、599-602行:esi6进行比较,如果等于,则意味着6个数已经比较完毕,跳转到8048ef7。

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

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

    8608-610行:ebx+=1,然后判断ebx是否小于等于5,如果是,则跳转到8048ec1,即604行,也即跳转到第7步。

    9611行:跳转到8048e9f,进行num[esi]的比较(注意num[esi]在第608行加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();
            }
        }
    }

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

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

     

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

    17619-621行:ebx += 1,然后与6相比较,如果等于6,则跳转到0x8048f0e 。

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

    19626-628行:eax赋值为1edx赋值为0x804c174,跳转到8048eda <phase_6+0x59>。(第612行)

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

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

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

    2)当num[i] == 2时,将0x804c180(node2)压入到esp + 0x28 + i * 4

    3)当num[i] == 3时,将0x804c18c(node3)压入到esp + 0x28 + i * 4

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

    5)当num[i] == 5时,将0x804c1a4(node5)压入到esp + 0x28 + i * 4

    6)当num[i] == 6时,将0x804c1b0(node6)压入到esp + 0x28 + i * 4

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

    struct node
    {
        int d1;//尚不清楚含义,以4个字节的int暂替
        int d2;//尚不清楚含义,以4个字节的int暂替
        struct node* next;
    }

    6个节点,分别为:

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

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

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

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

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

    node6 = {0x285b, 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、以上操作结束,则跳转到 8048f0e <phase_6+0x8d>(第629行,参见第17步分析)。

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

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

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

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

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

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

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

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

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

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

    10639行:跳转到8048f1c <phase_6+0x9b>,即第633行,可以转到上面第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行)继续。

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

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

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

    24641 - 行:

    1641行:5 --> esi

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

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

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

    5645-646行:如果node6.d1 >= node5.d1,则跳转到8048f46 <phase_6+0xc5>,否则引爆炸弹

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

    7648行:esi-= 1

    8649行:如果esi不为0,则跳转到642行,按以上的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个数为6/5/4/3/2/1。

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

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

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

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

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

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

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

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

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

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

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

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

  • 相关阅读:
    Power of Cryptography
    Radar Installation
    Emag eht htiw Em Pleh
    Help Me with the Game
    89. Gray Code
    87. Scramble String
    86. Partition List
    85. Maximal Rectangle
    84. Largest Rectangle in Histogram
    82. Remove Duplicates from Sorted List II
  • 原文地址:https://www.cnblogs.com/wkfvawl/p/10742405.html
Copyright © 2011-2022 走看看