本帖最后由 sandikast 于 2018-5-2 14:38 编辑
全国大学生信息安全竞赛CISCN也举办了CTF,吐槽一下非要挑五一放假的时候进行比赛。划水做了两道题,记录并分享一下其中安卓题TryGetFlag的思路,为各位大佬抛砖引玉。题目:
task-TryGetFlag_02_D45L2DW.zip
(1015.49 KB, 下载次数: 71)
0x01 观察
安装后,软件运行界面如图:
扔到AndroidKill中,分析结果如图:
不用多说,根据经验,既然出现了爱加密,八成是要进行adb + IDA的so动态调试了。不过新版的爱加密挺难破解的,应该不是让强行脱壳吧/疑惑脸。开始做这个题的时候已经由好几组解了出来,估计八成是由其他信息要利用。根据APK里面内容和文件结构,发现相对普通爱加密的文件多出一个flag文件,点开是一个Base64码,解密一下是一串非可见字符,猜测应该后续能用到。
点开看看.smail,果然相关代码都被加密了,应该是存在.so中,在.so加载的过程中动态解密的。分析至此静态反面没什么可做的了,掏出家伙开始动态分析。
0x02 准备工作
1.由于手头有个淘汰的三星A8,因此我个人偏好实机调试,这样能减少模拟器调试过程中出现一些稀奇古怪的SIG异常信号。动态调试的过程中必须的软件环境:IDA主程序 + IDA的android_server + DDMS(现在叫monitor集成在android的SDK中) + adb + jdb(集成在Java环境中) 。
2.为了达到调试的目的,手机必须root,通过 adb shell 使android_server 在手机中以root权限运行,否则IDA在attach到remote debuger后只能找到零星的一两个进程,反之则会找到很多进程。另外要打开DDMS为要调试的进程分配8700端口,否则无法使用jdb进行附加以进一步运行程序。
3.设置程序可调试!这个非常重要,否则在DDMS中和IDA中都找不到要调试的进程。网上流传的方法基本上有三种:
(1)修改boot.img:可支持对所有进程调试,但是方法太复杂了,作为小白看着就头晕
(2)修改APK的AndroidManifest.xml,添加android:debuggable="false"。但是这种方法需要对apk进行重新打包,我在使用工具操作的过程中均需要重新编译才能完成(是否有不需要编译就修改并打包的方法?)。由于APK被加密了,使用AndroidKiller重打包的时候会报错导致失败,而使用APK改之理3.5.0.0月少版(膜拜大佬)能够成功打包。因此这种方法具有局限性。
(3)使用mprop工具修改安卓系统default.prop文件里的配置信息,能产生和(1)类似的效果。mprop工具很好用,github上有编译好的版本,不用到需要账号登录并扣积分的某SDN上下载。
0x03 调试命令
具体动态调试的过程不再赘述,记录一下我用到的一下命令。
CMD1:
[Shell] 纯文本查看 复制代码 adb push D:\\android_server /data/local/tmp
adb shell chmod 755 /data/local/tmp/android_server
adb shell
su
/data/local/tmp/android_server
CMD2:
[Shell] 纯文本查看 复制代码 adb forward tcp:23946 tcp:23946
adb shell am start -D -n monkeylord.trygetflag/monkeylord.trygetflag.MainActivity #此时程序应该已经开始执行,暂停在调试界面
#在IDA中attach到android_server,选择进程并设置好对应的断点
jdb -connect "com.sun.jdi.SocketAttach:hostname=localhost,port=8700" #开始执行程序
0x04 调试过程
在调试模式下运行程序,一步一步执行下去不出意料的出现了SIGSEGV信号,应该是加入了反调试机制。动态调试的首要任务是要防止被反调试干掉,作为小白一开始显得摸不到头绪,通过对libc.so中函数和fgets函数开始于返回位置下断点,找到了读取/prpc/pid/status中的TracerPid内容的操作,这是典型的Native反调试手段。手动修改内存,使得fgets到的内容从“TracerPid:\tXXX”变为"TracerPid:\t0"。注意到此时程序加载了classes.dex与libexec.so。
[Shell] 纯文本查看 复制代码 B46D2000: loaded /data/dalvik-cache/arm/data@app@monkeylord.trygetflag-1@base.apk@classes.dex
B3C9E000: loaded /data/data/monkeylord.trygetflag/files/libexec.so
那么可以推测以上反调试操作是那些.so加载之后,在OnLoad()中完成的,或者另开了专门的检测线程。
现有的关于爱加密的帖子,是要找到libdvm.so和dvmDexFileOpenPartial函数,在主程序dex从.so中解压之后dump内存从而获得真正的dex。然而这道题目始终没有找到libdvm.so,更别提那个函数了。推测真正的dex可能是通过mmap()函数加载的,于是在mmap()函数入口和返回的地方下断点,追踪程序执行过程,然而每次都没有弄明白加载的过程就报错了。
但是让我费解的是,即使绕过了反调试,在上述两个东西加载后还是会出现SIGSEGV异常信号。仔细看是“LDRB R2,[R4]”指令中R4值为0x00000000,不管怎么调试都会走到这一步引发一场。(一开始以为是使用(2)的方法设置程序可调试时重打包引起的,换成了(3)之后还是会如此。无助....)小白我陷入了僵局。。。
0x05 转机
题做不出来,胡乱摆弄的时候突然注意到加载的classes.dex开头魔数竟然不是“dex.035”而是“ELF”,这莫非是ART编译后的可执行程序?要不dump出现看看吧
使用IDC代码进行dump:
[C] 纯文本查看 复制代码 static main(void)[/size]
[size=4]{[/size]
[size=4] auto fp, dex_addr, end_addr;[/size]
[size=4] fp = fopen("C://dump.dex", "wb");[/size]
[size=4] for ( dex_addr = 0xB3C9D000; dex_addr < 0xB3D57000; dex_addr ++ )[/size]
[size=4] fputc(Byte(dex_addr), fp);[/size]
[size=4]}
将结果扔进IDA被识别为ELF-OAT类型文件,看的眼花缭乱。不过程序中主要函数都在其中,说明这个应该就是负责实现程序主要功能的函数。
之前听说过有能够把ART代码转换为DEX的工具,到github上果然找到了。ManyFace/ExtractDexFromOat:https://github.com/ManyFace/ExtractDexFromOat
试了试竟然成功生成了dex,拖进jadx查看:
果然成功获取到了功能代码,到了这一步可以直接写代码计算flag,开头那个base64加密原来是AES算法的key,代码如下:
[Java] 纯文本查看 复制代码 import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class decode {
public static String base64 = "pLFw0Gh8EQFJJU9fxmoK1jRE0yxmkpakGxEWdydK/Yg=";
public static void main(String argv[]){
System.out.println(GetFlag());
}
static String GetFlag() {
byte[] aeskey = new byte[]{(byte) 119, (byte) 111, (byte) 119, (byte) 44, (byte) 105, (byte) 116, (byte) 39, (byte) 115, (byte) 115, (byte) 111, (byte) 98, (byte) 114, (byte) 117, (byte) 116, (byte) 97, (byte) 108};
try {
byte[] encryptedData = Base64.decode(base64);
SecretKeySpec skeySpec = new SecretKeySpec(aeskey, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(2, skeySpec);
return new String(cipher.doFinal(encryptedData));
} catch (Exception e) {
e.printStackTrace();
return "There must be some bug";
}
}
}
最终得到了flag。
0x06 总结
好久没接触过安卓破解相关的内容,因为手生仅环境就折腾了好久。这道题做下来脑袋特别迷糊,LDRB指令为什么总是会带来异常还是没想明白,具体是哪些调用加载了class.dex与libexec.so也没整明白,研究了好久均无果而终。这道题披着爱加密的壳,但是自始至终没有见到加载dex的过程,和网上流传的爱加密破解方法不太一致,作为小白的确是长见识了,最后感谢出题人的智慧与劳动。
|