HTTP TCP/IP协议与抓包原理
zsk Lv4

网络基础 TCP/IP

在说Http之前,先来说一下TCP/IP协议。

TCP/IP协议是众多协议的统称,像HTTP,TCP,UDP,DNS等都包括在里面

TCP/IP 协议会把这些进行分层,按层次分为4层:应用层、传输层、网络层、数据链路层。

也有细分为7层的,应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
像HTTP、FTP、DNS就属于应用层,TCP、UDP在传输层,IP在网络层

image


TCP/IP通信传输流

image

image

以HTTP来举例,首先作为发送端的客户端在应用层以HTTP协议发出一个想看某个Web页面的HTTP请求。在应用层会经过DNS解析,通过域名查找IP地址

接着,为了传输方便,在传输层用了TCP协议把从应用层处收到的数据HTTP请求的报文进行分割,并在各个报文上打上标记序号及端口号转发给网络层

在网络层IP协议,增加作为通信目的地的MAC地址后转发给链路层。

接收端的服务器在链路层接受数据,按序往上层发送,一直到应用层。在传输到传输层的时候,会把接收到的数据包按原来的顺序重组请求报文。当传输到应用层,才算真正接受到由客户端发送过来的HTTP请求。

作为接收端的服务器收到发送端的请求后,会把对应的页面以同样的TCP/IP通信协议进行回传。

发送端在层与层之间传输数据时,每经过一层时会被打上该层所属的首部信息,反之,接受端在层与层传输数据时,每经过一层时会把对应的首部消去。


TCP协议的可靠性(三次握手)

TCP位于传输层,提供可靠的字节流服务。
所谓字节流服务指,为了方便传输,将大块数据分割成报文段为单位的数据包进行管理,可靠性指能够准确可靠的传输给对方。一言以蔽之,TCP协议为了更容易传输大数据才把数据分割,而且TCP协议能够确认数据最终是否送达对方。

确保数据能到达目标
为了准确无误的将数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送后的情况置之不理,它一定会向对方确认是否成功送达,握手过程中使用了TCP的标志SYN和ACK。

发送端首先发送一个带SYN标志的数据包给对方。 接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。 最后,发送端再回传一个带ACK标志的数据包,代表握手结束。 若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。

为什么不是二次,四次握手,而是三次?

四次?

很显然1.2和1.3 这两个步骤可以合并,只需要三次握手,可以提高连接的速度与效率。


二次?

现假定出现一种异常情况,即A发出的第一个请求连接报文段并没有丢失,而是在某些网络结点长时间滞留了,以至到连接释放以后的某个时间才到达B。本来这是一个已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了。

由于现在A并没有发出建立请求的连接,因此不会理睬B的确认,也不会向B发送数据,但B却以为新的运输连接已经建立了,并一直等待A发来的数据。B的许多资源就这样白白浪费了。

采用三次握手的办法可以防止上述现象发生。例如在刚才的情况下,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接,所以就不会分配资源给这个连接

image

image


HTTP 是不保存状态的协议

HTTP 是一种不保存状态,即无状态(stateless)协议。HTTP 协议自 身不对请求和响应之间的通信状态进行保存。也就是说在 HTTP 这个 级别,协议对于发送过的请求或响应都不做持久化处理。

虽然是无状态协议,但为了实现期望的保持状态功能,于是引入了Cookie 技术。有了 Cookie 再用 HTTP 协议通信,就可以管 理状态了。


持久连接

HTTP/1.1 和一部分的HTTP/1.0 想出了 持久连接(HTTP Persistent Connections,也称为 HTTP keep-alive 或 HTTP connection reuse)的方法。持久连接的特点是,只要任意一端 没有明确提出断开连接,则保持 TCP 连接状态。

持久连接的好处在于减少了 TCP 连接的重复建立和断开所造成的额 外开销,减轻了服务器端的负载。另外,减少开销的那部分时间,使 HTTP 请求和响应能够更早地结束,这样 Web 页面的显示速度也就相 应提高了。

image

持久连接的好处在于减少了 TCP 连接的重复建立和断开所造成的额 外开销,减轻了服务器端的负载。另外,减少开销的那部分时间,使 HTTP 请求和响应能够更早地结束,这样 Web 页面的显示速度也就相 应提高了。


四次挥手:

TCP连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。这时对方会回一个ACK,此时一个方向的连接关闭。但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个FIN段来关闭此方向上的连接。接收方发送ACK确认关闭连接。


HTTP的缺点

通信使用明文(不加密),内容可能会被窃听

不验证通信方的身份,因此有可能遭遇伪装

