Android抓包总结

抓包总结

背景

对于App的 逆向分析抓包 是一个绕不开的话题,同时 被抓包 我看来也算是一个App的命门吧。首先声明下,网上已经有很多大佬写了很详细的文章来介绍各种抓包场景,为什么我还要写,主要为自己能更好地理解,毕竟也是刚入门,嘿嘿。

关于抓包

前言

对于我们来说,所有数据都是通过标准协议的网络线路传输,所以关于抓包的一切都是以此为基础。从物理层开始,所有的数据就开始通过某种方式开始传输;不过幸运的是,对于我们来说一般只需要关心 网络层 or 传输层 or 它们的上层协议。常用的抓包分为 中间人攻击(mitm)VPN抓包 两种方式。

中间人攻击

中间人攻击,是从英文 Man-in-the-middle attack翻译过来的,缩写为 mitm

VPN抓包

通过自建VPN服务,对流量重定向。

多场景对抗

0x01 抓不到https包,APP可联网

这种情况,大概率是没有成功配置抓包环境,需要正确安装证书。网上有很多教程,就不展开讲了,我也讲不好。这里有一篇关于 Charles捕获HTTPS 原理的文章,感兴趣的可以看看。

0x02 抓不到包,APP可联网

首先确定app是可以上网的,但是抓不到包。那么可能会有以下情况:

  • 流量没有走到代理抓包软件上
  • 流量走的协议比较低,代理抓包软件没法代理
  • 流量走的自己自建协议,代理抓包软件没法代理

第一种情况,是由于app在代码中设置了不走代理(okhttp如下)。

1
OkHttpClient client = new OkHttpClient().newBuilder().proxy(Proxy.NO_PROXY).build();

针对这种情况,可以使用VPN将流量导向抓包工具,常见的有Postern配合 Charles使用Socks5代理。

第二种情况,常见于某团系、某里系产品。但是在代码中一般有开关,可以通过hook强行走http。

1
2
3
4
5
6
7
8
Java.perform(function () {
send("Inside java perform");
// disable socket connect
var tunnel2 = Java.use("com.guess.nvnetwork.tunnel2.a");
tunnel2.isSocketConnected.implementation = function () {
return false;
}
}

第三种情况,这种情况只能具体问题具体分析了,分析具体的组包、序列化的过程;最后通过逆向还原出来。

0x03 抓不到https包,APP不可联网

这种情况基本上就是APP开启SSL PinningSSL Pinning简单来说,就是应用程序中只信任固定证书或是公钥。而我们是使用的是抓包软件的证书,当然校验不过去,所以就断网了。针对SSL Pinning,可以从两个角度去破解:

  1. 让客户端不校验证书

    • 使用现成的trust模块,如JustTrustMe
    • 逆向app,找校验的位置,bypass掉
      Frida example for bypass okhttp
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      Java.perform(function () {
      //okttp3.x unpinning
      try {
      var CertificatePinner = Java.use("okhttp3.CertificatePinner");
      CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1){
      // do nothing
      console.log("Called! [Certificate]");
      return;
      };
      CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1){
      // do nothing
      console.log("Called! [List]");
      return;
      };
      } catch (e) {
      console.log("okhttp3 not found");
      }
      //okhttp unpinning
      try {
      var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");
      OkHttpClient.setCertificatePinner.implementation = function(certificatePinner){
      // do nothing
      console.log("Called!");
      return this;
      };

      // Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)
      var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");
      CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1){
      // do nothing
      console.log("Called! [Certificate]");
      return;
      };
      CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1){
      // do nothing
      console.log("Called! [List]");
      return;
      };
      } catch (e) {
      console.log("okhttp not found");
      }
      }
  2. 让客户端信任抓包证书(硬编码校验服务器证书的情况并不适用)

    • 换一个低安卓版本的(低于7.0)手机
    • 将抓包工具的证书安装到系统根证书目录中

0x04 抓得到包,APP不可联网

这种情况很有可能是服务端校验了客户端证书,在这里我们不能控制服务端,所以只能拿到客户端证书导入抓包工具。拿到客户端的方法:

  • 可以在assets、raw目录中找找看
  • 逆向app,对证书进行提取
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Java.perform(function () {
    var StringClass = Java.use("java.lang.String");
    var KeyStore = Java.use("java.security.KeyStore");
    KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) {
    console.log("KeyStore.load1:", arg0);
    this.load(arg0);
    };
    KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) {
    send(arg0)
    console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1) : null);
    this.load(arg0, arg1);
    };
    });

特别感谢