引言
【上一篇文章】 介绍了服务端混淆插件获取请求之后完成真实数据的提取工作,接下来代理服务器会根据客户端的请求数据扮演客户端的角色完成真实的请求,拿到响应之后,将该响应数据混淆之后发送回客户端。
Server Hello
服务端处理完客户端发送过来了 Hello Client
之后,服务端以 Server Hello
包进行响应。
混淆状态的检测
1
if (obfs == NULL || obfs->obfs_stage < 0) return 0;
分配临时缓存用于封装之前的响应数据存放
1
static buffer_t tmp = {0, 0, 0, NULL};
Server Hello 包作为响应时,此时混淆阶段为 0,所以我们就直接进入混淆的处理逻辑,与客户端请求的混淆类似。
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 存储真实数据的长度
size_t buf_len = buf->len;
// 计算 server_hello 模板的大小
size_t hello_len = sizeof(struct tls_server_hello);
// 计算双方通讯的加密算法(只作为混淆用)模板大小
size_t change_cipher_spec_len = sizeof(struct tls_change_cipher_spec);
// 计算握手模板大小
size_t encrypted_handshake_len = sizeof(struct tls_encrypted_handshake);
// 总的 Server Hello 包大小
size_t tls_len = hello_len + change_cipher_spec_len + encrypted_handshake_len + buf_len;将真实数据复制到临时缓存中
1
2
3
4brealloc(&tmp, buf_len, cap);
brealloc(buf, tls_len, cap);
// 复制真实数据到 tmp.data
memcpy(tmp.data, buf->data, buf_len);计算出了整个
Server Hello
包的大小,那接下来就要填充数值了,首先开始填充sever hello 模板数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/* Server Hello */
memcpy(buf->data, &tls_server_hello_template, hello_len);
struct tls_server_hello *hello = (struct tls_server_hello *) buf->data;
// 生成当前时间
hello->random_unix_time = CT_HTONL((uint32_t) time(NULL));
// 生成 28 字节的随机数
rand_bytes(hello->random_bytes, 28);
/*
* 在上一步解混淆的时候,应创建好 `obfs->buf`,并且将客户端的 `session id` 保存到了 `obfs->buf->data` 中,现在只需要取出来存到 `hello` 模板中(同一个连接,共用同一个 obfs 上下文对象)
*/
if (obfs->buf != NULL)
{
memcpy(hello->session_id, obfs->buf->data, 32);
} else
{
// 如果不存在,只能随机一个了
rand_bytes(hello->session_id, 32);
}我们看下 server hello 模板:
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
40static const struct tls_server_hello
tls_server_hello_template = {
// 0x16 代表此次内容为握手协议
.content_type = 0x16,
// 0x0301 代表 TLS 版本号 1.0
.version = CT_HTONS(0x0301),
// 内容长度固定 91 字节,也就是 len 字段之后到 ec_point_formats 字段的总长度
.len = CT_HTONS(91),
// 握手类型 2 代表 server hello 握手
.handshake_type = 2,
// 以下两个字段固定为 0x0057,也就是 87 字节代表 handshake_version 到 ec_point_formats 的总长度
.handshake_len_1 = 0,
.handshake_len_2 = CT_HTONS(87),
// 握手版本,0x0303 代表 TLS 1.2
.handshake_version = CT_HTONS(0x0303),
.random_unix_time = 0,
.random_bytes = {0},
.session_id_len = 32,
.session_id = {0},
// 0xCCA8 代表加密算法用的是 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
.cipher_suite = CT_HTONS(0xCCA8),
// 不启用压缩算法
.comp_method = 0,
// 扩展长度为 0,也就意味着以下内容将不进行展示
.ext_len = 0,
.ext_renego_info_type = CT_HTONS(0xFF01),
.ext_renego_info_ext_len = CT_HTONS(1),
.ext_renego_info_len = 0,
.extended_master_secret_type = CT_HTONS(0x0017),
.extended_master_secret_ext_len = 0,
.ec_point_formats_ext_type = CT_HTONS(0x000B),
.ec_point_formats_ext_len = CT_HTONS(2),
.ec_point_formats_len = 1,
.ec_point_formats = {0},
};密钥交换模板
1
2/* Change Cipher Spec */
memcpy(buf->data + hello_len, &tls_change_cipher_spec_template, change_cipher_spec_len);模板数据:
1
2
3
4
5
6
7
8
9
10
11static const struct tls_change_cipher_spec
tls_change_cipher_spec_template = {
// 类型 0x14 代表密钥交换
.content_type = 0x14,
// 代表 TLS 1.2
.version = CT_HTONS(0x0303),
// 内容长度
.len = CT_HTONS(1),
// 消息内容
.msg = 0x01,
};加密的握手信息
1
2
3/* Encrypted Handshake */
memcpy(buf->data + hello_len + change_cipher_spec_len, &tls_encrypted_handshake_template,
encrypted_handshake_len);模板数据:
1
2
3
4
5
6
7
8
9
10static const struct tls_encrypted_handshake
tls_encrypted_handshake_template = {
// 0x16 代表内容为加密的握手信息
.content_type = 0x16,
// 版本为 TLS 1.2
.version = CT_HTONS(0x0303),
// 实际的数据长度
.len = 0,
// char msg[len];
};真实数据填充
1
2
3// 将数据复制到报文尾部,也就是存到 tls_encrypted_handshake 的 msg 字段中
memcpy(buf->data + hello_len + change_cipher_spec_len + encrypted_handshake_len,
tmp.data, buf_len);收尾
1
2
3
4
5
6
7
8
9// 获取加密的握手信息指针
struct tls_encrypted_handshake *encrypted_handshake =
(struct tls_encrypted_handshake *) (buf->data + hello_len + change_cipher_spec_len);
// 设置数据大小
encrypted_handshake->len = CT_HTONS(buf_len);
// buf->len 保存整个报文大小
buf->len = tls_len;
// 递增混淆阶段
obfs->obfs_stage++;
实际的 TLS 混淆响应数据包
总结
请求数据的混淆是将数据放到了 session ticket
中,而响应的混淆是将数据存放到了 encrypted handshake
中。