0x00 前言
本来准备写一篇长篇大论从原理到操作都写一写然后拿去投稿的,但是后来就只写了操作,那只好放到博客上自己看看了,原理分析下次再写
0x01 Java层分析
咱先看看Java层,用jadx打开apk
很明显,应用被加固了,一般来说,应用加固常用的方法就是外面套一层壳,然后壳程序进行一些解密加载变换的操作,将源应用还原出来,然后进行加载。
那么必然有一个壳程序的入口,那么我们看看入口在哪
咱知道,因为Application是早于activity的,那么常见的壳入口一般都会设置在这,这样就能早于源Activity执行一些操作,那么咱跟过去瞅瞅壳在java层都做了些啥
首先这里有一点,那就是attachBaseContext函数是早于onCreate函数执行的,咱们跟过去看看它做了啥
咱们看看关键的,跟进这个a函数
此处调用了两个主要函数,一个是函数e,一个是native函数load,咱先一个一个来,跟进这个e函数
1 | private void e() { |
有点小长,作用就是加载对于的so库,先是判断一下存不存在,以及根据对应的架构加载相应的so,那么这么多so文件,到底加载了哪个呢,来看这
咱们看看f函数以及c函数都返回了啥
那么很明显了,咱们打开这个so文件进行查看
IDA打开,各种异常报错,先不管,强制打开
打开之后发现所有函数都没识别
那么很明显,so文件进行了加密,静态分析这条路暂时就走不通了,那么咱这就开始IDA动态分析
0x02 so文件动态分析
动态分析的烦处就在于需要找到各种反调试操作然后绕过它,这篇文章会详细讲讲这个
IDA怎样动态调试就不介绍了,要是什么都写一通的话就很累了
附加上调试后,F9运行几次后发现直接异常退出了,那么就存在反调试,
这里讲讲几个点
一是.init/init_array
二是jni_Onload函数
一般一些初始化,加解密,反调试操作都会放在这(init_array要早于jni_Onload函数执行),那么咱们先在这两处下断点
将手机下的 /system/bin/linker
以及/system/lib/libdvm.so
文件放入IDA中
在linker中搜索 [ Calling %s @ %p for ‘%s’ ]” ,然后在此处下断
在libdvm.so的dvmLoadNativeCode 函数下断
OK,断点下好后,咱继续开始调试
运行个几次后,开始进入libshella-2.10.1.so文件
这里按快捷键P让IDA将它识别成函数
之后呢,咱们F5查看伪代码会发现是有两个循环的操作,咱先不知道他是干啥,先慢慢单步执行
然后大概单步N下后发现又回到linker处了
那么,大胆猜测,init处的函数应该以及执行完了,那么是不是so已经解密了呢?咱还不确定,不管,先dump下来看看
打开dump下来的so文件
依然报异常,先强制打开
一个一个看,可以发现大部分函数好像已经解密,但是还是需要修复,因为我们有很多系统函数还是没有识别出来
咱利用so修复工具修复一下section表
修复完后发现jni_Onload函数已经解密,而且大部分系统函数也是别出来了(这里有个地方贼好玩,jni_Onload函数里调用jni_Onload函数,0.0,先不管,后面动态调的时候就能见分晓)
这个时候,东瞧瞧,西看看
看的有点晕0.0,好像差不多了
咱刚刚调到哪来着?
哦对了,刚刚执行完init段的解密函数,那么咱现在去jni_Onload函数瞅瞅
为了不让反调试干扰咱们,咱先处理掉这个
这里就直接说操作了,没有为啥,你调个几百行你就晓得哪有反调试了
第一处:
回到刚才解密函数那,此处看起了一个反调试进程,咱把它nop掉
咱们可以确认下,以面失误,一步步跟进这个函数
赞和刚才解密出的函数进行个对比,没错,九四它了
这里开启了一个线程,具体干了啥,检查了啥,有兴趣都可以更一下,也就那些个操作
我们这直接先把他干掉
然后继续运行
进入jni_Onload函数,咱继续对比下
此处有个raise(9)的操作,也是反调试杀死进程,咱同上,把此处nop掉
执行完此处的时候,咱们已经获取到了新的jni_Onload地址了,跳到v7处进行查看
此处有一个点,Thumb和arm模式的转换,因为IDA并不能每次都那么好地识别,所以需要手动转换(有个好记得办法,地址为奇数,那么就是Thumb)
快捷键alt+g,将value修改为1即可,然后按P创建函数
看不懂啊,这写的啥玩意儿?
先别着急,慢慢来看
先导入下JNI常用的几个结构体
然后对函数进行查看以及结构体修复然后函数重命名
1 | int __fastcall sub_6048B912(JavaVM *a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) |
咱看看这个string_init函数
1 | unsigned int string_init() |
这里对字符串进行了初始化的操作,方便后面的函数调用
再跟进下一个函数
1 | jclass __fastcall sub_6048B8D4(int a1) |
这里进行了native函数的注册
跟过去看下注册了哪些函数
此处IDA没有识别出来 ,
直接右键->Structure->JNINativeMethod
咱们跟过去查看下(有几个函数名进行了重命名)
1 | int *__fastcall sub_60492C54(JNIEnv *a1, int a2, int a3) |
查看下dalvik_load
1 | int *__fastcall dalvik_load(int a1, int a2, int a3) |
那么脱壳的话,此处就已经可以开始手动脱了。
那么整个逆向分析部分到这就结束了,后面的函数具体分析以及如何手动脱壳,咱们下次有机会再分享
0x03 结语
想说的是,这篇文章在操作上花了很重的笔墨,怎么说呢,要想详细介绍原理细节不是一句两句能说清楚的,比如为什么在这下断,还有dex加载,so加载等等细节,以及一些脱壳点,咱们下次有机会再分享。