安卓学习思路方法总结(六)
字数 1499 2025-08-09 15:23:13

Android NDK开发与ARM汇编基础教程

一、Eclipse创建NDK工程

  1. 使用Eclipse创建Android工程

    • 选择"File" → "New" → "Android Application Project"
    • 一路默认设置,点击"Finish"完成创建
  2. 工程配置

    • 删除默认的"Hello World"示例
    • 添加UI组件:
      • 2个PlainText(用于输入数字)
      • 2个TextView(用于显示标签)
      • 4个Button(用于加减乘除运算)
  3. XML布局修改

    • 修改组件ID:
      • "+"按钮:add
      • "-"按钮:sub
      • "*"按钮:mul
      • "/"按钮:div
    • 调整组件位置布局

二、Java层代码实现

  1. 初始化控件
private void init() {
    // 绑定编辑框变量
    EditText first = (EditText) findViewById(R.id.firstNum);
    EditText second = (EditText) findViewById(R.id.secondNum);
    
    // 绑定运算符按钮
    Button addBtn = (Button) findViewById(R.id.add);
    Button subBtn = (Button) findViewById(R.id.sub);
    Button mulBtn = (Button) findViewById(R.id.mul);
    Button divBtn = (Button) findViewById(R.id.div);
}
  1. 定义native方法
public native float add(float a, float b);
public native float sub(float a, float b);
public native float mul(float a, float b);
public native float div(float a, float b);
  1. 按钮监听与运算逻辑
addBtn.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        float one = Float.parseFloat(first.getText().toString());
        float two = Float.parseFloat(second.getText().toString());
        float result = add(one, two);
        Toast.makeText(MainActivity.this, String.valueOf(result), Toast.LENGTH_SHORT).show();
    }
});
// 其他运算符类似实现

三、JNI层实现

  1. 创建JNI文件夹和.c文件

    • 在项目根目录创建jni文件夹
    • 创建native-lib.c文件
  2. 实现native方法

#include <jni.h>

jfloat add(JNIEnv* env, jobject obj, jfloat a, jfloat b) {
    return a + b;
}

jfloat sub(JNIEnv* env, jobject obj, jfloat a, jfloat b) {
    return a - b;
}

jfloat mul(JNIEnv* env, jobject obj, jfloat a, jfloat b) {
    return a * b;
}

jfloat div(JNIEnv* env, jobject obj, jfloat a, jfloat b) {
    return a / b;
}
  1. 动态注册JNI方法
// 定义JNINativeMethod结构体数组
static JNINativeMethod nativeMethod[] = {
    {"add", "(FF)F", (void*)add},
    {"sub", "(FF)F", (void*)sub},
    {"mul", "(FF)F", (void*)mul},
    {"div", "(FF)F", (void*)div}
};

// 注册函数
jint registerNativeMethods(JNIEnv* env, const char* className) {
    jclass clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
        return JNI_ERR;
    }
    if ((*env)->RegisterNatives(env, clazz, nativeMethod, 
        sizeof(nativeMethod)/sizeof(nativeMethod[0])) < 0) {
        return JNI_ERR;
    }
    return JNI_OK;
}

// JNI_OnLoad实现
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    if (registerNativeMethods(env, "com/example/MainActivity") != JNI_OK) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

四、编译配置

  1. Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jsq          # 模块名称
LOCAL_SRC_FILES := jsq.c     # 源文件
LOCAL_ARM_MODE := arm        # 编译后的指令集
LOCAL_LDLIBS += -llog        # 依赖库
include $(BUILD_SHARED_LIBRARY) # 指定编译类型
  1. Application.mk文件
APP_ABI := x86 armeabi-v7a   # 支持的CPU架构
APP_PLATFORM := android-8    # 平台版本

五、ARM汇编基础

1. 跳转指令

  • B: 无条件跳转
  • BL: 带链接的无条件跳转
  • BX: 带状态切换的无条件跳转
  • BLX: 带链接和状态切换的无条件跳转
  • BEQ/BNE: 条件跳转

