首页
社区
课程
招聘
[原创]安卓加固方案从落地加载到类指令抽取编写报告
发表于: 2020-9-9 14:38 12431

[原创]安卓加固方案从落地加载到类指令抽取编写报告

2020-9-9 14:38
12431

    PS:突然想起来好久没在看雪发过啦,这次就同步一下吧!!!

    PS:该文已经首发于某公众号,介意者勿喷!!!

    安卓的加固方案是从19年底开始写的,到现在为止差不多快一年了,写这个目的还是学习怎么脱壳,前几个月再看雪看到有人直接分析壳来学习,不过我感觉从加壳写起也是一种浪漫。因为个人原因,在类指令抽取壳哪里为半完成状态,在今年大概率没有时间接着修改了,在java层的加固就止于此吧!!!(PS:以后有时间会接着修改)

  环境配置:

    
     Android studio v3.5.3
    
华为G621-TL00 android v4.4.4

    a、原理很简单,就是首先将我们的dex文件或者apk文件解密,然后利用DexClassLoader加载器将其加载进内存中,然后利用反射加载待加固的apk的appkication,然后运行待加固程序即可,我画了个流程图详细说明如下:

        1.png

    b、上面说了大概原理,现在来说明一下具体细节,我们知道,在一个app开始运行的时候,第一个加载的类是ActivityThread,该类有个关键属性currentActivityThread,通过该属性能够获取到一系列其他关键的属性,例如mPackages,通过该属性,我们可以获取到mClassLoader属性,通过替换该属性我们可以替换系统加载器,如下所示:

2.png

    接着来说怎么获取待加固apk的application,这个通过在脱壳apk的AndroidManifest.xml中使用meta-data来获取,如下所示:

3.png

    在然后就是怎么替换application,我们可以知道在android.app.LoadedApk类中有一个方法makeApplication可以生成一个application,通过该方法生成一个application,然后通过替换android.content.ContentProvider类中的mContext属性完成application的替换,如下图所示:

4.png

    ps:因为第一代壳网上一大堆,所以讲得很粗略,同时这也不是本文的重点!!!

    通过上面的代码我们可以得到脱壳apk,然鹅待加固的apk放在哪里,网上大多放在脱壳dex的尾部,我又画了一张图,应该可以看图就懂了:

5.png

    这个我采用通过python读取二进制然后重新计算chunksum和签名字段实现,代码入戏:

    将上述apk重新签名后,安装运行,如下图所示:

6.png

7.png

    运行时报错如下所示:

8.png

    解决方案:报错显示无法实例化activity,经过检查是无法加载到正确格式的dex文件,检查你的解密代码,即使是你加密是象征型的异或了一个0xff,解密时也不能因为异或0xff值不变而不异或0xff。其次是打包成apk之前删除签名文件之后在签名!!!

    大体原理和第一代壳相同,和第一代壳不同的是,第一代壳将dex文件解密出来会保存到文件中,在通过DexClassLoader加载进内存中,而不落地加载直接重写DexClassLoader使其可以直接加载字节数组,避免写入文件中。我们要做的是重写DexClassLoader,而这涉及到三个函数defineClassfindClassloadClass,在一个类被加载的时候,会先后调用这三个函数加载一个类,所以我们需要重写这三个函数,但是我们怎么在重写的过程中操控dex中的类(通过字节数组加载进来的并不能直接操控)?其实系统的DexClassLoader加载dex进入内存的也必然是通过字节加载的,而在系统so中的libdvm.so中的openDexFile可以直接加载dex文件,那么现在清楚了,我们可以通过编写so文件调用openDexFile函数加载dex字节数组,值得注意的是,openDexFile函数返回值为一个int类型的cookie,可以简单理解成一个dex文件的'身份码',通过该'身份码'即可操控这个dex文件,至于怎么调用该函数,可以通过dlopendlsym函数调用,相关代码如下所示:

9.png

10.png

    a、首先编写样本,这里我写了一个类和一个方法,作用就是打印一个特征字符串,如下所示:

11.png

    b、将上面的样本打包成apk后提取出dex文件然后放置到assest文件夹下(该文件夹需要自己建立)供程序调用(ps:我这里图方便未对dex文件加密然后解密,有需要的可以加上),然后脱壳apk和上面的第一代壳没什么区别,唯一不同的是就是我们使用的是我们自己重写的DexClassLoader,如下图所示:

12.png

    c、运行截图如下:

13.png

  a、报错java.lang.UnsatisfiedLinkError: Native method not found

    解决方案:在配置文件中添加packagingOptions{ pickFirst "lib/armeabi-v7a/libtwoshell.so" pickFirst "lib/arm64-v8a/libtwoshell.so" pickFirst "lib/x86/libtwoshell.so" pickFirst "lib/x86_64/libtwoshell.so" },如下所示:

14.png

  b、运行到加载dex文件中的方法时,app直接闪退

    解决方案:重写的loadClass方法有问题,不能通过直接super调用父类方法,而是应该通过反射调用defineClassNative方法,如下所示:

15.png

    a、什么是类指令抽取壳,从名字就能看出来,就是把dex文件中的方法指令抽空,变成nop,然后在运行时再将指令还原!!!

    b、指令抽取可以通过010修改,现在来说指令还原,其余代码和第二代基本一样,不一样的地方在加载完dex之后执行指令还原函数,指令还原现在有两种方法,第一种是通过读取maps文件获取加载的dex文件地址,然后对dex文件进行解析,找到被nop的指令处进行还原(ps:该种方法需要及其熟悉dex文件格式,不了解的可以看我之前的文章关于解析dex文件,因为我之前解析的时候用的是python,改成c要大量时间,所以我选择了第二种方法);第二种方法就是通过免root hook系统函数(最简单的就是deFindClass函数)然后进行指令还原!!!

    c、接下来就将一下怎么通过hook dexFindClass函数来进行指令还原(PS:看懂下面的内容需要理解dex文件格式)。dexFindClass函数在libdvm.so库中,如下所示:

16.png

    免root hook框架有点多,我选择的是android inline hook,原因很简单,很适合在so层使用,其他的经过我测试不知道为啥我写出来的没反应,该框架github地址:https://github.com/ele7enxxh/Android-Inline-Hook,用法可以参考作者github,该inline hook框架需要原函数地址、新函数地址和原始函数的二级指针,用法如下所示(怎么使用不是重点,接下来的才是重点,所以这里比较粗略):

17.png

    我们要hook的是dexFindClass函数,该函数定义在DexFile.h文件中,该函数返回值为一个类结构指针,第二个参数为类名字,通过该参数我们就可以指定类进行指令还原,如下所示:

18.png

19.png

    上面我们得到的classDataOff,我们可以通过该地址获取到类数据,该偏移地址指向的是一个DexClassData结构,该结构的header存储了相关类信息,该结构的directMethods指针指向的方法的结构题,如下所示:

20.png

    通过directMethods指针我们可以顺着找到DexMethod结构体,通过该结构体的methodIdx调用系统函数dexGetMethodIddexStringById可以获取到方法名字,精确还原方法指令,通过该结构的codOff(这是个偏移地址)可获取方法指令,该偏移地址指向DexCode结构,该结构即存储了方法指令,利用memcpy替换即可达到指令还原的效果,如下所示:

21.png

22.png

    java层基本和第二代壳一样,只是多了一个调用hook的函数,so层关键代码如下所示:(ps:不知道为啥Android inline hook稳定性很差,上一个测试app还得行,下一个就疯狂报错了,所以代码是基本完成了,但是android inline hook报错未解决,有时间我会修改)

23.png

    报错未定义函数,如下所示:

24.png


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (12)
雪    币: 1385
活跃值: (5609)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
2
大佬,,都0202了,该更新成art了。
2020-9-9 15:19
1
雪    币: 0
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
直接搜索内存中的dex特征对抽取壳来说,应该可以做到还原的吧
2020-9-12 14:34
0
雪    币: 2914
活跃值: (4946)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
4
ljssrv 直接搜索内存中的dex特征对抽取壳来说,应该可以做到还原的吧
做不到,现在基本都是inline hook实现抽取壳,只有在类被调用的时候才还原指令,现在比较好的脱壳方案是主动调用所有函数
2020-9-12 21:47
0
雪    币: 346
活跃值: (145)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
看不懂,太深奥了。
2020-9-13 15:59
0
雪    币: 2914
活跃值: (4946)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
supperlitt 大佬,,都0202了,该更新成art了。
没必要
2020-9-13 21:43
0
雪    币:
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7

第一代壳运行不起来啊,本来以为是我的代码的问题然后下了你的源码还是运行不起来。报的Unable to instantiate application错误,连ProxyApplication都没建起来,环境用的雷电模拟器是Android5,望大佬有时间解答一下,看了你其他的贴子都很有帮助,谢谢!

最后于 2020-10-8 22:55 被wx_Simba编辑 ,原因:
2020-10-8 22:54
0
雪    币: 2914
活跃值: (4946)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
8
wx_Simba 第一代壳运行不起来啊,本来以为是我的代码的问题然后下了你的源码还是运行不起来。报的Unable to instantiate applicatio ...
我也遇见过这个问题在测试的时候,但导致这个问题出现的原因是解密代码未做异或操作,最后,建议使用真机进行测试(dv虚拟机下),虚拟机有时候确实会导致一些奇葩的问题(ps:我测试用的虚拟机是夜神)
2020-10-9 16:48
0
雪    币: 482
活跃值: (1007)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
赞大佬,“通过写加固来学习脱壳”支持这个想法
2020-10-16 18:21
0
雪    币:
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
之前那个报错解决了,然后现在是加载不了res,找了几天资料都是你代码里的这个方法,但是我在重写的getResources方法打log,发现根本没调用到重写的这个getResource方法,加载的资源还是脱壳Application的资源,真难搞
2020-10-17 00:00
0
雪    币:
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
windy_ll 我也遇见过这个问题在测试的时候,但导致这个问题出现的原因是解密代码未做异或操作,最后,建议使用真机进行测试(dv虚拟机下),虚拟机有时候确实会导致一些奇葩的问题(ps:我测试用的虚拟机是夜神)
大佬你写一二代壳的时候真的没有资源加载问题吗?还是你是直接把原APK的layout添加到脱壳项目里面了。我尝试了dv虚拟机也不行。
2020-10-21 13:03
0
雪    币: 1319
活跃值: (1960)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
现在又回到类似于PC版VM混析的地步分析了吗?
2020-10-21 14:02
0
雪    币: 234
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
学习了 赞
2022-12-28 23:06
0
游客
登录 | 注册 方可回帖
返回
//