Facebook和YouTube在我国被禁止。但是,如果我将openssl与Facebook连接,它将正常工作。
openssl s_client -connect facebook.com:443
如果将-servername参数添加到openssl,将无法正常工作!
openssl s_client -connect facebook.com:443 -servername facebook.com
我想从每个“客户端问候”请求中删除SNI扩展。 为此,我编写了以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <linux/tcp.h>
//#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h> /* for NF_ACCEPT */
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <getopt.h>
#include <fcntl.h>
#include <pwd.h>
uint8_t read_uint8(unsigned char **data) {
uint8_t value = (*data)[0];
*data += 1;
return value;
}
uint16_t read_uint16(unsigned char **data) {
uint16_t value = ((*data)[0] << 8) | (*data)[1];
*data += 2;
return value;
}
bool proto_check_ipv4(const unsigned char *data, int len)
{
return len >= 20 && (data[0] & 0xF0) == 0x40 && len >= ((data[0] & 0x0F) << 2);
}
void proto_skip_ipv4(unsigned char **data, int *len)
{
int l;
l = (**data & 0x0F) << 2;
*data += l;
*len -= l;
}
bool proto_check_tcp(const unsigned char *data, int len)
{
return len >= 20 && len >= ((data[12] & 0xF0) >> 2);
}
void proto_skip_tcp(unsigned char **data,int *len)
{
int l;
l = ((*data)[12] & 0xF0) >> 2;
*data += l;
*len -= l;
}
bool proto_check_tls_handshake(const unsigned char *data, int len)
{
return len >= 5 && data[0] == 0x16 && len >= (((data[3] << 8) | data[4]) + 5);
}
void proto_skip_tls_handshake(unsigned char **data, int *len)
{
*len = (((*data)[3] << 8) | (*data)[4]);
*data += 5; //handshake header length
}
bool proto_check_tls_client_hello(const unsigned char *data, int len)
{
return len >= 4 && data[0] == 0x01 && len >= ((data[1] << 16) || (data[2] << 8) || data[3]);
}
void proto_skip_tls_client_hello(unsigned char **data, int *len) {
*len = (((*data)[1] << 16) | ((*data)[2] << 8) | (*data)[3]);
*data += 4; //client hello header
}
unsigned short ip_checksum(unsigned short* ip, int len){
long sum = 0; /* assume 32 bit long, 16 bit short */
while(len > 1){
sum += *ip;
ip++;
if(sum & 0x80000000) /* if high order bit set, fold */
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if(len) /* take care of left over byte */
sum += (unsigned short) *(unsigned char *)ip;
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return ~sum;
}
uint16_t tcp_checksum(const void *buff, int len, in_addr_t src_addr, in_addr_t dest_addr)
{
const uint16_t *buf = (const uint16_t *)buff;
uint16_t *ip_src=(uint16_t *)&src_addr, *ip_dst=(uint16_t *)&dest_addr;
uint32_t sum;
int length=len;
// Calculate the sum
sum = 0;
while (len > 1)
{
sum += *buf++;
if (sum & 0x80000000)
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if ( len & 1 )
{
// Add the padding if the packet lenght is odd
uint16_t v=0;
*(uint8_t *)&v = *((uint8_t *)buf);
sum += v;
}
// Add the pseudo-header
sum += *(ip_src++);
sum += *ip_src;
sum += *(ip_dst++);
sum += *ip_dst;
sum += htons(IPPROTO_TCP);
sum += htons(length);
// Add the carries
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
// Return the one's complement of sum
return (uint16_t)(~sum);
}
void tcp_fix_checksum(struct tcphdr *tcp, int len, in_addr_t src_addr, in_addr_t dest_addr)
{
tcp->check = 0;
tcp->check = tcp_checksum(tcp,len,src_addr,dest_addr);
}
bool processPacketData(unsigned char *data, int len)
{
struct iphdr *iphdr = NULL;
struct tcphdr *tcphdr = NULL;
uint8_t proto;
int len_tcp;
unsigned char *phost;
if (proto_check_ipv4(data,len))
{
iphdr = (struct iphdr *) data;
proto = iphdr->protocol;
proto_skip_ipv4(&data, &len);
}
if (proto !=IPPROTO_TCP || !proto_check_tcp(data,len))
return false;
unsigned char* ip_length_start = (unsigned char *)&iphdr->tot_len;
int ip_total_len = read_uint16(&ip_length_start);
ip_length_start -= 2;
tcphdr = (struct tcphdr *) data;
len_tcp = len;
proto_skip_tcp(&data, &len);
if (!proto_check_tls_handshake(data, len))
return false;
unsigned char* tls_length_start = data + 3;
proto_skip_tls_handshake(&data, &len);
int tls_len = len;
printf("TLS len: %d\n", tls_len);
if (!proto_check_tls_client_hello(data, len))
return false;
unsigned char* client_hello_length_start = data + 1;
proto_skip_tls_client_hello(&data, &len);
int client_hello_len = len;
printf("Client hello length: %d\n", client_hello_len);
data += 2; //version
data += 32; //random
int session_id_len = read_uint8(&data); //session id length
data += session_id_len; //skip session id
printf("Session length: %d\n", session_id_len);
uint16_t cipher_suites_len = read_uint16(&data);
data += cipher_suites_len; //skip cipher
printf("Cipher length: %d\n", cipher_suites_len);
uint8_t compression_methods_len = read_uint8(&data);
data += compression_methods_len; //ckip compression_methods
printf("Compression length: %d\n", compression_methods_len);
unsigned char* extensions_length_start = data;
uint16_t extensions_len = read_uint16(&data);
uint16_t extensions_len_ = extensions_len;
printf("EXT: %d\n", extensions_len);
uint32_t extl = 0;
uint8_t ext[2048] = {0};
while(extensions_len > 0) {
uint16_t ext_type = read_uint16(&data);
uint16_t ext_len = read_uint16(&data);
printf("%d %d %d\n", ext_type, ext_len, extensions_len);
extensions_len -= 2; //ext type
extensions_len -= 2; //ext len
extensions_len -= ext_len;
if (ext_type != 0) //server_name bo`lmasa
{
memcpy(ext + extl, data - 4, ext_len + 4);
extl += 4 + ext_len;
}
data += ext_len;
}
extensions_length_start[0] = (uint8_t )(extl >> 8);
extensions_length_start[1] = (uint8_t )(extl & 0xFF);
int diff = extensions_len_ - extl;
printf("Diff: %d\n", diff);
tls_len -= diff;
client_hello_len -= diff;
printf("New TLS len: %d\n", tls_len);
printf("New Client Hello len: %d\n", client_hello_len);
tls_length_start[0] = (uint8_t )(tls_len >> 8);
tls_length_start[1] = (uint8_t )(tls_len & 0xFF);
client_hello_length_start[0] = (uint8_t )(client_hello_len >> 16);
client_hello_length_start[1] = (uint8_t )(client_hello_len >> 8);
client_hello_length_start[2] = (uint8_t )(client_hello_len & 0xFF);
memcpy(extensions_length_start + 2, ext, extl);
printf("IPHDR: %d %d\n", ip_total_len, ip_total_len-diff);
ip_total_len -= diff;
ip_length_start[0] = (uint8_t )(ip_total_len >> 8);
ip_length_start[1] = (uint8_t )(ip_total_len & 0xFF);
tcp_fix_checksum(tcphdr,len_tcp,iphdr->saddr,iphdr->daddr);
iphdr->check = 0;
iphdr->check = ip_checksum((unsigned short*)iphdr, 20);
printf("Checksum: %x %x\n", tcphdr->check, iphdr->check);
return true;
}
static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *cookie)
{
int id,len;
struct nfqnl_msg_packet_hdr *ph;
unsigned char *data;
ph = nfq_get_msg_packet_hdr(nfa);
id = ph ? ntohl(ph->packet_id) : 0;
len = nfq_get_payload(nfa, &data);
if (len >= 0)
{
if (processPacketData(data, len)) {
// for(int i = 0; i < len; i++) {
// printf("%c ", data[i]);
// }
// printf("\n");
return nfq_set_verdict(qh, id, NF_ACCEPT, len, data);
}
}
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
}
bool droproot(uid_t uid, gid_t gid)
{
if (uid)
{
if (setgid(gid))
{
perror("setgid: ");
return false;
}
if (setuid(uid))
{
perror("setuid: ");
return false;
}
}
return true;
}
int main(int argc, char **argv)
{
struct nfq_handle *h;
struct nfq_q_handle *qh;
int fd;
int rv;
int qnum = 1;
char buf[4096] __attribute__ ((aligned));
uid_t uid=0;
gid_t gid;
printf("opening library handle\n");
h = nfq_open();
if (!h) {
fprintf(stderr, "error during nfq_open()\n");
exit(1);
}
printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
if (nfq_unbind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_unbind_pf()\n");
exit(1);
}
printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
if (nfq_bind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_bind_pf()\n");
exit(1);
}
printf("binding this socket to queue '%u'\n", qnum);
qh = nfq_create_queue(h, qnum, &cb, NULL);
if (!qh) {
fprintf(stderr, "error during nfq_create_queue()\n");
exit(1);
}
printf("setting copy_packet mode\n");
if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
fprintf(stderr, "can't set packet_copy mode\n");
exit(1);
}
fd = nfq_fd(h);
if (droproot(uid,gid))
{
printf("Success\n");
while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0)
{
for(int i = 0; i < 12; i++) {
printf("%x-", buf[i]);
if (i == 6) printf("\n");
}
printf("\n");
int r=nfq_handle_packet(h, buf, rv);
if (r) fprintf(stderr,"nfq_handle_packet error %d\n",r);
}
}
printf("unbinding from queue 0\n");
nfq_destroy_queue(qh);
#ifdef INSANE
/* normally, applications SHOULD NOT issue this command, since
* it detaches other programs/sockets from AF_INET, too ! */
printf("unbinding from AF_INET\n");
nfq_unbind_pf(h, AF_INET);
#endif
printf("closing library handle\n");
nfq_close(h);
}
iptables规则:
iptables -t mangle -I POSTROUTING -p tcp --dport 443 -m set --match-set zapret dst -j NFQUEUE --queue-num 1 --queue-bypass
ipset:
ipset -N zapret
ipset -A zapret usa.gov
我使用tshark进行分析:
tshark -i eth1 -f "dst 54.85.132.205"
一切正常,除了以下一项:
Capturing on 'eth1'
1 0.000000000 * → 54.85.132.205 TCP 74 48852 → 443 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=44927313 TSecr=0 WS=128
2 0.188082035 * → 54.85.132.205 TCP 66 48852 → 443 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=44927360 TSecr=152450118
3 0.188441788 * → 54.85.132.205 TLSv1 258 Client Hello [ETHERNET FRAME CHECK SEQUENCE INCORRECT]
为什么tshark给出[以太网帧检查序列不正确]错误!
我想更改以太网FCS,但是不能。
如何设置以太网FCS?
或者,在哪里可以找到从请求中删除SNI的工具?
更新:
我更改了server_name的值,但是,它不起作用。
...
proto_skip_tls_client_hello(&data, &len);
if ((phost = find_bin(data, len, "usa.gov", 7)))
{
phost[0] = 'b';
printf("Found: %.7s\n", phost);
}
...
有效:
openssl s_client -connect youtube.com:443 -servername buotube.com