加壳与脱壳之分类技术

篇幅有限

完整内容及源码关注公众号:ReverseCode,发送

历代加壳技术分类

动态加载是用到的时候再去加载,也叫懒加载,是dex加壳,插件化,热更新的基础,避免发布新版本需要用户全量更新app,快速迭代,提升用户体验。比如阿里的AndFix和HotFix,腾讯的tinker,美团的Robust等热修复框架的基础。动态加载的dex不具有生命周期特征,APP中的Activity,Service等组件无法正常工作,只能完成一般函数的调用。需要对ClassLoader进行修正,APP才能够正常运行,有反射替换反射插入两种修正手段,这是加壳app必然要做的一步。

第一代壳:dex加密

  1. Dex字符串加密

  2. 资源加密

  3. 对抗反编译

  4. 反调试

  5. 自定义 DexClass Loader

由于是dex整体保护,在内存中映射是整体连续的,通过定位起始地址将app完整脱下来

第二代壳:dex抽取与加固

  1. 对抗第一代壳常见的脱壳法

  2. Dex method代码抽取到外部(通常企业版)

  3. Dex动态加载

  4. So加密

首先dex整体保护,其次对关键函数进行抽取(dump内存区域中dex关键类为空)进行so加密,对抗第一代壳常用脱壳法(从关键hook点dump dex)

第三代壳:dex动态解密与混淆

  1. Dex method代码动态解密

  2. So代码膨胀混淆

  3. 对抗之前出现的所有脱壳法

对抗第二代壳的一个通用脱壳工具DexHunter(通过遍历dex所有类进行加载初始化,将内存中dex完整恢复),dex中method动态解密,指定函数只有被调用时才执行,dex在内存中始终不是完整的状态

第四代壳:vmp壳

目前仍然是smali指令级别的vmp,未来将出现arm指令级别vmp

加壳技术分析

dex加固

  • dex整体加固:文件加载(监控app访问文件的记录)和内存加载(对内存dex的解密直接动态加载
  • 函数抽取:在函数粒度完成代码的保护:dump下dex的关键函数体置空
  • VMP和Dex2C:JAVA函数 Native化。vmp->逆向分析解释器找到取址译码

dex整体加固

dex整体加壳是基础防护,所有加壳的app必然都有,关键在于怎么区分函数抽取,vmp以及dex2c甚至多种技术混合的混合型壳。

  1. 文件加载:定位解密文件是关键

  2. 内存加载:加载时机和内存起始地址是关键

通用方案:dex打开和优化的流程以及产出的odex、dex2oat编译的流程和生成的oat文件等等

函数抽取

  1. 类加载和函数执行前的流程解密
  2. 函数执行中动态自解密

方案:关注被抽取的函数的执行流程是关键!定位被抽取的函数的恢复时机即可。

获取到保护的dex后,函数体的内容是无效的,注意这里说的是无效,而不是无意义,有的app壳后函数依然是有意义的,但不是我们想要的。f5查看函数体全部为空,则表现为函数抽取。

函数抽取样本

函数抽取样本

VMP

定位解释器是关键,找到映射关系便可恢复。

获取到保护的dex后,函数的属性由java属性变为 Native,典型的有数字的 onCreate函数 Native化JNI函数。vmp壳的核心就是dalvik虚拟机的解释器原理。

vmp样本

若所有vmp保护的函数都有自己的解释器则so必然很大,所以必然共享一个解释器,vmp化的函数注册地址一致或者函数逻辑相似。

vmp化的函数注册地址一致

MainActivity动态注册地址在d3dec3f1,TestActivity地址一致,说明vmp保护。

参考vmp加壳方案之ADVMP,vmp对每一种smali指令处理,保护了函数后函数属性发生改变,因为解释器一般是用JNI实现,java易被反编译,故没用java实现,基本由c实现,防止快速定位到解释器的取址译码和执行的流程会加ollvm混淆保护之类。ADVMP

Dex2c

基础是编译原理,进行了等价语义转换,彻底还原难度巨大。

方案:关注JNI相关的api调用是关键,也是分析VMP和dex2c保护的函数的逻辑的关键。

获取到保护的dex后保护的函数属性有java变成native,dcc保护后所有java属性的函数变成jni函数,只能看到类名和参数。核心原理与编译原理相关,传统编译原理是编译器经过词法分析语法分析生成二进制代码,dex2c中生成结果是c、c++文件,经过ndk编译器最终编译为so,每一个函数基本编译成一个c/c++文件,ubuntu中编译最终生成一个so。

dcc保护

dex2c核心对指定java函数进行语义分析生成各个不同的c/c++代码编译为相应so,自然dex2c保护的函数逻辑不一致,自然注册地址不同。

dex2c保护注册地址不同

MainActivity的地址在7ba61b17c0,TestActivity地址在7ba61ac604,说明dex2c保护。

dcc_out.apk在lib下编译生成了so文件中,jni函数静态注册都存放于so文件中,使用IDA v7打开libnc.so,搜索java就可以找到所有jni函数。

dcc_out编译于项目LoadDex,新增FirstActivity,SecondActivity

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static{
try {
System.loadLibrary("nc");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
setContentView(R.layout.activity_main);
Button mybutton01=findViewById(R.id.button01);
mybutton01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
context.startActivity(new Intent(context,FirstActivity.class));
}
});
Button mybutton02=findViewById(R.id.button02);
mybutton02.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
context.startActivity(new Intent(context,SecondActivity.class));
}
});

AndroidManifest.xml

1
2
<activity android:name=".FirstActivity"></activity>
<activity android:name=".SecondActivity"></activity>

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="FirstActivity"
/>

<Button
android:id="@+id/button02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="SecondActivity"
/>

</LinearLayout>

dcc_ollvm.apk 在dex2c过程中加入了ollvm,在ida中打开so,jni找不到。

参考dex2c加壳方案之dcc

1
2
3
4
git clone https://github.com/amimo/dcc.git
pyenv local 3.8.5 && cd dcc && pip install -r requirements.txt
vim filter.txt 添加.*onejane*. 编译函数名含有onejane的函数
python dcc.py dcc.apk -o dcc_out.apk 加固app

VMP和dex2c是高级防护

  1. VMP:定位解释器是关键,找到映射关系便可恢复

  2. dex2c:基础是编译原理,进行了等价语义转换,彻底还原难度巨大:

通用分类技术:关注JNI相关的ap调用是关键,也是分析VMP和dex2c保护的函数的逻辑的关键。

混合型壳

多种加固技术混合使用,比如先将原有smal指令流使用VMP或dex2c保护,构建object数组传递给jni函数进行调用,然后再经过函数抽取进一步保护

混合型壳

区分apk保护技术

是否Native化函数体无效
函数抽取类壳
vmp壳native化
Dex2c壳native化

so加固

  • 基于init、 init_array以及JNI_Onload函数的加壳

  • 基于自定义linker的加壳

IDA快捷键

空格键反汇编窗口切换文本跟图形
ESC退到上一个操作地址
F5C伪代码
alt+t搜索文本

  • 一二三代壳和加壳技术分类识别
文章作者: J
文章链接: http://onejane.github.io/2021/02/21/加壳与脱壳之分类技术/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 万物皆可逆向
支付宝打赏
微信打赏