Andorid jni本地校驗應用簽名(MD5/SHA1)
阿新 • • 發佈:2019-02-08
編碼環境:android API 19+Android studio
功能:開啟應用呼叫os庫的init方法校驗app是否被篡改!
流程:
—>開啟應用
—>os庫中init方法
—>C層獲取應用簽名(md5/sha1)
—>和本地正常的簽名對比
—>匹配不成功直接在C中退出應用。
實現:
在java層,新建一個native方法如:
public static native void init();
在jni下面新建一個.c檔案,名字隨意如test.c,在該檔案中要實現獲取應用簽名,對比,退出應用。
這個地方的init方法可以使用動態註冊的方式避免又長又臭的方法名在C檔案中(如何動態註冊請移步: http://blog.csdn.net/leifengpeng/article/details/52447864)
直接擼程式碼吧:
void init(JNIEnv* env, jobject thiz){
//獲取到Context
jobject context= getApplication(env);
jclass activity = (*env)->GetObjectClass(env,context);
// 得到 getPackageManager 方法的 ID
jmethodID methodID_func = (*env)->GetMethodID(env,activity, "getPackageManager" , "()Landroid/content/pm/PackageManager;");
// 獲得PackageManager物件
jobject packageManager = (*env)->CallObjectMethod(env,context,methodID_func);
jclass packageManagerclass = (*env)->GetObjectClass(env,packageManager);
//得到 getPackageName 方法的 ID
jmethodID methodID_pack = (*env)-> GetMethodID(env,activity,"getPackageName", "()Ljava/lang/String;");
//獲取包名
jstring name_str = (jstring)((*env)->CallObjectMethod(env,context, methodID_pack));
// 得到 getPackageInfo 方法的 ID
jmethodID methodID_pm = (*env)->GetMethodID(env,packageManagerclass,"getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
// 獲得應用包的資訊
jobject package_info = (*env)->CallObjectMethod(env,packageManager, methodID_pm, name_str, 64);
// 獲得 PackageInfo 類
jclass package_infoclass = (*env)->GetObjectClass(env,package_info);
// 獲得簽名陣列屬性的 ID
jfieldID fieldID_signatures = (*env)->GetFieldID(env,package_infoclass,"signatures", "[Landroid/content/pm/Signature;");
// 得到簽名陣列,待修改
jobject signatur = (*env)->GetObjectField(env,package_info, fieldID_signatures);
jobjectArray signatures = (jobjectArray)(signatur);
// 得到簽名
jobject signature = (*env)->GetObjectArrayElement(env,signatures, 0);
// 獲得 Signature 類,待修改
jclass signature_clazz = (*env)->GetObjectClass(env,signature);
//---獲得簽名byte陣列
jmethodID tobyte_methodId = (*env)->GetMethodID(env,signature_clazz, "toByteArray", "()[B");
jbyteArray signature_byte = (jbyteArray) (*env)->CallObjectMethod(env,signature, tobyte_methodId);
//把byte陣列轉成流
jclass byte_array_input_class=(*env)->FindClass(env,"java/io/ByteArrayInputStream");
jmethodID init_methodId=(*env)->GetMethodID(env,byte_array_input_class,"<init>","([B)V");
jobject byte_array_input=(*env)->NewObject(env,byte_array_input_class,init_methodId,signature_byte);
//例項化X.509
jclass certificate_factory_class=(*env)->FindClass(env,"java/security/cert/CertificateFactory");
jmethodID certificate_methodId=(*env)->GetStaticMethodID(env,certificate_factory_class,"getInstance","(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
jstring x_509_jstring=(*env)->NewStringUTF(env,"X.509");
jobject cert_factory=(*env)->CallStaticObjectMethod(env,certificate_factory_class,certificate_methodId,x_509_jstring);
//certFactory.generateCertificate(byteIn);
jmethodID certificate_factory_methodId=(*env)->GetMethodID(env,certificate_factory_class,"generateCertificate",("(Ljava/io/InputStream;)Ljava/security/cert/Certificate;"));
jobject x509_cert=(*env)->CallObjectMethod(env,cert_factory,certificate_factory_methodId,byte_array_input);
jclass x509_cert_class=(*env)->GetObjectClass(env,x509_cert);
jmethodID x509_cert_methodId=(*env)->GetMethodID(env,x509_cert_class,"getEncoded","()[B");
jbyteArray cert_byte=(jbyteArray)(*env)->CallObjectMethod(env,x509_cert,x509_cert_methodId);
//MessageDigest.getInstance("SHA1")
jclass message_digest_class=(*env)->FindClass(env,"java/security/MessageDigest");
jmethodID methodId=(*env)->GetStaticMethodID(env,message_digest_class,"getInstance","(Ljava/lang/String;)Ljava/security/MessageDigest;");
//如果取SHA1則輸入SHA1
//jstring sha1_jstring=(*env)->NewStringUTF(env,"SHA1");
jstring sha1_jstring=(*env)->NewStringUTF(env,"MD5");
jobject sha1_digest=(*env)->CallStaticObjectMethod(env,message_digest_class,methodId,sha1_jstring);
//sha1.digest (certByte)
methodId=(*env)->GetMethodID(env,message_digest_class,"digest","([B)[B");
jbyteArray sha1_byte=(jbyteArray)(*env)->CallObjectMethod(env,sha1_digest,methodId,cert_byte);
//toHexString
jsize array_size=(*env)->GetArrayLength(env,sha1_byte);
jbyte* sha1 =(*env)->GetByteArrayElements(env,sha1_byte,NULL);
char hex_sha[array_size*2+1];
int i;
for (i = 0;i<array_size;++i) {
hex_sha[2*i]=HexCode[((unsigned char)sha1[i])/16];
hex_sha[2*i+1]=HexCode[((unsigned char)sha1[i])%16];
}
hex_sha[array_size*2]='\0';
LOGV("sin-sha1:%s",hex_sha);
const char *sign=(*env)->GetStringUTFChars(env,signstr,NULL);
if (strcmp(hex_sha,sha1final)!=0){
LOGV("驗證不通過!");
exitApplication(env,0);
}else{
LOGV("驗證通過!");
}
}
獲取Context上下文方法:
jobject getApplication(JNIEnv *env) {
jclass localClass = (*env)->FindClass(env,"android/app/ActivityThread");
if (localClass!=NULL)
{
// LOGI("class have find");
jmethodID getapplication = (*env)->GetStaticMethodID(env,localClass, "currentApplication", "()Landroid/app/Application;");
if (getapplication!=NULL)
{
jobject application = (*env)->CallStaticObjectMethod(env,localClass, getapplication);
return application;
}
return NULL;
}
return NULL;
}
直接退出應用方法:(直接尋找到java/lang/Systemz這個類)
void exitApplication(JNIEnv *env, jint flag){
jclass temp_clazz = NULL;
jmethodID mid_static_method;
// 1、從classpath路徑下搜尋ClassMethod這個類,並返回該類的Class物件
temp_clazz =(*env)->FindClass(env,"java/lang/System");
mid_static_method = (*env)->GetStaticMethodID(env,temp_clazz,"exit","(I)V");
(*env)->CallStaticVoidMethod(env,temp_clazz,mid_static_method,flag);
(*env)->DeleteLocalRef(env,temp_clazz);
}
常量
const char HexCode[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
const char *sha1final="3A0558B19DE14EEC82FFA3721CED4AA9";
以上程式碼即可實現在應用中直接呼叫init方法即可觸發校驗。