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 统一破局思路(很重要)

下一篇分析

相关推荐
云烟成雨TD1 分钟前
Agent Scope Java 2.x 系列【1】核心架构
java·人工智能·agent
愛~杦辷个訾6 分钟前
Java Springboot使用阿里云oss对图片进行等质量压缩,转换成webp格式的压缩图。
java·spring boot·阿里云·oss
故渊at13 分钟前
第十三板块:Android 综合架构与未来演进 | 第三十一篇:Android 架构演进与 Fuchsia OS 的挑战
android·架构·宏内核·微内核·fuchsia·ipc 性能博弈
aqi0013 分钟前
一文速览 HarmonyOS 6.1.1 推出的十个新特性
android·华为·harmonyos·鸿蒙·harmony
霸道流氓气质17 分钟前
Spring Boot Multipart 表单中文乱码问题全解析
java·spring boot·后端
dadaobusi19 分钟前
Linux内核完成大量内存/调度/时间子系统初始化的关键阶段
java·linux·前端
唐墨12320 分钟前
关于linux kernel错误码为负数编码这件事情,我个人的一些看法
linux·运维·服务器
matrixmind124 分钟前
aiomysql:异步场景下的 MySQL 驱动
android·数据库·mysql·其他
随遇丿而安25 分钟前
第8周:弹窗 / 提示组件全功能与弹窗优化
android
zh_xuan25 分钟前
诡异Bug:输入框删除字符,却越删越多
android·bug