本帖最后由 XOR 于 2018-11-20 12:26 编辑
一、前言
前一段时间才偶然看到这个论坛,并且看了一篇@jiangwei212 大神的《Android支付宝蚂蚁森林能量自动收取插件开发原理解析》,这才知道XPosed和VirtualXPosed的存在,觉得这个框架神器非常的有意思,所以继续学习了框架原理和使用方法,我发现有一些做支付宝微信自动收款提醒的网站,有生成二维码的功能,但是我没有找到相关开源的插件,所以就拿支付宝二维码这个功能先练练手,这个插件用来实现自定义备注内容和支付金额,自动生成付款二维码。下面就是我hook支付宝,反编译代码找到关键点的一些思路。
二、寻找突破口
首先需要找到入手点,生成二维码的页面在支付宝首页中的【收款】选项里,点击进入后是一个可以设置金额备注生成二维码的页面,这个页面可以用adb shell dumpsys activity top命令查看到顶层Activity是哪一个,或者更简单一点,在logcat中,搜索ActivityManager,看看对应启动的activity是哪一个,会发现是PayeeQRActivity:
接下来,就需要反编译支付宝的apk,然后看一下这个类的实现,这里会有两个问题:
1. 我们都知道二维码是把一些信息写入二维码图片中,那么支付二维码都需要写入哪些信息呢?
2. 知道了写入信息,那接下来便是如何生成一个二维码了。
关于第一个问题,很容易想到,我们要能够设置备注和金额,所以这两个信息也是必须的,其它还需要哪些信息呢?需要我们看一下具体代码,首先就是onCreate方法:
刚一进入onCreate,我们就能看到请求了一个UserInfo类型的数据,根据名称应该知道和用户信息相关,再看下面的 this.c,就知道是用来标识用户的唯一ID,还有用户的昵称和头像,应该是用来在页面做显示。继续分析流程:
代码块(1)中看到从preference中读取了一些字符串类型的变量,从key的名称看应该是和生成二维码地址有关的信息,但是前两个变量的默认值是空,也就是你第一次进入这个页面的时候,应该读取出来是空值,之后某些地方会对他们进行赋值。
代码块(2)是创建了一些服务接口类,从名称看第一个最符合生成二维码相关的信息,如果查看这个接口CollectMoneyRpc的声明,会发现猜测是正确的,其中的一些方法就是设置金额,备注等信息。
这些是设置金额和备注相关的Rpc服务接口,之后的代码会用到这些接口方法去设置金额和备注并返回信息。
继续下面的分析,之后onCreate代码就是获取UI组件相关变量,设置点击监听等等,比较重要的肯定是设置金额的按钮:
其中设置金额的点击监听事件是内部类bi,整个onCreate还有一个b()方法比较可疑,如果查看该方法的话,会发现是在主线程中最终调用了一个a(boolean z, ImageView imageView)的方法,该方法可以明显看出是用来真正生成二维码并显示在ImageView上:
至此,onCreate的流程分析完毕,收获不小,几乎所有相关的初始化工作都是在这里进行了,总结一下看到的代码流程:
1. 获取用户id,有可能会根据id来设置二维码中的信息?
2. 获取RPC远程调用服务接口。
3. 设置和金额相关的按钮点击监听。
4. 调用生成二维码方法生成二维码,这里就是你第一次进入该页面而没有设置任何金额和备注信息是现实的默认支付二维码。
那接下来,我们首先看一下设置金额和备注按钮点击时,会发生什么,方法就是上面提到的内部类bi:
直接看onClick方法,有两个分支,如果PayeeQRActivity.this.j是空值,会启动PayeeQRSetMoneyActivity,这个Activity肯定是设置金额和备注的activity了,至于this.j是什么?先让我们去看一下新的PayeeQRSetMoneyActivity,这个activity启动的方式是startActivityForResult,那么当该activity finish的时候,会回调到PayeeQRActivity的onActivityResult,让我们来看一下这几个流程,先来看一下PayeeQRSetMoneyActivity:
首先依旧会请求一个CollectMoneyRpc服务接口,看来是要在这里实际设置金额,然后会有个一sessionId,这个sessionId具体是什么值我没有找到,但是不影响继续分析,然后根据支付宝设置金额页面UI来看,最终会点击【确认】按钮完成设置,也就是图中的this.e和监听回调ct,ct最终的处理方法是该类的a方法:
一个真正使用rpc接口进行异步调用的函数,RpcRunner的两个参数第一个是异步调用接口,第二个是处理返回结果,都非常简单,下面那个带参数的a方法,就是回调结果调用的处理方法,他把处理post到主线程,执行cv方法,cv方法很重要,因为他是真正调用成功后返回结果的地方:
已经可以看到,结果中包含这些项:codeId,qr_money,beiZhu,qrCodeUrl,qrCodeUrlOffline,从名称就都可以知道是什么作用,然后通过调用setResult和finish把结果返回给PayeeQRActivity,而在PayeeQRActivity中的onActivityResult,无非就是去读取这些结果然后生成二维码,还记得生成二维码的方法吗?就是那个b()函数(最终调用的是a(boolean z, ImageView imageView)这个方法)。所以最好再来看一下生成二维码的方法,需要用到什么东西:
方法逻辑很清晰,用到哪些类,传入哪些参数,都很清楚了,最后使用ZXingHelper的genCodeImageView方法生成二维码并更新到ImageView上显示,具体逻辑就不贴图展示和分析了,以上就是代码层面上的分析,那么做一下总结:
1. 关于之前说的第一个问题,生成二维码用到哪些信息并且怎样生成,现在很清楚的知道,至少需要金额和备注,然后使用的是Rpc服务接口CollectMoneyRpc去生成二维码信息。
2. 关于之前的第二个问题,怎样生成二维码图像,这个也很清晰了,就是使用上面的a(boolean z, ImageView imageView)这个方法了。
那么接下来就是根据这些分析结果来构造hook的条件了
一、先来看如何构造1提到的条件:
我们从前面分析的代码知道需要一个CollectMoneyRpc接口,他是通过下面的调用来生成的:
关键点是这个mMicroApplicationContext是怎么来的,这个需要追溯到基类BaseActivity中,有个ActivityHelper辅助类,其中又再次使用LauncherApplicationAgent来获取这个值,这个值是在LauncherApplicaitonAgent构造函数中来构造的,LauncherApplicationAgent又是一个单例类,他有个init方法,一看就是用来做初始化的,我们去hook这个init方法,然后反射拿到成员变量mMicroApplicationContext,直接给出代码:
拿到这个CollectMoneyRpc后,我们就可以根据设置金额以及备注参数进行调用了,反射从调用结果中拿到上面分析的哪些codeId,url,等等:
二、最后构造之前的2提到的条件:
已经说过真正生成二维码是在哪个a(boolean, ImageView)方法中进行的,只要去按照这个方法中的参数构造,然后在反射使用ZXingHelper这个辅助类去写二维码图片就可以实现了,直接给出代码:
三、总结
使用XPosed开发插件还是新手,需要学习的东西还有很多,这次分析支付宝代码,使用的是旧版本的支付宝,最新版支付宝,jadx即使分包加载,我的电脑还是会卡死。。。支付宝代码反编译后,还是很清晰的,混淆的不是很严重,但是从中也能看到支付宝的复杂性以及架构上的合理性,研究他的代码也能学到很多的东西,估计这个工作我还会持续进行。已经把整个工程上传到github,文末是地址,如果这个东西不小心触犯了谁的利益或者影响了软件安全,可以联系我删除,再次声明,仅仅是个人学习目的使用,请勿他用,谢谢!!
AlipayQRHook开源地址:https://github.com/wayu002/AlipayQRHook
|