1) 理解arm汇编语言,学会使用调试器。
2) 熟悉安卓开发板的使用和环境配置;
3)熟悉并掌握在linux系统下的shell命令使用。
linux系统,终端,arm-gdb工具,安卓开发板
二进制炸弹是作为一个目标代码文件提供给我们的程序,程序运行中有6个关卡(6个phase),运行时,它提示用户输入6个不同的字符串。如果其中任何一个不正确,炸弹就会“爆炸”:打印出一条错误信息。我们首先要在linux下配置好arm-gdb环境并且得到反汇编问卷,然后通过反汇编和逆向工程来确定是哪六个字符串,从而解除他们各自炸弹的雷管。
在实验过程中我们运用了课上对于ARM部分指令的知识,例如读写指令ldr和str,跳转指令b和bl的区别,以及比较指令cmp等,还有条件域的实践应用。并且为了完成实验我们自学了关于在Linux环境下运用gdb设置断点进行调试,并且了解了更多的ARM指令和与之相关的寄存器和堆栈的使用。在拆炸弹似过程中我也运用到了相关的数学知识,主要是递归的算法和循环算法。
步骤一:环境配置
在进行环境配置时,我们按照指导书进行了Linux下的环境配置和gdb安装包的解压安装。在此阶段我们学习了在Linux终端用命令行进行新建,打开,检验文件夹,以及保存文档中的内容。主要运用了sudo来执行一些root命令,cd用来打开或退出文件夹,tar命令进行文件的备份。主要命令行如下:
tar -jxvf gdb-7.10.tar.bz2 解压gdb安装包
sudo gedit ~/.bashrc 修改环境变量
在进行PC与目标板的连接时,先分别获取IP地址,然后在目标板上运行PC中的bomb程序,在PC上运用arm-gdb进行程序的调试。主要运用了调试工具adb,ifconfig配置网络设备来设置IP,gdbserver来让PC可以对目标板进行远程调试。主要命令行如下:
adb push bomb /data/local 将bomb程序push到目标板上
ifconfig eth0 192.168.0.100 设置目标板IP
ifconfig eth1 192.168.0.101 设置PC的IP
gdbserver 192.168.0.101:2345 bombg (ip 为 pc 机机 ip)在目标板上可以运行bomb程序。
步骤二:六个phase的解决
首先是找到main函数,发现它调用了从phase1到phase6这六个函数。这应该就是每一关需要看懂的函数了。
开头还是栈的开辟,往下看,首先特别明显注意到sscanf函数,推测应该是一个类似C中的一个函数,然后想要知道将要返回的参数,发现
“84ec: e59f1198 ldr r1, [pc, #408] ; 868c
“8890:e50b0048 str r0, [fp, #-72] ; 0x48
8894:e59f3260 ldr r3, [pc, #608] ; 8afc
8898:e50b3010 str r3, [fp, #-16]
889c:e24b3028 sub r3, fp, #40 ; 0x28
88a0:e51b0048 ldr r0, [fp, #-72] ; 0x48”
储存和取值的代码,推测应该是个链表结构,.word中存的应该是链表头。
仔细看了一遍代码后,大概找到了几个循环,推测应该是给链表从小到大的排序,用C语言表示大概是:
for(int i=0;i<=5;i++){
if(a[i]>6)
explode_bomb();
for(int j=i+1;j<=5;j++){
if(a[i]=a[j])
explode_bomb();
}}//输入的小于6的6个整数两两不等
for(int i=0;i<=5;i++){
l=list;
for(int j=1;jnext;
list_array[i]=l;
}//把链表里面数存入数组
l=list_array;
for(int i=0;i<=4;i++){
if(*l<*l->next)
explode_bomb();
l=l->next;
}//排序
基本确定好是给链表从小到大排序后,只需找到链表中的数据就可以了。看到“88b8: e51b200c ldr r2, [fp, #-12]”里面把地址减去12经常出现,猜测应该是链表每隔12位存一个数据,所以用打印16进制地址的方式“(gdb)p/x *(0x00091104-12)”依次打印出地址,然后找到数据就可以了。最后打印出链表中数据是:“253,725,301,997,212,432”,然后给他们排序就是“5 1 3 6 2 4”即应该输入的6个整数。
隐藏关的开启方式在phase_defused中,由9158: e3530002 cmp r3, #2可以看出需要输入两个字符才能进入隐藏关卡。
9168: e59f103c ldr r1, [pc, #60] ;
91ac <phase_defused+0x88>
916c: ebffff04 bl 8d84 <strings_not_equal>
通过gdb查看91ac中的文档,文档中存储的是一个字符串austinpower,通过查看另一文档可知输入的路径密码格式是(d%,s%),因此在前面的六个关卡中,只有实验四是输入一个数字9,因此密码为9 austinpower,从而进入了隐藏关卡。
隐藏关的关键代码如下:
8bb0: eb0000f5 bl 8f8c 读入一行
8bb4: e1a03000 mov r3, r0
8bb8: e50b300c str r3, [fp, #-12]
8bbc: e51b300c ldr r3, [fp, #-12]
8bc0: e1a00003 mov r0, r3
8bc4: e3a01000 mov r1, #0
8bc8: e3a0200a mov r2, #10
8bcc: eb000d0b bl c000 把数字转化为长整型
8bd0: e1a03000 mov r3, r0
在读入数字之后,接着调用了另一个文档中的数字:
8bec: e59f0028 ldr r0,[pc,#40];8c1c
8bf0: e51b1008 ldr r1, [fp, #-8]
8bf4: ebffffc1 bl 8b00
可见从文档中读入的数字和输入的数字一起作为参数进入了函数fun7。接着向下看secret_phase中的代码。
8bfc: e3530007 cmp r3, #7
因此输出的值应为7,接着就进行fun函数,主要部分如下:
8b18: e3530000 cmp r3, #0 r3为0时,输出0
8b1c: 1a000001 bne 8b28 0x28>
8b20: e3e03000 mvn r3, #0
8b24: ea00001b b 8b98 0x98> 跳出函数
对于一些寄存器的变换不再赘述。
8b34: e1520003 cmp r2, r3
8b38: da000007 ble 8b5c 0x5c>
8b3c: e51b3008 ldr r3, [fp, #-8] 此时r2>r3
8b40: e5933004 ldr r3, [r3, #4] r3取其地址加4的地址所存数字
8b44: e1a00003 mov r0, r3
8b48: e51b100c ldr r1, [fp, #-12]
8b4c: ebffffeb bl 8b00 fun7函数是一个循环嵌套
8b50: e1a03000 mov r3, r0
8b54: e1a03083 lsl r3, r3, #1 r3=r3*2
8b58: ea00000e b 8b98 0x98>
8b68: e1520003 cmp r2, r3
8b6c: aa000008 bge 8b94 0x94>
8b70: e51b3008 ldr r3, [fp, #-8] 此时r2
8b74: e5933008 ldr r3, [r3, #8] r3为其地址加8所得地址中存的数字
8b78: e1a00003 mov r0, r3
8b7c: e51b100c ldr r1, [fp, #-12]
8b80: ebffffde bl 8b00
8b84: e1a03000 mov r3, r0
8b88: e1a03083 lsl r3, r3, #1
8b8c: e2833001 add r3, r3, #1 r3=r3*2+1
8b90: ea000000 b 8b98 0x98 >
8b94: e3a03000 mov r3, #0
8b98: e1a00003 mov r0,
此时可以看出这是一个if-else的递归循环。C语言代码如下:
fun7(const int *a, int b)
{
if (a == NULL)
return -1;
int ret = 0;
if (*a - b > 0)
{
ret = fun7(*(a + 4), b);
ret *= 2
}
else if (*a - b == 0)
return 0;
else
{
ret = fun7(*(a + 8), b);
ret = ret * 2 + 1;
}
return ret;
}
由于最终返回值为7,我们进行逆向递归,7=2*3+1,3=1*2+1,1=0*2+1。所以前三次比较中都是a
1.环境配置过程中我们遇到的主要问题比如安装包与PC的位数不符,无法正确读出bomb的.s文件,在设置IP时因连接其他网络而无法获取地址,运行gdb进行调试时找不到目标文件等,但在我们仔细阅读指导书并且询问助教老师后,都得到了及时的解决。
2.实验三耗费了比较长的时间,关键就是一直在读反汇编的代码,没有去用gdb直接调试,所以总是在猜测哪里会存储什么数据,陷入了瓶颈。后来去机房调试,发现只要找到各个word里面存的数据就很容易知道这个题目答案,问题解决。
3.实验六遇到的问题主要是循环体没搞清楚,r3和r2的重复出现,弄得很混乱。但是在发现是链表结构后,基本上就豁然开朗了,gdb打印出链表内容,估计也就是个排序,按照自己的猜测再来用C语言把汇编翻译出来,整个题目就解决了。
4.隐藏关主要问题有两个,一个是寻找隐藏关打开方式,另一个是弄清楚函数。因为发现每一次成功过一关都会调用defuse函数,所以研究那个函数就搞明白进去的方法了;函数后来是突然想到了第四关的递归,再加上第六关中地址中存放地址的特点,找到了最终需要输入的数据。