首页
社区
课程
招聘
[原创]无源码加解密实现 && NDK Native Hook
2014-9-8 17:11 49601

[原创]无源码加解密实现 && NDK Native Hook

2014-9-8 17:11
49601
帖子名字取得有点搓,请各位见谅。
---------------------------------------
上回小弟小小研究了一番有源码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

附件:
Shelldemo.zip
NativeMethodHook.zip
ndk hook.pdf

阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (32)
雪    币: 250
活跃值: (251)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
OnlyEnd 2014-9-8 17:30
2
0
真棒!!!  学习学习`
雪    币: 6334
活跃值: (3249)
能力值: ( LV8,RANK:138 )
在线值:
发帖
回帖
粉丝
zhighest 2014-9-8 19:59
3
0
学习………………感谢分享
雪    币: 76
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mingxuan三千 2014-9-8 22:58
4
0
学习 望楼主多发文章
雪    币: 2307
活跃值: (968)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
非虫 7 2014-9-9 13:04
5
0
感谢分享,加精处理。
雪    币: 370
活跃值: (1181)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
ThomasKing 6 2014-9-9 13:46
6
0
感谢非虫大哥鼓励!
雪    币: 9
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hwuyule 2014-9-9 13:53
7
0
追随王总脚步呀
雪    币: 45
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
丘比龙 2014-9-9 13:57
8
0
看来一下,没看太懂
雪    币: 270
活跃值: (234)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
wule 2 2014-9-9 15:12
9
0
楼主加油!!感觉越来越牛了!!
雪    币: 378
活跃值: (2903)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
appview 2014-9-9 15:13
10
0
好、支持、有希望!
雪    币: 370
活跃值: (1181)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
ThomasKing 6 2014-9-9 15:46
11
0
多谢你上次提醒我!
雪    币: 370
活跃值: (1181)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
ThomasKing 6 2014-9-9 15:47
12
0
感谢大家的支持!
雪    币: 250
活跃值: (251)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
OnlyEnd 2014-9-9 15:50
13
0
王总威武,跟着王总学习各种姿势
雪    币: 29
活跃值: (499)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
万抽抽 2 2014-9-9 16:19
14
0
王总真是高产啊!学习了。
雪    币: 79
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
silence刘 2014-9-10 13:29
15
0
先mark一下,有时间再看看
雪    币: 107
活跃值: (311)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Fido 2014-9-12 10:05
16
0
谢谢分享...学习............
雪    币: 107
活跃值: (311)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Fido 2014-9-12 10:07
17
0
谢谢分享..学习了...
雪    币: 11
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aiseven 2014-9-15 16:22
18
0
感谢分享

我运行的方法是

编译 NativeMethodHook 安装到手机上
然后用 cmd里面用adb shell 把 inject和 libhook.so push到/data/data下面
然后 /data/data/inject pid
然后点击 NativeMethodHook 里面的按钮

没有hook成功,能请教一下该怎么运行吗?
雪    币: 370
活跃值: (1181)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
ThomasKing 6 2014-9-15 19:08
19
0
你看下logcat有hook输出没? 注意需要root权限
雪    币: 11
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aiseven 2014-9-16 09:18
20
0
感谢楼主的回复!

手机肯定是root的。inject 和libhook.so 肯定都push到/data/data/ 下面

奇怪的是 inject后 cat /proc/pid/maps 后内存里面没有/data/data/libhook.so
所有的文件除了apk是我自己编译生成的,libs下面的都是用你工程里面带的。
能帮忙指点一下问题在哪吗?

再次感谢!

以下是logcat 里面的信息,看起来很正常

09-16 09:09:19.140: D/Thomas Say:(16226): [+] Injecting process: 2610
09-16 09:09:19.150: D/Thomas Say:(16226): [+] get_remote_addr: local[40094000], remote[40087000]
09-16 09:09:19.150: D/Thomas Say:(16226): [+] Remote mmap address: 4009fbbd
09-16 09:09:19.150: D/Thomas Say:(16226): [+] Calling mmap in target process.
09-16 09:09:19.150: D/Thomas Say:(16226): [+] Target process returned from mmap, return value=5c90b000, pc=0
09-16 09:09:19.165: D/Thomas Say:(16226): [+] get_remote_addr: local[b0001000], remote[b0001000]
09-16 09:09:19.180: D/Thomas Say:(16226): [+] get_remote_addr: local[b0001000], remote[b0001000]
09-16 09:09:19.180: D/Thomas Say:(16226): [+] get_remote_addr: local[b0001000], remote[b0001000]
09-16 09:09:19.180: D/Thomas Say:(16226): [+] Get imports: dlopen: b0005a7d, dlsym: b00059e9, dlclose: b0005929
09-16 09:09:19.180: D/Thomas Say:(16226): [+] Inject code start: 11018, end: 11094
09-16 09:09:19.180: D/Thomas Say:(16226): [+] _dlopen_param1_s: 5c90ec9c
09-16 09:09:19.180: D/Thomas Say:(16226): [+] _dlsym_param2_s: 5c90ed9c
09-16 09:09:19.180: D/Thomas Say:(16226): [+] _saved_r0_pc_s: 5c90ee9c
09-16 09:09:19.180: D/Thomas Say:(16226): [+] _inject_function_param_s: 5c90ef9c
09-16 09:09:19.180: D/Thomas Say:(16226): [+] Remote shellcode address: 5c90ec00
雪    币: 370
活跃值: (1181)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
ThomasKing 6 2014-9-16 11:09
21
0
那你换一个so试试,看能正常注入么? 我这边又试了下,没什么问题。
雪    币: 27
活跃值: (36)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jecray 2014-9-16 13:06
22
0
谢谢,学习了!
雪    币: 270
活跃值: (234)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
wule 2 2014-9-16 23:10
23
0
楼主大哥,小弟一直看不懂那个findclass函数,,能给点提示或者建议啥的么?
雪    币: 14
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
老党 2015-1-22 09:57
24
0
有大量兼容适配问题
雪    币: 370
活跃值: (1181)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
ThomasKing 6 2015-1-22 11:32
25
0
额,有什么问题,请大大指出?
游客
登录 | 注册 方可回帖
返回