案例引入:
app:泡泡聊天
版本:v1.7.4
发送登录请求,抓包发现提示:403 Forbidden
这里就是使用了服务端证书校验,因为charles没有安装证书,所以到达服务器的响应没有通过验证,返回异常。
美之图:
一,校验逻辑
在安卓开发时,在客户端预设证书(p12/bks),客户端向服务端发送请求时,携带证书信息,在服务端会校验客户端携带过来证书的合法性。
服务端证书的校验逻辑更详细的过程为:
-
在apk打包时,将 bks 或 p12 格式的证书保存在 assets 或 raw 等目录。
-
安卓代码,发送请求时 【读取证书文件内容】+ 【证书密码】
charles想要抓到包,就需要找到集成在app中的bks/p12证书文件+密码,然后在charles中导入证书(bks格式需要转换p12格式),就可以抓到包。
在使用requests发送请求时,也需要携带上证书文件+密码,才可以正常得到响应数据。
注意:有些app,会有双向证书校验,即有客户端证书校验也有服务端证书校验。
**有个疑问:**charles拦截到客户端发送给服务端的证书和密码后,转发不就行了吗!
二,获得证书
**绕过的关键就是得到证书和密码,**可以通过hook keyStore.load() 方法得到,此方法的第一个参数是证书文件的输入流对象,参数2是证书的密码,还可以通过函数内的 this.getType() 得到证书的类型。
在开发时,是将证书文件加载到 InputStream
对象中,后续发送请求时携带。。。
想要获取证书有两种方式:
-
定位代码,找到加载证书的文件路径,然后去apk中寻找。或者在解压后的apk文件中的assets、raw文件夹中寻找bks、p12文件,不过有些app会对bks、p12文件进行隐藏,改变它们的后缀,使难以找到。
-
直接Hook证书加载位置,将证书的内容从
InputStream
写入到自定义文件,实现自动导出**【推荐:更加通用,甚至都不需要任何逆向】。**
2.1 hook导出证书
注意:在手机上一定要先给当前app开启可以操作硬盘的权限(手机的应用信息->权限),否则无法导出证书文件。
推荐使用这种方法,因为KeyStore是系统的包,不会受混淆的影响,因此通用。
Java.perform(function () {
var KeyStore = Java.use("java.security.KeyStore");
var String = Java.use("java.lang.String");
KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (inputStream, v2) {
var pwd = String.$new(v2);
console.log('\n------------')
console.log("密码:" + pwd, this.getType());
if (this.getType() === "BKS") {
// pkcs12
var myArray = new Array(1024);
for (var i = 0; i < myArray.length