You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android C++读取系统级JNI库并校验一致性的技术实现问询

Android JNI库校验和对比实现(防Root替换)

核心逻辑

  1. 要么提前将JNI库的原始SHA256校验和存入APK的assets目录,要么直接读取APK内的JNI库文件计算基准校验和
  2. 获取当前应用安装后,系统中JNI库的实际存储路径
  3. 计算系统中JNI库的校验和,与APK内的基准值对比,不一致则判定库被篡改

C++代码示例

1. 计算文件SHA256校验和的工具函数

#include <openssl/sha.h>
#include <fcntl.h>
#include <unistd.h>
#include <vector>
#include <string>

std::string calculateSHA256(const char* filePath) {
    int fd = open(filePath, O_RDONLY);
    if (fd < 0) {
        return "";
    }

    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    char buffer[4096];
    ssize_t bytesRead;

    while ((bytesRead = read(fd, buffer, sizeof(buffer))) > 0) {
        SHA256_Update(&sha256, buffer, bytesRead);
    }

    close(fd);
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_Final(hash, &sha256);

    // 转换为十六进制字符串
    char hexHash[SHA256_DIGEST_LENGTH * 2 + 1];
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
        sprintf(hexHash + i * 2, "%02x", hash[i]);
    }
    hexHash[SHA256_DIGEST_LENGTH * 2] = '\0';

    return std::string(hexHash);
}

2. 读取APK内assets中的基准校验和

如果是提前将校验和写入lib_baseline_sha256.txt并存入assets:

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

std::string getBaselineChecksum(AAssetManager* assetManager) {
    AAsset* asset = AAssetManager_open(assetManager, "lib_baseline_sha256.txt", AASSET_MODE_BUFFER);
    if (!asset) {
        return "";
    }

    const char* buffer = (const char*)AAsset_getBuffer(asset);
    off_t length = AAsset_getLength(asset);
    std::string checksum(buffer, length);
    AAsset_close(asset);

    // 去除可能的换行符
    size_t pos = checksum.find('\n');
    if (pos != std::string::npos) {
        checksum = checksum.substr(0, pos);
    }
    return checksum;
}

3. 直接读取APK内JNI库计算基准校验和

如果不想预存校验和,直接读取APK内的原始库文件计算:

std::string calculateBaselineFromAsset(AAssetManager* assetManager, const char* libPath) {
    AAsset* asset = AAssetManager_open(assetManager, libPath, AASSET_MODE_STREAMING);
    if (!asset) {
        return "";
    }

    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    char buffer[4096];
    int bytesRead;

    while ((bytesRead = AAsset_read(asset, buffer, sizeof(buffer))) > 0) {
        SHA256_Update(&sha256, buffer, bytesRead);
    }

    AAsset_close(asset);
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_Final(hash, &sha256);

    char hexHash[SHA256_DIGEST_LENGTH * 2 + 1];
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
        sprintf(hexHash + i * 2, "%02x", hash[i]);
    }
    hexHash[SHA256_DIGEST_LENGTH * 2] = '\0';

    return std::string(hexHash);
}

4. 获取当前JNI库的系统路径

#include <dlfcn.h>
#include <string>

std::string getCurrentLibPath() {
    // 获取当前库的句柄(假设当前代码就在要校验的JNI库中)
    void* handle = dlopen(nullptr, RTLD_NOW);
    if (!handle) {
        return "";
    }

    Dl_info info;
    if (dladdr((void*)&getCurrentLibPath, &info) == 0) {
        dlclose(handle);
        return "";
    }

    std::string path(info.dli_fname);
    dlclose(handle);
    return path;
}

5. 主校验入口(JNI调用函数)

#include <jni.h>

extern "C" JNIEXPORT jboolean JNICALL
Java_com_your_package_SecurityChecker_checkLibIntegrity(JNIEnv* env, jobject thiz, jobject assetManager) {
    AAssetManager* am = AAssetManager_fromJava(env, assetManager);
    if (!am) {
        return JNI_FALSE;
    }

    // 二选一:使用预存校验和
    std::string baseline = getBaselineChecksum(am);
    // 或者直接读取APK内的库计算(需替换为对应架构的库路径)
    // std::string baseline = calculateBaselineFromAsset(am, "lib/arm64-v8a/libyour_core.so");

    if (baseline.empty()) {
        return JNI_FALSE;
    }

    std::string systemLibPath = getCurrentLibPath();
    if (systemLibPath.empty()) {
        return JNI_FALSE;
    }

    std::string systemChecksum = calculateSHA256(systemLibPath.c_str());
    if (systemChecksum.empty()) {
        return JNI_FALSE;
    }

    return (baseline == systemChecksum) ? JNI_TRUE : JNI_FALSE;
}

关键注意事项

  • 依赖配置:需要在NDK项目中链接OpenSSL库,或改用Android系统内置的libcrypto.so
  • 架构适配:APK内的JNI库路径需匹配设备架构(如armeabi-v7a、arm64-v8a),可通过android_getCpuAbi()获取当前架构
  • 权限问题:Android 10+无需额外权限即可读取自身安装目录,低版本需确保应用有对应文件读取权限
  • 增强防护:可将基准校验和加密存储,避免攻击者直接篡改APK内的校验和文件
  • Root环境联动:结合Root检测逻辑,在Root环境下强制触发校验,甚至终止应用运行

内容的提问来源于stack exchange,提问作者suren

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 20:57:21