AccessibilityService分析与防御"/>
AccessibilityService分析与防御
转载请注明出处:
前言
提起AccessibilityService,你最容易联想到的肯定是微信抢红包插件!但这个服务的设计初衷,是为了帮助残障人士可以更好的使用App。
一些“调皮”的开发者利用AccessibilityService可以监控与操作其他App的特性加上系统远超人类的反应速度,在某些竞争类场景开发出了作弊外挂,最常见的就是你所嫉愤的微信抢红包插件了。
微信抢红包插件对原本平等的竞争环境产生了不公,不过这是微信团队要操心解决的事。可万万没想到,有一天,我正在写的App也遭此毒手!!!这都欺负到头上了能忍吗?不能啊!
OK,所以我们今天先来分析一下AccessibilityService运行原理,然后分享一些我在应对此类竞争场景下基于AccessibilityService等自动化作弊工具的防御措施。
外挂简史
先说下背景:
场景是和抢红包类似的另一种:抢单。用户下单后订单会经过系统,在配送端App发布,配送人员在配送端App通过距离、价钱、时间等维度进行筛选并抢单然后配送。显而易见,价高距离短的订单非常抢手,这样就形成一种竞争环境,于是,自动抢单外挂也就有了存在的理由。
然后我们来看下外挂进化史:
第一代外挂
第一代外挂还比较粗糙,需要依赖按键精灵来实现,且需要Root权限。
【防御】简单反编译拆包了解后,考虑暂时没有更好的办法禁止按键精灵对App的模拟点击,直接封禁Root可能会有大量误杀,第一代防御仅简单的检查是否安装了按键精灵,然后限制用户抢单。
第二代外挂
可能因为第一代的防御过于粗糙,第二代外挂很快有了新的改进,不再需要单独安装按键精灵这个App,他们把按键精灵集成到了自己的app里……
【防御】此时团队内部简单商量后决定,快刀斩乱麻,直接封禁Root权限,检测到Root后将限制抢单。
第三代外挂
禁止Root后终于消停了一段时间,但显然人民群众的智慧是无限的,很快新的免Root外挂出世了……经过反编译外挂后,第三代外挂采用了AccessibilityService来实现。
【防御】此时已知的外挂并不多,所以除了继续封禁Root以外,还建立了可远程配置的外挂package name黑名单列表,若检测到已安装app列表里存在特定外挂包名后,将会进行抢单限制。
package name需要先获取到安装包来查看包名,随着外挂数量逐步上涨,外挂安装包获取难度大的缺点开始暴露了。【第三代防御】
此时针对上一个版本的防御措施做了一次优化:
1.优先检查外挂package name
2.次级检测外挂app name,加package name白名单防误判。这样就不需要再获取app的安装包了
3.增加骑手举报反馈入口
4.收集了已启动的辅助模式列表备用(本想再快到斩乱麻的禁止辅助模式的开启,但这个误杀范围实在是太大了,最终还是停留在了想一想的阶段)第四代外挂
在通过app name封禁后,外挂们挣扎了几次都被即时遏制了。但很快,我们收到了最新的外挂信息:新出来的外挂没有图标,看不到名字…… (你们厉害你们厉害!!!)
哎呀~真是活久见,两波从来没见过的人在互相进步啊这是!!!禁止外挂安装这种简单的防御措施已经挡不住这帮疯狂的人类了,我只能一头扎进了AccessibilityService的源码中,看这到底是个啥东西,然后去思考相应的防御方案。
AccessibilityService运行原理
AccessibilityService内部运行
这不是一篇AccessibilityService教程文章,没有AccessibilityService完整的使用示例代码和源码,但为了上下文不至于断档太大,我们这里还是会简单贴一些小段代码。同时需要说明的是,严谨的来说AccessibilityService只是一个Service,文本查找点击事件等操作对于一个Service来说是完全没法做到的。但为了行文方便,所以后面某些AccessibilityService代指辅助模式服务。
public class MyAccessibilityService extends AccessibilityService {...@Overridepublic void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {//获取eventTypeint eventType = accessibilityEvent.getEventType();if (eventType == AccessibilityEvent.TYPE_VIEW_CLICKED) {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {//查询文案为BUTTON3的ViewList<AccessibilityNodeInfo> button3 = nodeInfo.findAccessibilityNodeInfosByText("BUTTON3");nodeInfo.recycle();for (AccessibilityNodeInfo item : button3) {//对这个View执行点击操作item.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}}}...
}
AccessibilityService真的很简单,只要写一个Service继承AccessibilityService,然后还有其他一些配置,之后每当你监控的应用界面有变动时就会回调到这个onAccessibilityEvent这个方法,你可以在里面取得此时变动的event类型是什么,还能拿到当前这个应用可视化的View树,然后取得其中的某个View来执行某些操作。
那至于其原理,用屁股想想也知道是肯定是被监控的App发生界面改变时通知了系统,然后系统又通知给了我们注册的Service。嗯……屁股想的没错……那App怎么通知系统的?系统又怎么通知我们的呢?
哎呀,屁股想不出来了,没关系,屁股决定脑袋,脑袋知道怎么办。这个时候我们就该钻到源码里来一探
更多推荐
AccessibilityService分析与防御
发布评论