android 应用tips2: so加载逻辑&防护手段

2026-10-30

1. apk so加载逻辑

1.1 函数的基本介绍

函数名 描述
android_dlopen_ext()、dlopen()、do_dlopen() 这三个函数主要用于加载库文件。android_dlopen_ext 是系统的一个函数,用于在运行时动态加载共享库。与标准的 dlopen() 函数相比,android_dlopen_ext 提供了更多的参数选项和扩展功能,例如支持命名空间、符号版本等特性。
find_library() find_library() 函数用于查找库,基本的用途是给定一个库的名字,然后查找并返回这个库的路径。
call_constructors() call_constructors() 是用于调用动态加载库中的构造函数的函数。
init 库的构造函数,用于初始化库中的静态变量或执行其他需要在库被加载时完成的任务。如果没有定义 init 函数,系统将不会执行任何动作。需要注意的是,init 函数不应该有任何参数,并且也没有返回值。
init_array init_array 是 ELF(Executable and Linkable Format,可执行和可链接格式)二进制格式中的一个特殊段(section),这个段包含了一些函数的指针,这些函数将在 main() 函数执行前被调用,用于初始化静态局部变量和全局变量。
jni_onload 这是 Android JNI(Java Native Interface)中的一个函数。当一个 native 库被系统加载时,该函数会被自动调用。JNI_OnLoad 可以做一些初始化工作,例如注册你的 native 方法或者初始化一些数据结构。如果你的 native 库没有定义这个函数,那么 JNI 会使用默认的行为。JNI_OnLoad 的返回值应该是需要的 JNI 版本,一般返回 JNI_VERSION_1_6。

下断点时机

  • 应用级别的:java_com_XXX。
  • 外壳级别的:JNI_Onload、.init、.init_array(反调试)。
  • 系统级别的:fopen、fget、dvmdexfileopen(脱壳)。

理清楚so的加载逻辑,有助于我们分析反调试代码的加载逻辑,(反调试机制一般在启动时执行),从而实现破解。

2. so常见防护手段

主要功能 描述
SO 加壳 对 C/C++ 源码编译出来的 SO 文件进行加壳,使 SO 文件无法正确反编译和反汇编。
SO 代码虚拟化保护 将原始汇编指令翻译为自定义的虚拟机指令,跳转到自定义的虚拟机中执行,每次保护生成的虚拟机指令随机,且对虚拟机解释器再度混淆。
SO 防调用 对 SO 文件进行授权绑定,防止 SO 文件被非授权应用调用运行。
SO Linker 对整个 SO 文件进行加密压缩,包括代码段、符号表和字符串等,运行时再解密解压缩到内存,从而有效的防止 SO 数据的泄露。
SO 代码混淆 常量字符串加密、分裂基本块、等价指令替换、虚假控制流、控制流平坦化。
SO 环境监测 防 frida\xposed\root、防动态调试、防模拟器、防多开等。

2.1 so虚拟化/代码混淆 —— ollvm

ollvm提供了如下功能:

分类 描述
指令替换(Instructions Substitution)(Sub) 将一条运算指令替换为多条等价的运算指令,例如:y=x+1 变为 y=x+1+1-1。
虚假控制流(Bogus Control Flow)(bcf) 通过加入包含不透明谓词的条件跳转和不可达的基本块,来干扰 IDA 的控制流分析和 F5 反汇编。
控制流平坦化(Control Flow Flattening)(Fla) 主要通过一个主分发器来控制程序基本块的执行流程,将所有基本代码放到控制流最底部,然后删除原理基本块之间跳转关系,添加次分发器来控制分发逻辑,然后过新的复杂分发逻辑还原原来程序块之间的逻辑关系。
字符串加密 编写一个 pass 将其中的字符串信息使用一些加密算法进行加密,然后特定的时间进行还原。

2.1.1 指令替换

将一条运算指令,替换为多条等价的运算指令。例如:y=x+1变为y=x+1+1-1

2.1.2 虚假控制流(bcf)

虚假控制流混淆主要通过加入包含不透明谓词(相邻数字相乘恒为偶数)的条件跳转和不可达的基本块,来干扰IDA的控制流分析和F5反汇编
常见特征:不透明谓词–>例如y > 10 || x * (x + 1) % 2 != 0

2.1.3 控制流平坦化(Control Flow Flattening)

控制流平坦化,主要通过一个主分发器来控制程序基本块的执行流程。该方法将所有基本代码放到控制流最底部,然后删除原理基本块之间跳转关系,添加次分发器来控制分发逻辑,然后过新的复杂分发逻辑还原原来程序块之间的逻辑关系。

2.1.4 字符串加密

字符串加密的原理很简单,编写一个pass将其中的字符串信息使用一些加密算法进行加密,然后特定的时间进行还原。一般含有字符串混淆、函数名混淆、不在init_array解密等
常见的特征:datadiv_decoded

3. ollvm对抗

没有太好的傻瓜式方法(运行xxx工具一键跳转),一般都需要人结合工具的输出进行分析。

1.简单ollvm可以通过交叉引用分析
2.angr去除不透明谓词
3.Unicorn/Unidbg/AndroidNativeEmu模拟执行
4.IDA Trace
5.binary ninja
6.后端编译优化
7.frida辅助分析

references

https://github.com/ZJ595/AndroidReverse/
大佬写的真好~
《安卓逆向这档事》十二、大佬帮我分析一下
《安卓逆向这档事》十三、是时候学习一下Frida一把梭了(上)