android jni签名验证(二)

时间:2022-10-13 23:57:28

此方法主要是应对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:xiaobaiyey
date:2015年11月7日20:52:00
email: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是为了防止hook
char * 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)/libzip
include $(CLEAR_VARS)
LOCAL_MODULE := sign
LOCAL_SRC_FILES := sign.cpp
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -ldl -llog -lz #打印日志
LOCAL_STATIC_LIBRARIES := libc \
libzip
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))