移动安全之Android逆向系列:JNI动态注册
字数 973 2025-08-09 13:33:32
Android逆向系列:JNI动态注册详解
一、Android工程创建与配置
1. 创建新工程
- 使用Eclipse开发环境
- 选择File → New → Android Application Project创建工程
2. 添加UI组件
-
添加PlainText和TextView
- 从Palette面板拖入PlainText和TextView组件
- 用于用户输入和结果显示
-
添加Button组件
- 添加四个Button分别对应加减乘除运算
-
修改activity_main.xml
- 配置组件属性
- 为每个Button设置唯一ID:R.id.add, R.id.sub, R.id.mul, R.id.div
二、MainActivity.java实现
1. 初始化控件绑定
private EditText textone;
private EditText texttwo;
private Button add;
private Button sub;
private Button mul;
private Button div;
private void init() {
// 绑定编辑框
textone = (EditText) findViewById(R.id.editText1);
texttwo = (EditText) findViewById(R.id.editText2);
// 绑定按钮
add = (Button) findViewById(R.id.add);
sub = (Button) findViewById(R.id.sub);
mul = (Button) findViewById(R.id.mul);
div = (Button) findViewById(R.id.div);
}
2. 定义native方法
private float one;
private float two;
// 定义native的加减乘除方法,实现在so层
public native float add(float one, float two);
public native float sub(float one, float two);
public native float mul(float one, float two);
public native float div(float one, float two);
3. 获取编辑框值
one = Float.parseFloat(textone.getText().toString());
two = Float.parseFloat(texttwo.getText().toString());
4. 运算方法实现
private void yunsuan(){
final OnClickListener ner=new OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.add:
one = Float.parseFloat(textone.getText().toString());
two = Float.parseFloat(texttwo.getText().toString());
Toast.makeText(MainActivity.this, add(one,two)+"", 1).show();
break;
// 类似实现sub、mul、div...
}
}
};
add.setOnClickListener(ner);
sub.setOnClickListener(ner);
mul.setOnClickListener(ner);
div.setOnClickListener(ner);
}
三、JNI动态注册实现
1. 创建jni文件夹及C源文件
- 在项目根目录创建jni文件夹
- 创建computer.c文件
2. C层运算函数实现
#include <jni.h>
jfloat addcc(JNIEnv *env, jobject obj, jfloat a, jfloat b){
return a+b;
}
jfloat subcc(JNIEnv *env, jobject obj, jfloat a, jfloat b){
return a-b;
}
jfloat mulcc(JNIEnv *env, jobject obj, jfloat a, jfloat b){
return a*b;
}
jfloat divcc(JNIEnv *env, jobject obj, jfloat a, jfloat b){
return a/b;
}
3. 绑定Java与C层代码
JNINativeMethod nativeMethod[]={
{"add", "(FF)F", (void*)addcc},
{"sub", "(FF)F", (void*)subcc},
{"mul", "(FF)F", (void*)mulcc},
{"div", "(FF)F", (void*)divcc}
};
- 第一个参数:Java方法名
- 第二个参数:方法签名((FF)F表示两个float参数,返回float)
- 第三个参数:C层函数指针
4. 注册函数实现
jint reg(JNIEnv *env){
jclass _jclass = (*env)->FindClass(env, "com/example/MainActivity");
if ((*env)->RegisterNatives(env, _jclass, nativeMethod,
sizeof(nativeMethod)/sizeof(nativeMethod[0])) != JNI_OK) {
return JNI_ERR;
}
return JNI_OK;
}
5. JNI_OnLoad动态注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv* env;
if( (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK){
return JNI_ERR;
}
if( reg(env) != JNI_OK){
return JNI_ERR;
}
return JNI_VERSION_1_4;
}
- JNI_OnLoad是动态注册的关键入口
- 系统会自动调用JNI_OnLoad
- 逆向分析时可在JNI_OnLoad下断点
四、NDK配置文件
1. Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := computer
LOCAL_SRC_FILES := computer.c
LOCAL_ARM_MODE := arm
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
2. Application.mk
APP_ABI := armeabi-v7a
五、编译与测试
1. 生成so文件
ndk-build
2. 加载so库
static{
System.loadLibrary("computer");
}
3. 逆向分析要点
- 动态注册相比静态注册更隐蔽
- 关键分析点:JNI_OnLoad → RegisterNatives
- 需要分析三个参数:Java方法名、方法签名、Native函数指针
六、总结
JNI动态注册相比静态注册:
- 通过JNI_OnLoad入口进行注册
- 使用RegisterNatives方法显式绑定
- 逆向分析时需要关注JNINativeMethod结构体
- 动态注册更灵活,也更难被静态分析发现
动态注册的关键数据结构:
- JNINativeMethod:连接Java和Native的桥梁
- JNI_OnLoad:动态注册的入口点
- RegisterNatives:实际的注册函数