安卓学习思路方法总结(六)
字数 1499 2025-08-09 15:23:13
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
- 调整组件位置布局
- 修改组件ID:
二、Java层代码实现
- 初始化控件
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);
}
- 定义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);
- 按钮监听与运算逻辑
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层实现
-
创建JNI文件夹和.c文件
- 在项目根目录创建jni文件夹
- 创建native-lib.c文件
-
实现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;
}
- 动态注册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;
}
四、编译配置
- 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) # 指定编译类型
- 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处理器寻址方式
-
寄存器寻址
- 示例:
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地址的值加载到R3ADD R1, PC→ R1=PC+R1LDR R3, [R3, #0x18]→ 将R3+0x18地址的值加载到R3BLX R3→ 跳转到R3地址执行
-
函数调用过程:
- 使用STM保存寄存器状态
- 执行函数逻辑
- 使用LDM恢复寄存器状态
- 返回调用处
八、总结
本教程涵盖了从Android NDK开发到ARM汇编基础的全流程,重点包括:
- Eclipse创建NDK工程的基本步骤
- Java层与JNI层的交互实现
- JNI方法的动态注册过程
- ARM汇编基础指令集
- ARM处理器的多种寻址方式
- SO库的基本分析方法
掌握这些知识可以为Android底层开发、逆向分析和安全研究打下坚实基础。