好友
阅读权限 30
听众
最后登录 1970-1-1
原帖:http://bbs.pediy.com/showthread.php?t=192047
上回小弟小小研究了一番有源码so简单加解密方式。像各种加固软件一样,是拿不到别人的源码的。经过小弟一番折腾,实现了简单粗暴的无源码加解密方法。在此过程中,某晚YY到native method,有了一种简单的native method hook思路。看到论坛没有类似帖子,故来一帖,也作为学习笔记。限于小弟水平,肯定会有不少疏漏和错误之处,还请各位大牛指正,感激不尽!
---------------------------------------
一、 概述
在上个帖子 http://bbs.pediy.com/showthread.php?t=191649 中介绍了so加解密实现的基本思路和有源码自加解密实现。仅仅实现有源码的方式,肯定不是我们想要的。下面介绍一种无源码的加解密实现和简单的Native method hook思路。
二、 无源码加解密实现
在上个简单粗暴的so加解密帖子中,介绍了基于section和特定目标函数(或数据区)加解密实现思路。加密步骤中,我们并不需要被加密so文件的源码,而需要源码原因是:解密的时机放在了so文件被linker加载执行.init_array中的代码过程中,实现so加载时的自解密。把解密代码插到.init_array中,自然需要源码。其实,只要在函数被执行前进行解密,都是可以的。由于所有被加载的so处于同一进程中,故可以将解密函数放在另一个so执行,即实现无源码解密。
将解密函数放在另一个so中,只需保证解密函数在被加密函数执行前执行即可。和其他so一样,解密so的加载也放在类的初始化static{}中。我们可以在解密so加载过程中执行解密函数,就能保证被加密函数执行前解密。执行时机可以选在linker执行.init_array时,也可以选在OnLoad函数中。当然,解密so一定要放在被解密so后加载。否则,搜索进程空间找不到被解密的so。
加密方法可以随意选择上个帖子中的一种,解密只需将原来的解密函数单独放在一个so文件即可。相信读者已经明白,这里就不啰嗦,见附件源码shelldemo。
三、 Native method Hook 简单实现
1. 解包添加hook.so实现
很容易知道,native 方法并不像so中的其他函数被直接被调用。而是通过注册并被间接调用的。在so加载和卸载过程中,Onload 和 Unload函数会被调用,类似于驱动的加载和卸载。Onload函数主要功能是调用RegisterMethod注册native函数;而Unload则相反。那么如果增加一个so文件,在Onload中调用UnregisterMethod函数,将当前绑定的函数注销,再调用RegisterMethod注册我们自己的函数,即实现简单的hook功能。相信读者已经懂得如何做,这里就不给出源码了。
这里存在着一个问题,UnregisterMethod会将clazz所有的函数都注销掉(Un-register all native methods associated with the class)。当一个类存在多个native函数时,这种方法显然是不可取的。我们不可能实现原so中的所有函数,且也达不到hook的某一或某些函数的目的。
我们知道,同一个native方法是可以被多次注册的。既然可以被多次注册,那么struct Method insns 字段绑定的是最后一次注册的函数。Hook的思路很简单了:调用RegisterMethod,替换原来的某一或某些native方法。相信读者已经懂得如何做,这里就不给出源码了。
2. 进程注入实现
既然可以hook native方法,绕过一些简单注册什么的,还是可以的,读者可以自行试试。但美中不足的是,现很多apk作了自校验,直接添加hook.so到原包中,添加smali 加载代码实现hook肯定不现实。在不动原包的情况下,我们只能通过进程注入来实现了。
网上有很多大大的注入源码,这里假设读者已经有源码并能正常注入进程(我这里使用古河大大的注入代码)。为了便于读者理解,我这里构建一个简单的apk。
Button调用native foo()函数,获取字符串并在TextView中显示。在demo.so中,foo函数为:static jstring foo(JNIEnv* env){
return env -> NewStringUTF("Wait for hook!");
};
我们的目标是将foo函数hook,返回字符串:”Hooked! Oh yeah!”。可能读者已经想到,在hook_entry中调用findclass,然后直接注册函数即可。当我写好代码测试时,发现如下错误:
NoClassDefFoundError,初看以为类名写错了,复制demo.c的代码过来测试,还是同样的错误。仔细看还有[generic],翻看源码和各种谷歌,发现此错误其实可能不是类名没找到,而类加载器不对。可能读者会问,第一种实现并未出现异常。的确,那是因为so是通过loadLibrary加载的,而这里是通过注入实现的。loadLibrary 会自动找到合适的类加载器,而这里使用默认的类加载器bootclassloader加载,出现未找到的异常(不太了解jni机制的读者,可以参看老罗关于jni加载机制的帖子)。具体异常原因就是:调用findclass由于类加载器不对,出现异常并且未作异常处理而抛出的。
知道这个原因,具体实现就简单了,就在是findclass失败后,添加调用自己实现的findclass即可。主要代码:
extern "C" void HookNativeMethod(char *para){
JNIEnv *env = NULL;
jclass clazz = NULL;
env = android::AndroidRuntime::getJNIEnv();
if(env == NULL){
__android_log_print(ANDROID_LOG_INFO, "INJECTED", "Get JNIEnv failed\n");
goto _error;
}
clazz = android::AndroidRuntime::findClass(env, JNIREG_CLASS);
if(clazz == NULL){
__android_log_print(ANDROID_LOG_INFO, "INJECTED", "Find Class failed by BootClassLoader\n");
if(env->ExceptionOccurred()){
env->ExceptionDescribe();
env->ExceptionClear();
}
clazz = myFindClass(env, JNIREG_CLASS);
if(clazz == NULL)
{
__android_log_print(ANDROID_LOG_INFO, "INJECTED", "Find Class failed\n");
goto _error;
}
}
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
__android_log_print(ANDROID_LOG_INFO, "INJECTED", "Register Hook Method failed\n");
goto _error;
}
return;
_error:
__android_log_print(ANDROID_LOG_INFO, "INJECTED", "Hook Native Method failed\n");
}
具体实现见源码,这里就不贴了。
注入后的效果如下:
四、参考文献
http://blog.csdn.net/luoshengyang/article/details/8923483
后面发现,其实在这篇帖子中:
http://bbs.pediy.com/showthread.php?t=186054&highlight=%E6%B3%A8%E5%85%A5
也有findclass的实现
安卓源码: dalvik\vm\jni.c native.cpp jniinternal.h androidRuntime.cpp androidRuntime.h
附件:http://pan.baidu.com/s/1gd9XWER
免费评分
查看全部评分
发帖前要善用【论坛搜索 】 功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。