java jni 签名_Android JNI 防二次打包应用签名检测

国内android市场的环境比较混乱,大小市场有数百家,应用被修改或加广告病毒后二次打包发布也是常有的事情,对开发者和用户都造成了损失

经各方资料研究,在C++中做签名检测处理,安全性更高一些,结合网上资料,写下此文

签名检测逻辑获取应用签名生成的MD5值写入C++中 (C++中会打印出当前的MD5,后面会说)

获取Application

获取应用的签名信息

将签名信息MD5化

匹配签名MD5是否与写入的相同

签名不同时在JNI_OnLoad 中返回 -1

在App的代码中加载so文件,会自动调用JNI_OnLoad,若返回-1,App Crash掉

NDK编译

NDK编译使用的实验性NDK构建插件 Experimental Plugin

只要是因为该插件支持C语言Debug调试,但是使用该插件需要修改Module的Build.gradle文件,不太建议使用在开发的项目中,可以使用之前的NDK或CMake来编译NDK

Java代码上的逻辑/**

* 获取Application,ActivityThread是一个不被公开的类,java代码是不可直接调用的

* 对应C++代码中的static jobject getApplication(JNIEnv *env)

*/Application application = ActivityThread.currentApplication();/**

* 获取Signature数据

* 对应C++ 的jstring loadSignature(JNIEnv *env, jobject * *context) 方法

*/

PackageManager manager = application.getPackageManager();

String packageName = application.getPackageName();

PackageInfo packageInfo = manager.getPackageInfo(packageName,PackageManager.GET_SIGNATURES);

Signature signature = packageInfo.signatures[0];        byte[] chars = signature.toByteArray();

/**

* 将数据MD5化

* 对应C++的jstring ToMd5(JNIEnv *env, jbyteArray source)

*/

MessageDigest digest = MessageDigest.getInstance("md5");

digest.update(chars);        byte[] objArraySign = digest.digest(chars);

//MD5化的C++ 代码

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);//检测当前应用的签名MD5与我们设置的MD5是否一致

奉上C++ 代码#include #include #include #include #define  LOG_TAG    "native-dev"#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)const char *APP_SIGNATURE = "10645EA8A12BE7A2C04B1F81DF3B4D90";void ByteToHexStr(const char *source, char *dest, int sourceLen) {    short i;    char highByte, lowByte;    for (i = 0; i 

highByte = source[i] >> 4;

lowByte = source[i] & 0x0f;

highByte += 0x30;        if (highByte > 0x39) {

dest[i * 2] = highByte + 0x07;

} else {

dest[i * 2] = highByte;

}

lowByte += 0x30;        if (lowByte > 0x39) {

dest[i * 2 + 1] = lowByte + 0x07;

} else {

dest[i * 2 + 1] = lowByte;

}

}

}// byte数组转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);

env->DeleteLocalRef(classMessageDigest);

env->DeleteLocalRef(objMessageDigest);    return stringResult;

}//获取应用签名

jstring loadSignature(JNIEnv *env, jobject context) {    // 获得Context类

jclass cls = env->GetObjectClass(context);    // 得到getPackageManager方法的ID

jmethodID mid = env->GetMethodID(cls, "getPackageManager",                                     "()Landroid/content/pm/PackageManager;");    // 获得应用包的管理器

jobject pm = env->CallObjectMethod(context, mid);    // 得到getPackageName方法的ID

mid = env->GetMethodID(cls, "getPackageName", "()Ljava/lang/String;");    // 获得当前应用包名

jstring packageName = (jstring) env->CallObjectMethod(context, mid);    // 获得PackageManager类

cls = env->GetObjectClass(pm);    // 得到getPackageInfo方法的ID

mid = env->GetMethodID(cls, "getPackageInfo",                           "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");    // 获得应用包的信息

jobject packageInfo = env->CallObjectMethod(pm, mid, packageName, 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);

}//检测签名是否匹配jboolean checkSignature(

JNIEnv *env, jobject context) {

jstring appSignature = loadSignature(env,

context); // 当前 App 的签名

jstring releaseSignature = env->NewStringUTF(APP_SIGNATURE); // 发布时候的签名

const char *charAppSignature = env->GetStringUTFChars(appSignature, NULL);    const char *charReleaseSignature = env->GetStringUTFChars(releaseSignature, NULL);//    LOGI("  start cmp  getSignature");

__android_log_print(ANDROID_LOG_INFO, LOG_TAG, charAppSignature);//    LOGI("  start cmp  getReleaseSignature");

//  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, charAppSignature);

jboolean result = JNI_FALSE;    // 比较是否相等

if (charAppSignature != NULL && charReleaseSignature != NULL) {        if (strcmp(charAppSignature, charReleaseSignature) == 0) {

result = JNI_TRUE;

}

}

env->ReleaseStringUTFChars(appSignature, charAppSignature);

env->ReleaseStringUTFChars(releaseSignature, charReleaseSignature);    return result;

}static jobject getApplication(JNIEnv *env) {

jobject application = NULL;

jclass activity_thread_clz = env->FindClass("android/app/ActivityThread");    if (activity_thread_clz != NULL) {

jmethodID currentApplication = env->GetStaticMethodID(

activity_thread_clz, "currentApplication", "()Landroid/app/Application;");        if (currentApplication != NULL) {

application = env->CallStaticObjectMethod(activity_thread_clz, currentApplication);

} else {            //           LOGE("Cannot find method: currentApplication() in ActivityThread.");

}

env->DeleteLocalRef(activity_thread_clz);

} else {//        LOGE("Cannot find class: android.app.ActivityThread");

}    return application;

}/**

* 检查加载该so的应用的签名,与预置的签名是否一致

*/static jboolean checkSignature(JNIEnv *env) {    // 调用 getContext 方法得到 Context 对象

jobject appContext = getApplication(env);    if (appContext != NULL) {

jboolean signatureValid = checkSignature(

env, appContext);        return signatureValid;

}    return JNI_FALSE;

}/**

* 加载 so 文件的时候,会触发 OnLoad

* 检测失败,返回 -1,App 就会 Crash

*/JNIEXPORT jint JNICALLJNI_OnLoad(JavaVM *vm, void *reserved) {

JNIEnv *env;//    LOGI("  JNI_OnLoad  ");

if (vm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {        return -1;

}//    LOGI("  start checkSignature  ");

if (checkSignature(env) != JNI_TRUE) {//        LOGI("  checkSignature = false ");

// 检测不通过,返回 -1 就会使 App crash

return -1;

}    return JNI_VERSION_1_6;

}在C++的 checkSignature方法中,

__android_log_print(ANDROID_LOG_INFO, LOG_TAG, charAppSignature);

此代码会打印出当前应用的签名MD5码,也是我们需要写在C++中用于检测的MD5码,获取到之后写C++中并注释掉这行代码

加载jnistatic{

System.loadLibrary("SignatureLib");

}

参考资料

如有问题 欢迎指正 与君共勉

你可能感兴趣的:(java,jni,签名)