0%

使用 C++ 实现对 http 网站加密内容的监听,修改以及重定向

* 本文内容代码执行需要管理员权限

主要难点

  1. 将用户访问请求重定向到劫持服务器,
  2. 如何获取 https 传输内容并解密成明文.
  3. 修改 https 内容后重新打包并保证浏览器可以正常打开.

难点一

重定向用户请求相对来说比较简单, 这里只列举几个 windows 的方法:

  1. 通过配置系统 VPN 来过滤 443 端口请求
  2. hook mswsock.dll->connect 来重定向 443 端口请求
  3. 通过编写 Windows LSP 协议来重定向 443 端口请求

难点二

https 协议是公开协议, 其安全主要是靠证书来保证的, 如果你可以抓到 https 服务器和客户端的握手包即可拿到整次访问的加密秘钥.

你可以手动解析 https 协议, 或者寻找任意一个 https 服务器即可. 这里使用 golang 来作为劫持服务器.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

func HTTPSGetCertificate(clientHello *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
if cert, err = QueryTlsCertificate(clientHello.ServerName); nil == err {
return cert, err
}

return CreateTlsCertificate(nil, clientHello.ServerName, -(365 * 24 * time.Hour), 200)
}

func StartHTTPSProxy(addr string, router socks.Dialer, tran *HTTPTransport) {
serverHTTPS := &http.Server{
ErrorLog: log.Warn,
TLSConfig: &tls.Config{
GetCertificate: HTTPSGetCertificate,
},

Addr: addr,
Handler: &HTTPHandler{
scheme: "HTTPS",
proxy: socks.NewHTTPProxy("https", router, tran),
},
}

if err := serverHTTPS.ListenAndServeTLS("", ""); nil != err {
log.Error("Start HTTP proxy at ", addr, " failed, err:", err)
}
}

难点三

怎样修改 https 内容后重新打包并保证浏览器可以正常打开, 这个可以明确的告诉你没辙. 除非你能拿到对方服务器的私钥.

我们既然拿不到对方的私钥, 那可不可以伪造一个?

这个是可以的, https 的证书是可以随意生成的, 我们可以通过生成自己的证书来重新打包 https 内容, 只是没办法通过浏览器验证而已,.

浏览器是通过什么验证证书是不是伪造证书?

其实系统中有个证书信任库, 里面保存了所有可以信任的证书列表, 浏览器是通过查询证书是否存在于这个库中来确定证书可不可以信任的.

了解了上面的内容后, 我们可以得到一个结论, 不管当前证书是否是真正服务器证书只要当前证书存在于系统的信任证书库中, 就可以通过浏览器的验证. 想通了这点我们可不可以手动将证书加到信任库里? 答案是可以的,下面给出 C++ 通过 Windows API 给系统添加证书的代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// pszSRCCert = 常规 X509 编码证书内容, 可以通过记事本打开证书文件获得
// pwszStoreName = 值可以为 "My" | "CA" | "Root", 分别对应 个人 | 颁发机构 | 根证书

bool WINAPI AddCertificateCryptContextToStore(const wchar_t * pwszStoreName, const wchar_t * pszSRCCert) {
unsigned long dwCertData = 0;
ULONG dwRootCert = (ULONG)wcslen(pszSRCCert);

if (!CryptStringToBinaryW(pszSRCCert, dwRootCert, CRYPT_STRING_BASE64HEADER, NULL, &dwCertData, NULL, NULL)){
return false;
}

unsigned char* pbCertData = (unsigned char*)calloc(1, dwCertData);
if (NULL == pbCertData){
return false;
}
defer_free _free1(pbCertData);

if (!CryptStringToBinaryW(pszSRCCert, dwRootCert, CRYPT_STRING_BASE64HEADER, pbCertData, &dwCertData, NULL, NULL)) {
return false;
}

return AddCertificateContextToStore(pwszStoreName, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, pbCertData, dwCertData);
}

bool WINAPI AddCertificateContextToStore(const wchar_t * pwszStoreName, DWORD dwCertEncodingType, BYTE * pbCertData, DWORD dwCertSize) {
bool bIsSucc = false;
HCERTSTORE hStore = NULL;
PCCERT_CONTEXT m_pctx = NULL;

do
{
m_pctx = CertCreateCertificateContext(dwCertEncodingType, pbCertData, dwCertSize);
if (m_pctx == NULL) {
break;
}

hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_LOCAL_MACHINE, pwszStoreName);//my 个人 ca 中间证书颁发机构 root 受信任的根证书颁发机构,显然中国铁路的根证书要安装在root里面
if (NULL == hStore) {
break;
}

if (FALSE == CertAddCertificateContextToStore(hStore, m_pctx, CERT_STORE_ADD_NEW, 0) && CRYPT_E_EXISTS != GetLastError()) { // 如果安装失败,并且错误码不是以安装过
break;
}

bIsSucc = true;
} while (false);

if (m_pctx)
CertFreeCertificateContext(m_pctx);
if (hStore)
CertCloseStore(hStore, 0);

return bIsSucc;
}