通过某软件学习简单本地签名检测方法

1.准备工作

帮一个朋友看一个软件的检测,说对软件的Application里面也加入了一些调试打印的方法,但是一直都走不了,软件就直接停止运行。

这是他的截图:

通过某软件学习简单本地签名检测方法_第1张图片



推辞不过,就打开电脑看一下了。

2.开始分析


打开先看AndroidManifest.xml配置清单文件,去了解我们需要知道的信息。




通过某软件学习简单本地签名检测方法_第2张图片



软件包名,是否有Application字段,启动Activity都是我们需要了解的内容。

结合朋友说的测试方法,以及看到软件的Application字段,我们先打开看一下这个类里面的流程。Application类是优先于MainActivity的,去做一些初始化的操作。

我们打开Application:




通过某软件学习简单本地签名检测方法_第3张图片



构造方法这里没什么内容,我们继续往下看:



通过某软件学习简单本地签名检测方法_第4张图片


结合朋友给我发的那张图他自己加入的打印方法,可以发现第一个方法vr.h(this)上面他没加打印,所以我们打开看一下这个方法.



通过某软件学习简单本地签名检测方法_第5张图片


看到这个,既有Signature的获取方法,又有killProcess方法,我们就可以直接确定这里是验证的地方了。

好吧,都不用调试就直接判断了,对朋友的分析也是佩服了,就差这一行代码舍不得看,把下面的几个方法都加入调试信息了。



3.测试

我们直接将那个h(context)方法返回void.


通过某软件学习简单本地签名检测方法_第6张图片


可以看到直接测试通过。



4.思考

虽然写这个判断需要很多行代码,但是过验证的方法,最终是都可以归结到一个判断上面的。

然后我们这里补充一下在java层的常用方法:

这个是从网上摘抄的:

http://www.oschina.net/code/snippet_196085_37562

大家也可以跳转过去看下原文章。

前人栽树,后人乘凉,在技术研究的路上,大家都是站在前辈们的肩膀上的。

 

Java层:

 

int checkAPP(Context context) {

    try {

        PackageInfo packageInfo = context.getPackageManager()

                .getPackageInfo(context.getPackageName(),

                        PackageManager.GET_SIGNATURES);

        Signature[] signs = packageInfo.signatures;

        Signature sign = signs[0];

         

        int hashcode = sign.hashCode();

        Log.i("test""hashCode : " + hashcode);

        return hashcode == -82892576 ? 1 : 0;

    } catch (Exception e) {

        e.printStackTrace();

    }

    return -1;

}

 

一般常用的是签名的hashCode()数值的验证。

而放到native层:

 

#include <string.h>

#include <stdio.h>

#include <jni.h>

#include<ALog.h>

 

jvalue JNU_CallMethodByName(JNIEnv *env, jboolean *hasException, jobject obj,

        const char *name, const char *descriptor, ...) {

    va_list args;

    jclass clazz;

    jmethodID mid;

    jvalue result;

    if ((*env)->EnsureLocalCapacity(env, 2) == JNI_OK) {

        clazz = (*env)->GetObjectClass(env, obj);

        mid = (*env)->GetMethodID(env, clazz, name, descriptor);

        if (mid) {

            const char *p = descriptor;

            /* skip over argument types to find out the

             · return type */

            while (*p != ')')

                p++;

            /* skip ')' */

            p++;

            va_start(args, descriptor);

            switch (*p) {

            case 'V':

                (*env)->CallVoidMethodV(env, obj, mid, args);

                break;

            case '[':

            case 'L':

                result.l = (*env)->CallObjectMethodV(env, obj, mid, args);

                break;

            case 'Z':

                result.z = (*env)->CallBooleanMethodV(env, obj, mid, args);

                break;

            case 'B':

                result.b = (*env)->CallByteMethodV(env, obj, mid, args);

                break;

            case 'C':

                result.c = (*env)->CallCharMethodV(env, obj, mid, args);

                break;

            case 'S':

                result.s = (*env)->CallShortMethodV(env, obj, mid, args);

                break;

            case 'I':

                result.i = (*env)->CallIntMethodV(env, obj, mid, args);

                break;

            case 'J':

                result.j = (*env)->CallLongMethodV(env, obj, mid, args);

                break;

            case 'F':

                result.f = (*env)->CallFloatMethodV(env, obj, mid, args);

                break;

            case 'D':

                result.d = (*env)->CallDoubleMethodV(env, obj, mid, args);

                break;

            default:

                (*env)->FatalError(env, "illegaldescriptor");

            }

            va_end(args);

        }

        (*env)->DeleteLocalRef(env, clazz);

    }

    if (hasException) {

        *hasException = (*env)->ExceptionCheck(env);

    }

    return result;

}

 

//合法的APP包名

const char *global_app_packageName = "com.example.abc";

//合法的hashcode

const int global_app_signature_hash_code = -82892576;

//合法标记

int legitimate = 0;

