系列文章目录
第一章 汇编学习记录-二进制炸弹-x86架构
第二章 汇编学习记录-二进制炸弹-arm64架构
第三章 汇编学习记录-简单总结
文章目录
前言
学校课程计算机系统的实验二:二进制炸弹
这里记录一下破解过程
ps:答案仅供参考,拿走也没用,据老师说是根据我们的学号生成的
一、炸弹1
密码如下:I am for medical liability at the federal level.
破解过程:
<phase_1>中另一个参数放入%esi并一起作为<strings_not_equal>的参数,我们可以知道该参数即为炸弹1的密码
接下来通过gdb来查看该参数,过程如下
二、炸弹2
密码如下:1 2 4 7 11 16
首项为一个非负数,后面+1,+2,+3……
破解过程:
首先看方法中,将<read_line>的返回值放入%rdi,充当参数
然后看<pharse_2>中,将栈指针先移动20字节,再将栈指针的值作为参数,传给<read_six_numbers>
在<read_six_numbers>中,我们读入了6个数,并放在<pharse_2>中开拓出的栈空间里
回到<pharse_2>中,接下来是一个循环,用c语言表示大致为
int ebx = 1;
do{
if(ebx>5)break;
rax=ebx;
edx=rbx-1;
rdx=edx;
ecx=ebx;
ecx+=M[rsp+4*rdx];
ebx++;
}while((M[rsp+4*rax]-ecx)!=0);
将其简化,我们知道若M[rsp+4*ebx]-M[rsp+4*(ebx-1)]==0
,则符合条件。
若将我们输入的数放在数组M中,那么其应该满足条件M[i-1]+i==M[i]
三、炸弹3
密码如下:
0 236或1 -509或2 28或3 -945或4 0或5 -945
破解过程:
在<pharse_3>中,可以看到将$0x40334f放到%esi中作为scanf函数的参数,所以我们可以知道这个代表密码的格式
通过gdb查看该字符出得到”%d %d”,可知密码是两个整数
由jmpq *0x4031c0(,%rax,8)知道这里是使用switch进行跳转。当读入的第一个数>7时,破解失败
通过gdb我们可以看到跳转表的内容
通过跳转表,我们可以对其进行标记并分析
当读入的第一个数为
又因为读入第一个数应<=5,排除后两个
四、炸弹4
密码如下:
176 2或264 3或352 4
破解过程:
首先我们查看要求输入的字符串格式
接下来,进入
将整理为类似c语言的形式
fun4(edi, esi){
//若将edi中定为n,esi中定为x
//若x<=0,返回0
if(edi<=0)return 0;
else{
push r12,rbp,rbx;
ebp = edi;
ebx = esi;
if(edi==1){
//若n==1,返回x
eax=esi;
pop r12,rbp,rbx;
}else{
//构造fun4(n-1,x)
edi--;
fun4(edi,esi);
//将返回值+x保存
r12d=rax+rbx;
//构造fun4(n-2,x)
edi=rbp-2;
esi=ebx;
fun4(edi,esi);
//将返回值加上之前保存的值
eax+=r12d;
pop r12,rbp,rbx;
//返回fun4(n-1,x)+x+ fun4(n-2,x)
return rax;
}
}
}
进一步整理得到
int fun4(int n, int x){
if(n<=0)return 0;
else{
if(n==1)return x;
else{
return fun4(n-1,x)+fun4(n-2,x)+x;
}
}
}
容易知道当n(%edi)=9时,fun4最终返回的值为88*x(%esi)
回到<pharse_4>,可知输入的第1个数应为第2个数的88倍,且第2个数应为2、3、4
五、炸弹5
密码如下:
5 115
破解过程:
先看输入答案的格式,为两个整数
这段代码大致有几个过程
将输入的第1个数对16求模
进入循环
将读入第1个数求模后记为x1,第2个数记为x2
则循环可写成
edx =0;
ecx=0;
while(x1!=15){
x1=M[x1];
ecx+=x1;
}
其中M是一个数组,通过gdb可查看其中的值{a,2,e,7,8,c,f,b,0,4,1,d,3,9,6,5}
由简单的代入知道,若不停循环x1的值会形成一个不断重复的序列,a,1,2,e,6,f,5,c,3,7,b,d,9,4,8,0……
由后面条件知,若循环完成后,edx不为15,则会爆炸
那么x1只能在循环第15次时取f,所以x1的初始值应为5,同时经过简单的计算可知,此时ecx应为115,那么输入的第2个数也应该为115
六、炸弹6
密码如下:5 2 3 1 4 6
破解过程:
先读入6个数
接下来时一个循环,整理这个循环
ebp=0;
while(ebp<=5){
eax=M[ebp];
eax--;
if(eax>5)call<explode_bomb>;
r12d=rbp+1;
ebx=r12d;
while(ebx<=5){
rax=ebp;
rdx=ebx;
edi=M[rdx];
if(edi==M[rax]) call<explode_bomb>;
ebx++;
}
ebp=r12d;
}
我们可以知道这个二重循环的作用是确保读入的6个数字全都不同
接下来还是循环
eax=0;
while(eax<=5){
rcx=eax;
edx=7;
edx-=M[rcx];
M[rcx]=edx;
eax++;
}
这个循环是在将我们输入的每个数都对7求补,并放回原位
esi=0;
while(esi<=5){
eax=1;
edx=0x4052d0;
rcx=esi;
while(M[rcx]>eax){
rdx=(rdx+8);
eax++;
rcx=esi;
}
rsp[rcx]=rdx;
esi++;
}
rbx=(rsp);
rcx=rbx;
通过gdb我们知道0x4052d0指向一个链表
node1(0x39)->node2(0x3e5)->node3(0xc7)->node4(0x334)->node5(0x3ad)->node6(0xef)->null
这个循环的效果是将链表各节点的地址以数组中数的顺序放到栈中
eax=1;
while(eax<=5){
rdx=eax;
rdx=rsp[rdx];
(rcx+8)=rdx;// rcx=(rsp);
eax++;
rcx=rdx;
}
(rcx+8)=0;
进一步简化
rcx=rsp[0];
eax=1;
while(eax<=5){
rdx=rsp[eax];
rcx->next=rdx;
rcx=rdx;
eax++;
}
这个循环,将链表的结点按照栈中的顺序重新连接;
ebp=0;
while(ebp<=4){
rax=(rbx+8);// rbx=(rsp);
eax=(rax);
if((rbx)>=eax){
rbx=(rbx+8);
ebp++;
}else call<explode_bomb>
}
进一步简化
ebp=0;
rbx=rsp[0]
while(ebp<=4){
rax=rbx->next;
eax=rax->num;
if(rbx->num>=eax){
rbx=rbx->next;
ebp++;
}else call<explode_bomb>
}
这个循环检测重新排列的链表是否按照降序排列
综上,我们应该将链表节点按照其中数字降序排序得到2 5 4 6 3 1
,再对其与7求补,得到5 2 3 1 4 6
七、隐藏关
密码如下:7
破解过程:
先看<phase_defused>我们知道其与0x40576c处比较以决定是否跳转;
通过gdb查看该处的值
当输入字符串后,值发生变化
我们可以知道,当输入六个字符串时,该函数才会继续分析,才有可能进入<secret_pharse>
接下来,读入了三个值,且通过gdb可以看到其格式
格式为两个整数和一个字符串
查看0x405870处的值,
在输入第四个字符串前
在输入第四个字符串后
可以看到0x405870处保存了输入的第四个字符串的值,
接下来将读入的字符串与0x4033a2字符串进行比较
在检测完字符串后,会输出特定字符串,并且进入<secret_pharse>
综上,我们知道,在第四个炸弹输入密码时,在后面增加DrEvil,在完成六个炸弹后,即可进入<secret_pharse>
将0x4050f0作为参数传给了,用gdb查看0x4050f0
可以推测这里是一种二叉树
接下来进入,整理得到
fun7(rdi , esi){
If(rdi==null){call<explode_bomb>}
eax=rdi->num;
if(eax>esi){
rdi=rdi->lchild
return 2*fun7(rdi , esi)
}else if(eax <esi){
rdi=rdi->rchild
return 2*fun7(rdi , esi)+1
}else return 0;
}
通过后面分析知道的返回值应该为4
我们算出
由此知道,密码应为7