文章
技术

如何讓chrome支持ESNI

霏艺Faye 图书管理员
霏艺Faye  ·  2020年3月27日 图书管理员

同學們先自己去看下火狐的源碼,理解下火狐的ESNI代碼實現!

我昨天剛下好,300多MB的壓縮包!你們慢慢解壓縮吧。。。

代碼太深了,估計很多人不知道ESNI的代碼實現在哪裏。我解釋下,代碼在

security/nss/lib/ssl/tls13esni.c

函數是SSLExp_EnableESNI和tls13_ClientSetupESNI,整個代碼實現其實不難,當然,前提是你和我一樣無聊看完了openssl的代碼

重點講解,我放到https://2049bbs.xyz/t/3750 這裏了

菜单
  1. 霏艺Faye 图书管理员
    霏艺Faye   图书管理员

    言歸正傳!

    我們講解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

  2. 霏艺Faye 图书管理员
    霏艺Faye   图书管理员

    繼續查看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的編譯環境都覺得浪費時間

  3. 张怀义  

    大神你好,我一上来,就看你更新了好多文章啊!大神学习这些有什么方式方法么,我想当一个程序员!