如何讓chrome支持ESNI
言歸正傳!
我們講解Chrome了,chrome的TLS實現叫做BoringSSL,fork自著名的openSSL。
開始講如何boringssl的代碼。
翻開課本,boringssl\ssl\handshake_client.cc這個代碼。開始閲讀ssl_client_handshake
對!ESNI是在客戶端發起握手的時候發送的!所以從握手開始讀起。
找到do_start_connect
這個函數!很多人覺得看著這個代碼,很嚇人。別怕,其實關鍵信息就i是ssl_write_client_hello
這麽一行罷了。其他的都不重要~
ssl_write_client_hello
這個函數,什麽都不做,純粹拼報文而已!二進制級別拼寫,類似給一個struct賦值。
我們就看ssl_add_clienthello_tlsext
這個函數!因爲ESNI或者SNI都是放在ext字段。
翻開課本 boringssl\ssl\t1_lib.cc 找到 ssl_add_clienthello_tlsext
for (size_t i = 0; i < kNumExtensions; i++) {
const size_t len_before = CBB_len(&extensions);
if (!kExtensions[i].add_clienthello(hs, &extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
return false;
}
意思是遍歷所有的ext,調用對應ext的add_clienthello函數,拼clienthello報文
// kExtensions contains all the supported extensions.
static const struct tls_extension kExtensions[] = {
{
TLSEXT_TYPE_server_name,
NULL,
ext_sni_add_clienthello,
ext_sni_parse_serverhello,
ext_sni_parse_clienthello,
ext_sni_add_serverhello,
},
{
TLSEXT_TYPE_extended_master_secret,
NULL,
ext_ems_add_clienthello,
ext_ems_parse_serverhello,
ext_ems_parse_clienthello,
ext_ems_add_serverhello,
},
{
TLSEXT_TYPE_renegotiate,
NULL,
ext_ri_add_clienthello,
ext_ri_parse_serverhello,
ext_ri_parse_clienthello,
ext_ri_add_serverhello,
},
{
TLSEXT_TYPE_supported_groups,
NULL,
ext_supported_groups_add_clienthello,
ext_supported_groups_parse_serverhello,
ext_supported_groups_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_ec_point_formats,
NULL,
ext_ec_point_add_clienthello,
ext_ec_point_parse_serverhello,
ext_ec_point_parse_clienthello,
ext_ec_point_add_serverhello,
},
{
TLSEXT_TYPE_session_ticket,
NULL,
ext_ticket_add_clienthello,
ext_ticket_parse_serverhello,
// Ticket extension client parsing is handled in ssl_session.c
ignore_parse_clienthello,
ext_ticket_add_serverhello,
},
{
TLSEXT_TYPE_application_layer_protocol_negotiation,
NULL,
ext_alpn_add_clienthello,
ext_alpn_parse_serverhello,
// ALPN is negotiated late in |ssl_negotiate_alpn|.
ignore_parse_clienthello,
ext_alpn_add_serverhello,
},
{
TLSEXT_TYPE_status_request,
NULL,
ext_ocsp_add_clienthello,
ext_ocsp_parse_serverhello,
ext_ocsp_parse_clienthello,
ext_ocsp_add_serverhello,
},
{
TLSEXT_TYPE_signature_algorithms,
NULL,
ext_sigalgs_add_clienthello,
forbid_parse_serverhello,
ext_sigalgs_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_next_proto_neg,
NULL,
ext_npn_add_clienthello,
ext_npn_parse_serverhello,
ext_npn_parse_clienthello,
ext_npn_add_serverhello,
},
{
TLSEXT_TYPE_certificate_timestamp,
NULL,
ext_sct_add_clienthello,
ext_sct_parse_serverhello,
ext_sct_parse_clienthello,
ext_sct_add_serverhello,
},
{
TLSEXT_TYPE_channel_id,
ext_channel_id_init,
ext_channel_id_add_clienthello,
ext_channel_id_parse_serverhello,
ext_channel_id_parse_clienthello,
ext_channel_id_add_serverhello,
},
{
TLSEXT_TYPE_srtp,
ext_srtp_init,
ext_srtp_add_clienthello,
ext_srtp_parse_serverhello,
ext_srtp_parse_clienthello,
ext_srtp_add_serverhello,
},
{
TLSEXT_TYPE_key_share,
NULL,
ext_key_share_add_clienthello,
forbid_parse_serverhello,
ignore_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_psk_key_exchange_modes,
NULL,
ext_psk_key_exchange_modes_add_clienthello,
forbid_parse_serverhello,
ext_psk_key_exchange_modes_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_early_data,
NULL,
ext_early_data_add_clienthello,
ext_early_data_parse_serverhello,
ext_early_data_parse_clienthello,
ext_early_data_add_serverhello,
},
{
TLSEXT_TYPE_supported_versions,
NULL,
ext_supported_versions_add_clienthello,
forbid_parse_serverhello,
ignore_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_cookie,
NULL,
ext_cookie_add_clienthello,
forbid_parse_serverhello,
ignore_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_quic_transport_parameters,
NULL,
ext_quic_transport_params_add_clienthello,
ext_quic_transport_params_parse_serverhello,
ext_quic_transport_params_parse_clienthello,
ext_quic_transport_params_add_serverhello,
},
{
TLSEXT_TYPE_token_binding,
NULL,
ext_token_binding_add_clienthello,
ext_token_binding_parse_serverhello,
ext_token_binding_parse_clienthello,
ext_token_binding_add_serverhello,
},
{
TLSEXT_TYPE_cert_compression,
NULL,
cert_compression_add_clienthello,
cert_compression_parse_serverhello,
cert_compression_parse_clienthello,
cert_compression_add_serverhello,
},
{
TLSEXT_TYPE_delegated_credential,
NULL,
ext_delegated_credential_add_clienthello,
forbid_parse_serverhello,
ext_delegated_credential_parse_clienthello,
dont_add_serverhello,
},
};
因爲我們要做的是SNI,所以看到了第一個ext,找到了它對應的add_clienthello是ext_sni_add_clienthello
繼續查看ext_sni_add_clienthello函數
發現CBB_add_bytes(&name, (const uint8_t *)ssl->hostname.get(),這行代碼,會對SNI賦值hostname
也就是說,爲什麽我們通過wireshark抓包,可以明文看到自己訪問什麽網站,就是這行代碼造成的!
GFW通過全網檢查ClientHello的SNI字段,來判斷你在訪問什麽網站!而且因爲是明文,所以GFW的工作很簡單,只要檢查你的TLS握手SNI字段,如果是敏感詞,就RST掉你的tcp連接。你就上不去了。品蔥就是這樣,所以上不去,需要開啓ESNI功能,就沒事了~
代碼修改:
1.增加一個ext,取名叫ESNI,參考SNI的實現,完成相關代碼
2.移植火狐的esni加密代碼到boringssl,完成第一步的esni報文拼接
3.編譯自己的boringssl庫,替換掉chrome自帶的boringssl
4.chrome有自己dll校驗機制,你手動替換有點問題,好像跑不起來,得網上找下教程,學習如何替換chrome的boringssl
別找我要代碼或者已經編譯好的dll !我要是寫好了,早提PR給chrome了~
關於爲什麽不自己寫代碼完成剛才我説的這麽多事情:
我翻墻的條件很糟糕,下載chrome的源碼,經常斷掉!失敗就得重新下載。至今沒有下載到chrome源碼。
另外編譯環境配置非常複雜,我沒有那個功夫去完成這麽費時間的工作。老了啊,想當年自己花了3個星期,搭了Gentoo Linux,現在搭個chrome的編譯環境都覺得浪費時間
大神你好,我一上来,就看你更新了好多文章啊!大神学习这些有什么方式方法么,我想当一个程序员!