部分APP无法代理抓包的原因及解决方法
引言
HTTP 应用层的抓包已经成为日常工作测试与调试中的重要一环,最近接触新项目突然之间发现之前的抓包手段都不好使了,顿时模块与模块之间的前端与服务之间的交互都变成了不可见,整个人都好像被蒙住了眼睛。
其实自己也很早就发现平时使用的支付宝等 APP 使用 Fiddler 或 Charles 这类代理抓包软件默认情况下就无法抓取请求的,但使用 Wireshark 这类网卡抓包软件可以看到这些 APP 的流量,而已这些流量也表明这些 APP 使用的主要应用层协议仍然是 HTTP(https),不过我们的代理抓包工具却失效了。如今终于在实际工作中遇到了,也不得不解决了,毕竟眼前有东西挡住会让我浑身不适。
解决方案
这里省略了 原理 解析(知道你们不爱看,先跳过了)。有需要先查看下方的
原理
说明
不过既然我们已经知道了 Fiddler 和 Charles 不能抓包的具体原因,前面也提到了代理抓包的原理,那我们就总有办法解决。
前面说到了我们 APP 使用的 HTTP 客户端没有连接到代理服务器,导致我们的代理抓包软件无法正常抓包,那我们只要想办法让客户端重新连接到代理服务器就好了(当然这一切都是以不修改客户端软件 APP 为前提的)
-
方法 1:控制 DNS 解析,通过修改 dns 的方式让客户端以为我们的代理服务器就是目标服务器。
优势:操作方便,通过修改设备的 hosts 可以十分方便的首先
劣势:需要为每个需要操作的域名提前添加 host,在手机等手持设备上难以修改 hosts(即对移动 APP 这类应用很难实现)
-
方法 2:在网络设备上直接做流量转发,将指定终端设备上发往 80 及 443 端口的数据直接转发到代理服务器的 目标端口上
优势:可以针对连接到网络设备上的终端设备进行分别配置,而手机等终端设备不需要进行任何设备
劣势:需要单独的硬件设备
-
方法 3:使用 VPN 将终端设备的流量转发到代理服务器
优势:使用 VPN 软件不用添加其他测试。
劣势:终端上的 VPN 默认会直接对所有流量进行转发,要进行合理的配置可能需要额外的学习成本
操作步骤 (Android)
笔者这里直接使用上面提到第 3 种方法(方法 1 在对于手机 APP 很难操作,方法 2 可能需要其他设备所以这里不使用),因为我们的测试对象是手机移动 APP,所以我们首先要在手机上安装一个 VPN,这里使用一个十分方便的 VPN 软件 drony (drony 介绍),drony 会在你的手机上创建一个 VPN,将手机上的所有流量都重定向到 drony 自身(不是流向 vpn 服务器) ,这样 drony 就可以管理所有手机上的网络流量,甚至可以对手机上不同 APP 的流量进行单独配置。
安装 drony
您可以在网络上搜索 drony 选择自己想要的版本进行安装(安装包下载:Droni.apk )
下载地址:
安装完成后打开软件如下图:
开启代理抓包软件
这里代理抓包软件使用的是 Fiddler,Fiddler 的使用这里不再介绍,需要打开远程代理,并在手机中安装 Fiddler 根证书
这里笔者开启的远程代理的地址是 192.168.2.244:8888
关于证书安装有些细节,Fiddler 默认的根证书是 cer 格式,部分手机可能只能识别 pem 格式证书
直接使用 openssl 转一下就行了 (openssl x509 -inform der -in FiddlerRoot.cer -out FiddlerRoot.pem)
openssl 在中 windows/mac 一般都安装有,命令直接使用就行了,使用生成的 FiddlerRoot.pem 安装到手机上就行了(Charles 默认就是 pem 证书)
配置 drony 转发
打开 Drony,切换到 SETTING,选择 wifi 设置
网络详情设置
-
设置 Proxy type
-
设置代理 hosetname,默认是电脑局域网 ip
-
设置代理 Port,fiddler 默认是 8888
-
设置 Filter default value 为 Direct all
-
设置 Rules
-
启动 Drony
安装 fiddler 证书
192.168.0.102 是我电脑局域网 ip,8888 是 fiddler 抓包软件的端口号
配置 fiddler 只显示指定域名(多个域名 ;
分割),方便抓包调试
操作步骤(IOS)
ios(iphone)同样使用上面提到第 3 种方法(原理与 android 是一致的),同样需要使用到 VPN,在 ios 也有许多与 drony 功能类似的软件,大家可以自己选择自己喜欢的使用,笔者这里使用的是 Shadowrocket
如上图您可以直接在 App Store 找到这些软件(受限于大陆相关规定,您的 App Store 的区域如果在国内可能无法搜索到这些软件,您需要使用美区的账号)
为了完成流量重新定向,Shadowrocket 与 drony 一样会先在设备上创建本地 VPN 服务,再使用您设置的规则处理流量。
不过 Shadowrocket 的使用会更加方便,直接打开软件如下图配置即可:
- 选择全局路由为「代理」
- 添加服务节点(类型选择 HTTP 及 HTTPS ,服务器地址及端口为您代理抓包工具的地址与端口)
- 设置状态为启用 (IOS 会同时自动创建 VPN)
现在直接打开 iphone 上的任意 APP(不用再再 wifi 上重复设置代理) ,既可以在代理抓包工具上看到流量了,同样不能解析 HTTPS 的流量,不过 IOS 并没有像新版的 android 一样可以让 APP 拒绝用户手动信任的用户根证书,所以 IOS 证书安装 也比 android 任意的多,并没有这么多额外的操作,按正常证书安装流程操作即可。证书安装完成后即可查看 HTTPS 流量
原理
代理抓包原理
为了弄明白为什么 Fiddler 或 Charles 对这些 APP 无效,我们有必要先了解代理抓包我原理(当然您不想了解也是完全可以的,直接看后面的实际操作就可以完成,原理分析什么的可以有兴趣随时回来看)。
Fiddler 或 Charles 这类使用的代理的抓包软件与 Wireshark 是完全不同的(Wireshark 使用的网卡数据复制,只要是经过指定网卡都会被抓取),其只能对使用代理的应用层网络协议生效,比如常见的 HTTP(https),Websocket 。
这里以 HTTP 为例简单说明下:
客户端需要完成一次 HTTP 请求,通常需要先找到服务器,客户端会根据 http 请求中 url 的主机名(实际会使用 host 中的主机名)及其端口与目标主机建立 tcp 连接,建立连接后会将 http 报文发送给目标服务器 (更多细节请参考 https://tools.ietf.org/html/rfc7232)
接下来我来看下 HTTP 代理是如何运作的,我们启动 Fiddler 或 Charles 就是启动了一个 HTTP 代理服务器,这类工具会通知操作系统,“现在我在系统上创建了一个 HTTP 代理,IP 为 XXXXXX 端口为 XX”。如果您使用的是 linux 您可以手动通知操作系统 (export http_proxy=ip:port export https_proxy=$http_proxy), 如果您使用的是手机等移动设备您可以在当前 wifi 设置处告诉系统你要使用 http 代理。 现在我们已经告诉系统我们想要使用代理,这个时候运行在系统上的 http 客户端再去发送请求的时候,他就不会再去进行 DNS 解析,去连接目标服务器,而是直接连接系统告诉他代理所在的地址(代理的 ip 及端口,注意无论是 http 或 https 或其他支持代理的协议都会连接同一个端口)。然后代理服务器会与客户端建立连接,再然后代理服务器根据请求信息再去连接真正的服务器。
这里还有个细节正常在没有代理的情况下客户端向服务器发送的请求行里只包含部分 URI(实际上是没有方案,主机名及端口的)
如上图如果在没有代理的情况下,对 www.baidu.com/index.html 的请求的请求行实际上是 GET /index.html HTTP/1.1 其实并不是我们常见的完整 uri。因为在原始的 HTTP 设计中没有考虑中间服务器(即代理)的情况,客户端在发送报文前已经知道服务器的地址并与之建立了连接,没有必要再发送方案,主机名及端口。不过代理出现后这种做法就会有问题了,客户端连接了代理服务器,而代理服务器却没有办法连接正确的服务器。因此客户端发送给代理的请求其实稍有不同,客户端会在请求行里使用完整的 uri,这样代理服务器才能解析真实的服务器的地址。
现在我们的请求实际上都是通过代理服务器(Fiddler 或 Charles)发送出去的,所以代理抓包软件不仅知道 http 请求及响应的所有报文,甚至还可以随时修改请求及响应。
部分应用不能抓包的原因
可以看到代理抓包的关键就是需要 HTTP 客户端按照要求去连接代理服务器,一般情况下我们已经在系统层面上设置了代理,通常 http 客户端都是按要求去实现的,在进行 http 请求前会先检查系统代理,如果有设置代理,客户端会直接使用完整 uri 去连接代理服务器。不同的平台通常会实现自己的的 http 客户端的,虽然他们都按照协议要求实现了代理功能,但是并不一定在默认情况下会直接使用系统代理。
在现实中这种况下这种情况还不少,笔者当前项目使用到的 Flutter 就是这种情况,默认 Flutter 不会主动使用系统代理,需要单独设置。(当然个人认为这种策略也是有理由,虽然给测试及调试带来了不便不过也在一程度上提高了些许数据安全)
正是因为 HTTP 客户端没有使用我们设置的系统代理,他们自然也不会连接 Fiddler 或 Charles 创建的代理服务器,最终导致我们无法获取任何请求。
证书校验原理
无论 Fiddler 或 Charles 都使用中间人攻击的方式替换 tls 链路证书,解密报文然后再加密发送给真实服务器。
您可以参看 HTTPS 中间人攻击实践(原理 · 实践)了解中间人攻击的细节
存在代理的情况下客户端首先连接的是代理服务器(即是图中的攻击者),实际 client 是直接与 Proxy 建立的 TLS 通道,所以代理当然 TLS 通道的传输密钥然后解密报文。
不过由于证书的存在,client 会校验证书的合法性,然后决定是否连接服务器。我们使用 Fiddler 或 Charles 抓取 https 前在设备中安装根证书正是为了通过 client 的证书校验。
在浏览器中任意找一个 https 的网页,查看其证书信息。
从这里面我们能看到证书包含以下内容:
- Validity 也即有效期,有效期包含生效时间和失效时间,是一个时间区间;
- 公钥信息 Subject Public Key Info,包括公钥的加密算法和公钥内容;
- 指纹信息,指纹用于验证证书的完整性,也是证书校验的关键,他保证书没有被修改过。 其原理就是在发布证书时,发布者根据指纹算法 (此处证书使用了 SHA-1 和 SHA-256 算法 有多个指纹是为了兼容老的客户端) 计算整个证书的 hash 指纹【证书内容 hash 值使用 CA 私钥加密就是指纹】并和证书放在一起,client 在打开证书时,自己也根据指纹算法计算一下证书的 hash 值,同时使用自己信任的根证书的公钥解密 hash 指纹计算出原始 hash,如果 hash 值不一致,则表明证书内容被篡改过;
- 证书的签名 Certificate Signature Value 和 Certificate Signature Algorithm,对证书签名所使用的 Hash 算法和 Hash 值;
- 签发该证书的 CA 机构 Issuer;
- 该证书是签发给哪个组织 / 公司信息 Subject;
- 证书版本 Version、证书序列号 Serial Number 以及 Extensions 扩展信息等。
上图即是证书指纹校验的过程,可能看到 Client 校验证书的核心其实是 CA 公钥解密原始指纹,CA 公钥从哪里来,为了确保安全设备系统会有一批自己信任的 CA 公钥列表(根证书)。这些 CA 公钥对应的一般是权威机构或组织,然后由这些权威机构颁发证书时会使用他们自己的私钥去签名(为证书生成指纹)。这样就确保了只有权威机构颁发给各个网站的证书才会被客户端校验通过。
Filddler 没有这些证书里公钥对应的私钥(CA 只会把为完整颁发的证书对应的私钥给网站的所有者),所以没有办法与客户端完成 TLS 握手。Filddler 为了完成握手只能自己为不同的站点生成证书,
不过自己的生成的证书肯定是用自己的私钥签名的,客户端在自己信任的 CA 公钥列表找不到对应根证书,肯定是不能通过证书校验的。所以 Filddler 要求我们安装他的根证书到设备,这样自己签发的证书就可以通过证书校验,自己就能解密 https 报文了。
不能解密的原因
其实通过上面的描述也很明白了不能正常建立连接解密 https 报文的原因就是证书校验失败,我们的根证书安装不够完全。
从 Android7.0 以后,系统允许每个应用可以定义自己的可信 CA 集。有部分应用默认只会信任系统预装的 CA 证书,而不会信任用户安装的 CA 证书(或者说是应用使用的开发框架默认只信任系统证书,因为开发者通常不关心这些配置,也不会去更改他)。而在 Android 中用户安装的证书都是用户证书,所以无论是 Filddler 还是 Charles 我们都只是把他们的根证书安装到了用户证书,这些应用并不使用他们,所以我们的安装的证书是无效的。
解决方法及操作方法
既然又知道了原因,那就总还是有办法去解决的。我们只要把代理软件的根证书安装成系统证书就可以了。
实际上将证书安装到系统区操作还是相对简单的,将证书用指定的名称放到指定的位置(/system/etc/security/cacerts/)就可以了
先将我们的根证书名称改为 <Certificate_Hash>.<Number>
Certificate_Hash 表示证书文件的 hash 值,Number 是为了防止证书文件的 hash 值一致而增加的后缀(用 0 就行了)
下载自己的根证书 FiddlerRoot.cer,使用 openssl x509 -subject_hash_old -in <Certificate_File>
计算证书 hash ,根据 hash 将证书重命名为 269953fb.0 (269953fb 是笔者证书的 hash,大家的肯定不一样)
然后将 269953fb.0 文件复制到 / system/etc/security/cacerts/
完成后我们就可以看到代理软件的证书出现在系统区了。
这里还有一点需要单独说明,/system/etc/security/cacerts/
目录的写权限,需要手机 root 权限。
也就是说复制证书到该目录需要您 root 自己的设备。
关于 Android 手机的 root,通常手机厂家都会有自己官方的教程,建议大家按官方的操作进行 root
以小米手机为例 (http://www.miui.com/thread-12281379-1-1.html)
当前小米手机的 root 需要手机绑定小米账号 7 天以上才能解锁,解锁后刷入开发版即可完成 root
需要注意的是不是所有小米的手机都有对应的开发版的,所以购买测试设备时留意下。(http://www.miui.com/download.html 这里可以看下自己的手机有没有开发版可以使用)
效果
现在证书也被安装到了系统区,再回到 Fiddler 看下效果
再次打开闲鱼我们已经可以看到完整的 https 请求了。
下面我们找个请求修改一下请求返回数据看下效果
借助 Fiddler 插件 FreeHttp 修改这个请求的返回数据将二手手机修改为二手马总并将图片也替换掉
配置如下图:
(FreeHttp 的具体使用请参考借助 FreeHttp 任意篡改 http 报文 (使用 · 实现))
再次打开闲鱼,可以看到经过代理的数据已经被篡改了(注意测试时清除咸鱼的缓存及应用数据,以保证每次打开 APP 都会请求 firstdata)
经过上面到配置,这些 APP 的 HTTP 流量我们就可以通过代理抓包软件获取,https 流量也可能正常解码。
附录
真诚点赞 诚不我欺~