基于NDK验签的方式实现APP重签名校验方案

APP重签名是指黑客通过修改APP的签名信息,使得APP看起来像是由原开发者签名发布的,但实际上是被黑客篡改过的。这种行为会破坏APP的完整性和安全性,给用户带来不必要的风险。因此,开发者需要采取一些措施来防止APP重签名,保护APP的安全性。其中一种常见的方式是基于NDK验签的方式实现APP重签名校验方案。

基于NDK验签的方式实现APP重签名校验方案的原理如下:

  • 开发者在应用程序中添加一些Native代码,用于实现校验APP签名的功能
  • 在APP启动时,Native代码会对APP的签名信息进行校验,判断APP是否被篡改
  • 如果APP的签名信息与原始签名不一致,Native代码会拒绝APP的运行
  • 为了防止黑客通过Hook等手段绕过Native代码的校验,开发者可以采取一些措施,如加入反调试、加密等机制

需要注意的是,基于NDK验签的方式实现APP重签名校验方案并不能完全防止所有的破解行为,但可以有效地提高APP的安全性,减少被破解的风险。因此,开发者需要根据自己的实际情况选择合适的安全措施来保护自己的APP。

总的来说,基于NDK验签的方式实现APP重签名校验方案是一种有效的安全保护手段,可以帮助开发者提高APP的安全性,避免被重签名的风险。希望开发者们能够重视APP安全问题,采取一些有效的措施来保护自己的APP。

static const char *applicationClassPath = "com/zhupeng/demo/app/CoreApplication";

// 应用签名
static const char *appSign = "加密后的应用签名";

//加密
jstring encrypt(JNIEnv *env, jbyteArray data) {
    jclass messageDigestClass = env->FindClass("java/security/MessageDigest");
    jmethodID getInstanceStaticMethodID = env->GetStaticMethodID(messageDigestClass,
                                                                 "getInstance",
                                                                 "(Ljava/lang/String;)Ljava/security/MessageDigest;");
    jobject digest = env->CallStaticObjectMethod(messageDigestClass, getInstanceStaticMethodID,
                                                 env->NewStringUTF("SHA1"));

    jmethodID digestMethodID = env->GetMethodID(messageDigestClass, "digest", "([B)[B");
    jbyteArray sha1Bytes = (jbyteArray) env->CallObjectMethod(digest, digestMethodID, data);

    // 将字节数组转换为十六进制字符串
    jsize sha1BytesLen = env->GetArrayLength(sha1Bytes);
    jbyte *sha1BytesData = env->GetByteArrayElements(sha1Bytes, NULL);
    char *hexChars = "0123456789ABCDEF";
    char *sha1Hex = static_cast<char *>(malloc(sha1BytesLen * 2 + 1));
    for (int i = 0; i < sha1BytesLen; i++) {
        sha1Hex[i * 2] = hexChars[(sha1BytesData[i] & 0xf0) >> 4];
        sha1Hex[i * 2 + 1] = hexChars[sha1BytesData[i] & 0x0f];
    }
    sha1Hex[sha1BytesLen * 2] = 0;

    env->ReleaseByteArrayElements(sha1Bytes, sha1BytesData, JNI_ABORT);

    return env->NewStringUTF(sha1Hex);
}

// 验证签名是否被修改
jint verifySignature(JavaVM *vm) {
    try {
        JNIEnv *env;
        if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
            return JNI_ERR;
        }

        jclass contextClass = env->FindClass("android/content/Context");
        jmethodID getPackageManagerMethodID = env->GetMethodID(contextClass, "getPackageManager",
                                                               "()Landroid/content/pm/PackageManager;");
        jmethodID getPackageNameMethodID = env->GetMethodID(contextClass, "getPackageName",
                                                            "()Ljava/lang/String;");
        jclass packageManagerClass = env->FindClass("android/content/pm/PackageManager");
        jmethodID getPackageInfoMethodID = env->GetMethodID(packageManagerClass, "getPackageInfo",
                                                            "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
        jclass packageInfoClass = env->FindClass("android/content/pm/PackageInfo");
        jfieldID signaturesFieldID = env->GetFieldID(packageInfoClass, "signatures",
                                                     "[Landroid/content/pm/Signature;");
        jclass signatureClass = env->FindClass("android/content/pm/Signature");
        jmethodID toByteArrayMethodID = env->GetMethodID(signatureClass, "toByteArray", "()[B");
        jmethodID toCharsStringMethodId = env->GetMethodID(signatureClass, "toCharsString",
                                                           "()Ljava/lang/String;");

        jclass applicationClass = env->FindClass(applicationClassPath);
        jmethodID getInstanceStaticMethodID = env->GetStaticMethodID(applicationClass,
                                                                     "getInstance",
                                                                     "()Landroid/app/Application;");
        // 获取当前的Context对象
        jobject context = env->CallStaticObjectMethod(applicationClass, getInstanceStaticMethodID);
        jobject packageManager = env->CallObjectMethod(context, getPackageManagerMethodID);
        jstring packageName = (jstring) env->CallObjectMethod(context, getPackageNameMethodID);
        jobject packageInfo = env->CallObjectMethod(packageManager, getPackageInfoMethodID,
                                                    packageName,
                                                    0x00000040);
        jobjectArray signatures = reinterpret_cast<jobjectArray>(env->GetObjectField(packageInfo,
                                                                                     signaturesFieldID));
        jobject signature = env->GetObjectArrayElement(signatures, 0);
        jbyteArray signatureBytes = (jbyteArray) env->CallObjectMethod(signature,
                                                                       toByteArrayMethodID);
        jobject signatureObj = env->CallObjectMethod(signature, toCharsStringMethodId);

        // 将签名字节数组转换为C++字符串
        // jsize length = env->GetArrayLength(signatureBytes);
        // jbyte *bytes = env->GetByteArrayElements(signatureBytes, NULL);
        // std::string signatureString(reinterpret_cast<char *>(bytes), length);
        // env->ReleaseByteArrayElements(signatureBytes, bytes, JNI_ABORT);
        // 将签名字符串打印出来
        // __android_log_print(ANDROID_LOG_INFO, "JNI", "App Signature: %s", signatureString.c_str());

        jstring signatureString = reinterpret_cast<jstring>(signatureObj);
        const char *signatureChars = env->GetStringUTFChars(encrypt(env, signatureBytes), 0);

        // 将签名字符串打印出来
        __android_log_print(ANDROID_LOG_INFO, "JNI", "App Signature: %s", signatureChars);

        jclass buildConfigClass = env->FindClass("com/virtual/video/BuildConfig");
        jfieldID channelFieldID = env->GetStaticFieldID(buildConfigClass, "channel",
                                                           "Ljava/lang/String;");
        jstring channel= env->GetStaticObjectField(buildConfigClass, channelFieldID);
 
        jboolean equals = (strcmp(appSign, signatureChars) == 0) ? JNI_TRUE : JNI_FALSE;

        if (!equals) {
            // 签名不一致,退出应用
            // 似乎无效,会停留在启动页,直接返回JNI_ERR
            jclass systemClass = env->FindClass("java/lang/System");
            jmethodID exitMethod = env->GetStaticMethodID(systemClass, "exit", "(I)V");
            env->CallStaticVoidMethod(systemClass, exitMethod, 0);
            
            return JNI_ERR;
        }
    } catch (const char *msg) {
    }
    return JNI_VERSION_1_6;
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    global_jvm = vm;
    return verifySignature(vm);
}

感谢大家的支持,如有错误请指正,如需转载请标明原文出处!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值