1. 程式人生 > >Andorid jni本地校驗應用簽名(MD5/SHA1)

Andorid jni本地校驗應用簽名(MD5/SHA1)

編碼環境: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方法即可觸發校驗。