android jni签名验证(二)

来源:互联网 发布:linux查看应用占用内存 编辑:程序博客网 时间:2024/06/07 06:46

此方法主要是应对hook框架,目前比较流行的API hook框架有xposed,cydia substrate,这两个都可以对系统的api进行hook,

由于上述框架都是系统层次上进行Hook,很难进行检测。下面给出一个简单的例子,可以简单的防hook框架。

说明:

  1. 使用libzip库,可以自行到网上下载

  2. 可以防止对apk安装路径的hook,如getPackageResourcePath();,Application.getApplicationInfo().sourceDir等方法

  3. 不采用系统api进行签名获取,通过内建相关方法获取签名信息。防Hook。如网上流传“空道大神”提供的HOOK模块。

  4. 暂时就这些,基本可以防止签名进行破解,相关介绍和代码如下

#include <jni.h>#include <dlfcn.h>#include <fcntl.h>#include <signal.h>#include <stdlib.h>#include <string.h>#include<sys/types.h>#include <stdio.h>#include <android/log.h>#include "libzip/zip.h"/*author:xiaobaiyeydate:2015年11月7日20:52:00email:xiaobaiyey@outlook.com*/#define LOG_TAG    __FILE__ ":" STRINGIFY(__LINE__)#define LOGE(format, ...)   __android_log_print(ANDROID_LOG_ERROR, "xiaobai", format, ##__VA_ARGS__);#define LOGI(format, ...)   __android_log_print(ANDROID_LOG_INFO, "xiaobai", format, ##__VA_ARGS__);int ECsigoffset = 0;int signlength = 0;unsigned char *g_sig = NULL;struct zip* APKArchive;char * findApkSignFile(const char * apkPath, char * sigdir, char * pOut);void verifySign(JNIEnv *env, jobject obj);jstring getentyString();//获取app包名int PidToName(int pid, char *lpOutBuf) {    if (lpOutBuf == NULL) {        return 0;    }    int nLen = 0;    char filename[256] = { 0 };    char cmdline[0x200] = { 0 };    sprintf(filename, "/proc/%d/cmdline", pid);    FILE *fp = fopen(filename, "r");    if (fp) {        if (fgets(cmdline, sizeof(cmdline), fp) != NULL) {            char *p = strchr(cmdline, ':');            if (p) {                *p = 0;            }            strcpy(lpOutBuf, cmdline);            nLen = strlen(cmdline);        }        else {            nLen = 0;        }         fclose(fp);    }    return nLen;}//获取apk的安装路径,不用系统api是为了防止hookchar * getpacknameTopath(char *packname, char *pOut) {    FILE *fr = fopen("/proc/self/maps", "r");    if (NULL == fr) {        return NULL;    }     char szfindbuf[256] = "/data/app/";    char szbuf[256] = { 0 };    strncat(szfindbuf, packname, sizeof(szfindbuf));    char *ppath = NULL;    char *ptmp = NULL;    while (!feof(fr))  //当文件有多行时要做的判断 判断是否到末尾    {        memset(szbuf, 0, sizeof(szbuf));        if (fgets(szbuf, sizeof(szbuf), fr)) {            if ((ppath = strstr(szbuf, szfindbuf))                && (ptmp = strstr(szbuf, "apk"))) {                ptmp[strlen("apk")] = 0;                strcpy(pOut, ppath);                fclose(fr);                return pOut;            }        }    }    fclose(fr);    return NULL;}char* findApkSignFile(const char* apkPath, char *sigdir, char *pOut) {    // LOGI("Loading APK %s", apkPath);    APKArchive = zip_open(apkPath, 0, NULL);    if (APKArchive == NULL) {        //LOGI("Error loading APK");        return NULL;    }    //Just for debug, print APK contents    int numFiles = zip_get_num_files(APKArchive);    int i = 0;    for (i = 0; i < numFiles; i++) {        const char* name = zip_get_name(APKArchive, i, 0);        if (name == NULL) {            //LOGI("Error reading zip file name at index %i : %s", zip_strerror(APKArchive));            return NULL;        }        //   LOGI("File %i : %s\n", i, name);        if (0 == memcmp(sigdir, name, strlen(sigdir)) && strrchr(name, '.')            && (0 == memcmp("RSA", strrchr(name, '.') + 1, strlen("RSA"))                || 0                == memcmp("DSA", strrchr(name, '.') + 1,                    strlen("DSA"))                || 0                == memcmp("EC", strrchr(name, '.') + 1,                    strlen("EC")))) {            if (0 == memcmp("EC", strrchr(name, '.') + 1, strlen("EC"))) {                ECsigoffset = 4;            }            strcpy(pOut, name);            return pOut;        }    }    return NULL;}static JNINativeMethod gMethods[] = {    { "verifySign", "()V", (void *)verifySign },{ "getentyString",    "(I)Ljava/lang/String;", (jstring *)getentyString }, };static int registerNativeMethods(JNIEnv *env, const char *className,    JNINativeMethod *gMethods, int numMethods) {    jclass clazz;    clazz = env->FindClass(className);    if (clazz == NULL) {        return JNI_FALSE;    }    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {        return JNI_FALSE;    }    return JNI_TRUE;} static int registerNatives(JNIEnv *env) {    const char *kClassName = "com/example/dump/MainActivity"; //指定要注册的类 即nativie方法所在的类    return registerNativeMethods(env, kClassName, gMethods,        sizeof(gMethods) / sizeof(gMethods[0]));}//读取签名信息int zipreadbuf(char *fname, char **ppbuf) {    int nfilesize = 0;    struct zip_stat fstat;    struct zip_file* file = zip_fopen(APKArchive, fname, 0);    if (file) {        zip_stat(APKArchive, fname, 0, &fstat);    }     char *buffer = (char *)malloc(fstat.size + 1);    buffer[fstat.size] = 0;    int numBytesRead = zip_fread(file, buffer, fstat.size);    ;    nfilesize = buffer[0x36 + ECsigoffset] * 0x100 + buffer[0x37 + ECsigoffset];    if (buffer[0x36 + ECsigoffset + 2] >= 0x80) {        ECsigoffset += 3;        nfilesize = buffer[0x36 + ECsigoffset] * 0x100            + buffer[0x37 + ECsigoffset];    }    *ppbuf = (char *)malloc(nfilesize + 1);    memset(*ppbuf, 0, nfilesize + 1);    memcpy(*ppbuf, buffer + 0x38 + ECsigoffset, nfilesize);    free(buffer);    zip_fclose(file);    return nfilesize;}//android.content.pm.Signature中的  parseHexDigit方法,备用static int parseHexDigit(int nibble) {    if ('0' <= nibble && nibble <= '9') {        return nibble - '0';    }    else if ('a' <= nibble && nibble <= 'f') {        return nibble - 'a' + 10;    }    else if ('A' <= nibble && nibble <= 'F') {        return nibble - 'A' + 10;    }    else {        LOGI("what fuck!!!!");    }}  void verifySign(JNIEnv *env, jobject obj) {    char PackName[256] = { 0 };    char ApkPath[256] = { 0 };    //获取包名和路径    if (PidToName(getpid(), PackName) && getpacknameTopath(PackName, ApkPath)) {        LOGI("packName:%s  apkPath:%s", PackName, ApkPath);        char szSigPath[256] = { 0 };        if (findApkSignFile(ApkPath, "META-INF", szSigPath)) {            //将签名文件读取到 g_sig            signlength = zipreadbuf(szSigPath, (char**)&g_sig);        }        else {            LOGI("zip Not Find %s", "META-INF");        }        if (signlength) {            //char* 转jbyte[]            jbyteArray array = env->NewByteArray(signlength);            env->SetByteArrayRegion(array, 0, signlength, (jbyte*)g_sig);            LOGI("the signtrue length is:%d", signlength);            //下面方法将签名信息转字符并输出            char *text = new char[signlength * 2];            int N = signlength;            for (int j = 0; j < N; j++) {                char v = g_sig[j];                char d = (v >> 4) & 0xf;                text[j * 2] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d));                d = v & 0xf;                text[j * 2 + 1] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d));            }            //输出签名字符串            LOGI("sign string is: %s", text);            //简单比较签名            if (strcmp(text, "这里是原的签名")            {                LOGI("verifySign success!!!");            }            else            {                LOGI("verifySign fail!!!");                kill(getpid(), SIGABRT)            }        }    } }jstring getentyString() {     return NULL;}JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {    JNIEnv *env = NULL;    jint result = -1;     //LOGI("in jni onload");    if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {        return -1;    }    //LOGI("register natives");    if (!registerNatives(env)) {  //注册                                  //LOGI("register failed");        return -1;    }    //成功    result = JNI_VERSION_1_4;    //LOGI("register success");    return result; }
android.mk文件
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS)LOCAL_C_INCLUDES :=        \    $(LOCAL_PATH)        \    $(LOCAL_PATH)/libzipinclude $(CLEAR_VARS)LOCAL_MODULE    := signLOCAL_SRC_FILES := sign.cppLOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -ldl -llog -lz  #打印日志LOCAL_STATIC_LIBRARIES := libc \                        libzip include $(BUILD_SHARED_LIBRARY)include $(call all-makefiles-under,$(LOCAL_PATH))




0 0
原创粉丝点击