背景

当下,数据就像水、电、空气一样无处不在,说它是“21世纪的生产资料”一点都不夸张,由此带来的是,各行业对于数据的争夺热火朝天。随着互联网和数据的思维深入人心,一些灰色产业悄然兴起,数据贩子、爬虫、外挂软件等等也接踵而来,互联网行业中各公司竞争对手之间不仅业务竞争十分激烈,黑科技的比拼也越发重要。随着移动互联网的兴起,爬虫和外挂也从单一的网页转向了App,其中利用Android平台下Dalvik模式中的Xposed InstallerCydia Substrate框架对App的函数进行Hook这一招,堪称老牌经典。

接下来,本文将分别介绍针对这两种框架的防护技术。

Xposed Installer

原理

Zygote

在Android系统中App进程都是由Zygote进程“孵化”出来的。Zygote进程在启动时会创建一个虚拟机实例,每当它“孵化”一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的App进程里面去,从而使每个App进程都有一个独立的Dalvik虚拟机实例。

Zygote进程在启动的过程中,除了会创建一个虚拟机实例之外还会将Java Rumtime加载到进程中并注册一些Android核心类的JNI(Java Native Interface,Java本地接口)方法。一个App进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的虚拟机实例拷贝,还会与Zygote进程一起共享Java Rumtime,也就是可以将XposedBridge.jar这个Jar包加载到每一个Android App进程中去。安装Xposed Installer之后,系统app_process将被替换,然后利用Java的Reflection机制覆写内置方法,实现功能劫持。下面我们来看一下细节。

Hook和Replace

Xposed Installer框架中真正起作用的是对方法的Hook和Replace。在Android系统启动的时候,Zygote进程加载XposedBridge.jar,将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法xposedCallHandler,这个方法再通过调用handleHookedMethod这个Java方法来调用被劫持的方法转入Hook逻辑。

上面提到的hookMethodNativeXposedBridge.jar中的私有的本地方法,它将一个方法对象作为传入参数并修改Dalvik虚拟机中对于该方法的定义,把该方法的类型改变为Native并将其实现指向另外一个B方法。

换言之,当调用那个被Hook的A方法时,其实调用的是B方法,调用者是不知道的。在hookMethodNative的实现中,会调用XposedBridge.jar中的handleHookedMethod这个方法来传递参数。handleHookedMethod这个方法类似于一个统一调度的Dispatch例程,其对应的底层的C++函数是xposedCallHandler。而handleHookedMethod实现里面会根据一个全局结构hookedMethodCallbacks来选择相应的Hook函数并调用他们的beforeafter函数,当多模块同时Hook一个方法的时候Xposed会自动根据Module的优先级来排序。

调用顺序如下:A.before -> B.before -> original method -> B.after -> A.after。

检测

在做Android App的安全防御中检测点众多,Xposed Installer检测是必不可少的一环。对于Xposed框架的防御总体上分为两层:Java层和Native层。

Java层检测

需要说明的是,Java层的检测基本只能检测出基础的Xposed Installer框架,而不能防护其对App内方法的Hook,如果框架中带有反检测则Java层检测大多不起作用。

下面列出Java层的检测点,仅供参考。

① 通过PackageManager查看安装列表

最简单的检测,我们调用Android提供的PackageManager的API来遍历系统中App的安装情况来辨别是否有安装Xposed Installer相关的软件包。

