Android逆向实战:SSLPining处理

今天来逆向一个开启了 SSL Pining 的 APP。

APP4

APP4 地址:https://app4.scrape.center/

APP4 说明:

设置了 SSL Pining,如果设置了非法证书则无法正常请求数据,适合做反 SSL Pining 处理。

先安装 APP 看看:

image-20250213212135071

打开直接会报错,证书校验失败,因为检测到了非法证书,所以报错了。但是我们提前知道了这是由于设置了 SSL Pining,所以直接从这里入手。

证书异常

image-20250213214005351

image-20250213214026540

image-20250213214124703

image-20250213214145541

通过错误日志,一点点的往上找调用链路,我们可以找到请求的位置,可以看到请求的位置和函数基本上没有什么变化。

SSL Pining 位置

现在的问题是如何找到是哪里设置了 SSL Pining 呢?那肯定是从 okhttp 的 client 初始化入手,我们看到数据请求函数的时候,client 肯定已经初始化完成了,那我们顺着请求的位置继续往上找,找 client 初始化的地方,肯定通过某种方式设置了 SSL Pining,我们来找一下。

image-20250213220039061

这里是一个单例的 http 数据源类,通过私有构造器的方式保证单例,并且暴露 getInstance 接口给外界调用,用来初始化当前类的实例,继续找当前类的引用位置。

image-20250213220233276

发现这里引用到了,并且上面的 RetrofitClient 类调用了 getInstance 方法获取实例,大概率 client 就是在这里初始化的,初始化之后调用 create 函数来创建 MovieService 类的实例。

image-20250213220450234

查看 client 类,我们发现了请求的地址,私有化的构造器,还有一个很可疑的地方就是这里通过 new 关键字创建了一个 CertificatePinnerBuilder 类的实例,并且添加了一个域名的哈希值进去,大概率这里就是我们要找的位置了,哈希值应该是这个域名的证书的公钥的哈希值了。

SSL Pining 原理

原理就是将域名的公钥的哈希值固定在代码中,也就是说如果客户端收到的证书的公钥的哈希值和代码中的哈希值不相等的话,就会报错,也就达到目的了。

找到了对应的哈希值,我们看看 add 函数实际做了哪些事情。

image-20250213220826406

发现它将传入的参数添加到了 pins 这个变量中,那既然它是通过添加证书哈希值的方式来实现的话,应该如何解决呢?

解除 SSL Pining

有两个方案:

  • 我们可以不让它生效,让 add 函数不去添加哈希值。
  • 让它实际传入的哈希值是我们的抓包软件的哈希值,来一手狸猫换太子。

我们这里使用第一种方案试试,那怎么能不让它生效呢?这就要说到 Hook 大法了,Android 中 Hook 有两种方式,一种是通过 XP 框架的方式,写 Java 代码来实现 Hook,另一种就是使用 Frida,写 JS 代码来实现。

Frida 环境准备

我这里使用 Frida,它和 XP 框架的区别就是 Frida 是动态的,我可以随时修改 Hook 代码,而不用重新编译。

使用 Frida 需要先配置环境,首先 Android 手机需要安装 Magisk 框架,并且安装 MagiskFrida 模块,它可以在开机之后自动启动 Frida 客户端,不用我们在使用命令行启动了。还有这里推荐一个好用的模块,MoveCertificate,它可以自动将我们安装的用户凭证自动复制到系统凭证中去,无需我们手动将证书安装到系统信任区了。因为 Android7 以后安装到用户凭证中的证书已经不会被系统信任了,所以要想正常抓包,只能将证书安装到系统的信任区。

当然了,以上的一些前提都是:手机需要有 root 权限。

手机环境搞定了,再来配置 Pythonnodejs 环境。这里我们需要安装两个 Python 依赖包:fridafrida-tools,还有一个 nodejs 依赖包:@types/frida-gum

Python 的两个依赖是为了通过 Python 连接手机上的 Frida 服务端,并且将 JS 代码注入到 APP 中去运行。nodejs 的依赖包是为了能更好的编写 FridaJS 代码,它能提供类型提示,不用我们再记那么多函数了。

好了,到了这里环境基本上准备好了,接下来开始见证奇迹!

开始调试

新建一个 code 文件,写入以下代码:

1
2
3
4
5
6
7
8
9
10
Java.perform(function () {
console.log('Hello, World!');
let Builder = Java.use("okhttp3.CertificatePinner$Builder");
Builder["add"].implementation = function (pattern, pins) {
console.log(`Builder.add is called: pattern=${pattern}, pins=${pins}`);
//let result = this["add"](pattern, pins);
// console.log(`Builder.add result=${this}`);
return this;
};
})

然后再找到 APP4 的包名,可以从反编译工具中找到,位置在这里:

image-20250213222316771

接下来,运行以下命令:

1
frida -U -f com.goldze.mvvmhabit -l code.js

上面的命令含义是:使用 Frida 启动 APP4,并且将 code.js 的代码注入到 APP 中并且运行,JS 代码含义是获取到 okhttp 中的 CertificatePinner$Builder 这个类,覆盖掉它的 add 方法,并且在打印日志后就直接返回,不调用原有的 add 方法,这样就可以将 add 方法置空,也就是说调用 add 方法实际没有任何作用,也就解除了 SSL Pining,或者说相当于没有设置 SSL Pining。

运行以后,控制台应该会输出以下日志:

image-20250213222627102

可以看到,我们成功的输出了 add 方法的参数值,并且手机上的 APP4 会被自动打开,这个时候我们再看看,已经不会提示证书异常了,而是可以正常返回请求的数据了,大功告成!

如果想要更改 add 方法的参数,也可以在这里做手脚,在实际调用 add 函数的时候动态的将传入的哈希值换成我们自己的就可以了,大家感兴趣的可以自己试一下。

最终代码详见:https://github.com/libra146/learnscrapy/tree/main/Android/APP4

总结

SSL Pining 就这样被我们解决了,是不是很神奇,这里面涉及到了很多其他的知识,一篇文章是写不完的,如果有问题,欢迎大家在评论区留言,最好是自己动手操作一遍,可以加深自己的 Frida 使用的理解。

本文章首发于个人博客 LLLibra146’s blog

本文作者:LLLibra146

更多文章请关注公众号 (LLLibra146):LLLibra146

版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!

本文链接
https://blog.d77.xyz/archives/7e68b529.html