android native 中的函数动态注册方式总结

一、最标准 & 官方的动态注册方式(JNI_RegisterNatives)

1️⃣ JNI_OnLoad + RegisterNatives(最常见

复制代码
static JNINativeMethod gMethods[] = {
    {"nativeAdd", "(II)I", (void *)native_add},
    {"nativeInit", "()V", (void *)native_init},
};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    vm->GetEnv((void **)&env, JNI_VERSION_1_6);

    jclass clazz = env->FindClass("com/example/Test");
    env->RegisterNatives(clazz, gMethods,
                         sizeof(gMethods)/sizeof(gMethods[0]));
    return JNI_VERSION_1_6;
}

✅ 特点

  • 方法名、签名、函数指针三元组

  • 字符串 + 函数指针同时存在

  • 非常适合静态分析 & Frida/Unidbg

🔍 逆向切入点

  • JNI_OnLoad

  • RegisterNatives

  • JNINativeMethod[]

2️⃣ 多 Class / 分模块注册

复制代码
registerClass(env, "com/a/A", methodsA);
registerClass(env, "com/b/B", methodsB);

✅ 特点

  • 一个 so 注册几十上百个类

  • 常见于大型 SDK(腾讯、阿里)

🔍 逆向技巧

  • FindClass

  • 跟字符串池里的 Java 类路径

二、半隐藏型:JNI 函数指针二次封装

3️⃣ 封装 RegisterNatives(初级混淆

复制代码

int my_register(JNIEnv *env, const char *cls, JNINativeMethod *m, int n) { jclass c = env->FindClass(cls); return env->RegisterNatives(c, m, n); }

甚至:

复制代码

(*(env->functions + 215))(env, clazz, methods, n);

✅ 特点

  • IDA 看不到 RegisterNatives 符号

  • 靠 JNI function table 调用

🔍 逆向

  • 识别 JNIEnv->functions

  • Frida hook RegisterNatives真实地址

三、反编译最烦的:字符串 / 表结构混淆

4️⃣ JNINativeMethod 表动态生成

复制代码
JNINativeMethod *m = malloc(sizeof(JNINativeMethod) * n);
m[i].name = decrypt("xxx");
m[i].signature = decrypt("xxx");
m[i].fnPtr = calc_func(i);

特点

  • 没有静态表

  • name/signature 运行时解密

🔍 逆向

  • 解密函数

  • Hook RegisterNatives 直接 dump

5️⃣ 函数指针运行时计算(offset / hash)

复制代码
m[i].fnPtr = base + offset[i];

或者:

fn = (void *)((char *)so_base + hash_table[name]);

✅ 特点

  • 函数地址无法静态交叉引用

  • 常见于加固 SDK

🔍

  • 先拿 so base

  • 动态调试时下断点

四、进阶:完全不走 RegisterNatives 的骚操作

6️⃣ 利用 JNI 函数表劫持(少见但狠

复制代码
env->functions->CallVoidMethod = my_call;

或者修改 ART 内部结构

⚠️ 极少见

  • 强依赖 Android 版本

  • 高风险但极难分析

7️⃣ 利用 dlsym + 手动调用 Java 方法

复制代码
void *handle = dlopen("libart.so", RTLD_NOW);
void *sym = dlsym(handle, "_ZN3art...");

直接调用 ART 内部接口

⚠️

  • 非 JNI 官方

  • 加固厂常用(某些 Dex2C / VMProtect)

五、结合 Java 层的"假动态注册"

8️⃣ Java 层 native 方法名动态生成

复制代码
static {
    String m = decrypt("native_" + x);
    System.loadLibrary("xxx");
    register(m);
}

Java 层看不到真实 native 名

🔍

  • 反 Java 混淆

  • 或直接 Hook JNI 层

六、现代加固常见组合拳(真实项目)

实战中经常是 多种方式叠加

技术 是否常见
JNI_OnLoad + RegisterNatives ⭐⭐⭐⭐⭐
字符串加密 ⭐⭐⭐⭐
函数指针偏移 ⭐⭐⭐
多 so 分散注册 ⭐⭐⭐
Dex2C + Native 调度 ⭐⭐⭐⭐
ART 私有 API ⭐⭐

七、逆向/Hook 统一破局思路(很重要)

下一篇分析

相关推荐
nono牛2 小时前
Makefile中打印变量
android
兮动人2 小时前
Maven指定加载的类
java·maven·maven指定加载的类
wangkay883 小时前
【Java 转运营】Day04:抖音新号起号前准备全指南
java·开发语言·新媒体运营
小李独爱秋3 小时前
计算机网络经典问题透视:TLS协议工作过程全景解析
运维·服务器·开发语言·网络协议·计算机网络·php
亲爱的非洲野猪3 小时前
Java线程池深度解析:从原理到最佳实践
java·网络·python
没有了遇见3 小时前
Android 关于RecycleView和ViewPager2去除边缘反馈
android
城东米粉儿3 小时前
android gzip数据压缩 笔记
android
城东米粉儿3 小时前
android 流量优化笔记
android
亲爱的非洲野猪3 小时前
深入解析享元模式:用Java实现高性能对象复用
java·开发语言·享元模式