移动安全之Android逆向系列:NDK开发&JNI接口
字数 2449 2025-08-09 15:23:15
Android逆向系列:NDK开发与JNI接口详解
一、NDK开发基础
1.1 NDK简介
Android NDK(Native Development Kit)是原生开发工具包,用于:
- 编译生成.so共享库文件
- 编译生成基于ARM架构的可执行程序
- 实现Java与C/C++代码的交互
1.2 交叉编译概念
- 平台差异:Linux系统通常使用x86_64架构,而Android手机基本使用ARM架构
- 交叉编译器:在本平台编译其他平台可执行程序的工具
- NDK作用:在Windows/Linux上编译出基于ARM架构的程序
二、NDK环境配置
2.1 安装步骤
- 下载NDK工具包(如android-ndk-r10e)
- 解压至系统目录(如C:\android-ndk-r10e)
- 配置环境变量,添加NDK根目录到PATH
2.2 目录结构
jni/
├── Android.mk # 模块编译规则
├── Application.mk # 应用配置
└── anquan.c # C源文件
三、NDK编译流程
3.1 编写C源文件
示例anquan.c:
#include <stdio.h>
int main() {
printf("test");
printf("\n");
return 0;
}
3.2 Makefile文件详解
Android.mk
LOCAL_PATH := $(call my-dir) # 获取当前目录路径
include $(CLEAR_VARS) # 清理LOCAL_变量
LOCAL_ARM_MODE := arm # 指定ARM指令集
LOCAL_MODULE := anquan # 模块名称
LOCAL_SRC_FILES := anquan.c # 源文件
# PIE安全机制编译选项
LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE
include $(BUILD_EXECUTABLE) # 编译为可执行文件
关键变量说明:
LOCAL_PATH:当前目录路径CLEAR_VARS:清理LOCAL_开头的全局变量LOCAL_ARM_MODE:指定指令集(arm/thumb)LOCAL_MODULE:输出文件名LOCAL_SRC_FILES:源文件列表BUILD_EXECUTABLE:编译目标类型(可执行文件)
Application.mk
APP_ABI := x86 armeabi-v7a
APP_ABI:指定目标CPU架构armeabi-v7a:第7代及以上ARM处理器
3.3 编译命令
在jni目录下执行:
ndk-build
输出目录结构:
libs/
└── armeabi-v7a/
└── anquan # 生成的可执行文件
四、Android设备测试
4.1 ADB操作流程
-
连接设备并检查:
adb devices -
推送可执行文件:
adb push libs/armeabi-v7a/anquan /data/local/tmp -
进入设备shell:
adb shell su cd /data/local/tmp -
修改权限并执行:
chmod 777 anquan ./anquan
五、JNI接口详解
5.1 JNI概述
JNI(Java Native Interface)是:
- Java与Native代码(C/C++)之间的桥梁
- 一大组函数接口API集合
- 实现双向调用(Java↔Native)
5.2 JNI作用
- 保护核心逻辑:反编译工具(如Jadx)只能显示方法名,无法显示Native方法体
- 复用现有C/C++库
- 提升关键代码性能
- 实现跨平台开发
5.3 JNI头文件分析
基本类型定义
Java类型与JNI类型对应关系:
| Java类型 | JNI类型 |
|---|---|
| byte | jbyte |
| char | jchar |
| short | jshort |
| int | jint |
| long | jlong |
| float | jfloat |
| double | jdouble |
| boolean | jboolean |
| void | void |
引用类型对应关系:
| Java类型 | JNI类型 |
|---|---|
| 所有对象 | jobject |
| java.lang.Class | jclass |
| java.lang.String | jstring |
| 数组 | jarray |
| Throwable | jthrowable |
本地接口结构体
主要包含三类方法:
-
Call方法 - 调用Java方法
jobject CallObjectMethod(JNIEnv*, jobject, jmethodID, ...);参数说明:
JNIEnv*:JNI环境指针jobject:Java对象实例jmethodID:方法ID(通过GetMethodID获取)...:可变参数(方法参数)
-
Get方法 - 获取字段/方法ID
jfieldID GetFieldID(JNIEnv*, jclass, const char*, const char*);参数说明:
jclass:Java类对象(FindClass获取)const char*:字段名const char*:字段签名
-
Set方法 - 设置字段值
void SetIntField(JNIEnv*, jobject, jfieldID, jint);
5.4 方法签名格式
Java方法在JNI中的表示形式:
(参数类型)返回值类型
示例:
()V:无参,返回void(I)V:接收int,返回void(Ljava/lang/String;)Z:接收String,返回boolean
基本类型签名:
| 类型 | 签名 |
|---|---|
| boolean | Z |
| byte | B |
| char | C |
| short | S |
| int | I |
| long | J |
| float | F |
| double | D |
| void | V |
| 类 | L全限定名; |
| 数组 | [类型 |
六、实践建议
-
编译优化:
- 使用最新NDK版本
- 合理设置APP_ABI(armeabi-v7a, arm64-v8a等)
- 启用PIE安全机制(Android 5.0+要求)
-
JNI开发:
- 使用
javah或javac -h自动生成头文件 - 注意内存管理(局部引用/全局引用)
- 处理Java异常(ExceptionCheck/ExceptionOccurred)
- 使用
-
逆向分析:
- IDA Pro分析.so文件
- 关注JNI_OnLoad函数
- 跟踪JNIEnv的函数调用
-
兼容性考虑:
- 32位/64位兼容
- 不同Android版本API差异
- 多CPU架构支持
通过本教程,您应该掌握了NDK开发的基本流程、JNI接口的核心概念以及如何在Android设备上运行原生程序。这些知识是Android逆向工程和底层开发的重要基础。