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

下一篇分析

相关推荐
Honmaple2 分钟前
OpenClaw 钉钉插件安装指南
服务器·网络·钉钉
范纹杉想快点毕业3 分钟前
嵌入式系统架构之道:告别“意大利面条”,拥抱状态机与事件驱动
java·开发语言·c++·嵌入式硬件·算法·架构·mfc
2501_940315268 分钟前
【无标题】2390:从字符串中移除*
java·开发语言·算法
半聋半瞎11 分钟前
Flowable快速入门(Spring Boot整合版)
java·spring boot·后端·flowable
特立独行的猫a13 分钟前
从XML到Compose的UI变革:现代(2026)Android开发指南
android·xml·ui·compose·jetpack
代码AI弗森17 分钟前
WSL2:在 Windows 上获得一整套可托付的 Linux 工程环境
linux·运维·服务器
散峰而望20 分钟前
【算法竞赛】树
java·数据结构·c++·算法·leetcode·贪心算法·推荐算法
xiangxiongfly91522 分钟前
Android 共享元素转场效果
android·动画·共享元素转场效果
我是阿亮啊22 分钟前
Android 中线程和进程详解
android·线程·进程·进程间通信
毕设源码-邱学长27 分钟前
【开题答辩全过程】以 基于SpringBoot的理工学院学术档案管理系统为例,包含答辩的问题和答案
java·spring boot·后端