最近要写个支付宝小程序爬虫,看了下网上没有相关资料,就把自己的研究成果放出来供大家参考。
破解https验证
使用命令adb shell dumpsys activity | grep Focuse
查看当前activity
mFocusedActivity: ActivityRecord{12d334a u0 com.eg.android.AlipayGphone/com.alipay.mobile.nebulax.integration.mpaas.activity.NebulaActivity$Lite1 t857}
mFocusedStack=ActivityStack{15c8721 stackId=1, 6 tasks} mLastFocusedStack=ActivityStack{15c8721 stackId=1, 6 tasks}
查看支付宝的manifest.xml,找到这个activity,发现它是运行在支付宝其他进程中的,所以用frida hook时一定要找对小程序的进程,这里是com.eg.android.AlipayGphone:lite1
,否则就白忙活了。
分析过程
这里使用frida进行hook测试。代码已上传到github AlipayTinyAppCrack。
因为笔者已经安装了burp、charles的根证书到系统,而且安卓系统是6.0的,所以猜测抓包失败的点有两个:
- 使用了HostnameVerifier验证了代{过}{滤}理的证书与网站域名不符
- 使用了证书绑定
笔者先大概了解了下小程序的开发。发现支付宝小程序跟微信小程序类似,都是用js开发,所以猜测是运行在WebView引擎下的。WebView的证书验证方式是创建WebViewClient,重写onReceivedSslError方法里实现,例如
webview.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if (customCertificateCheck(error.getCertificate())) {
handler.proceed(); // 如果证书一致,忽略错误
}
}
}
搜索grep -Hrn '.method public onReceivedSslError' smali* | grep nebula
,挨个分析查找到的类,定位到'com.alipay.mobile.nebulacore.web.H5WebViewClient'
所以使用如下代码尝试
// webview 证书绑定
const H5WebViewClient = Java.use('com.alipay.mobile.nebulacore.web.H5WebViewClient');
const SslErrorHandler = Java.use("android.webkit.SslErrorHandler");
H5WebViewClient.onReceivedSslError.implementation = function(webview, sslHandler, sslError){
console.log('H5WebViewClient onReceivedSslError called, proceed');
var handler = Java.cast(sslHandler, SslErrorHandler);
handler.proceed();
};
执行后发现没有效果,这个方法没有被调用。但在里面发现了log的打印方法。所以试着用如下代码打开日志
// h5小程序 log
const H5Log = Java.use("com.alipay.mobile.nebula.util.H5Log");
H5Log.d.overload("java.lang.String", "java.lang.String").implementation = function (tag, msg) {
console.log("debug: [", tag, "] - ", msg);
};
运行后继续抓包,请求失败后看到有日志
其中有如下信息
debug: [ H5HttpCallback ] - onFailed 2 javax.net.ssl.SSLException: hostname in certificate didn't match: <app.gsxt.gov.cn> != <default.ssl.cdn.jiasule.com> OR <default.ssl.cdn.jiasule.com>
可以确定是证书的域名验证不通过。
- 查找'H5HttpCallback',找到类'com.alipay.mobile.nebulaappproxy.ipc.handler.H5HttpCallback',发现这条日志是由类的'onFailed'方法打印的,所以继续查找这个方法的调用。
- 查找'H5HttpCallback;->onFailed'定位到'com.alipay.mobile.nebulaappproxy.ipc.H5EventHandlerServiceImpl'类,在这个类的
'public H5HttpRequestResult httpRequest'方法中通过'Future enqueue = new H5NetworkManager(H5Utils.getContext()).enqueue(h5HttpUrlRequest);'异步发起http请求。
- 继续跟进,得到整个http的执行流程,这个地方比较绕。最后发现httpclient对象是在'com.alipay.mobile.common.transport.http.inner.CoreHttpManager#getHttpClient'方法创建的,返回对象是'com.alipay.mobile.common.transport.http.AndroidHttpClient'。查看这个类的构造函数
查看ZSSLSocketFactory
private X509HostnameVerifier a = org.apache.http.conn.ssl.SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
此时我们成功找到HostnameVerifier了,它继承自AbstractVerifier,所以注入以下代码
// disable ssl hostname check
const AbstractVerifier = Java.use("org.apache.http.conn.ssl.AbstractVerifier");
AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean').implementation=function(a,b,c,d){
console.log('HostnameVerifier wants to verify ', a, ' disabled');
return;
};
搞定
取得小程序源码
上面得到的协议中还没有加密参数,但如果有参数怎么办呢?支付宝小程序是用js写的,理论上只要拿到其源码就可以得到加密算法。所以下面介绍如何找到小程序的源码文件。
在抓包时发现如下请求
请求参数中带有tinyAppId,这个值应该是小程序的惟一id。然后进入adb shell,切换su后进入支付宝应用的数据目录
/data/data/com.eg.android.AlipayGphone
, 在files/nebulaInstallApps/
目录下存储了所有加载过的小程序(猜测小程序在支付宝部门里的项目名叫nebula)
文件夹名即小程序的tinyAppId值。目录结构如下
tar压缩文件即为小程序的源码包。这个包没有加密,使用adb pull拿出来,直接解压即可。如下是解压后的文件结构
解压后对js格式化下代码,就可以分析了。js代码没有混淆,条理很清晰。