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比較,如果是一致的話,那麼通過,如果不一樣,有可能程序被篡改了,就不能通過,然後採取其他的措施,比如殺掉進程等等方法來處理,這個需要在實際的業務中根據實際情況決定。