JNI全原生实现:如何移除指定文件剩余Java层override方法声明
解决方案
完全可以实现将对应Java文件的所有逻辑迁移到原生层,不需要在源码层面保留override方法的native声明,可通过以下两种方案实现:
方案1:编译期字节码插桩
- 原理:编译阶段通过ASM、Javassist等字节码操作工具,自动向目标类插入你需要的native方法声明,你本地开发的Java源码可以完全清空所有逻辑,仅保留空类定义即可,打包后字节码里会自动生成对应的native声明供JNI注册使用。
- 注意事项:
- 插桩逻辑要在混淆前执行,同时给自动生成的native方法添加混淆排除规则,避免被ProGuard/R8优化移除
- 插桩逻辑本身可以加密放到构建脚本里,避免逆向人员拿到插桩规则反向还原方法列表
方案2:运行时动态生成类
- 原理:完全不需要提前编写Java类,在
JNI_OnLoad执行阶段,通过JNI调用Android系统的DexClassLoader,或者在原生层直接拼接dex字节码,动态生成继承自目标父类(比如Activity)的子类,自动重写所有需要override的方法,直接给动态生成的类注册原生方法实现,最后替换AndroidManifest中注册的启动类为动态生成的类即可,源码层面完全没有相关Java代码。 - 注意事项:
- Android 11及以上版本对动态加载的dex、动态生成的字节码有更严格的校验,需要提前适配隐藏API绕过逻辑,避免方法调用被拦截
- 部分定制ROM对动态类的加载有限制,需要提前做好兼容性测试
现有代码优化建议
你当前的实现可以做以下调整提升防篡改效果:
- 原生层
onCreate方法里创建的全局引用,要在Activity的onDestroy回调里调用DeleteGlobalRef释放,避免内存泄漏 - 注册完JNI方法后清空
natives数组的操作可以保留,能提升内存dump时获取方法映射关系的门槛 - 如果选择插桩方案,可以配合dex整体加密,把残留的Java空类也放到加密dex里,启动时在原生层解密加载,进一步提升逆向难度
现有参考代码
Java层现有代码
@Override protected native void onCreate(Bundle savedInstanceState);
CPP层现有代码
void onCreate(JNIEnv *env, jobject classObject, jobject bundle) { /**super.onCreate**/ gObjects[0] = env->NewGlobalRef(classObject); gClasses[0] = env->GetObjectClass(gObjects[0]); jclass s_Class = env->GetSuperclass(gClasses[0]); jmethodID oc_ID = env->GetMethodID(s_Class, "onCreate", "(Landroid/os/Bundle;)V"); env->CallNonvirtualVoidMethod(gObjects[0], gClasses[0], oc_ID, bundle); } extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *curVM_, void *reserved) { curVM = curVM_; curVM->GetEnv((void **) &environments[0], JNI_VERSION_1_6); /**Start of Native Method Registers**/ JNINativeMethod natives[1]; uint64_t pCount = sizeof(natives) / sizeof(natives[0]); jclass klass = environments[0]->FindClass("com/company/app/Activity"); natives[0] = {"onCreate", "(Landroid/os/Bundle;)V", (void *)onCreate}; environments[0]->RegisterNatives(klass , natives, pCount); for (uint64_t i = 0; i < pCount; i++) natives[i] = {"", ""}; return JNI_VERSION_1_6; }
内容的提问来源于stack exchange,提问作者user11988193
相关产品推荐
相关产品推荐

