ZUCC_计算机系统原理实验_大作业 bomb 破解
浙江大学城市学院实验报告
一、实验目的:
综合掌握程序的机器级表示以及汇编逆向调试过程。
二、实验内容:
二进制炸弹是作为一个目标代码文件提供给学生们的程序,运行时,它提示用户输入6个不同的字符串。如果其中任何一个不正确,炸弹就会“爆炸”:打印出一条错误信息。学生通过反汇编和逆向工程来确定是哪六个字符串,从而解除他们各自炸弹的雷管。该作业需要有比较好的汇编语言阅读能力,涉及理论课第3章的所有知识内容。该作业每个同学都需要做,满分是120分,每个关卡的分数根据难易程序不一样。
三、实验步骤:
给出的main文件
这里的关键是利用每次main函数内的read_line来判断断点位置,然后进入phase函数查看比较的部分;
ps:作者真是个话痨
#include <stdio.h>
#include <stdlib.h>
#include "support.h"
#include "phases.h"FILE *infile;int main(int argc, char *argv[]){char *input;if (argc == 1) {infile = stdin;} else if (argc == 2) {if (!(infile = fopen(argv[1], "r"))) {printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]);exit(8);}} else {printf("Usage: %s [<input_file>]\n", argv[0]);exit(8);}initialize_bomb();//加工函数printf("Welcome to my fiendish little bomb. You have 6 phases with\n");printf("which to blow yourself up. Have a nice day!\n");input = read_line();//第一次调用read_line(),这里是判断断点的位置phase_1(input);//判断输入文本,这里是突破口phase_defused();printf("Phase 1 defused. How about the next one?\n");input = read_line();//第二次调用read_line()phase_2(input);//判断输入文本phase_defused();printf("That's number 2. Keep going!\n");input = read_line();//第三次调用read_line()phase_3(input);//判断输入文本phase_defused();printf("Halfway there!\n");input = read_line();//第四次调用read_line()phase_4(input);//判断输入文本phase_defused();printf("So you got that one. Try this one.\n");input = read_line();//第五次调用read_line()phase_5(input);//判断输入文本phase_defused();printf("Good work! On to the next...\n");input = read_line();//第六次调用read_line()phase_6(input);//判断输入文本phase_defused();//这里会有隐藏题目return 0;
}
反编译生成文本文件
找到主程序入口(main)
利用read_line找到调用的phase
read_lion函数传递分析
设断点在phase_1
因为这里EAX传递到ESP,推测输入文本存放在main栈帧内
查看栈帧
第一题
查看phase_1
函数
观察到调用了strings_not_equal
函数,并对返回值EAX判断,如果为0则跳转,否则调用explode_bomb
。
查看explode_bomb
函数
结合explode_bomb
函数,推断是用于退出程序并输出爆炸的效果。
查看strings_not_equal
函数
结合strings_not_equal
函数,推断适用于判断两个字符串是否相等。
继续分析phase_1
函数
因为在phase_1
调用的函数不是用于判断字符串,因此推测判断的字符串就在phase_1
内
注意到,这里调用strings_not_equal
之前栈帧内压入了0x804a24e和EAX,而EAX是输入的字符串,因此推测0x804a24e是判断的字符串
查看0x804a24e内存内容
验证答案
第二题
查看phase_2
这里调用了read_six_numbers
函数,然后比较了0和M(R(EBP)-28)不等就跳到8048d2e,然后比较1和M(R(EBP)-24),如果相等就跳转到8048d35。即第一和二个整数必须是0 1,否则爆炸。
查看read_six_number
推测此函数的目的是读入六个数字,并判断是否为整数,如果不满足就爆炸。
继续分析phase_2
这里更新了EBX为R(EBP)-20,EAX为M(R(EBP)-8)+M(R(EBP)-4),且如果M(R(EAX))与M(R(EBX))不同则爆炸。即第三个数字应该为一二数字之和,为1。
然后将EBX加4,并比较是否为最后一个数字。即如果没有到达最后一个数字便开始了一次循环,计算第四个数字为二三数字之和,为2。
……
显然,这里是一个斐波那契数列,于是得到答案:0 1 1 2 3 5
第二题补充
其他题目
显然,这里是一个等比数列,于是得到答案:1 2 4 8 16 32
第三题
查看phase_3
观察到调用了sscanf@plt
,该函数原型为int sscanf( string str, string fmt, mixed var1, mixed var2 ... );
。而在该函数调用之前,传入了两个参数,一个为EAX,一个为0x804a34a。如下图:
猜测其中的0x804a34a为sscanf内的str,于是尝试查看
查看内存
可以看见,这里需要传入的是两个整数
这里可以看见具体将两个数字放在了EBP-4和EBP-8
继续分析phase_3
首先输入的数字数量应该大于1,同时第一个数应该小于等于7,即(0~7)
紧接着发现上述的汇编代码类似于switch结构,同时0x804a280类似于switch case跳转表,于是查看内存求证。
查看内存
这正式一张跳转表
分析switch-case
先选择参数为0:
首先将179赋值给EAX,然后减去190,加上867,减去211,加211,减去211,加211,减去211,为645。然后如果第二个数大于5,则爆炸;且如果第一个数与EAX相等则退出,否则爆炸。
很快得出其余选项
第一个输入 | 第二个输入 |
---|---|
0 | 645 |
1 | 466 |
2 | 656 |
3 | -211 |
4 | 0 |
5 | -211 |
6 | -211 |
7 | -211 |
随机选择一个即可
第三题补充
其他题目
输入一个整数一个字符和一个整数,如果读入的有效参数小于等于2则引爆,然后将7与读入的第一个参数比较,如果超过则引爆,因此第一个参数小于7.
发现是switch-case表,因此发现是根据输入的一个参数进行指令跳转,决定执行那一部分代码,由此判断其C语言结构应该为switch case
单独取一个case
首先讲eax赋一个值,将0x1c2=450与输入的第三个值比较,因此第三个值应该为411跳转之后
输入的字符的ascii码为之前eax中al的值即b,所以其中一个答案为0 m 450
第四题
查看phase_4
可以看见调用了sscanf@plt
和func4
,且要求sscanf@plt
必须读入两个有效值
查看sscanf@plt
函数引用的地址0x804a34a
可以看见两个数字仍然放在EBP-4和EBP-8
分析phase_4
这里第二个数字要求大于1且小于等于4,然后执行func4(5, 第二个数字)
,并要求其返回值与第一个数字相同。
查看func4
发现func4
是一个递归函数,并且当第一个参数小于等于0退出并返回0,或者第一个参数为1时退出并返回第二个参数。
查看递归内容可以发现,每次调用func(第一个参数-1, 第二个参数)
和func(第一个参数-2, 第二个参数)
同时将两个返回值和第二个参数相加作为最终返回值。
分析递归
假设第二个参数为2,写出等价c函数代码:
#include <stdio.h>int func(int x, int y) {if (x <=0 )return 0;if (x == 1)return y;return func(x-1, y) + func(x-2, y) +y;
}int main(){int x = 2;printf("%d", func(5, x));
}
得出答案为第一个参数=12*第二个参数,因此最终答案为:
第一个参数 | 第二个参数 |
---|---|
24 | 2 |
36 | 3 |
48 | 4 |
第四题补充
其他题目
这里第一个数字要求非负且小于等于14,然后执行func4(第一个数字,0,14),并要求其返回值和第二个数都为21。
转化为C语言
#include <stdio.h>
int func(int x, int y, int z) {int sub = z - y;int m = 0;if (sub < 0) m = 1;int n = (sub + m) >> 1;n += y;if (x == n) return n;else if (x < n) return n + func(x, 0, n-1);else return n + func(x, n+1, z);
}
int main(){int x = 6;printf("%d", func(x, 0, 14));return 0;
}
因此答案为 6 21
第五题
查看phase_5
这里仍然是输入了两个整数,分别为EBP-4和EBP-8
将第一个数字&0xF,即除以16取余,并将其写入栈帧;最后如果EAX为15则爆炸。
初始化EDX、ECX为0,随后进入一个循环,循环结束条件是EAX为15。循环体是每次DEX加1,EAX被赋值为M(R(EAX)*4+0x804a2a0),ECX每次加上EAX。
将16写入第一个数字的内存。然后判断EDX不为15或者ECX不等于第二个数字则爆炸。
查看0x804a2a0的内存
显然这可以看成一个循环链表
分析循环链表
题目要求我们再一次循环中遍历完所有的结点,并且最后以15结点结束,因此我们可以追溯到开始结点为5,而第二个数字为1+2+……+15-5=115。故答案为:5 115
也可以写出等价c代码:
#include <stdio.h>int main(){int gt[16]={10, 2, 14, 7, 8, 12, 15, 11, 0, 4, 1, 13, 3, 9, 6, 5};int x, y, count = 0, sum = 0;x = 5;while (x != 15) {count++;x = gt[x];sum += x;}printf("%d\t%d", count, sum);
}
也可以得出答案:5 115
第五题补充
其他题目
这一关用到了string_length和string_not_equal,可以推断该关与字符串有关,目的应该为输入一个长度为6的字符串,随后一个循环,取每一个字符的后四位,根据后四位进行一个寻址操作将内存中的值取出,并放进ecx所在的一个新字符串。比较新字符串与内存中的预设字符串,要求相等。
系统用来比较的预置字符串,根据比较发现需要选取ASCLL码后四位 d 6 3 4 8 7的字符组成的字符串,这里选用mfcdhg
第六题
查看phase_6
调用了read_six_numbers
的函数,将读入六个数字放入栈帧中起始位置为R(EBP)-36。
这里出现了一个嵌套循环,外部循环中,在输入数字后,先将六个数字范围控制在1~6。
对于内部循环来说,先将遍历比较数字,结合外部循环可知各个数字不能相同,因此就是1到6的数字的某个组合。
完成内部循环后,重置ECX为1进入外部循环。
第一个循环用7减去了每个数字,并替换了原先的数字。
在这一部分注意到特殊的寄存器ECX,当数字小于EDX即序号时,ECX会被赋值M(R(ECX)+8),随后ECX又会放入内存。随后EDX加1,当EDX不大于5时,EDX和ECX分别被重置为1和0x804b1e4。猜测ECX是链表的指示部位,查看内存进行验证:
查看内存
发现M(R(ECX)+8)是一个地址
随即发现M(M(R(ECX)+8)+8)也是一个地址,而M(M(M(R(ECX)+8)+8))也是,以此类推……我们可以发现,这里是通过数组的顺序来决定链表的顺序。
最后循环部分是把数字压到栈中,并要求前者大于后者。
注意到这里进行了循环排序五次,因此只要满足数组元素对应的内存数字从大到小的顺序就可以了,注意,在之前有一个7减数组元素的过程,因此答案为:6 1 5 4 3 2
第六题补充
有些题目是类似于如下
通过GDB查看内存之后得到链表中的值和存放顺序。发现把数字放入一个栈中然后进行循环比较要求前一个小于后一个,比价五次成功后就退出函数,即破解成功。因此很显然,答案为 5 3 4 2 1 6
彩蛋
在查看汇编代码时发现有一个secret_phase,而在C语言源代码中没有发现该函数,搜索汇编代码后发现在phase_defused函数中有调用secret_phase,观察C语言代码,发现每关结束后均调用phase_defused函数。
查看phase_defused
在secret_phase
设置断点,查看后发现每一次拆完一个炸弹0x804b4d4的值就加1,也就是说当它为6时,才能加入彩蛋。
查看内存
这里可以看见需要传入两个整数和一个字符串,同时激发条件是在第四题的时候添加一个额外的字符串“DrEvil”。
查看secret_phase
调用了函数__strtol_internal@plt
,函数原型为long int strtol(const char *nptr,char **endptr,int base)
,将一个字符串转换为long int型整数。然后再调用fun7(36, 转换后的long int型整数)
,并且要求返回值为1。
查看fun7
显然fun7
是一个递归函数。且将两个参数存放在EDX,ECX。
分析出口条件为EBX为0时返回-1。当第一个参数大于第二个参数调用fun7(M(R(EBP)+4), 第二个参数)
,并返回2倍fun7
返回值;当第一个参数等于第二个参数退出,并返回0;当第一个参数小于第二个参数调用fun7(M(R(EBP)+8), 第二个参数)
,并返回2倍fun7
+1的返回值。
因此为了返回1,我们只要有1个内部调用就行了。具体来说,在最内部必须是调用参数1等于参数2的fun7
,然后利用返回值0去调用参数1小于参数2的fun7
,做到2*(0)+1 = 1
。
查看内存
因此,第二个参数为45。于是有:
fun7(36,45)=2∗fun7(45,45)+1=2∗0+1=1fun7(36, 45)=2*fun7(45, 45)+1=2*0+1=1 fun7(36,45)=2∗fun7(45,45)+1=2∗0+1=1
最后的测试
彩蛋补充
fun7函数发现其为一个递归函数。
分析出口条件为EBX为0时返回-1。当第一个参数大于第二个参数调用fun7(M(R(ESP)+4), 第二个参数),并返回2倍fun7返回值;当第一个参数等于第二个参数退出,并返回0;当第一个参数小于第二个参数调用fun7(M(R(ESP)+8), 第二个参数),并返回2倍fun7+1的返回值。
故答案为7
ps.想方便一点的话可以用IDA
ZUCC_计算机系统原理实验_大作业 bomb 破解相关推荐
- ZUCC_计算机系统原理实验_实验四 数据的存储
浙江大学城市学院实验报告 一.实验目的: 掌握计算机内部数据存储相关的概念:地址和内容: 掌握常见数据类型的存储宽度: 掌握计算机内部数据存储的顺序: 掌握计算机内部数据存储的对齐方式. 二.实验内容 ...
- ZUCC_计算机系统原理实验_实验五 位运算
浙江大学城市学院实验报告 一.实验目的: 了解高级语言中数据类型的转换和移位操作结果,从而能更好地理解指令系统设计和计算机硬件设计所需满足的要求和需要考虑的问题: 二.实验内容: 编写程序,完成实验讲 ...
- ZUCC_计算机系统原理实验_实验八 Linux汇编语言初步
浙江大学城市学院实验报告 一.实验目的: 了解Linux汇编语言的基本语法.汇编语言的编写.调试技巧 二.实验内容: 用给定的hello.s和test.s练习linux汇编语言的汇编.链接.执行.调试 ...
- ZUCC_操作系统原理实验_实验九 消息队列
操作系统原理实验报告 课程名称 操作系统原理实验 实验项目名称 实验九 消息队列 实验目的 了解 Linux 系统的进程间通信机构 (IPC): 理解Linux 关于消息队列的概念: 掌握 Linux ...
- 编译原理实验:代码生成作业(1)
编译原理实验4:中间代码生成实验包-C++文档类资源-CSDN下载编译原理实验4:中间代码生成实验包更多下载资源.学习资料请访问CSDN下载频道.https://download.csdn.net/d ...
- linux程序设计项目报告,Linux程序设计实验报告大作业
Linux程序设计实验报告大作业 实 验 报 告 课程名称: LINUX程序设计 学 院: 计算机学院 专 业: 软件工程 班 级: 14-3 姓 名: 张正锟 学 号: 201401061038 2 ...
- 计算机系统原理实验——微程序控制器
计算机系统原理实验--微程序控制器 一.模拟机的操作 1.程序表 2.执行过程及分析 3.流程图及分析 4.运行结果及分析 二.ROM模块设计 1.VHDL语言设计模块: 三.微程序控制器 1.ROM ...
- java大作业设计_Java程序设计_大作业.doc
Java程序设计_大作业.doc Java程序设计_大作业 专业:计算机科学与技术专业 学号:1245713131 姓名: 2014年12月10日 目录 作业内容:2 1.IPublisherDao接 ...
- ZUCC_操作系统原理实验_Lab9进程的通信消息队列
lab9进程的通信–消息队列 一.两个进程并发执行,通过消息队列,分别进行消息的发送和接收 1.代码: //接受消息 #include<stdio.h> #include<stdli ...
最新文章
- DNS隧道工具使用 不过其网络传输速度限制较大
- oracle 添加登陆数据库触发器--记录IP 地址
- 瑞幸咖啡的每一个环节,都蕴含着增长知识点
- linux内核镜像解压,解压内核镜像
- 将数据、代码、栈放入不同的栈(8086)
- 应用实践 | 网络智能运维下的知识图谱
- 扫地机器人什么牌子好?2021最新扫地机器人排行榜
- HDU 4380 Farmer Greedy(叉积和三角形知识的综合应用)
- 13.4 Shelve模块
- doctrine2 mysql_doctrine2到底是个什么玩意
- vs2017 git 操作重置、还原、挑拣对比
- 即将到来的Xcode8 都更新了什么?
- 申宝投资-昨日三大指数缩量探底回升
- word打开文档很久很慢_Office软件打开速度慢怎么处理?Word打开很慢如何解决?...
- 2021年计算机考研408操作系统真题(客观题)
- JavaScript下雨效果
- 去除字符串中所有的空格
- Hive表分区查询show partitions tablename
- MYSQL数据库----删除命令
- RuoYi-Vue 分离版 收获与总结
热门文章
- Java拼图游戏总结,Java拼图游戏课程设计报告
- 还在寻找一款DVD全能转换器吗?WinX DVD Ripper for Mac它不香吗?
- python turtle隐藏画笔_Python turtle库的画笔控制说明
- vue中el-radio-group点击事件,双击取消
- centos7.8 swoole安装和使用
- GZIP压缩和解压缩不删除原始文件
- 利用开源 SNI PROXY+DNSMASQ 工具链实战 Netflix 流媒体解锁
- Chemdraw 基础操作【图文】
- 单片机c语言中sbuf的定义,SBUF的详细介绍!(51单片机)
- 蓝桥杯java备赛Day3——跳马