[安卓逆向]在Android Studio中编写SO文件并测试调用 (四)

安卓逆向在Android Studio中编写SO文件并测试调用 (四)

一、准备工作

Android Studio 版本: 2024.3

JDK 版本: 17

新建项目前,请先确认 NDK 已安装:File → Settings → Android SDK → SDK Tools,勾选 NDK (Side by side)CMake ,点击 Apply 安装。

二、创建 Native C++ 项目

  1. 打开 Android Studio,点击 New Project ,选择 Native C++ 模板(注意不是 Empty Activity)。
  2. 填写项目信息:
    • NameHelloJNI
    • Package namecom.example.hellojni
    • Minimum SDK:API 21(NDK 建议的最低版本)
    • Language:Java 或 Kotlin 均可
  3. 点击 Finish,等待项目构建完成。

项目会自动生成以下核心文件:

  • MainActivity:Java/Kotlin 层,包含 native 方法声明和 System.loadLibrary() 调用。
  • native-lib.cpp:C++ 层,实现 native 方法的具体逻辑。
  • CMakeLists.txt:CMake 构建脚本,配置 SO 库的编译规则。

三、编写 JNI 方法

3.1 在 Java/Kotlin 层声明 native 方法(可选)

打开 MainActivity,默认已经有一个 stringFromJNI() 方法:

java 复制代码
public native String stringFromJNI();

如果需要添加新方法,只需在类中声明:

java 复制代码
public native int add(int a, int b);

3.2 在 C++ 层实现 native 方法

打开 native-lib.cpp,按以下 JNI 函数命名规则实现:

cpp 复制代码
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_hellojni_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

函数名规则Java_包名_类名_方法名,包名中的 . 替换为 _

3.3 配置 CMakeLists.txt

项目自动生成的 CMakeLists.txt 通常位于 app/src/main/cpp/ 目录下,内容如下:

cmake 复制代码
cmake_minimum_required(VERSION 3.22.1)

project("hellojni")

add_library(
        hellojni
        SHARED
        native-lib.cpp
)

find_library(log-lib log)

target_link_libraries(
        hellojni
        ${log-lib}
)

默认配置已满足需求,一般无需修改。

3.4 在 build.gradle.kts 中配置 NDK 和 CMake

项目会自动生成 externalNativeBuild 配置,确认 app/build.gradle.kts(或 build.gradle)中包含:

kotlin 复制代码
android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    externalNativeBuild {
        cmake {
            path = file("src/main/cpp/CMakeLists.txt")
            version = "3.22.1"
        }
    }
}

如需指定 CPU 架构(减少包体积),可在 defaultConfig 中添加:

kotlin 复制代码
ndk {
    abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a"))
}

若不配置,SO 文件将默认生成到所有支持的架构目录中。

四、编译生成 SO 文件

  1. 点击菜单栏 Build,选择编译(或按 Ctrl+F9)。

  2. 编译完成后,生成的 SO 文件位于:

    复制代码
    app/build/intermediates/xx/

    各架构目录下均可找到 libhellojni.so 文件。

  3. 如需获取 Release 版,可将 Build Variant 切换为 release 后再执行编译。

五、调用并测试 SO 方法

由于 AS 2024.3 的默认布局中 MainActivity 已包含加载 SO 库和调用 stringFromJNI() 的示例代码,直接运行 App 即可验证。

5.1 确保 MainActivity 中已加载 SO 库

java 复制代码
static {
    System.loadLibrary("hellojni");
}

5.2 在界面中调用 native 方法

找到 onCreate 方法,已有示例:

java 复制代码
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());

5.3 运行 App

连接设备或启动模拟器,点击 Run 。界面应显示 Hello from C++,表示 SO 库加载成功,native 方法被正确调用。

5.4 查看 Logcat 确认 SO 加载信息

打开 Logcat 窗口,筛选 hellojninative,可看到类似以下信息:

复制代码
2026-06-01 10:00:00.000 12345-12345/? I/hellojni: Loading native library...

说明 SO 库已成功加载并运行。

六、SO 库导出和引用

如需将生成的 SO 库提供给其他项目使用:

  1. 复制 SO 文件 :从 app/build/intermediates/xx/ 中提取各架构目录下的 libhellojni.so

  2. 在目标项目中配置

    • 在目标 App 的 src/main/ 目录下创建 jniLibs 文件夹。

    • 按架构将 SO 文件放入对应子目录,如 jniLibs/arm64-v8a/libhellojni.so

    • build.gradle 中添加 sourceSets 配置:

      kotlin 复制代码
      android {
          sourceSets {
              main {
                  jniLibs.srcDirs = ['src/main/jniLibs']
              }
          }
      }
  3. 使用 SO 库 :将原 Java/Kotlin 类(包含 native 方法声明的类)完整复制到新项目中,注意 包名和类名必须与原项目完全相同 ,否则会报 UnsatisfiedLinkError

七、如何生成 .h 头文件

在 Android Studio 的 Native C++ 项目中,.h 头文件 不会自动生成,需要根据你的接口定义方式手动创建或通过工具生成。下面分两种常见场景说明。

7.1 纯 C/C++ 库(无 JNI,供其他 C/C++ 代码调用)

手动编写头文件

app/src/main/cpp 目录下新建 mylib.h,内容示例:

cpp 复制代码
// mylib.h
#ifndef MYLIB_H
#define MYLIB_H