jint Java_com_example_abc_MainActivity_jniCheckAPP(JNIEnv* env, jobject context,

        jobject thiz) {

    LOGI("start jniCheckAPP");

 

    // 获得 Context 类

    jboolean hasException;

 

    //获取包名

    jstring jstr_packageName = (jstring) JNU_CallMethodByName(env,

            &hasException, thiz, "getPackageName""()Ljava/lang/String;").l;

 

    if ((*env)->ExceptionCheck(env) || jstr_packageName == NULL) {

        LOGI("can't get jstr of getPackageName");

        return -1;

    }

    //获取包名的字符串

    const char* loc_str_app_packageName = (*env)->GetStringUTFChars(env,

            jstr_packageName, NULL);

    if (loc_str_app_packageName == NULL) {

        LOGI("can't get packagename from jstring");

        return -2;

    }

    //当前应用包名与合法包名对比

    if (strcmp(loc_str_app_packageName, global_app_packageName) != 0) {

        LOGI("this app is illegal");

        return -3;

    }

 

    //释放loc_str_app_packageName

    (*env)->ReleaseStringUTFChars(env, jstr_packageName,

            loc_str_app_packageName);

 

    // 获得应用包的管理器

    jobject package_manager = JNU_CallMethodByName(env, &hasException, thiz,

            "getPackageManager""()Landroid/content/pm/PackageManager;").l;

    if ((*env)->ExceptionCheck(env) || package_manager == NULL) {

        LOGI("can't get obj of getPackageManager");

        return -4;

    }

 

    // 获得应用包的信息

    jobject package_info = JNU_CallMethodByName(env, &hasException,

            package_manager, "getPackageInfo",

            "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;",

            (*env)->NewStringUTF(env, global_app_packageName), 64).l;

    if ((*env)->ExceptionCheck(env) || package_info == NULL) {

        (*env)->ExceptionClear(env);

        LOGI("can't get obj of package_info");

        return -5;

    }

 

    // 获得 PackageInfo 类

    jclass pi_clazz = (*env)->GetObjectClass(env, package_info);

 

    // 获得签名数组属性的 ID

    jfieldID fieldID_signatures = (*env)->GetFieldID(env, pi_clazz,

            "signatures""[Landroid/content/pm/Signature;");

    (*env)->DeleteLocalRef(env, pi_clazz);

    // 得到签名数组,待修改

    jobjectArray signatures = (*env)->GetObjectField(env, package_info,

            fieldID_signatures);

 

    if ((*env)->ExceptionCheck(env) || signatures == NULL) {

        LOGI("can't get jobjectArray of signatures");

        return -6;

    }

 

    // 得到签名

    jobject signature = (*env)->GetObjectArrayElement(env, signatures, 0);

    if ((*env)->ExceptionCheck(env) || signature == NULL) {

        LOGI("can't get obj of signature");

        return -7;

    }

    //获取当前应用hashcode

    int hash_code = JNU_CallMethodByName(env, &hasException, signature,

            "hashCode""()I").i;

    if ((*env)->ExceptionCheck(env) || package_manager == NULL) {

        LOGI("can't get hash_code of signature");

        return -8;

    }

 

    LOGI("this app hash_code of signature is %d", hash_code);

    //合法返回1,否则返回0,并改变legitimate的值

    return legitimate = (hash_code == global_app_signature_hash_code);

}

 

//=====================================================================

typedef union {

    JNIEnv* env;

    void* venv;

} UnionJNIEnvToVoid;

static JNINativeMethod methods[] = { { "jniCheckAPP",

        "(Landroid/content/Context;)I",

        (void*) Java_com_example_abc_MainActivity_jniCheckAPP } };

 

static const char *classPathName = "com/example/abc/MainActivity";

 

static int registerNativeMethods(JNIEnv* env, const char* className,

        JNINativeMethod* gMethods, int numMethods) {

    jclass clazz;

    clazz = (*env)->FindClass(env, className);

    if (clazz == NULL) {

        return JNI_FALSE;

    }

    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {

        LOGE("RegisterNatives failed");

        return JNI_FALSE;

    }

    return JNI_TRUE;

}

static int registerNatives(JNIEnv* env) {

    if (!registerNativeMethods(env, classPathName, methods,

            sizeof(methods) / sizeof(methods[0]))) {

        return JNI_FALSE;

    }

    return JNI_TRUE;

}

 

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

    UnionJNIEnvToVoid uenv;

    uenv.venv = NULL;

    jint result = -1;

    JNIEnv* env = NULL;

    if ((*vm)->GetEnv(vm, &uenv.venv, JNI_VERSION_1_4) != JNI_OK) {

        goto bail;

    }

    env = uenv.env;

    if (registerNatives(env) != JNI_TRUE) {

 

        goto bail;

    }

    result = JNI_VERSION_1_4;

    bail: LOGI("JNI_ONload result '%d' ", result);

    return result;

}

 

 

3.总结

从本篇文章从头看到尾,我们可以通过自己的逆向能力了解到自己可以做到哪一点,是只能做到java层的验证,还是可以做好native层的验证。

上面的都是简单常用的一些验证,想一想如果是在java层加上反射,将那些函数中的字符串如signature全部反射调用,把方法名字符串全部用一个算法来生成,如反转字符串或者一些其他算法来组合呢?

Native层的,我们也可以使用上面的方法,还可以将方法多次分割几次,然后每次分割的短方法都做一下验证,虽然最终有个判断,但是如果将判断单独提到一个方法,然后再做几个方法检测这个方法是否是被人修改过的,都是可以加深下逆向难度的~



你可能感兴趣的:(签名验证,安卓签名验证,破解防护)