无法证明报文的完整性,所以有可能已遭篡改

对于 HTTP 来说,服务器也好,客户端也好,都是没有办法确认通信方的。因为很有可能并不是和原本预想的通信方在实际通信。 并且还需要考虑到接收到的报文在通信途中已经遭到篡改这一可能性。

为了统一解决上述这些问题,需要在 HTTP 上再加入加密处理和认证 等机制。我们把添加了加密及认证机制的 HTTP 称为 HTTPS

通信的加密

一种方式就是将通信加密。HTTP 协议中没有加密机制,但可以通过和 SSL(Secure Socket Layer,安全套接层)或 TLS(Transport Layer Security,安全层传输协议)的组合使用, 加密 HTTP 的通信内容。

用 SSL建立安全通信线路之后,就可以在这条线路上进行 HTTP 通信了。与 SSL组合使用的HTTP 被称为 HTTPS(HTTP Secure,超文本传输安全协议)。


HTTP+ 加密 + 认证 + 完整性保护 =HTTPS

可以说HTTPS 是身披 SSL 外壳的 HTTP

HTTPS 并非是应用层的一种新协议。只是 HTTP 通信接口部分用 SSL和TLS协议代 替而已。

通常,HTTP 直接和 TCP 通信。当使用 SSL时,则演变成先和 SSL通信,再由 SSL和 TCP 通信了。简言之,所谓HTTPS,其实就是身披 SSL协议这层外壳的 HTTP。

在采用 SSL后,HTTP 就拥有了 HTTPS 的加密、证书和完整性保护 这些功能。
image


SSL/TLS加密

image

第一次访问采用非对称加解密(非对称指客户端和服务器都有自己的公钥和私钥)
客户端访问服务器,

服务器收到客户端访问返回服务器的公钥,

客户端收到服务器的公钥,用服务器的公钥对客户端的公钥进行加密发送给服务器,

服务器收到加密后的公钥,用服务器的私钥进行解密取得客户端的公钥,

将session key用客户端的公钥进行加密发送给客户端,

客户端用自己的私钥进行解密获取session key


后续通信使用对称加解密(对称加密指客户端和服务器使用同一套公钥私钥)
对session key加密后发送session key进行通信

非对称加解密只是用于session key安全的在两者之间进行传递,因为非对称加解密的开销很大。session key是有时效性的


加入了抓包软件后SSL/TLS加密

image

Charles对客户端来说是服务器,对服务器来说是客户端

1.客户端去访问Charles,Charles去访问服务器

2.服务器会给Charles发送服务器的公钥,Charles会给客户端发送Charles的公钥

3.客户端用Charles的公钥对自己的公钥进行加密发送给Charles,Charles用服务器的公钥对自己的公钥进行加密发送给服务器

4.服务器用自己的私钥解开得到Charles的公钥,Charles用自己的私钥解开得到客户端的公钥

5.服务器得到Charles的公钥对session key加密发送给Charles,Charles用自己的私钥解开得到session key

6.Charles把session key用客户端的公钥对session key加密后发送给客户端,客户端用自己的私钥解开得到session key
到这里客户端,Charles,服务器的session key就统一了。

Charles抓不到包

打开Charles抓不到包,为什么导入Charles的证书之后,app抓包就正常了呢?
image

有了Charles置于中间之后,本来c/s架构的通信过程会“分裂”为两个独立的通信过程,app本来验证的是服务器的证书,服务器的证书手机的根证书是认可的,直接内置的;但是分裂成两个独立的通信过程之后,app验证的是Charles的证书,它的证书手机根证书并不认可,它并不是由手机内置的权威根证书签发机构签发的,所以手机不认,然后app也不认;所以我们要把Charles的证书导入到手机根证书目录中去,这样手机就会认可,如果app没有进行额外的校验(比如在代码中对该证书进行校验,也就是SSL pinning系列API)的话,app也会直接认可接受。

在高版本的安卓上,用户安装的证书不会直接安装到系统根证书目录
中,需要root手机后将证书移动到系统根证书目录中去。

当Charles的证书安装到系统根目录中去之后,系统就会信任来自Charles的流量包了,我们的抓包过程就会回归正常

1.安装个人证书
通过VPN连接到Charles,然后用浏览器访问chls.pro/ssl,会自动弹出安装个人证书。

个人证书安装在手机的 /data/misc/user/0/cacerts-added 目录下

2.复制个人证书到系统目录(安卓8以上需要)

1
2
3
4
5
6
cd /data/misc/user/0/cacerts-added/ 
mount -o remount,rw /system
mv * /etc/security/cacerts/
cd /etc/security/cacerts/
ls -alit
mount -o remount,ro /system


