JNI验证应用签名

JNI验证应用签名

原理:获取当前的签名信息并且跟期待的签名信息是否一致,如果是一致,则通过,否则失败。
这个工作在JNI_OnLoad中完成,如下代码:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv *evn;
    if (vm->GetEnv((void **)(&evn), JNI_VERSION_1_6) != JNI_OK)
    {
        return -1;
    }
    jclass appClass = evn->FindClass("com/***/App");
    jmethodID getAppContextMethod = evn->GetStaticMethodID(appClass, "getContext", "()Landroid/content/Context;");
    //获取APplication定义的context实例
    jobject appContext = evn->CallStaticObjectMethod(appClass, getAppContextMethod);
    // 获取应用当前的签名信息
    jstring signature = loadSignature(evn, appContext);
    // 期待的签名信息
    jstring keystoreSigature = evn->NewStringUTF("31BC77F998CB0D305D74464DAECC2");
    const char *keystroreMD5 = evn->GetStringUTFChars(keystoreSigature, NULL);
    const char *releaseMD5 = evn->GetStringUTFChars(signature, NULL);
    // 比较两个签名信息是否相等
    int result = strcmp(keystroreMD5, releaseMD5);
    if (DEBUG_MODE)
        LOGI("strcmp %d", result);
    // 这里记得释放内存
    evn->ReleaseStringUTFChars(signature, releaseMD5);
    evn->ReleaseStringUTFChars(keystoreSigature, keystroreMD5);
    // 得到的签名一样,验证通过
    if (result == 0){
        return JNI_VERSION_1_6;
    }
    return -1;
}

loadSignature(evn, appContext)也是反射调用Java代码实现的,是系统自带的功能,代码如下:

jstring loadSignature(JNIEnv *env, jobject context)
{
    // 获取Context类
    jclass contextClass = env->GetObjectClass(context);
    if (DEBUG_MODE)
        LOGI("获取Context类");
    // 得到getPackageManager方法的ID
    jmethodID getPkgManagerMethodId = env->GetMethodID(contextClass, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    if (DEBUG_MODE)
        LOGI("得到getPackageManager方法的ID");
    // PackageManager
    jobject pm = env->CallObjectMethod(context, getPkgManagerMethodId);
    if (DEBUG_MODE)
        LOGI("PackageManager");
    // 得到应用的包名
    jmethodID pkgNameMethodId = env->GetMethodID(contextClass, "getPackageName", "()Ljava/lang/String;");
    jstring  pkgName = (jstring) env->CallObjectMethod(context, pkgNameMethodId);
    if (DEBUG_MODE)
        LOGI("get pkg name: %s", getCharFromString(env, pkgName));
    // 获得PackageManager类
    jclass cls = env->GetObjectClass(pm);
    // 得到getPackageInfo方法的ID
    jmethodID mid = env->GetMethodID(cls, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    // 获得应用包的信息
    jobject packageInfo = env->CallObjectMethod(pm, mid, pkgName, 0x40); //GET_SIGNATURES = 64;
    // 获得PackageInfo 类
    cls = env->GetObjectClass(packageInfo);
    // 获得签名数组属性的ID
    jfieldID fid = env->GetFieldID(cls, "signatures", "[Landroid/content/pm/Signature;");
    // 得到签名数组
    jobjectArray signatures = (jobjectArray) env->GetObjectField(packageInfo, fid);
    // 得到签名
    jobject signature = env->GetObjectArrayElement(signatures, 0);
    // 获得Signature类
    cls = env->GetObjectClass(signature);
    // 得到toCharsString方法的ID
    mid = env->GetMethodID(cls, "toByteArray", "()[B");
    // 返回当前应用签名信息
    jbyteArray signatureByteArray = (jbyteArray) env->CallObjectMethod(signature, mid);
    return ToMd5(env, signatureByteArray);
}

获取签名信息并且转换为MD5格式的,如下:

jstring ToMd5(JNIEnv *env, jbyteArray source) {
    // MessageDigest类
    jclass classMessageDigest = env->FindClass("java/security/MessageDigest");
    // MessageDigest.getInstance()静态方法
    jmethodID midGetInstance = env->GetStaticMethodID(classMessageDigest, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;");
    // MessageDigest object
    jobject objMessageDigest = env->CallStaticObjectMethod(classMessageDigest, midGetInstance, env->NewStringUTF("md5"));
    // update方法,这个函数的返回值是void,写V
    jmethodID midUpdate = env->GetMethodID(classMessageDigest, "update", "([B)V");
    env->CallVoidMethod(objMessageDigest, midUpdate, source);
    // digest方法
    jmethodID midDigest = env->GetMethodID(classMessageDigest, "digest", "()[B");
    jbyteArray objArraySign = (jbyteArray) env->CallObjectMethod(objMessageDigest, midDigest);
    jsize intArrayLength = env->GetArrayLength(objArraySign);
    jbyte* byte_array_elements = env->GetByteArrayElements(objArraySign, NULL);
    size_t length = (size_t) intArrayLength * 2 + 1;
    char* char_result = (char*) malloc(length);
    memset(char_result, 0, length);
    // 将byte数组转换成16进制字符串,发现这里不用强转,jbyte和unsigned char应该字节数是一样的
    ByteToHexStr((const char*)byte_array_elements, char_result, intArrayLength);
    // 在末尾补\0
    *(char_result + intArrayLength * 2) = '\0';
    jstring stringResult = env->NewStringUTF(char_result);
    // release
    env->ReleaseByteArrayElements(objArraySign, byte_array_elements, JNI_ABORT);
    // 释放指针使用free
    free(char_result);
    return stringResult;
}

这个也是系统的MD5加密功能,可以看到先获取了系统自带的签名信息,然后跟一个预期的信息进行strcmp比较,如果是一致的话,那么通过,如果不一样,有可能程序被篡改了,就不能通过,然后采取其他的措施,比如杀掉进程等等方法来处理,这个需要在实际的业务中根据实际情况决定。

转自:https://mp.weixin.qq.com/s/q2_7E1Nnhyv4hJ9ziIFKLQ

猜你喜欢

转载自blog.csdn.net/jiejingguo/article/details/79917302