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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章