#ifdef __cplusplus
extern "C" {
#endif

int add(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

然后在 mylib.cpp 中实现,并在 CMakeLists.txt 中正常添加。

生成的头文件直接用于其他 C/C++ 项目的 #include

7.2 JNI 库(供 Java/Kotlin 调用)

头文件需要根据 Java 中的 native 方法声明自动生成,保证函数签名与 JNI 规范一致。

7.2.1 编写 Java 类(声明 native 方法)

例如在 com.example.hellojni 包下创建 MyJNI.java

java 复制代码
package com.example.hellojni;

public class MyJNI {
    static {
        System.loadLibrary("hellojni");
    }
    public static native String stringFromJNI();
    public static native int add(int a, int b);
}
7.2.2 编译 Java 类生成 .class 文件

在 Android Studio 中点击 Build → Assemble Project(或按 Ctrl+F9)。

.class 文件会生成在 \app\build\intermediates\javac\release\compileReleaseJavaWithJavac\classes\com\example\hellojni\MyJNI.class

7.2.3 使用 javac -h 生成 JNI 头文件

打开 Android Studio 的 Terminal,执行:

bash 复制代码
cd app/src/main/java
javac -h ../../cpp com/example/hellojni/MyJNI.java
  • -h ../../cpp:指定头文件输出到 app/src/main/cpp 目录。
  • 后面的路径是 Java 源文件相对于当前目录的位置。

执行后,会在 app/src/main/cpp 下生成 com_example_hellojni_MyJNI.h,内容包含:

cpp 复制代码
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_hellojni_MyJNI */

#ifndef _Included_com_example_hellojni_MyJNI
#define _Included_com_example_hellojni_MyJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_hellojni_MyJNI
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_hellojni_MyJNI_stringFromJNI
  (JNIEnv *, jclass);

/*
 * Class:     com_example_hellojni_MyJNI
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_hellojni_MyJNI_add
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
7.2.4 在 C++ 代码中实现头文件声明的函数

native-lib.cpp 中:

cpp 复制代码
#include "com_example_hellojni_MyJNI.h"

JNIEXPORT jstring JNICALL Java_com_example_hellojni_MyJNI_stringFromJNI(JNIEnv *env, jclass clazz) {
    return env->NewStringUTF("Hello from JNI");
}

JNIEXPORT jint JNICALL Java_com_example_hellojni_MyJNI_add(JNIEnv *env, jclass clazz, jint a, jint b) {
    return a + b;
}
7.2.5 编译生成 .so 文件

按之前的操作 Ctrl+F9.so 文件会出现在 app/build/intermediates/xx/... 等路径中。

八.其他项目如何使用你的 .h + .so

C++ 调用Java 调用 两种场景。

8.1 Java 调用

纯 Java/Kotlin 代码,通过 JNI 调用 native 方法。

只需要 .so 文件和 Java 包装类,不需要 .h 头文件,也不需要 CMake。

8.1.1 交付物
复制代码
交付物/
├── jniLibs/
│   ├── arm64-v8a/
│   │   └── libhellojni.so
│   ├── armeabi-v7a/
│   │   └── libhellojni.so
│   ├── x86/
│   │   └── libhellojni.so      
│   └── x86_64/
│       └── libhellojni.so
└── MyJNI.java                   (Java 包装类)
8.1.2 使用方操作步骤
  1. 放置 SO 文件

    app/src/main/jniLibs/ 下按架构创建子目录(如 arm64-v8a),将对应的 .so 文件放进去。

  2. 复制 Java 包装类

    MyJNI.java 复制到项目的 java 目录下,保持原包名不变 (例如 com.example.hellojni)。

  3. 在代码中直接调用

    java 复制代码
    import com.example.hellojni.MyJNI;
    int result = MyJNI.add(1, 2);
  4. 可选:限制打包架构 (减少 APK 体积)

    app/build.gradle 中添加:

    kotlin 复制代码
    defaultConfig {
        ndk {
            abiFilters.addAll(listOf("arm64-v8a", "armeabi-v7a"))
        }
    }

不需要.h 头文件、CMakeLists.txtcpp 目录、externalNativeBuild 配置。

8.2 C++ 调用(其他 Native 库调用的 SO)

使用方有自己的 C++ 代码,需要直接调用你库中的函数(非 JNI 入口)。

需要同时提供 .h 头文件和 .so 库,并配置 CMake。

8.2.1 交付物
复制代码
交付物/
├── include/
│   └── com_example_hellojni_MyJNI.h   (或纯 C++ 头文件)
└── lib/
    ├── arm64-v8a/
    │   └── libhellojni.so
    └── armeabi-v7a/
        └── libhellojni.so
8.2.2 使用方操作步骤
  1. 放置文件

    • .h 文件放入 app/src/main/cpp/include/
    • .so 文件按架构放入 app/src/main/cpp/lib/${ANDROID_ABI}/
  2. 配置 CMakeLists.txt

  3. 在 C++ 代码中 #include 并调用

总结

  • 纯 C/C++ 库.h 手动编写。

  • JNI 库 :使用 javac -h 根据 Java native 方法生成 .h

相关推荐
January12071 小时前
IDEA 快捷键
java·ide·intellij-idea
ImTryCatchException1 小时前
Android 卡顿诊断 SDK:从痛点出发的设计思考
android·gitee
流星白龙2 小时前
【MySQL高阶】14.MySQL存储结构
android·数据库·mysql
流星白龙2 小时前
【MySQL高阶】15.MySQL存储结构,页结构
android·mysql·adb
赏金术士2 小时前
Android Tinker Demo 使用手册
android·热修复·tinker
Meteors.2 小时前
Kotlin协程序使用技巧和应用场景
android·开发语言·kotlin
黄林晴3 小时前
官方实战指南!Compose 项目无缝迁移 KMP
android·kotlin
tryqaaa_3 小时前
学习日志(五)【php反序列化全加例题】【pop链,字符逃逸,session,伪协议】
android·学习·php·web·pop·session
jingling5553 小时前
自建技术博客实战(三):工具专栏——地图定位、声音复刻与 rembg 抠图
android·开发语言·前端·ai·nextjs