乐于分享
好东西不私藏

【安卓逆向】模拟安卓环境调用SO库

【安卓逆向】模拟安卓环境调用SO库

最近帮群友逆向一个 APP,加密参数都已经分析出来了,唯独缺一个加密算法。

后面在一堆 SO 文件里找到了目标,IDA 加载一看——混淆非常严重,纯算法还原太耗时间。于是决定直接调用 SO 文件中的加密方法,走模拟执行路线。

 0 获取调用链

通过 JEB 或 MT 文件管理器加载 APK 后反编译,定位到目标 SO 文件路径。

根据路径可以初步判断这是一个 JNI 库。接下来用 nm -D查看 JNI 导出的函数名称:

▸ Shell 命令

nm -D libsu.so | grep encrypt

可以看到是静态注册的方式,理论上可以直接用 Java 调用这个 JNI 方法。

 1 直接调用(Java 方式)

先尝试最直观的方案:直接用 Java 加载 SO 文件并调用 native 方法。

▸ NativeDemo.java

public class NativeDemo {    static {        System.load("C:\\sofile\\libsu.so");    }    public native String encrypt(String input, String salt);    public static void main(String[] args) {        NativeDemo demo = new NativeDemo();        String input = "test";        String salt = "123456";        System.out.println(demo.encrypt(input, salt));    }}

编译运行后报错:

AMD-64 平台无法加载 ARM64 SO 文件的错误信息

⚠️ 报错原因无法在 AMD-64 平台调用 ARM64 架构的 SO 文件。这是安卓原生库与桌面 Java 环境的架构差异导致的,直接加载方案行不通。

不可能为了调用一个方法单独起个 APP 去跑,所以必须换方案——模拟安卓环境

 2 模拟安卓环境(Unidbg)

Unidbg 基于 Unicorn 引擎,可以模拟安卓原生库的执行环境,支持 ARM/ARM64 架构。

相关项目Unidbg 项目:https://github.com/zhkl0228/unidbgUnicorn 项目:https://www.unicorn-engine.org/需要 JDK 1.8 + Maven 环境,拉取项目后在本地编译。

▸ Maven 编译命令

& "C:\Program Files\JetBrains\IntelliJ IDEA 2026.1.1\plugins\maven\lib\maven3\bin\mvn.cmd" install "-Dgpg.skip=true" "-DskipTests"

编译完成后 unidbg-android 依赖已安装到本地 Maven 仓库

编译完成后,在项目中引入依赖:

▸ pom.xml 依赖配置

<dependency>    <groupId>com.github.zhkl0228</groupId>    <artifactId>unidbg-android</artifactId>    <version>0.9.10-SNAPSHOT</version></dependency>

然后编写 Unidbg 调用引擎:

▸ UnidbgEngineContext.java

package cn.mxpy;import com.github.unidbg.AndroidEmulator;import com.github.unidbg.Module;import com.github.unidbg.linux.android.AndroidEmulatorBuilder;import com.github.unidbg.linux.android.dvm.*;import com.github.unidbg.memory.Memory;import com.github.unidbg.linux.android.AndroidResolver;import java.io.File;import java.io.IOException;public class UnidbgEngineContext extends AbstractJni {    private final AndroidEmulator emulator;    private final VM vm;    private final DalvikModule dm;    private final Module module;    public UnidbgEngineContext() throws IOException {        emulator = AndroidEmulatorBuilder                .for64Bit()                .setProcessName("com.xxxx.xxxx")                .build();        Memory memory = emulator.getMemory();        memory.setLibraryResolver(new AndroidResolver(23));        vm = emulator.createDalvikVM();        vm.setJni(this);        vm.setVerbose(false);        File soFile = new File("C:\\Users\\i\\sofile\\libsu.so");        dm = vm.loadLibrary(soFile, false);        module = dm.getModule();        dm.callJNI_OnLoad(emulator);    }    public String encrypt(String arg0, String arg1) {        DvmClass clazz = vm.resolveClass("com/xxx/xx/xxx");        StringObject result = clazz.callStaticJniMethodObject(                emulator,                "encrypt(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",                new StringObject(vm, arg0),                new StringObject(vm, arg1)        );        return result.getValue();    }}

 App.java 中调用测试:

▸ App.java

package cn.mxpy;import java.io.IOException;public class App {    public static void main(String[] args) throws IOException {        System.out.println("Hello World!");        UnidbgEngineContext unidbgEngineContext = new UnidbgEngineContext();        System.out.println(unidbgEngineContext.encrypt("1", "1"));    }}

控制台输出加密后的字符串,证明调用成功

调用成功!

 3 集成 SpringBoot 提供 HTTP 接口

 Unidbg 与 SpringBoot 结合,把 SO 库中的加密方法封装成 HTTP 服务,供外部调用。

Controller 层接收参数,调用 UnidbgEngine 返回加密结果

通过 HTTP 请求即可获取加密结果:

▸ HTTP 调用示例

GET /api/encrypt?input=test&salt=123456→ 返回加密后的字符串

 4 总结与注意事项Unidbg 实例初始化耗时较长,本地测试约 7-8 秒,云主机约 12 秒左右。生产环境建议做实例池化或预热。另外 Unidbg 存在 "老化" 问题(长时间运行后稳定性下降),需要定期重启或维护。