2. 存储器与寄存器交互指令

  • LDR: 从存储器加载数据到寄存器
    • 示例: LDR R8,[R9,#4] → 将R9+4地址的值加载到R8
  • STR: 将寄存器数据存储到存储器
    • 示例: STR R8,[R9,#4] → 将R8的值存储到R9+4地址
  • LDM/STM: 多寄存器加载/存储
  • PUSH/POP: 堆栈操作

3. 数据传送指令

  • MOV: 数据传送
    • 示例: MOV R0, #8 → R0=8

4. 算术运算指令

  • ADD/SUB/MUL/DIV: 加减乘除运算

5. 逻辑运算指令

  • AND: 与运算
  • ORR: 或运算
  • EOR: 异或运算
  • LSL/LSR: 逻辑左移/右移

6. 比较指令

  • CMP: 比较两个值并设置标志位

六、ARM处理器寻址方式

  1. 寄存器寻址

    • 示例: MOV R0, R1 → R0=R1
  2. 立即寻址

    • 示例: MOV R0, #0x1234 → R0=0x1234
  3. 寄存器偏移寻址

    • 示例: MOV R0, R1, LSL #2 → R0=R1*4
  4. 寄存器间接寻址

    • 示例: LDR R0, [R1] → 将R1的值作为地址,取该地址的值给R0
  5. 基址寻址

    • 示例: LDR R0, [R1, #-4] → 将R1-4的值作为地址,取该地址的值给R0
  6. 多寄存器寻址

    • 示例: LDMIA R1!, {R2-R7,R12} → 从R1地址连续加载数据到多个寄存器

七、SO库分析基础

  1. 常见指令解析:

    • LDR R3, [R0] → 将R0地址的值加载到R3
    • ADD R1, PC → R1=PC+R1
    • LDR R3, [R3, #0x18] → 将R3+0x18地址的值加载到R3
    • BLX R3 → 跳转到R3地址执行
  2. 函数调用过程:

    • 使用STM保存寄存器状态
    • 执行函数逻辑
    • 使用LDM恢复寄存器状态
    • 返回调用处

八、总结

本教程涵盖了从Android NDK开发到ARM汇编基础的全流程,重点包括:

  1. Eclipse创建NDK工程的基本步骤
  2. Java层与JNI层的交互实现
  3. JNI方法的动态注册过程
  4. ARM汇编基础指令集
  5. ARM处理器的多种寻址方式
  6. SO库的基本分析方法

掌握这些知识可以为Android底层开发、逆向分析和安全研究打下坚实基础。

Android NDK开发与ARM汇编基础教程 一、Eclipse创建NDK工程 使用Eclipse创建Android工程 选择"File" → "New" → "Android Application Project" 一路默认设置,点击"Finish"完成创建 工程配置 删除默认的"Hello World"示例 添加UI组件: 2个PlainText(用于输入数字) 2个TextView(用于显示标签) 4个Button(用于加减乘除运算) XML布局修改 修改组件ID: "+"按钮:add "-"按钮:sub "* "按钮:mul "/"按钮:div 调整组件位置布局 二、Java层代码实现 初始化控件 定义native方法 按钮监听与运算逻辑 三、JNI层实现 创建JNI文件夹和.c文件 在项目根目录创建jni文件夹 创建native-lib.c文件 实现native方法 动态注册JNI方法 四、编译配置 Android.mk文件 Application.mk文件 五、ARM汇编基础 1. 跳转指令 B : 无条件跳转 BL : 带链接的无条件跳转 BX : 带状态切换的无条件跳转 BLX : 带链接和状态切换的无条件跳转 BEQ/BNE : 条件跳转 2. 存储器与寄存器交互指令 LDR : 从存储器加载数据到寄存器 示例: LDR R8,[R9,#4] → 将R9+4地址的值加载到R8 STR : 将寄存器数据存储到存储器 示例: STR R8,[R9,#4] → 将R8的值存储到R9+4地址 LDM/STM : 多寄存器加载/存储 PUSH/POP : 堆栈操作 3. 数据传送指令 MOV : 数据传送 示例: MOV R0, #8 → R0=8 4. 算术运算指令 ADD/SUB/MUL/DIV : 加减乘除运算 5. 逻辑运算指令 AND : 与运算 ORR : 或运算 EOR : 异或运算 LSL/LSR : 逻辑左移/右移 6. 比较指令 CMP : 比较两个值并设置标志位 六、ARM处理器寻址方式 寄存器寻址 示例: MOV R0, R1 → R0=R1 立即寻址 示例: MOV R0, #0x1234 → R0=0x1234 寄存器偏移寻址 示例: MOV R0, R1, LSL #2 → R0=R1* 4 寄存器间接寻址 示例: LDR R0, [R1] → 将R1的值作为地址,取该地址的值给R0 基址寻址 示例: LDR R0, [R1, #-4] → 将R1-4的值作为地址,取该地址的值给R0 多寄存器寻址 示例: LDMIA R1!, {R2-R7,R12} → 从R1地址连续加载数据到多个寄存器 七、SO库分析基础 常见指令解析: LDR R3, [R0] → 将R0地址的值加载到R3 ADD R1, PC → R1=PC+R1 LDR R3, [R3, #0x18] → 将R3+0x18地址的值加载到R3 BLX R3 → 跳转到R3地址执行 函数调用过程: 使用STM保存寄存器状态 执行函数逻辑 使用LDM恢复寄存器状态 返回调用处 八、总结 本教程涵盖了从Android NDK开发到ARM汇编基础的全流程,重点包括: Eclipse创建NDK工程的基本步骤 Java层与JNI层的交互实现 JNI方法的动态注册过程 ARM汇编基础指令集 ARM处理器的多种寻址方式 SO库的基本分析方法 掌握这些知识可以为Android底层开发、逆向分析和安全研究打下坚实基础。