一.实验题目及目的
1.实验题目
程序运行在linux环境中。程序运行中有6个关卡(6个phase),每个phase需要用户在终端上输入特定的字符或者数字才能通关,否则将会引爆炸弹。需要通过分析汇编代码,使用gdb调试等方式找到正确的字符。
2.实验目的
熟悉汇编代码的分析方法,c程序的机器级表达以及控制,过程等相关知识,熟悉gdb调试的方法及过程。
二.实验内容
1.实验过程
首先观察主函数:
每一个炸弹的程序部分都相同,读入字符串,调用phase函数,如果结果正确将继续进行下一个字符串输入,否则炸弹爆炸。为了方便分析,将bomb文件反汇编的代码保存至txt文件,并使用gdb调试程序,找到程序设定的正确的字符串。
(1)phase1
找到汇编代码对应主函数中调用phase_1的位置,在调用read_line读取字符串与调用phase_1之间有一条mov指令,将eax寄存器中的值转移到esp寄存器保存的地址当中,这个值应该为输入字符串的地址
可以看到phase_1中调用了strings_not_equal,判断输入的字符是否与正确的字符相等,并使用test指令测试结果,字符串不正确(eax存放的值不为0)将调用explode_bomb爆炸。在调用strings_not_equal前有三条mov指令,包括一个转移立即数到esp+4的mov指令,这个立即数可能是正确字符串的地址。
使用gdb查看以这个立即数为地址所保存的内容,为一个字符串“Verbosity leads to unclear, inarticulate things.”,输入这个字符串,phase_1通过。
(2)phase_2
分析phase_2的汇编代码。phase_2中调用了read_six_number,提示答案应该为6个数字。首先将esp+24所指向的位置保存的数据与1进行比较,不相同则炸弹爆炸。相同将esp+28赋值给ebx,esp+48赋值给esi。接下来将esp-4所指向位置的数乘2与ebx比较,比较成功后将ebx+4,并将ebx与esi比较,作为一个循环。开始比较时ebx与esi相差20,5次循环刚好判断后5个数是否正确,而每个数正确与否又是与上一个数乘2进行比较。根据这部分逻辑可以得出,这段代码先判断数字第一位是否是1,接下来判断每一位是否是上一位的2倍,即答案的数字应该为1 2 4 8 16 32,输入这6个数字,phase_2通过。
(3)phase_3
分析phase_3的汇编代码,phase_3中是通过调用sscanf()读取格式化字符串的。注意到在调用sscanf()前有一条操作将立即数804a2ef指向的数据保存到esp+4的位置,gdb调试将断点设置在phase_3,查看该立即数指向的数据,得到一个字符串,推断出该字符串是sscanf()的参数,由此可知phase_3的答案为两个整数。
继续分析phase_3,调用sscanf()后将eax寄存器保存的值与1比较,小于等于1则爆炸。eax中保存的值为sscanf返回的成功匹配的个数,说明输入的数个数小于等于1将会直接爆炸。
接下来将esp+24处的值与7进行比较,如果大于7将爆炸。可以判断esp+24处的值为第一个输入的数字,这个数字必须小于7(且大于等于0)。接下来的跳转指令为间接跳转指令,将跳转到的地址与这个数字有关,打开gdb找到当这个数字为0时将要跳转到的位置,即跳转表的地址,为0x8048c4c。
先假设第一个数字为0进行跳转,分析汇编代码进行的操作。主要是将第一个数字进行一些加减操作,再与第二个数比较,相等即可通过。计算后得到当第一个数为0时,第二个数应该为-1235。
输入0 -1235,phase_3通过。
注意到在执行完对第一个数的加减操作后将原来的第一个数与5比较,若大于5则爆炸,故第一个数的实际范围是0-5。分析跳转表中的其他情况,当第一个数字为1,2,3,4,5时,对应的第二个数字分别为-1346,-476,-761,0,-761,经测试这些答案同样可通过phase_3。
故该题共6个答案:0 -1235;1 -1346;2 -476;3 -761;4 0;5 -761。
(4)phase_4
分析汇编代码,phase_4同样使用调用了sscanf,且参数的地址仍然是0x804a2ef,即参数为“%d %d”,答案也是两个整数。根据算数指令和跳转指令,第二个数大于4将调用explode_bomb,故第二个数应该小于等于4(且大于等于2)。接下来将第二个数移到esp+4的位置,将esp指向的位置赋值9,调用func4。如果第二个数与func4返回的结果相同,则phase_4通过。
接下来分析func4的汇编代码,func4栈帧的情况如下:
设func4的两个参数为A,B其中A是输入的第二个数,发现func4存在递归调用的情况,且有两次递归调用:
对应C代码的逻辑为:
计算出phase_4的答案为176 2;264 3;352 4;
(5)phase_5
分析phase_5的汇编代码。首先调用string_length得到输入的字符串长度,并与6比较,不相等则爆炸,phase_5答案为长度为6的字符串。
接下来的部分是一个6次的循环计算一个累加值,累加值是以每个字符的ASCII码保留后4位+一个立即数0x804a1a0为地址处的值,最终结果等于0x3f(63)时,phase_5通过。gdb中查看这个立即数地址开头的表:
将63分解为16+16+16+12+2+1,则六个ASCII码的后四位分别应该为0101,0101,0101,0100,0000,0011,对照ASCII码表查找符合条件的一组字符EEED@C,输入后phase_4通过,存在其他可能的答案且字符顺序不限。
(6)phase_6
分析phase_6的汇编代码。phase_6中调用了read_six_number,答案为6个数字。根据跳转指令跳转到的位置,读取数字后进入了一个双层循环。
外层循环当esi值为6时结束,每次比较前esi+1,即外层循环6次,对应6个答案的数字,每次将数字取出-1与5比较,>5爆炸,数字的范围为1-6。内层循环则是每一个数与下标大的数的比较,相等则爆炸,所以6个数均不相同,可以得出本题答案是123456的一种排列。
接下来的部分仍然包含循环,且出现了一个立即数地址,gdb查看:
这个地址保存的是一个节点数据,节点包含的数据类型还不明确,继续分析汇编代码,汇编代码中有一条指令mov 0x8(%ebx),%ebx,将ebx保存的值+8为地址取数据后保存到ebx寄存器,这条指令会循环执行多次,这表明ebx保存的值+8处的数据仍是一个地址。将node1+8处的数据作为地址查看数据:
找到了node2,此时可知节点中包含一个指针数据类型,指向下一个节点。这说明程序中存在一个链表,继续分析汇编代码:
这次的循环仍然循环6次,取出每个数字,找到数字对应的节点的地址,将其存入内存中,存放的地址是按顺序由低到高的。例如输入数字543216,在内存中由低到高存放着节点5.4.3.2.1.6的地址。
继续分析下一部分,下一部分中首先包含一个循环,循环中的操作修改了每个节点的下一节点地址,将第二个数字对应节点的地址设置为第一个数字对应节点的下一节点,即对于输入数字543216,将会把原链表的节点修改为node5->node4->node3->node2->node1->node6,按照输入的数字调整了链表的顺序。
接下来又是一个5次的循环,其中ebx保存的是修改后第一个节点的地址,第一次循环进行的操作如下图:
可以得出循环的操作是判断每个节点的值都必须大于下一个节点。
接下来gdb查看原链表每一个节点的值:
node+4的位置应该是节点编号,比较node处的值,node2>node6>node5>node4>node1>node3,按照要求重排后每个节点值大于下一节点值,输入2 6 5 4 1 3,phase_6通过。
(7)secret_phase
在汇编代码中可以看到一个secret_phase的函数,在phase_6通过后没有出现,查找secret_phase,在phase_defused中出现了secret_phase调用,且上方调用了一次strings_not_equal,与一个指定的字符串相等将进入secret_phase,上方还有一些立即数,分别对应的是正确进入secret_phase的提示和一个输入格式,这个格式推测为进入secret_phase_的方式,“%d %d %s”,与三四关的输入格式类似,在第四关结果后输入DrEvil,phase_6结束后正确进入了secret_phase。
找到了进入secret_phase的方法,接下来分析secret_phase的代码。首先调用read_line读一行,接下来调用strtol将字符转换为长整型,参数为a,即转换为10进制数,输入的数字本身。
接下来将这个数字-1与1000比较,需要小于等于1000才不会触发爆炸,因此输入的数是一个1-1000的数。接下来将这个数作为参数(A),立即数0x804c088作为参数(B)传入并调用fun7。fun7的结果需要等于3.
查看并分析fun7的代码,含有2次递归调用fun7:
在递归调用时,取参数B+4和B+8处的数据作为参数传入了fun7,与phase_6类似,B+8和B+4的位置应该是一个地址,指向下一个节点,而node节点存储了两个节点的地址,与二叉树节点的形式相同,此处的操作可能是对一颗二叉树进行操作。根据汇编代码的操作,写出对应的c代码如下:
找到第一次调用fun7传入的立即数地址,这个地址处为二叉树的根节点,gdb调试查看地址处的内容,构建出二叉树。使用graphviz做出了图,这是一颗二叉搜索树,节点值小于等于A时查找右节点,大于A时查找左节点。
编写程序建立二叉树,调用fun7,查找使fun7返回值为3的值,得到99和107,输入通过。
2.实验结果
phase_1: Verbosity leads to unclear, inarticulate things.
phase_2:1 2 4 8 16 32
phase_3: 0 -1235;1 -1346;2 -476;3 -761;4 0;5 -761,共6个答案
phase_4: 176 2;264 3;352 4,共3个答案
phase_5: EEED@C,有多种答案
phase_6:2 6 5 4 1 3
secret_phase:99 107,两个答案
三.实验总结
六关+隐藏关对应了以下内容:
- 常量字符串存储
- 二叉树在汇编代码中的表示
- 链表在汇编中代码的表示
- 字符的ASCII码表示
- 递归调用的过程
- 跳转表
- 循环