本文感谢"有一种呆叫做三笠"同学投稿,主要分为两部分内容,一部分是分析加固宝的加固流程以及破解教程。一部分是分析加固宝加固的脱壳案例分析。
因现在部分APP使用360加固都使用了解析执行,所以写一个简单的demo进行学习。分析的结果可能不正确,本人对解析执行不是很了解,所以分析得出最后的结果可能会有些偏差。加固前demo中的onCreate函数smali显示如下图:
使用加固宝加固后的onCreate函数smali显示如下图:
360加固解析执行的文件是libjiagu.so运行时在内存释放的,故需要单步调试dump这个文件。使用IDA pro + android_server 进行动态调试:具体的使用步骤如下:
第一、安装IDA Pro 服务器
adb push android_server /data/local/tmp
设置服务器运行权限:
cd data/local/tmp/
chmod 777 android_server
运行服务:
adb shell & cd /data/local/tmp & ./android_server
设置端口转发:
adb forward tcp:23946 tcp:23946
第二、调试运行
adb shell am start D n Android.support.v8/android.support.v7.q448870015
第三、IDA 附加进程调试
Debugger > Attach
Ctrl+F 搜索 android.support.v8
第四、函数下断点
Modules中搜索 libc.so 给fopen,fgets下断点 Modules 中搜索 libdvm.so
给dvmDexFileOpenPartia 下断点
第五、jdb调试
打开monitor,鼠标选中要调试的进程(android.support.v8)
jdb–connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700
从内存中dump出so文件后,将其重命名为 libVM 。因为ELF文件映射到内存中会有内存对齐,所以需要使用十六进制工具进行修改。使用010 Editer 打开并执行ELF文件格式解析脚本。修改 program_table_element[3]数组,将Elf32_Off 的偏移修改成与Elf32_Addr相同的偏移。将Elf32_Word p_Filesz 的大小修改成与Elf32_Word p_memsz 相同的大小。修改program_table_element[7]数组,将Elf32_Off 的偏移修改与Elf32_Addr相同的偏移。将Elf32_Word p_Filesz 的大小修改成与Elf32_Word p_memsz 相同的大小。至此可以使用 IDA pro 静态分析libVM文件了。
从单步调试中可以知道,JNI_OnLoader函数主要的作用是进行 dex文件解密,DexClassloder 替换,使用registerNativeMethods函数进行注册(在dex壳文件中会有大量的Native函数),虚拟执行的初始化。处理方法:
根据索引获取方法结构体
Struct NativeMethod{
int classIdx;
int dexMethodIdx;
int dexCodeOff;
int** pDexAddr;
}*pNativeMethod;
根据结构体获取方法信息
根据方法shorty,构造参数
解析指令
根据opcode进行相应的case处理,一条Dalvik指令会被翻译成多条 NDK 函数执行,如 FindClass getMethodID CallMethod等。
指令解密
通过动态调试,找到了虚拟指令表,但这个表是加密过的,libVM取该表中的指令进行解密,并匹配switch case块跳转到相应的handler中去执行操作。
加密虚拟指令
B8 00 67 0A 01 20 25 20 39 20 24 5F B2 00 39 1E
21 20 35 20 04 00 02 00 03 00 00 00 47 B7 0F 00
0B 00 00 00 1A 00 C7 04 12 01 71 30 12 3E 02 01
0C 00 6E 10 14 3E 00 00 0E 00 00 00 01 00 01 00
01 00 00 00 50 B7 0F 00 04 00 00 00 70 10 AE 3E
00 00 0E 00 01 00 01 00 01 00 00 00 55 B7 0F 00
04 00 00 00 70 10 AE 3E 00 00 0E 00 01 00 01 00
01 00 00 00 5A B7 0F 00 04 00 00 00 70 10 AE 3E
00 00 0E 00 01 00 01 00 01 00 00 00 60 B7 0F 00
04 00 00 00 70 10 AE 3E 00 00 0E 00 01 00 01 00
01 00 00 00 66 B7 0F 00 04 00 00 00 70 10 AE 3E
00 00 0E 00 01 00 01 00 01 00 00 00 6C B7 0F 00
04 00 00 00 70 10 AE 3E 00 00 0E 00 01 00 01 00
01 00 00 00 72 B7 0F 00 04 00 00 00 70 10 AE 3E
00 00 0E 00 01 00 01 00 01 00 00 00 78 B7 0F 00
04 00 00 00 70 10 AE 3E 00 00 0E 00 01 00 01 00
01 00 00 00 7E B7 0F 00 04 00 00 00 70 10 AE 3E
00 00 0E 00 01 00 01 00 01 00 00 00 84 B7 0F 00
04 00 00 00 70 10 AE 3E 00 00 0E 00 01 00 01 00
01 00 00 00 8A B7 0F 00 04 00 00 00 70 10 AE 3E
00 00 0E 00 01 00 01 00 01 00 00 00
单步跟踪虚拟指令跳转的handle记录操作,解密指令:
B8 00 67 0A -> 98 20 00 00
01 20 25 20 -> 05 00 00 00
源:invokesuper {p0, p1}, Landroid/support/v7/app/AppCompatActivity; >onCreate(Landroid/os/Bundle;)V
39 20 24 5F B2 00 39 1E -> 92 20 00 00 19 00 04 7f
源:const v0, 0x7f040019
21 20 35 20 -> 15 00 00 00
源:invokevirtual {p0, v0}, Lcom/example/a71037/testos/MainActivity; >setContentView(I)V
首先使用IDA pro 静态分析libjiagu.so文件(这个文件是360加固所特有的文件,被加壳的dex文件加密保存在这个文件中)静态分析确定一些关键函数,如下图为入口函数,关于libjiagu分析的文章网上有很多,这里就不赘述了,下面直接开始动态调试。
动态调试使用 IDA pro + android_server 组合,具体的使用步骤如下:
第一、安装IDA Pro 服务器
adb push android_server /data/local/tmp
设置服务器运行权限:
cd data/local/tmp/
chmod 777 android_server
运行服务:
adb shell & cd /data/local/tmp & ./android_server
设置端口转发:
adb forward tcp:23946 tcp:23946
第二、调试运行
adb shell am start D n android.support.v8/android.support.v7.q448870015
第三、IDA 附加进程调试
Debugger > Attach
Ctrl+F 搜索 android.support.v8
第四、函数下断点
Modules中搜索 libc.so 给fopen,fgets下断点 Modules 中搜索 libdvm.so
给dvmDexFileOpenPartia 下断点
第五、jdb调试
打开monitor,鼠标选中要调试的进程(android.support.v8)
jdb–connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700
单步调试进入到switch处,在case29和case33处下断点,这两处为反调试和解密dex关键地方,该加固反调试手段有五处,分别为:
时间反调试,rtld_db_dlactivity反调试,调试服务UID检查反调试。
android_server以及其它调试工具的调试端口轮询,时间反调试。具体怎么过这些反调试网上也有相关博文说明,这里不在进行赘述,只相关调试地方标记显示出来。
第一个反调试:
第二个反调试:
第三个反调试:
第四个反调试:
第五个反调试:
单步调试过掉前面的反调试之后,在case 33处会调用释放出来的so文件中的JNI_OnLoader函数。这个JNI_OnLoader函数会进行检查当前虚拟机类型和其它参数,并且解密释放出被加壳的dex文件。
开始加载新的so文件
在内存中释放了解密操作,程序运行到解密处
开始定义dex文件结构体
此时在内存中已经解密处了源dex文件了,Ctrl+S找寻最后一个debug__xxx在 Hex View1查看即可看到dex头文件魔数
Hex View窗口
使用IDA脚本dump文件就行了
首先是xml文件中的修改,360加固会将被加固app中manifest.xml文件做部分修改,以便自己壳的启动,在这个apk中360加固会将Application替换为自己壳的Application,将Activity替换为自己壳中的Activity,还会添加一些其它熟悉。
第一、Application的修改
将com.qihoo.util.StubApp403779565 替换
com.qihoo.root.AutApp是被加固app的Application
第二、Activity的修改
注释掉 com.qihoo.root.SplashActivity
将com.qihoo.root.SplashActivity替换为 com.qihoo.root.SplashActivity
将com.qihoo.permmgr.COMMONPROVIDER 注释掉
在被加固的文件中com.qihoo.root.SplashActivity 类中onCreate函数第一行有调用壳文件中的类,这里需要注释掉。
加固流程:
=>shell启动
=>加载
=> libjiagu.so
=>反调试
=>内存中释放so文件
=>so文件判断当前虚拟机版本,跳转到对应的解析函数
=>解密dex文件
=>进行dex类加载器替换
这个so文件本身带有虚拟解析执行功能,可是在dump出的dex文件中并没Native化onCreate函数,可能是这个加固会在下个版本中实现虚拟解析功能。
手机查看文章不方便,可以网页看
http://www.wjdiankong.cn
咨询问题和技术交流请进入 [编码美丽技术圈]
更多技术干货长按二维码快速关注公众号