在日常的APP开发过程中我们都会遇到安全相关的问题,针对不同的安全需求会有不同的解决方案。那么我们今天来看看与Android动态库相关的安全问题。
动态库被第三方APP非法使用
我们辛辛苦苦开发了一个动态库,最终却被非授权的第三方APP用了,那么不是很尴尬嘛。
那么如何防止我们的动态库被第三方非法使用呢?客官往下看~
非授权使用的问题本质上是因为我们的动态库没有做验签功能,接下来就带大家实现对SO调用的签名验证功能。
敏感字符串信息可见
JNIEXPORT jstring JNICALL
Java_com_demon_so_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from static register jni function";
return env->NewStringUTF(hello.c_str());
}
so_string_visible
是不是突然感觉很没有安全感,这么随意。。。
我们这个字符串没有什么价值,但是如果我们存放了敏感的字符串信息又如何是好呢?
简单的解决办法有如下两种:
001 定义char数组
字符串信息以数组的方式初始化,比如以下写法:
JNIEXPORT jstring JNICALL
Java_com_demon_so_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
char* hello = ((char[]){'H','e','l','l','o',' ','f','r','o','m',' ','s','t','a','t','i','c',' ','r','e','g','i','s','t','e','r',' ','j','n','i',' ','f','u','n','c','t','i','o','n'});
return env->NewStringUTF(hello);
}
看到这么一长串的代码是不是要疯了,我擦,一个完整的字符串居然被凌乱掉了。这么写代码要疯掉了。
办法总是有的嘛......
用一个python脚本就可以解决问题了
content = "Hello from static register jni function"
length = len(content)
str = "((char[]){"
for i in range(length):
str = str+'\''+content[i]+'\''
if i !=(length-1):
str=str+','
str = str+"})"
print str
最终输出内容如下:
((char[]){'H','e','l','l','o',' ','f','r','o','m',' ','s','t','a','t','i','c',' ','r','e','g','i','s','t','e','r',' ','j','n','i',' ','f','u','n','c','t','i','o','n'});
注意这种写法只能用在函数内部
002 敏感字符串信息加密
加密分为对称加密和非对称加密,常见的对称加密包括DES
、AES
等,非对称加密包括RSA
、背包算法
等。
非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。
在项目针对可以采用DES
进行对称加密即可。
JNI函数信息可见
采用静态方式注册JNI函数会最终会包含在符号表中,那么我们就可以从符号表中看到以Java_
开头的所有JNI函数。那么别人就会通过JNI函数来最终分析APK中相应Class
对应的动态库文件,进而分析整个APK以及动态库的调用逻辑。
执行objdump -D libnative-lib.so | grep Java_*
可以查看以Java_
开头的符号表信息。
4118: 52 00 00 ea b #328 <Java_com_demon_so_MainActivity_stringFromJNI+0x28>
_ZN7_JavaVM6GetEnvEPPvi:
对比上边的输出结果我们可以很容易看出动态注册的JNI函数就无法看到我们定义的JNI函数了。
采用动态注册的方式即可解决JNI函数可见的问题。
Reference: