http_https分析

http/https网络协议分析

http与https的区别

http

http是一种应用层协议位于传输层协议tcp之上,其不会对数据进行任何的加密,只是按照一定的格式对数据进行封装(http报文格式),在数据封装完之后直接使用tcp的socket进行数据的发送。传输层tcp是一种长连接,http基于tcp实现短连接,所以每一次http会话都需要经过tcp三次握手--->http数据传输---->tcp四次挥手(为了提高网络传输效率,默认开启keep-alive支持了http的长连接)。

https

img

https是http + ssl,其通过ssl加密套件实现了数据在传输过程中加密。SSL也是通过tcp的socket进行数据的传输,只不过在传输数据前会先对数据进行加解密。https数据传输过程为tcp三次握手--->ssl握手--->http数据传输--->tcp四次挥手。

libcurl网络库分析

libcurl(http)

对于http连接而言,libcurl通过http协议的格式进行封包后调用Curl_send_plain函数发送http数据包,Curl_send_plain函数会直接调用tcp的socket函数send。
img

接着调用Curl_recv_plain获取http返回的数据包,Curl_recv_plain会调用tcp的socket函数recv。
img
libcurl会解析返回的http数据包,并判断Content-Length:长度的值,如果判断此次http请求返回的数据还没有读取完会继续调用Curl_recv_plain从socket中读取数据。直到读取到的http报文主体的长度等于Content-Length才返回。

img

libcurl(https)

libcurl利用openssl实现https,libcurl通过http协议的格式进行封包后调用ossl_send函数发送http数据包,ossl_send会使用openssl的接口SSL_Write发送http数据(在openssl中,SSL_Write会先通过会话密钥对http数据进行加密,然后调用socket的send函数进行发送)。

img

接着调用ossl_recv函数获取http返回的数据包,ossl_recv函数调用openssl的接口SSL_Read(SSL_Read会调用socket的recv函数并通过会话密钥对数据进行解密)。因此到达ossl_recv后的数据是已经解密后的。

img

接下来处理和libcurl对http的处理一样,libcurl会解析返回的http数据包,并判断Content-Length:长度的值,如果此次http请求返回的数据还未读取完就继续调用SSL_Read。

//开启ssl验证
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, TRUE);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, TRUE);
//指定根证书
curl_easy_setopt(curl, CURLOPT_CAINFO, pCAFile);
//指定客户端证书
curl_easy_setopt(curl, CURLOPT_SSLCERT, pCertFile);

libcurl通过开启ssl验证并指定根证书来与https服务器进行ssl握手,一般的web服务器只要求单项认证(客户端验证服务端),如果需要进行双向认证(服务端也验证客户端)就需要指定客户端的证书。

cpp-lib网络库分析

cpp-lib通过调用SSLSocketStream::Write发送http数据包,此函数会调用openssl的SSL_Write。

img

同样cpp-lib通过调用SSLSocketStream::Read--->SSL_Read获取http请求返回的reponse数据包。

img

libcef的net模块网络库分析

libcef基于开源的chromium,使用的是chromium的net模块进行http连接。分析chromium的net模块源码发现http/https最后会调用HttpStreamParser::DoLoop进行http请求数据的发送和返回数据的读取。

img

以DoSendHeader为例,其最后会调用stream_socket_->Write发送http数据包

img

对于http连接而言stream_socket_就是SocketStream,其会调用send和recv进行http数据的传输。
img

对于https连接而言stream_socket_就是SSLSocketStream,其会调用SSL_Write和SSL_Read进行http数据的传输。

img

其中获取http请求返回的reponse数据时需要注意,DoReadHeaders可能会读取部分boby数据,所以在调用DoReadBoby时需要把之前已经读取的boby数据给整合,最后boby数据全部放在user_read_buf_中。

img

https服务端

https服务端主要涉及到SSL的初始化,SSL握手,使用SSL_Write/SSL_Read与客户端进行数据通讯

SSL初始化

SSL初始化会加载必要的库,设置服务端证书(包含公钥)以及私钥,验证证书中的公钥与私钥是否匹配。

//加载服务端证书(包含公钥)
if (SSL_CTX_use_certificate_file(ctx, SERVER_CERT_FILE, SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    exit(0);
}
//加载服务端私钥
if (SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY_FILE, SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    exit(0);
}
// 判定服务端证书(包含公钥)与私钥是否匹配
if (!SSL_CTX_check_private_key(ctx)) {
    ERR_print_errors_fp(stderr);
    exit(0);
}

默认是SSL_VERIFY_NONE进行单项认证,即客户端验证服务端的真伪。可以通过设置SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT 并调用SSL_get_verify_result来实现双向认证(执行SSL_get_verify_result才进行真正的验证过程),即服务端也需要验证客户端的真伪。

SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, TRUE);
SSL_get_verify_result(ssl);