PackageManager packageManager = context.getPackageManager();
List applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo: applicationInfoList) {if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {// is Xposed TODO... }}

通常情况下使用Xposed Installer框架都会屏蔽对其的检测,即Hook掉PackageManager的getInstalledApplications方法的返回值,以便过滤掉de.robv.android.xposed.installer来躲避这种检测。

② 自造异常读取栈

Xposed Installer框架对每个由Zygote孵化的App进程都会介入,因此在程序方法异常栈中就会出现Xposed相关的“身影”,我们可以通过自造异常Catch来读取异常堆栈的形式,用以检查其中是否存在Xposed的调用方法。

try {throw new Exception("blah");
} catch(Exception e) {for (StackTraceElement stackTraceElement: e.getStackTrace()) {// stackTraceElement.getClassName() stackTraceElement.getMethodName() 是否存 在Xposed}
}
E/GEnvironment: no such table: preference (code 1): while compiling: SELECT keyguard_show_livewallpaper FROM preference
...
at com.meituan.test.extpackage.ExtPackageManager.checkUpdate(ExtPackageManager.java:127)
at com.meituan.test.MiFGService$1.run(MiFGService.java:41)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5072)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
...
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132) //发现Xposed模块
at dalvik.system.NativeStart.main(Native Method)

③ 检查关键Java方法被变为Native JNI方法

当一个Android App中的Java方法被莫名其妙地变成了Native JNI方法,则非常有可能被Xposed Hook了。由此可得,检查关键方法是不是变成Native JNI方法,也可以检测是否被Hook。

通过反射调用Modifier.isNative(method.getModifiers())方法可以校验方法是不是Native JNI方法,Xposed同样可以篡改isNative这个方法的返回值。

④ 反射读取XposedHelper类字段

通过反射遍历XposedHelper类中的fieldCachemethodCacheconstructorCache变量,读取HashMap缓存字段,如字段项的key中包含App中唯一或敏感方法等,即可认为有Xposed注入。

boolean methodCache = CheckHook(clsXposedHelper, "methodCache", keyWord);private static boolean CheckHook(Object cls, String filedName, String str) {boolean result = false;String interName;Set keySet;try {Field filed = cls.getClass().getDeclaredField(filedName);filed.setAccessible(true);keySet = filed.get(cls)).keySet();if (!keySet.isEmpty()) {for (Object aKeySet: keySet) {interName = aKeySet.toString().toLowerCase();if (interName.contains("meituan") || interName.contains("dianping") ) {result = true;break;} }}...return result;
}

Native层检测

由上文可知,无论在Java层做何种检测,Xposed都可以通过Hook相关的API并返回指定的结果来绕过检测,只要有方法就可以被Hook。如果仅在Java层检测就显得很徒劳,为了有效提搞检测准确率,就须做到Java和Native层同时检测。每个App在系统中都有对应的加载库列表,这些加载库列表在/proc/下对应的pid/maps文件中描述,在Native层读取/proc/self/maps文件不失为检测Xposed Installer的有效办法之一。由于Xposed Installer通常只能Hook Java层,因此在Native层使用C来解析/proc/self/maps文件,搜检App自身加载的库中是否存在XposedBridge.jar、相关的Dex、Jar和So库等文件。

bool is_xposed()
{bool rel = false;FILE *fp = NULL;char* filepath = "/proc/self/maps";...string xp_name = "XposedBridge.jar";fp = fopen(filepath,"r")) while (!feof(fp))                                 {fgets(strLine,BUFFER_SIZE,fp);                    origin_str = strLine;str = trim(origin_str);if (contain(str,xp_name)){rel = true; //检测到Xposed模块break;}}...
}

Cydia Substrate

原理

Cydia Substrate注入Hook的一个典型流程如下图所示,在Java层配置注入的关键So库libsubstrate.solibsubstratedvm.so。考虑到Java层检测强度太低,Substrate的检测主要在Native层来实现。

检测

动态加载式检测

读取/proc/self/maps,列出了App中所有加载的文件。

上图为Cydia Substrate在Android 4.4上注入后的进程maps表,其中libsubstrate.solibsubstrate-dvm.so两个文件为Substrate必载入文件。通过IDA Pro分析对其分析。

先来看libsubstrate-dvm.so的导出表,共有9个函数导出。

当进程maps表中出现libsubstrate-dvm.so,可以尝试去load该so文件并调用MSJavaHookMethod方法,它会返回该方法的地址即判定为恶意模块(第三方程序)。

void* lookup_symbol(char* libraryname,char* symbolname)
{void *imagehandle = dlopen(libraryname, RTLD_GLOBAL | RTLD_NOW);if (imagehandle != NULL){void * sym = dlsym(imagehandle, symbolname);if (sym != NULL){return sym; //发现Cydia Substrate相关模块}...
}

该方式基于载入库文件的文件名或文件路径和导出函数来判断是否为恶意模块,如果完全依赖此方式来判断可能会误判,但也不失为检测方式的一个点。

基于方法特征码检测

特征码即用来判断某段数据属于哪个计算机字段。在非Root环境下一般一个正常App在启动时候,系统会调度相关大小的内存、空间给App使用,此时App的运行环境内产生的数据、内存、存储等是独立于其它App的(即独立运行在沙箱中)。因为处于运行沙箱环境中的进程对沙箱的内存有最高读写权限,当我们的App进程被恶意模块附加或注入时,就可以通过对当前进程的PID所对应的maps中加载的模块进行合法校验。这里的模块校验我们可以采取对单个模块内容取样来判断是否为恶意模块,这种方式被定义为“基于方法的特征码检测”。

下面对一段程序段中OpcodeSample方法来提取特征码。

方法原型:

#define  LOGD(fmt, args...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, fmt, ##args)
void OpcodeSample(int a ,int b){  int c,d,e;c = a + b;d = a * b;e = a / b;LOGD("Hello It's c !%s\n", c);  LOGD("Hello It's d !%s\n", d);  LOGD("Hello It's e !%s\n", e);  return;
}

通过IDA Pro对其分析。

左侧红色方框代表为OpcodeSample方法的操作码,右边为操作码对应ARM平台的指令集。我们要在左侧的操作码中取出一段作为OpcodeSample的定位特征码,选用__android_log_print方法调用指令集上下文,来确定特征码。

第一次取样:"03 20 31 46 42 46 FF F7 ?? EA"

通过第一次取样,查找结果有三处相似,再进一步分析。这次我们加入一个常量取样:

第二次取样:"7E 44 ?? ?? F8 44 03 20 31 46 42 46 FF F7 ?? EA"

继而得出唯一特征码,到此,我们对特征码方法取样有了初步的了解。下面来把它转为实用的技能——动态加载式检测+特征码结合。

我们对libsubstrate-dvm.so中导出函数MSJavaHookMethod来精准定位。

IDA PRO导出函数表如图:

第三次取样:"55 57 56 53 E8 CC 14 ?? ?? 81 C3 DB ?? ?? ?? 8D 64 ?? ?? 8B 83 F4 ?? ?? ??"

以上即为对Cydia Substrate的注入检测识别,通过检测/proc/self/maps下的加载so库列表得到各个库文件绝度路径,通过fopen函数将so库的内容以16进制读进来放在内存里面进行规则比对,采用字符串模糊查找来检测是否命中黑名单中的方法特征码。

总结

在安全对抗领域,相比攻击方,防守方历来处于弱势的一方。上文所提到的Xposed InstallerCydia Substrate的检测也仅仅是保障App安全的手段之一。App安全的防御不应仅仅依赖于此,应该构建起整体的安全防御闭环,尽可能在所有已知的可能攻击点都追加检测,再配合代码加固,将防御代码隐藏。遗憾的是App防御代码隐藏再深也终究会被破解,仅仅依赖于客户端的防御显然是不足的。移动互联网领域的整体安全防御应该是走端云结合协作之道,共同防御,方能在攻防对抗中占据优势地位。

作者简介

  • 礼赞,美团安全工程师,2016年11月加入美团。专注于二进制、移动端攻防相关工作,现负责美团Android移动安全组件的建设工作。
  • 毅然,美团技术专家,2016年初加入美团。致力于美团配送App组的Android App crash解决工作、Android App性能优化、Android App反外挂、反爬虫。目前主导负责美团配送Android App移动安全相关建设。

招聘信息

美团集团安全部正在招募Web&二进制攻防、后台&系统开发、机器学习&算法等各路小伙伴。

我们想做的事情:

构建一套基于海量IDC环境下,横跨网络层、虚拟化层、Server 软件层(内核态/用户态)、语言执行虚拟机层(JVM/Zend/JavaScript V8)、Web应用层、数据访问层(DAL)的,基于大数据+机器学习的全自动人机识别与安全事件感知系统。规模上对应美团全线业务的服务器,技术栈覆盖了几乎大多数云环境下的互联网应用,数据规模也将是很大的挑战。

此外我们还关注全球互联网领域在企业安全建设方面的最佳实践,努力构建类似于Google的内置式安全架构和纵深防御体系,对在安全和工程技术领域有所追求的同学来说应该是一个很好的机会。

如果你想加入我们,欢迎将简历发至邮箱zhaoyan17#meituan.com,具体职位信息点击“岗位招聘”查看。

另外友情打个招聘:美团配送App团队,负责美团骑手、美团众包、美团跑腿等配送相关App的研发,涉及技术领域包括但不限于App的稳定性建设、App性能监控和优化、App动态化。对上述领域感兴趣的请联系 yulei10#meituan.com 。

Android Hook技术防范漫谈相关推荐

  1. 美团Hook技术防范漫谈

    Xposed Zygote 在Android系统中App进程都是由Zygote进程"孵化"出来的.Zygote进程在启动时会创建一个虚拟机实例,每当它"孵化"一 ...

  2. opengl源码 实现无缝切换图片过场_手把手讲解 Android hook技术实现一键换肤

    前言 产品大佬又提需求啦,要求app里面的图表要实现白天黑夜模式的切换,以满足不同光线下都能保证足够的图表清晰度. 怎么办?可能解决的办法很多,你可以给图表view增加一个toggle方法,参数Str ...

  3. 浅谈android hook技术

    前言 在测试android过程中,能对函数进行hook能帮助更加深入的进行测试,本文简单介绍了hook框架xposed和frida,从简单的小例子做了简单的演示,算是自己的学习的过程,是个入门的过程. ...

  4. Android hook技术实现一键换肤,移动应用开发项目案例

    3. 利用HOOK技术实现优雅的"一键换肤" 什么是hook **如题,我是用hook实现一键换肤.那么什么是hook? hook,钩子. 安卓中的hook技术,其实是一个抽象概念 ...

  5. Android Hook技术

    1. 什么是 Hook Hook 英文翻译过来就是「钩子」的意思,那我们在什么时候使用这个「钩子」呢?在 Android 操作系统中系统维护着自己的一套事件分发机制.应用程序,包括应用触发事件和后台逻 ...

  6. wegame一键蹲替换文件_手把手讲解 Android hook技术实现一键换肤

    前言 产品大佬又提需求啦,要求app里面的图表要实现白天黑夜模式的切换,以满足不同光线下都能保证足够的图表清晰度. 怎么办?可能解决的办法很多,你可以给图表view增加一个toggle方法,参数Str ...

  7. android hook技术教学视频,[科普向]hook技术简单介绍

    首先 1.  这只是科普向,会介绍相关Hook技术及原理,但是不会给源码.源码请自行百度 2.请至少有一点Windows编程经验,知道系统API是什么...以免看科普内容给您带来不适 3.这里结合了一 ...

  8. android hook 第三方app_【MiSRC】技术分享-浅谈android hook技术

    注:本文为"小米安全中心"原创,转载请联系"小米安全中心" 前言 xposed框架 xposed,主页:http://repo.xposed.info/modu ...

  9. android hook技术-Xposed框架 帮你轻松应对支付宝2016晒账单

    一.支付宝2016年账单. 最近几天微信朋友圈充斥着各式的支付宝2016账单,对于程序员屌丝来说打开发现年度消费9W+,和他们动辄十几万的消费没得比,细看更有80%的消费还都是还信用卡,顿时万念俱灰啊 ...

最新文章

  1. 双目视觉测距原理,数学推导及三维重建资源
  2. 如何在Linux下安装MySQL8.0
  3. JAVA GUI关闭按钮不起作用(用SwingWorker解决)
  4. 每个人都有自己的人生节奏
  5. mysql英文介绍_每日科技英文48: MySQL C API简介
  6. 硬件:开机如何进BIOS,U盘启动快捷键一键查询!
  7. JVM调优总结(1):一些概念
  8. CVPR2021 Oral《Seeing Out of the Box》北科大中山大学微软提出端到端视觉语言表征预训练方法...
  9. Android屏幕适配和文字屏幕适配
  10. 企业不同时期该如何做股权布局?
  11. allegro中10mil过孔_Allegro中增加过孔的方法
  12. html5烟花特效代码,漂亮的JavaScript烟花特效代码
  13. UiPath手把手中文教程PDF
  14. Android中视频播放以及解码
  15. 【RTF】如何使用python读取RTF格式的文件
  16. Android 应用换肤功能(白天黑夜主题切换)
  17. 专门记java语言单词app_赞Android背单词软件app源码(非常实用)
  18. 30分钟java桌球小游戏,30分钟完成桌球小游戏项目
  19. 系统调用号(eax 调用号) 及 EAX、ECX、EDX、EBX寄存器的作用
  20. python:pandas全DataFrame查询定位赋值数值所在行列

热门文章

  1. OSS音频编程概述(DSP部分)
  2. 一个简单又高效的日志系统
  3. 通信教程 | 串口丢数据常见的原因
  4. 面试官:如何写出让 CPU 跑得更快的代码?
  5. Binder远程转本地
  6. python制作点亮图片_MicroPython拼插编程实例:点亮心形8x8点阵
  7. gps84转换gcj02公式_百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换...
  8. linux 信号_[入门]谈一谈Linux中的信号
  9. python爬虫模拟登录人人网
  10. LeetCode 1953. 你可以工作的最大周数