Android NDK开发中加密so库,旨为保护自己的开发成果。
so反编译
使用IDA工具反编译so文件,破解分析时,很容易查看到so文件里,被Java层调用的函数,以及一些加密算法等等信息,进一步破解应用代码。
但是这种是我们不想见到的,我们希望把一些核心功能或者数据加密,如key,这就引入了so文件加固。
so加固
原理:JNI_OnLoad方法,当在系统中调用System.loadLibrary函数时,该函数会找到对应的动态库,然后首先试图找到”JNI_OnLoad”函数,如果该函数存在,则调用它。JNI_OnLoad可以和JNIEnv的registerNatives函数结合起来,实现动态的函数替换。
CMakeList开启配置,隐藏符号表
编辑Android Studio工具的CMakeList文件,添加下面两个语句:1
2set(CMAKE_C_VISIBILITY_PRESET hidden) # C语言写法
set(CMAKE_CXX_VISIBILITY_PRESET hidden) # C++写法注册JNI_OnLoad方法,函数对照表
记录调用C代码的Java文件路径
1
static const char *JNI_REG_CLASS = "com/excellence/test/tool/KeyUtils";
gMethods方法替换
将native方法getKey,指向gK,这样反编译就查不到getKey方法。1
2
3static JNINativeMethod gMethods[] = {
{"getKey", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void *) gK},
};JNINativemethod中结构体的定义:
1
2
3
4
5
6
7
8typedef struct {
// Java中函数的名字
const char* name;
// 描述了Java中函数的参数和返回值
const char* signature;
// fnPtr是函数指针,指向native函数。前面都要接 (void *)
void* fnPtr;
} JNINativeMethod;在JNI调用的C文件里注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36static 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) {
if (!registerNativeMethods(env, JNI_REG_CLASS, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])))
return JNI_FALSE;
return JNI_TRUE;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
assert(env != NULL);
//注册
if (!registerNatives(env)) {
return -1;
}
return JNI_VERSION_1_6;
}
这样加固了so文件,就可以做到一定的保护作用,使用IDA工具查看,会起到混淆的作用。