SSL握手

当服务端通过socket监听到有https客户端连接时accept函数就会返回,这时候已经完成了TCP的三次握手。然后https服务端需要进行SSL握手,通过调用SSL_accept与https客户端进行SSL握手的过程。

img

SSL_Write/SSL_Read

服务端使用openssl的接口SSL_Write和SSL_Read进行数据的传输,函数会对http的数据进行加密后在调用send/recv进行传输

img

http/https抓包

软件不支持设置代理如何抓包

fiddler是通过设置系统来实现http/https的抓包的,如何程序使用WinInet库(默认使用系统代理)或者使用的第三方网络库默认设置使用系统代理则可以通过fiddler来进行抓包。当然如果第三方程序可以设置代理,然后将代理设置为fiddler也是可以进行抓包的。

img

但是如果第三方程序不支持设置代理就无法使用fiddler进行抓包了,这时候可以使用Proxifier或者SocksCap64强制为程序设置代理。SocksCap64的原理是利用API hook,通过hook socket的api强制进行tcp流量的转发(不稳定),Proxifier则是利用windows的接口通过设置LSP模块过滤和转发tcp流量(稳定)。通过Proxifier设置代理服务器为fiddler监听的地址和端口即可,Proxifier还支持配置规则来代理指定应用程序。

img

例如在使用libcurl库时,没有调用curl_easy_setopt(curl, CURLOPT_PROXY, "127.0.0.1:8888");设置代理,也没有通过读取windows注册表设置代理为系统代理,这样fiddler是无法抓到这个应用的http/https数据的。通过proxifier+fiddler就可以强制为其设置代理并捕获http数据流量了。

img

软件默认不使用windows系统预制的根证书

SSL握手过程中如果是只是单项认证则需要服务端将证书发送给客户端,客户端会利用收到的服务端证书与信任的根证书进行验证。对于一般的浏览器应用其会默认信任windows系统中预制的CA根证书,而fiddler就是通过将自己伪造的证书设置在windows预制的信任根证书列表中来实现中间人抓包。但是有的应用程序并不使用windows预制的根证书,而是使用自身携带的一个信任证书列表,这样自然也不会信任fiddler伪造的根证书。例如libcurl就默认不使用windows预制的根证书,需要调用curl_easy_setopt(curl, CURLOPT_CAINFO, "E:\\ca.crt");设置信任的根证书列表。例如使用libcurl进行https连接,设置代理为fiddler并通过fiddler进行抓包。SSL握手会返回证书验证错误。

img

这个原因就是因为fiddler抓包时会返回自己伪造的服务端证书给libcurl客户端,而libcurl客户端只信任自己设置的根证书列表(E:\ca.crt),并不信任windows系统中的根证书列表(包含fiddler伪造的根证书),所以libcurl客户端获取到fiddler伪造的服务端证书后会验证失败。

img

这个时候如果需要对此应用程序进行http/https抓包的话就需要分析其使用的具体框架,通过注入和hook禁用掉SSL验证,或者通过将fiddler伪造的根证书加入到其内存信任的根证书列表中。例如libcurl是通过设置CURLOPT_SSL_VERIFYPEER和 CURLOPT_SSL_VERIFYHOST为TRUE开启SSL验证的,要想禁用SSL代理就需要设置verfpeer和verifyhost为FALSE。

img

例如这里先通过将fiddler伪造的CA根证书从计算机中导出

img

然后再libcurl源代码中设置代理为fiddler,并将导出的fiddler的根证书添加到程序信任列表中。

img

这样fiddler返回自己伪造的证书libcurl客户端就可以验证通过了(因为libcurl客户端信任的根证书列表中已经包含了fiddler的根证书),fiddler可以成功抓包。

img

双向认证的抓包

如果https服务器要求进行双向认证,需要客户端提供客户端证书。需要在fiddler默认不发送客户端证书,首先获取到客户端证书后需要通过fiddler脚本设置。通过charles也可以设置客户端证书,成功抓取取到https包

img

fiddler设置代理

http/https代理和sock4/sock5代理的区别是前者在应用层,后者是在传输层。在通过fiddler设置了http代理后可以继续设置fiddler的下一级代理为sock代理服务器(vpn),这样就可以抓取一些需要使用vpn的应用。

img

posted @ 2022-11-14 21:06  怎么可以吃突突  阅读(297)  评论(0编辑  收藏  举报