吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 28907|回复: 74
收起左侧

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

  [复制链接]
ThomasKing 发表于 2014-9-8 17:30
原帖: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。
p1.png

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,然后直接注册函数即可。当我写好代码测试时,发现如下错误:
error1.jpg

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");
}
具体实现见源码,这里就不贴了。
注入后的效果如下:
p2.png

四、参考文献
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


免费评分

参与人数 4威望 +1 热心值 +3 收起 理由
wanttobeno + 1 谢谢@Thanks!
sugie0708 + 1 我很赞同!
Syer + 1 已答复!
小试锋芒 + 1 期待更多更精彩的52首发文章!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Keepnear 发表于 2014-9-8 17:36
本帖最后由 Keepnear 于 2014-9-8 17:39 编辑

沙发 ???   谢谢分享
lucifer2046 发表于 2014-9-8 22:43 来自手机
scmyxcwl 发表于 2014-9-9 17:43
打不死的小强 发表于 2014-9-9 19:14
楼主好厉害啊 这么长的代码都记得
Hslim 发表于 2014-9-9 22:37
太高深了 没怎么看懂。还得继续学习
codelive 发表于 2014-9-10 10:15
不会吧,这么好的教程居然这么少的人气,感谢经验分享。
zjc424 发表于 2014-9-10 10:36
厉害 支持
leonchan 发表于 2014-9-10 19:59
厉害,支持作品
Jayden 发表于 2014-9-11 08:32
谢谢分享!!!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 13:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表