移动安全之Android逆向系列:JNI动态注册
字数 973 2025-08-09 13:33:32

Android逆向系列:JNI动态注册详解

一、Android工程创建与配置

1. 创建新工程

  • 使用Eclipse开发环境
  • 选择File → New → Android Application Project创建工程

2. 添加UI组件

  1. 添加PlainText和TextView

    • 从Palette面板拖入PlainText和TextView组件
    • 用于用户输入和结果显示
  2. 添加Button组件

    • 添加四个Button分别对应加减乘除运算
  3. 修改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动态注册相比静态注册:

  1. 通过JNI_OnLoad入口进行注册
  2. 使用RegisterNatives方法显式绑定
  3. 逆向分析时需要关注JNINativeMethod结构体
  4. 动态注册更灵活,也更难被静态分析发现

动态注册的关键数据结构:

  • JNINativeMethod:连接Java和Native的桥梁
  • JNI_OnLoad:动态注册的入口点
  • RegisterNatives:实际的注册函数
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. 初始化控件绑定 2. 定义native方法 3. 获取编辑框值 4. 运算方法实现 三、JNI动态注册实现 1. 创建jni文件夹及C源文件 在项目根目录创建jni文件夹 创建computer.c文件 2. C层运算函数实现 3. 绑定Java与C层代码 第一个参数:Java方法名 第二个参数:方法签名((FF)F表示两个float参数,返回float) 第三个参数:C层函数指针 4. 注册函数实现 5. JNI_ OnLoad动态注册 JNI_ OnLoad是动态注册的关键入口 系统会自动调用JNI_ OnLoad 逆向分析时可在JNI_ OnLoad下断点 四、NDK配置文件 1. Android.mk 2. Application.mk 五、编译与测试 1. 生成so文件 2. 加载so库 3. 逆向分析要点 动态注册相比静态注册更隐蔽 关键分析点:JNI_ OnLoad → RegisterNatives 需要分析三个参数:Java方法名、方法签名、Native函数指针 六、总结 JNI动态注册相比静态注册: 通过JNI_ OnLoad入口进行注册 使用RegisterNatives方法显式绑定 逆向分析时需要关注JNINativeMethod结构体 动态注册更灵活,也更难被静态分析发现 动态注册的关键数据结构: JNINativeMethod:连接Java和Native的桥梁 JNI_ OnLoad:动态注册的入口点 RegisterNatives:实际的注册函数