DDCTF2018 Reverse writeup
baby_mips
花了一天,终于写完了
这是一道mips指令集的逆向题。
关于mips架构 https://zh.wikipedia.org/wiki/MIPS%E6%9E%B6%E6%A7%8B
初步尝试
在ubuntu运行./baby_mips
在IDA中分析一下
IDA分析
进入IDA首先就是搜索字符串
显而易见
图形化分析这里
可以看出是一个简单的条件判断
如果你以为F5看算法写脚本就结束,那就too native了
使用qemu模拟器动态调试
baby_mips是MIPS指令集上的程序,IDA只能静态分析,不能debug。采取的方法是在linux机上安装qemu模拟器,利用qemu来运行MIPS指令程序。
因此,首先当然是在ubuntu上安装qemu模拟器了sudo apt-get install qemu
就可以了
关于用法,最简单的qemu-你需要的指令集 文件名
如果要利用IDA调试(IDA远程调试)qemu-你需要的指令集 -g 端口 文件名
原理:qemu -g port指令开启一个gdbserver。port另一端可以由IDA或gdb连接调试。
下面我们需要输入的是Hostname和Port,如果你是在运行qemu的系统里运行IDA,Hostname就填127.0.0.1,要是别的系统,比如你用的是虚拟机,就填运行qume的系统的IP,Port就填刚刚qemu里设置好的就行。(这些是常识了)
然后确认IDA就可以动态调试了
经过尝试,这里的baby_mips是mips小字端程序,所以我们运行指令qemu-mipsel -g 6666 baby_mips
然后IDA点确定
成功了
程序停在了00400430,并且这里的指令是以EB02开头的
往下翻翻,发现非常多的EB02,然后下面的指令就不被识别了
IDA有一个强大的插件
keypatch —–http://www.keystone-engine.org/keypatch/
可以用这个插件把第一个EB02 NOP ,然后运行的话会发现又在下一个EB02报错,所以应该就是EB02的问题了
至于原理,参阅了很多篇wp,大概就是
查找lwc1指令的含义,发现是与协处理器相关的指令。通过对后面的代码块进行分析发现,后面并没有用到$f29和$t1寄存器的内容
指令的头两个字节为 \xEB\x02 ,且在x86指令集中 \xEB 为跳转指令。
把操作码反汇编成汇编代码后发现第一条指令是 jmp 0x4 ,刚好MIPS指令集每条指令大小为4字节。
猜测程序让我们遇到这个指令就跳转四字节
于是现在的思路就是将所有EB02开头的指令nop
由于MIPS指令是定长的,均为4个字节。因此,可以在选定的代码块中,将所有以EB 02开始的4字节数据全部替换成00 00 00 00,在MIPS指令中,nop对应的机器码为00 00 00 00
需要固定监测指令的头部,是因为可能会误清除掉正常指令
使用脚本去除EB02指令
下面是各位大佬写的IDC或IDPython脚本,功能都是去除EB02指令
来自‘奈沙夜影’
#include <idc.idc> |
来自‘逢魔安全实验室’
import os |
来自‘cq674350529’
import idautils |
选择上述其中一个脚本,在桌面保存成一个.py或.idc文件
选择File->Script file或Script command
关闭这个窗口,会有一个提示是否保存修改,点击Yes,这时桌面会生成一个patch
IDA载入这个patch,可以发现在ubuntu下的IDA里指令都可以被识别,而且可以运行,我在win10运行的IDA会有几个小段不能识别,需要手动改为指令,将0x400420处的代码转换成函数
将数据转为代码和函数
首先往下看看,有几段没有识别的
把它们全部选中(00401A58-00401F28),右键,选择Analyze selected area或按C,
选Analyze,然后Yes,然后就会变成代码
当然这还没完,函数头部00400420没有变量声明,识别出来的肯定是错误的函数,因此在00400420右键,选择Create function
就可以了
然后图形化分析就像下面那样
记得保存修改,路径Edit->Patch program->Apply patches input file
标准转换方式
1.函数和数据互换
在重新格式化之前,首先必须删除其当前的格式(代码或数据)。右击你希望取消定义的项目,在结果上下文菜单中选择Undefine(也可使用Edit▶Undefine命令或热键U),即可取消函数、代码或数据的定义
转换成code后,此时不能使用图形view
创建函数(Edit—>Functions—>Create Function….)就可以了2.代码和数据转换
通过Edit▶Data和热键D来完成,之后使用Undefine
之后使用code指令
算法分析
1.反编译器分析
用图形化分析sub_400420这个函数
一共十六个条件,必须每条都得满足才行
IDA的F5不能用,我们需要能够分析MIPS的反编译器
· Retdec https://github.com/avast-tl/retdec
· JEB-MIPS https://www.pnfsoftware.com/jeb/demomips
下面是三种找出算法的方法
1.Retdec
RetDec 是一个可重定向的机器码反编译器,它基于 LLVM,支持各种体系结构、操作系统和文件格式:
支持的文件格式:ELF,PE,Mach-O,COFF,AR(存档),Intel HEX 和原始机器码。
支持的体系结构(仅限 32 位):Intel x86,ARM,MIPS,PIC32 和 PowerPC。
安装和使用方法GitHub上都写了
或者这里有一个很好的简易介绍 https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/5.11.1_retdec.html
我在win10安装之后,把patch放到了D盘根目录,Retdec文件夹也是,然后在cmd中执行D:\retdec\bin\decompile.sh D:\patch
等待分析(因为我装了Git,就自动调用Git bash执行了,其他情况还没试过)
结束后D盘生成下面几个文件
打开patch.c,看sub_400420
我的不知道出了什么问题,分析不了,直接return 1。。。感觉应该是把函数分解了
是16个方程求解,解方程下面说,接下来用JEB反编译
2.JEB-MIPS
先到官网下载试用版的JEB,试用版功能是完整版的百分之九十,所以其实足够学习用了(国外友人客服也非常专业且友好)
解压后有三个运行脚本,分别是Windows,Linux和Mac OS的,我为了方便就装Ubuntu了
不管是哪个系统,我们都需要JAVA8的环境
Ubuntu安装JAVA8sudo apt-get install openjdk-8-jdk
查看java版本,看看是否安装成功java -version
成功后运行脚本,就打开主程序了,导入patch程序,找到00400420
右键,Decompile
很长很长的算式,就是16个方程了
2.手工分析清洗方程
(来自‘奈沙夜影’)
f = open("code.txt", "r") |
解方程
解方程的方法很多
比如Python的有
Numpy 求解线性方程组
SciPy 求解非线性方程组
SymPy
上面三个有个链接可以参照https://zhuanlan.zhihu.com/p/24893371
numpy求解
来自‘niuwuwu’
import numpy as np |
来自‘ljt1000’
import numpy as np |
Z3约束器求解方程
Z3可以说很出名了
这里有个教程http://ericpony.github.io/z3py-tutorial/guide-examples.htm
来自‘奈沙夜影’
from z3 import * |
来自‘cq674350529’
#!/usr/bin/env python |
angr二进制自动化分析及符号执行
angr是一个基于python的二进制漏洞分析框架,集成了多种主流的分析技术,能够进行动态的符号执行分析和多种静态分析。
如果我们使用angr的话,就不再需要考虑算法内部的详细原理,利用符号执行可以大大减少分析时间
.text:00403620 lw $gp, 0x98+var_50($fp) |
通过静态分析可知,程序在0x40362c处调用sub_400420函数,其参数通过寄存器a0传递,然后返回值保存在v0寄存器中。之后对v0的内容进行判断,如果为1则输出flag(flag与用户输入的内容相关),为0则输出”Wrong”。因此,只需要求解输入,保证sub_400420的返回值为1即可。
angr脚本
#!/usr/bin/env python |
find_address是使得函数sub_400420返回值为1的地址,而avoid_address是使得函数sub_400420返回值为0的地址。同时,将输入的16个数字保存在内存地址0x10000000处,然后将其赋值给a0寄存器,实现参数的传递。之后,直接从函数sub_400420的开始处开始分析。
第二种
# -*- coding:utf-8 -*- |
flag每个人都是不一样的,我的用angr解的话,最后得到的是7fe2c58fc9a7eb90
Flag
于是flag: DDCTF{7fe2c58fc9a7eb90}
z3解的话还可以得出具体每个变量的值,这里就不放图了