SSL Pinning(情况更少)

就是客户端并不会默认信任系统根证书目录中的证书,而是在代码里再加一层校验,这就是证书绑定机制——SSL pinning,如果这段代码的校验过不了,那么客户端还是会报证书错误。

Https客户端代码校验服务器证书

案例:滴答清单

遇到这种情况的时候,有这两种方式,当然目标是一样的,都是hook住这段校验的代码,使这段判断的机制失效即可。

  1. hook住checkServerTrusted,将其所有重载都置空;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function hook_ssl() {
Java.perform(function() {
var ClassName = "com.android.org.conscrypt.Platform";
var Platform = Java.use(ClassName);
var targetMethod = "checkServerTrusted";
var len = Platform[targetMethod].overloads.length;
console.log(len);
for(var i = 0; i < len; ++i) {
Platform[targetMethod].overloads[i].implementation = function () {
console.log("class:", ClassName, "target:", targetMethod, " i:", i, arguments);
//printStack(ClassName + "." + targetMethod);
}
}
});
}

  1. 使用objection,直接将SSL pinning给disable掉 android sslpinning disable

  1. 如果还有一些情况没有覆盖的话,可以来看看
    https://github.com/WooyunDota/DroidSSLUnpinning
  • 目录ObjectionUnpinningPlus增加了ObjectionUnpinning没覆盖到的锁定场景.(objection)

    • 使用方法1 attach : frida -U com.example.mennomorsink.webviewtest2 —no-pause -l hooks.js

    • 使用方法2 spawn : python application.py com.example.mennomorsink.webviewtest2

  • ObjectionUnpinningPlus hook list:

    • SSLcontext(ART only)

    • okhttp

    • webview

    • XUtils(ART only)

    • httpclientandroidlib

    • JSSE

    • network_security_config (android 7.0+)

    • Apache Http client (support partly)

    • OpenSSLSocketImpl

    • TrustKit

应该可以覆盖到目前已知的所有种类的证书绑定了。


服务器校验客户端证书(情况少)

既然app客户端会校验服务器证书,那么服务器可不可能校验app客户端证书呢?答案是肯定的。

单一通信已经分裂成两个互相独立的通信,这时候与服务器进行通信的已经不是app、而是Charles了,所以我们要将app中内置的证书导入到Charles中去。

这个操作通常需要完成两项内容:

  1. 找到证书文件
  2. 找到证书密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function hook_KeyStore_load() {
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) {
//printStack("KeyStore.load1");
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load1:", arg0);
this.load(arg0);
};
KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) {
//printStack("KeyStore.load2");
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1) : null);
this.load(arg0, arg1);
};

console.log("hook_KeyStore_load...");
});
}

setImmediate(hook_KeyStore_load)
找到证书文件很简单,一般apk进行解包,直接过滤搜索后缀名为p12的文件即可,一般常用的命令为 tree -NCfhl |grep -i p12,直接打印出p12文件的路径

想要拿到密码也很简单,一般解密会调用现成的API,java.security.KeyStore ,直接使用frida打印出来,也可以hook系统文件库


1
2
3
objection -g cn.soulapp.android explore
//android hooking watch class_method java.io.File.$init - -dump-args --dump-backtrace --dump-return
android hooking watch class_method java.io.File.$init - -dump-args

有了证书和密码之后,就可以将其导入到抓包软件中


image

一般HTTPS:大部分只采用客户端检验服务器

双向绑定:很少会有服务器校验客户端

SSL cert Pinning:更少(案例:趣充)


推荐使用Postern+Charles组合进行抓包

我们平时用代理的时候,都是通过给wifi设置http代理的方式进行抓包,只是在应用层抓包,所以会被很轻易的检测到和绕过的。

很多应用会通过

System.getProperty(“http.proxyHost”)

System.getProperty(“http.proxyPort”);

这两个API来查看当前系统是否挂了http代理,会很轻松的让你的抓包失效。

这时候只能用Frida或Xposed来hook这个接口、修改其返回值


所以我们需要换一种方式来设置代理。就是设置vpn代理,vpn是属于网络层的,设置了vpn后,你的手机上ifconfig后会多一个tun0的接口,等于加了一个虚拟网卡,所有的流量都会从这走。应用层和传输层的请求都可以拿到,还不会被上面提及的两个api所检测。

输入ip route show table 0 |grep defalut查看路由表

路由表的第一条就指向了tun0,可以说开vpn的效果就和直连的效果一摸一样。

 评论