From 672e2be1c9cb3fa0d2d67b9a34ef2ac9614463c6 Mon Sep 17 00:00:00 2001 From: chn <897331845@qq.com> Date: Fri, 1 Nov 2019 20:24:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A1=86=E6=9E=B6=E5=86=99=E5=A5=BD=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++- src/Kconfig | 4 +- src/Makefile | 2 +- src/common.h | 64 +++++++++++- src/rkpManager.h | 15 +++ src/rkpStream.h | 249 +++++++++++++++++++++++++++++++++++++++------ src/scanner.h | 182 --------------------------------- src/stm_info.h | 187 ---------------------------------- src/stm_vec.h | 49 --------- src/xmurp-ua.c | 257 ++++++----------------------------------------- 10 files changed, 341 insertions(+), 684 deletions(-) create mode 100644 src/rkpManager.h delete mode 100644 src/scanner.h delete mode 100644 src/stm_info.h delete mode 100644 src/stm_vec.h diff --git a/README.md b/README.md index cdf747b..f0a185c 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,8 @@ sniffing 状态下,收集包直到得到全部的 HTTP 头,修改后发出 * `0x01` 位:表示这个包是否属于这个流。置为 1 时表示属于。 * `0x02` 位:表示这个包是否需要被截留。置为 1 时表示需要。 - * `0x04` 位:发生了某些错误,上层函数应该向用户发出警告,将所有流中的数据放出,并停止模块的运行。 + 0x04 是否被丢弃 + * `0x08` 位:发生了某些错误,上层函数应该向用户发出警告,将所有流中的数据放出,并停止模块的运行。 事实上,可能的返回结果和处理方法: @@ -78,13 +79,22 @@ sniffing 状态下,收集包直到得到全部的 HTTP 头,修改后发出 * `0x1`:包是客户端的请求。除非设置这个位,其它位都没有意义。 * `0x2`:包是携带了应用层数据。除非设置这个位,否则之后的都没有意义。 - * `0x4`:包是属于未来的。 - * `0x8`:包是属于过去的。这两个都没设置的话,说明包是现在的。 + * `0x4`:包是属于过去的。 + * `0x8`:包是属于未来的。这两个都没设置的话,说明包是现在的。只有这两个都没设置,之后的内容才有意义。 * `0x10`:包中出现了 http 头部结尾。只有在流为 sniffing 时可能设置这个。 * `0x20`:包有设置 push。 + `0x40`:包需要使用现有的包来替代。 * `void rkpStream_execute(struct rkpStream*, struct sk_buff*, u_int32_t)`:执行需要执行的动作。第三个参数传入 `rkpStream_judge` 对于这个包的返回值,它的低八位会被忽略。它要做的事情完全由 `rkpStream_judge` 的返回值来通知。以下数值是指 `rtn & 0xff00 >> 8` 的值。 + 收到后马上更新时间 + 如果不是客户端发出,更新sck后返回。 + 如果没有应用层数据,返回 + 如果属于过去,如果需要替代,替代后返回;如果不需要替代,返回。 + 如果是属于未来,截留。 + 如果是waiting,那么更新序列号,如果psh则设置状态,返回。 + 如果是sniffing,那么 + * `0x0`:返回。 * `0x1`:更新时间,然后返回。 * `0x2`:更新时间、确认序列号,释放 `buff_prev` 中的部分数据包。 diff --git a/src/Kconfig b/src/Kconfig index 6397d30..e0030f5 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -1,2 +1,2 @@ -config xmurp-ua - tristate "xmurp-ua" \ No newline at end of file +config rkp-ua + tristate "rkp-ua" \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index 6b570fb..eac476b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1 +1 @@ -obj-${CONFIG_xmurp-ua} += xmurp-ua.o \ No newline at end of file +obj-${CONFIG_rkp-ua} += rkp-ua.o \ No newline at end of file diff --git a/src/common.h b/src/common.h index c0a4578..08fd026 100644 --- a/src/common.h +++ b/src/common.h @@ -12,10 +12,16 @@ #include #include #include +#include -const char* str_ua = "User-Agent: "; -const char* str_end = "\r\n\r\n"; -const char* str_win = "Windows NT"; +const unsigned char* str_ua = "User-Agent: "; +const unsigned char* str_end = "\r\n\r\n"; +const unsigned char* str_win = "Windows NT"; + +u_int8_t mode_advanced = 0; +module_param(mode_advanced, bool, 0); +u_int8_t mode_winPreserve = 0; +module_param(mode_winPreserve, bool, 0); u_int32_t mark_capture = 0x100; module_param(mark_capture, uint, 0); @@ -26,3 +32,55 @@ module_param(mark_first, uint, 0); u_int32_t mark_winPreserve = 0x800; module_param(mark_winPreserve, uint, 0); +u_int8_t rkpSettings_capture(struct sk_buff*); +u_int8_t rkpSettings_request(struct sk_buff*); +u_int8_t rkpSettings_first(struct sk_buff*); +u_int8_t rkpSettings_winPreserve(struct sk_buff*); + +u_int8_t rkpSettings_capture(struct sk_buff* skb) +{ + if(mode_advanced) + return skb -> mark & mark_capture == mark_capture; + else + { + if(ip_hdr(skb) -> protocol != IPPROTO_TCP) + return 0; + else if(tcp_hdr(skb) -> dport == 80) + return 1; + else if(tcp_hdr(skb) -> sport == 80 && tcp_hdr(skb) -> ack) + return 1; + else + return 0; + } +} + +u_int8_t rkpSettings_request(struct sk_buff* skb) +{ + if(mode_advanced) + return skb -> mark & mark_request == mark_request; + else + return ip_hdr(skb) -> daddr == 80; +} + +u_int8_t rkpSettings_first(struct sk_buff* skb) +{ + if(mode_advanced) + return skb -> mark & mark_first == mark_first; + else + return tcp_hdr(skb) -> syn && !tcp_hdr(skb) -> ack; +} + +u_int8_t rkpSettings_winPreserve(struct sk_buff* skb) +{ + if(mode_advanced) + return skb -> mark & mark_winPreserve == mark_winPreserve; + else + return mode_winPreserve; +} + +time_t now() +{ + struct timespec* ts; + getnstimeofday(ts); + return ts -> tv_sec; +} diff --git a/src/rkpManager.h b/src/rkpManager.h new file mode 100644 index 0000000..34e3eb3 --- /dev/null +++ b/src/rkpManager.h @@ -0,0 +1,15 @@ +#include "rkpStream.h" + +struct rkpManager +{ + struct rkpStream** data; // 按照首包的两端口之和的低 8 位放置 +}; + +struct rkpManager* rkpManager_new(); // 构造函数 +void rkpManager_del(struct rkpManager*); // 析构函数 + +u_int8_t rkpManager_execute(struct rkpManager*, struct sk_buff*); // 处理一个数据包。返回值:0,出错,1,accept,2,stolen,3,drop +void rkpManager_refresh(struct rkpManager*); // 清理过时的流 + +struct rkpStream* __rkpManager_find(struct rkpManager*, struct sk_buff*); // 寻找一个数据包属于哪个流 +void __rkpManager_stream_add(struct rkpManager*, struct sk_buff*); // 增加一个流 diff --git a/src/rkpStream.h b/src/rkpStream.h index ee260a1..29409a2 100644 --- a/src/rkpStream.h +++ b/src/rkpStream.h @@ -9,7 +9,8 @@ struct rkpStream } status; u_int32_t id[3]; // 按顺序存储客户地址、服务地址、客户端口、服务端口,已经转换字节序 struct sk_buff *buff, *buff_prev, *buff_next; - u_int32_t seq, seq_ack; // 下一个服务端发出的字节的序列号,以及下一个服务端确认收到的字节的序列号 + u_int32_t seq_ack; // 下一个服务端确认收到的字节的序列号。以后所有的序列号都是以这个为基准的相对序列号。 + u_int32_t seq; // 下一个期待收到的序列号。 time_t last_active; u_int8_t scan_matched; u_int8_t win_preserve; @@ -18,17 +19,35 @@ struct rkpStream struct rkpStream* rkpStream_new(struct sk_buff*); // 构造函数,得到的流的状态是捕获这个数据包之前的状态。内存不够时返回 0。 void rkpStream_del(struct rkpStream*); // 析构函数 -u_int32_t rkpStream_judge(struct rkpStream*, struct sk_buff*); // 判断一个数据包是否属于这个流以及如何处理它 -u_int8_t rkpStream_execute(struct rkpStream*, struct sk_buff*, u_int32_t); // 执行上面判断的结果(如果包属于这个流) -void __rkpStream_refresh(struct rkpStream*); // 刷新流的时间戳 -u_int8_t __rkpStream_belong(struct rkpStream*, struct sk_buff*); // 判断一个数据包是否属于一个流 +u_int8_t rkpStream_belong(struct rkpStream*, struct sk_buff*); // 判断一个数据包是否属于一个流 +u_int8_t rkpStream_execute(struct rkpStream*, struct sk_buff*); // 处理一个数据包(假定包属于这个流) + +void __rkpStream_refresh_ack(struct rkpStream*, u_int32_t); // 刷新确认序列号。第二个参数就是即将设定的确认号。会自动重新计算序列号的偏移,以及释放 buff_prev 中的多余数据包 + +unsigned char* __rkpStream_skb_appStart(struct sk_buff*); // 返回一个包的应用层数据起始位置 +u_int16_t __rkpStream_skb_appLen(struct sk_buff*); // 返回一个包的应用层数据长度 +int32_t __rkpStream_skb_seq(u_int32_t, u_int32_t); // 返回一个序列号的相对序列号。两个参数分别为流的确认号、包的序列号(已经转换字节序)。 + +void __rkpStream_skb_send(struct sk_buff*); // 发送一个数据包 +struct sk_buff* __rkpStream_skb_copy(struct sk_buff*); // 复制一个数据包 +void __rkpStream_skb_del(struct sk_buff*); // 删除一个数据包 + +u_int16_t __rkpStream_data_scan(unsigned char*, u_int16_t, unsigned char*, u_int8_t); // 在指定字符串中扫描子字符串。返回值最低位表示是否完整地找到,其余 15 位表示匹配的长度(如果没有完整地找到)或子串结束时相对于起始时的位置 + +void __rkpStream_buff_retain_end(struct sk_buff**, struct sk_buff*); // 将一个数据包置入数据包链的末尾 +void __rkpStream_buff_retain_auto(struct sk_buff**, struct sk_buff*); // 将一个数据包置入数据包链的合适位置 +void __rkpStream_buff_rejudge(struct rkpStream*, struct sk_buff**); // 重新判定数据包链中的每个数据包 +void __rkpStream_buff_del(struct sk_buff**); // 删除数据包链 +struct sk_buff* __rkpStream_buff_find(struct sk_buff*, u_int32_t); +// 在一个已经按照序列号排序的数据包链中寻找序列号相符的包。如果没有相符的包,就返回最后一个序列号比要求的小的包。如果没有这样的包,就返回 0。第二个参数是要查找的序列号(绝对值,已转换字节序) + +void __rkpStream_buff_execute_core(struct sk_buff**, u_int16_t); // 最核心的步骤。集齐头部后,搜索、替换。 struct rkpStream* rpStream_new(struct sk_buff* skb) { struct rkpStream* rkps = kmalloc(sizeof(struct rkpStream), GFP_KERNEL); struct iphdr* iph = ip_hdr(skb); - strcut tcphdr* tcph = tcp_hdr(skb); - u_int16_t app_data_len = ntohs(iph -> tot_len) - iph -> ihl * 4 - tcph -> doff * 4; + struct tcphdr* tcph = tcp_hdr(skb); if(rkps == 0) return rkps; @@ -36,39 +55,207 @@ struct rkpStream* rpStream_new(struct sk_buff* skb) rkps -> id[0] = ntohl(iph -> saddr); rkps -> id[1] = ntohl(iph -> daddr); rkps -> id[2] = (((u_int32_t)ntohs(tcph -> sport)) << 16 ) + ntohs(tcph -> dport); - buff = buff_prev = buff_next = 0; - seq = seq_ack = ntohl(tcph -> seq); - __rkpStream_refresh(rkps); + rkps -> buff = rkps -> buff_prev = rkps -> buff_next = 0; + rkps -> seq_ack = ntohl(tcph -> seq); + rkps -> seq = 1; + rkps -> last_active = now(); rkps -> scan_matched = 0; - rkps -> win_preserve = (skb -> mark & mark_winPreserve != 0); + rkps -> win_preserve = rkpSettings_winPreserve(skb); rkps -> next = 0; return rkps; } - void rkpStream_del(struct rkpStream* rkps) { - kfree_skb_list(rkps -> buff); - kfree_skb_list(rkps -> buff_prev); - kfree_skb_list(rkps -> buff_next); - kfree(kkps); + __rkpStream_buff_del(&(rkps -> buff)); + __rkpStream_buff_del(&(rkps -> buff_prev)); + __rkpStream_buff_del(&(rkps -> buff_next)); + kfree(rkps); } - -u_int32_t rkpStream_judge(struct rkpStream* rkps, struct sk_buff* skb) +u_int8_t rkpStream_belong(struct rkpStream* rkps, struct sk_buff* skb) { - u_int32_t rtn = 0; - if(!__rkpStream_belong(rkps, skb)) - return rtn; + if(rkpSettings_request(skb)) + { + if(rkps -> id[0] != ntohl(ip_hdr(skb) -> saddr)) + return 0; + if(rkps -> id[1] != ntohl(ip_hdr(skb) -> daddr)) + return 0; + if(rkps -> id[2] != ntohs(tcp_hdr(skb) -> sport) << 16 + ntohs(tcp_hdr(skb) -> dport); + return 0; + return 1; + } else { - rtn &= 0x01; - if(!(skb -> mark & mark_request)) - return rtn; - else - { - rtn &= (0x01 << 8); - - } - + if(rkps -> id[0] != ntohl(ip_hdr(skb) -> daddr)) + return 0; + if(rkps -> id[1] != ntohl(ip_hdr(skb) -> saddr)) + return 0; + if(rkps -> id[2] != ntohs(tcp_hdr(skb) -> dport) << 16 + ntohs(tcp_hdr(skb) -> sport); + return 0; + return 1; } - +} +u_int8_t rkpStream_execute(struct rkpStream* rkps, struct sk_buff* skb) +// 不要害怕麻烦,咱们把每一种情况都慢慢写一遍。 +{ + int32_t seq; + + // 肯定需要更新时间 + rkps -> last_active = now(); + + // 服务端返回确认包的情况,更新一下确认号,返回 sccept。以后的情况,都是客户端发往服务端的了。 + if(!rkpSettings_request(skb)) + { + int32_t seq = __rkpStream_skb_seq(rkps -> seq_ack, ntohl(tcp_hdr(skb) -> ack_seq)); + if(seq > 0) + char __rkpStream_refresh_ack(rkps, ntohl(tcp_hdr(skb) -> ack_seq)); + return NF_ACCEPT; + } + + // 不携带应用层数据的情况。直接接受即可。以后的情况,都是含有应用层数据的包了。 + if(__rkpStream_skb_appLen(skb) == 0) + return NF_ACCEPT; + + + // 检查数据包是否是将来的数据包。如果是的话,需要放到 buff_next 等待处理。 + seq = __rkpStream_skb_seq(rkps -> seq_ack, ntohl(tcp_hdr(skb) -> seq)); + if(seq > rkps -> seq) + { + __rkpStream_buff_retain_auto(rkps -> buff_next, skb); + return NF_STOLEN; + } + + // 检查数据包是否是已经被确认的数据包。应该不会出现这种情况。出现的话就把它丢掉吧。 + if(seq < 0) + { + printk("rkp-ua::rkpStream: Re-transmission of asked package. Drop it.\n"); + return NF_DROP; + } + + // 检查数据包是否是重传数据包。如果是的话,可能需要修改数据。然后,将它发出。接下来的情况,就一定是刚好是需要的序列号的情况了 + if(seq < rkps -> seq) + { + struct sk_buff* skb_prev = __rkpStream_buff_find(rkps -> buff_prev, ntohl(skb -> seq)); + if(skb_prev != 0 && tcp_hdr(skb_prev) -> seq == tcp_hdr(skb) -> seq) + // 存在相符的数据包。将数据拷贝过去。 + { + if(skb_ensure_writable(skb, __rkpStream_skb_appStart(skb) - skb -> data + __rkpStream_skb_appLen(skb))) + { + printk("rkp-ua::rkpStream::rkpStream_execute: Can not make skb writable, may caused by leasing memory. Drop it.\n"); + return NF_DROP; + } + memcpy(__rkpStream_skb_appStart(skb), __rkpStream_skb_appStart(skb_prev), __rkpStream_skb_appLen(skb_prev)); + } + return NF_ACCEPT; + } + + // 如果是在 sniffing 的情况下,那一定先扫描一下再说 + if(rkps -> status == __rkpStream_sniffing) + { + u_int16_t scan = __rkpStream_data_scan(__rkpStream_skb_appStart(skb), __rkpStream_skb_appLen(skb), + str_end, rkps -> scan_matched); + + if(scan & 0x1) + // 扫描找到了 HTTP 头的结尾,那么将这个数据包补到 buff 中,更新 seq,开始查找、替换、发出,然后根据情况设置状态,再考虑 buff_next 中的包,最后返回 STOLEN + { + struct sk_buff* skbp = rkps -> buff; + + // 追加到 buff 后面 + __rkpStream_buff_retain_end(&(rkps -> buff), skb); + rkps -> seq = __rkpStream_skb_seq(rkps -> ack_seq, ntohl(tcp_hdr(skb) -> seq)) + __rkpStream_skb_appLen(skb); + + // 查找、替换 + __rkpStream_buff_execute_core(&(rkps -> buff), scan >> 1); + + // 循环复制一份到 buff_prev 下面,同时发出 + while(skbp != 0) + { + struct sk_buff* skbp2 = skbp -> next; + __rkpStream_buff_retain_end(rkps -> buff_prev, __rkpStream_skb_copy(skbp)); + __rkpStream_skb_send(skbp); + skbp = skbp2; + } + + // 清空查找情况,重新设置状态 + rkps -> scan_length = 0; + if(!(tcp_hdr(skb) -> psh)) + rkps -> status = waiting; + + // 考虑之前截留的数据包 + __rkpStream_buff_rejudge(rkps, &(skps -> buff_prev)); + + return NF_STOLEN; + } + else if(tcp_hdr(skb) -> psh) + // 如果没有找到却读到了 PUSH,这就比较迷了。打印一句警告,更新 seq,然后把截留的包都放行,然后考虑 buff_prev 里的数据。 + { + struct sk_buff* skbp = rkps -> buff; + + // 打印警告 + printk("rkp-ua::rkpStream::rkpStream_execute: Find PSH before header ending found. Send without modification.\n"); + + // 更新 seq + rkps -> seq = __rkpStream_skb_seq(rkps -> ack_seq, ntohl(tcp_hdr(skb) -> seq)) + __rkpStream_skb_appLen(skb); + + // 放行截留的包 + while(skbp != 0) + { + struct sk_buff* skbp2 = skbp -> next; + __rkpStream_skb_send(skbp); + skbp = skbp2; + } + + // 清空查找情况 + rkps -> scan_length = 0; + + // 考虑之前截留的数据包 + __rkpStream_buff_rejudge(rkps, &(skps -> buff_prev)); + + return NF_ACCEPT; + } + else + // 没有找到结尾,也没有push。那么,将这个数据包补到 buff 中,更新 seq 和 查找状态,再考虑 buff_next 中的包,最后返回 STOLEN + { + // 追加到 buff + __rkpStream_buff_retain_end(rkps -> buff, skb); + + // 更新 seq 和查找状态 + rkps -> seq = __rkpStream_skb_seq(rkps -> ack_seq, ntohl(tcp_hdr(skb) -> seq)) + __rkpStream_skb_appLen(skb); + rkps -> matched_length = scan >> 1; + + // 考虑 buff_next 中的包 + __rkpStream_buff_rejudge(rkps, &(skps -> buff_prev)); + + return NF_STOLEN; + } + } + else + // 如果是在 waiting 的状态下,那么设置 seq 和状态,然后考虑 buff_next 中的包,然后返回 ACCEPT 就可以了 + { + // 设置 seq 和状态 + rkps -> seq = __rkpStream_skb_seq(rkps -> ack_seq, ntohl(tcp_hdr(skb) -> seq)) + __rkpStream_skb_appLen(skb); + if(tcp_hdr(skb) -> psh) + rkps -> status = sniffing; + + // 考虑 buff_next + __rkpStream_buff_rejudge(rkps, &(skps -> buff_prev)); + + return NF_ACCEPT; + } +} + +void __rkpStream_refresh_ack(struct rkpStream* rkps, u_int32_t ack) +{ + struct sk_buff* skbp; + + // 重新计算 seq 偏移 + rkps -> seq -= ack - rkps -> seq_ack; + + // 丢弃 buff_prev 中已经确认收到的数据包 + skbp = rkps -> buff_prev; + while(skbp != 0) +} + +u_int16_t __rkpStream_dataLen(strct sk_buff* skb) +{ + return ntohs(ip_hdr(skb) -> tot_len) - ip_hdr(skb) -> ihl * 4 - tcp_hdr(skb) -> doff * 4; } diff --git a/src/scanner.h b/src/scanner.h deleted file mode 100644 index b3f13f8..0000000 --- a/src/scanner.h +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const char str_ua[] const = "User-Agent: "; -const char str_headtail[] const = "\r\n\r\n"; -const char str_uaend[] const = "\r\n"; -const char str_windows[] const = "Windows NT"; - -struct scanner -// 字符串扫描(跨 skb)需要的一些状态变量以及函数 -{ - struct sk_buff* skb; // 当前正在扫描的字符所在的 skb - char* data_start; - char* data_end; // skb 的应用层起始位置和终止位置,记下来避免每次计算 - char* pos; // 现在扫描到的位置 - - const char* target; // 要匹配的字符串 - u_int8_t target_legth; // 要匹配的字符串的长度 - - u_int8_t enum - { - matching_none, - matching_content, - matching_headtail - } matching_status; // 当前匹配的状态:没有正在匹配的,正在匹配所给的内容,正在匹配 http 头结尾 - u_int16_t matched_length; // 如果正在匹配给定的内容或者头结尾,这里指示已经匹配的长度(包含当前的字符) -}; -void scanner_init(struct scanner* scn, struct sk_buff* skb, u_int16_t pos, const char* target) -// 初始化扫描器 -// pos 指指针相对于给定的 skb 的应用层第一个字节的位置。skb 的第一个应用层字节的位置为 0。 -{ - // 找到目标字节真正所的 skb - while(true) - { - register struct tcphdr *tcph = tcp_hdr(skb); - register struct iphdr *iph = ip_hdr(skb); - u_int16_t data_length = ntohs(iph -> tot_len) - iph -> ihl * 4 - tcph -> doff * 4; - if(pos >= data_length) - pos -= data_length; - } - scn -> skb = skb; - scn -> data_start = (char*)tcph + tcph -> doff * 4; - scn -> data_end = (char*)tcph + ntohs(iph -> tot_len) - iph -> ihl * 4; - scn -> pos = scn -> data_start + pos; - scn -> target = target; - scn -> target_length = strlen(target); - scn -> matching_status = scn -> matching_none; - scn -> matched_length = 0; -} -u_int8_t scanner_next(struct scanner* scn) -// 尝试将指针移动到下一个字节。会自动跨 skb。 -// 返回值:0 没有匹配完毕什么;1 目标字符串匹配完毕;2 http 头匹配完毕;-1 需要下一个 skb(下一个 skb 的指针是 0,或者根据序列判断并不是紧邻的下一个 skb -// 注意到,如果要匹配 "\r\n" 之类以 '\r' 开头的字符串时,不能正确地识别 http 头结尾;以及,匹配长度为 1 的字符串时也会出错。但这并不影响最终结果。 -{ - // 尝试将指针移动到下一个位置 - if(scn -> pos + 1 == scn -> data_end) - { - if(scn -> skb -> next == 0) - return -1; - else - { - register u_int32_t target_seq = ntohl(tcp_hdr(scn -> skb) -> seq) + (scn -> data_end - scn -> data_start); - if(target_seq != ntolh(scn -> skb -> next -> seq)) - return -1 - else - { - scn -> skb = scn -> skb -> next; - register struct tcphdr *tcph = tcp_hdr(scn -> skb); - register struct iphdr *iph = ip_hdr(scn -> skb); - scn -> data_start = (char*)tcph + tcph -> doff * 4; - scn -> data_end = (char*)tcph + ntohs(iph -> tot_len) - iph -> ihl * 4; - scn -> pos = scn -> data_start; - } - } - } - else - scn -> pos++; - - // 检查匹配情况 - if(scn -> matching_status == scn -> matching_none) - { - if(scn -> pos[0] == scn -> target[0]) - { - scn -> matching_status == scn -> matching_target; - scn -> matched_length = 1; - return 0; - } - else if(scn -> pos[0] == str_headtail[0]) - { - scn -> matching_status == scn -> matching_headtail; - scn -> matched_length = 1; - return 0; - } - else - return 0; - } - else if(scn -> matching_status == scn -> matching_target) - { - if(scn -> pos[0] == scn -> target[scn -> matched_length]) - { - scn -> matched_length++; - if(scn -> matched_length == scn -> matched_length) - return 1; - else - return 0; - } - else if(scn -> pos[0] == str_headtail[0]) - { - scn -> matching_status == scn -> matching_headtail; - scn -> matched_length = 1; - return 0; - } - else - { - scn -> matching_status == scn -> matching_none; - scn -> matched_length = 0; - return 0; - } - } - else if(scn -> matching_status == scn -> matching_target) - { - if(scn -> pos[0] == str_headtail[scn -> matched_length]) - { - scn -> matched_length++; - if(scn -> matched_length == 4) - return 2; - else - return 0; - } - else if(scn -> pos[0] == scn -> target[0]) - { - scn -> matching_status == scn -> matching_target; - scn -> matched_length = 1; - return 0; - } - else - { - scn -> matching_status == scn -> matching_none; - scn -> matched_length = 0; - return 0; - } - } -} -void scanner_prev(struct scanner* scn) -// 将指针移动到上一个字节。会自动跨 skb。 -// 不需要考虑匹配的状态。保证上一个 skb 一定存在。 -{ - if(scn -> pos == scn -> data_start) - { - scn -> skb = scn -> skb -> prev; - register struct tcphdr *tcph = tcp_hdr(scn -> skb); - register struct iphdr *iph = ip_hdr(scn -> skb); - scn -> data_start = (char*)tcph + tcph -> doff * 4; - scn -> data_end = (char*)tcph + ntohs(iph -> tot_len) - iph -> ihl * 4; - scn -> pos = scn -> data_end - 1; - } - else - scn -> pos--; -} -void scanner_next_noscan(struct scanner* scn) -{ - if(scn -> pos + 1 == scn -> data_end) - { - scn -> skb = scn -> skb -> next; - register struct tcphdr *tcph = tcp_hdr(scn -> skb); - register struct iphdr *iph = ip_hdr(scn -> skb); - scn -> data_start = (char*)tcph + tcph -> doff * 4; - scn -> data_end = (char*)tcph + ntohs(iph -> tot_len) - iph -> ihl * 4; - scn -> pos = scn -> data_start; - } - else - scn -> pos++; -} \ No newline at end of file diff --git a/src/stm_info.h b/src/stm_info.h deleted file mode 100644 index 3082288..0000000 --- a/src/stm_info.h +++ /dev/null @@ -1,187 +0,0 @@ -#include "scanner.h" - -struct stm_info -// 用于存储每一条流的信息。 -{ - u_int8_t modify_finished:1, // 标识是否已经被修改完成。对于修改完成的流,肯定没有存储任何 skb,之后都不会再捕获。对于不需要修改的流,最初的时候就会被置为“已经修改完成”而直接放行。 - modify_force:1, // 是否强制修改这个流(而不论目标地址,也不论是否是 windows 主机),与 mark 中的内容对应。 - scan_finished:1, // 是否已经扫描完成;也就是说,已经完整地找到了 ua,或者完整地拿到了 http 头。之后不再会捕获这个流,但流中可能还有缓存的 skb 没有发出。windows 也已经被扫描(除非没有必要)。 - scan_ua_found:1, // 是否找到了 ua。只要找到了 "User-Agent: " 这一项就被置为 1,ua_start 同时被设置;ua_end 则会等到 scan_finished 才被设置。 - scan_windows:1, // ua 是否是 windows 的 ua。在完整地找到 ua 后,除非 modify_force 被设置,否则会再扫描一遍 ua 来确定这个值。 - preserved:3; - u_int32_t saddr; // 源地址 - u_int16_t sport; // 源端口。通过这两项应该足够区分不同的流。 - u_int32_t seq_offset; // 应用层字节的编号的偏移,减去偏移后,第一个应用层字节的编号为 0 - u_int32_t ua_start; // "User-Agent: xxxxx\r\n" 中,第一个 'x' 的位置(已经减去偏移) - u_int32_t ua_end; // "User-Agent: xxxxx\r\n?" 中,‘\r’ 的位置(已经减去偏移) - struct sk_buff* skb; - struct scanner* scn; -}; -void stm_init(struct stm_info* stm, struct sk_buff* skb) -// 初始化 stm_info -{ - stm -> modify_finished = 0; - if(skb -> mark & 0x20) - stm -> modify_force = 1; - else - stm -> modify_force = 0; - stm -> scan_finished = 0; - stm -> saddr = ntohl(ip_hdr(skb) -> daddr); - stm -> sport = ntohs(tcp_hdr(skb) -> dest); - stm -> seq_offset = ntohl(tcp_hdr(skb) -> ack_seq); - stm -> skb = 0; - stm -> scn = 0; -} -u_int32_t stm_realseq(struct stm_info* stm, struct sk_buff* skb) -// 计算 skb 的真实偏移 -{ - register u_int32_t real_seq = ntohl(tcp_hdr(skb) -> ack_seq); - if(real_seq >= stm -> seq_offset) - real_seq -= stm -> seq_offset; - else - real_seq += 0xffffffff - stm -> seq_offset + 1; - return real_seq; -} -void stm_append(struct stm_info* stm, struct sk_buff* skb) -// 追加一个 skb 到合适的位置。 -{ - register u_int32_t real_seq; - register struct sk_buff* i; - if(stm -> skb == 0) - { - stm -> skb = skb; - skb -> next = skb -> prev = 0; - } - - real_seq = stm_realseq(stm, skb); - if(stm_realseq(stm, stm -> skb) > real_seq) - { - stm -> skb -> prev = skb; - skb -> next = stm -> skb; - stm -> skb = skb; - } - - //尝试寻找最后一个序号比待插入 skb 小的 skb - for(i = stm -> skb;;i = i -> next) - { - if(i -> next == 0) - { - i -> next = skb; - skb -> prev = i; - skb -> next = 0; - } - else if(stm_realseq(stm, i -> next) > real_seq) - { - i -> next -> prev = skb; - skb -> next = i -> next; - i -> next = skb; - skb -> prev = i; - } - } -} -void stm_scan(struct stm_info* stm) -// 扫描直到头部末尾或者 ua 结束(总之是使得 scan_finished)或者不能再继续扫描了(需要下一个包)。也会扫描 windows,如果有必要。 -{ - // 如果没有初始化扫描器,就尝试初始化 - if(stm -> scn == 0) - { - if(stm -> skb == 0 || stm_realseq(stm, stm -> skb) != 0) - return; - else - { - stm -> scn = kmalloc(sizeof(scanner), GFP_KERNEL); - scanner_init(stm -> scn, stm -> skb, 0, str_ua); - } - } - - static char* ua_end = 0; // 仅仅用来扫描 windows 时使用一下。记录 ua 的结束位置,当扫描到这里时还没有 windows,那就是没有了。 - while(true) - { - u_int8_t rtn = scanner_next(stm -> scn); - if(rtn == 0 && stm -> scn -> pos == ua_end) - // 没有找到 windows - { - stm -> scan_finished = 1; - ua_end = 0; - } - else if(rtn == 0) - continue; - else if(rtn == 1 && stm -> scn -> target == str_ua) - { - // 将结果记录,然后去扫描 ua 尾。 - stm -> scan_ua_found; - stm -> ua_start = stm_realseq(stm, stm -> scn -> skb) + (stm -> scn -> pos - stm -> scn -> data_start); - scanner_init(stm -> scn, stm -> scn -> skb, stm -> scn -> pos - stm -> scn -> data_start, str_uaend); - } - else if(rtn == 1 && stm -> scn -> target == str_uaend) - { - // 将结果记录,然后去扫描 windows 或者结束。 - stm -> ua_end = stm_realseq(stm, stm -> scn -> skb) + (stm -> scn -> pos - stm -> scn -> data_start) + 1; - if(!stm -> modify_force) - scanner_init(stm -> scn, stm -> skb, stm -> ua_start, str_windows); - else - { - stm -> scan_finished = 1; - return; - } - } - else if(rtn == 1 && stm -> scn -> target == str_windows) - // 将结果记录,结束 - { - stm -> scan_windows = 1; - stm -> scan_finished = 1; - ua_end = 0; - return; - } - else if(rtn == 2) - { - stm -> scan_finished; - return; - } - } -} -void stm_modify_send(struct stm_info* stm) -// 将 ua 替换为 "XMURP/1.0" 加许多的空格 -{ - struct sk_buff* skb_start; - struct sk_buff* skb_end; // 两个 skb 之间的(包含这两个 skb)都需要重新计算校验和 - sturct sk_buff* skb; - u_int16_t i; - scanner_init(stm -> scn, stm -> skb, stm -> ua_start, str_ua); - skb_start = stm -> scn -> skb; - scanner_prev(stm -> scn); - for(int i = 0; i < stm -> ua_end - stm -> ua_start; i++) - { - scanner_next_noscan(stm -> scn); - if(i < strlen("XMURP/1.0")) - *(stm -> scn -> pos) = "XMURP/1.0"[i]; - else - *(stm -> scn -> pos) = ' '; - } - skb_end = stm -> scn -> skb -> next; - - skb = skb_start; - do - { - register struct tcp_hdr* tcph = tcp_hdr(skb); - register struct ip_hdr* iph = ip_hdr(skb); - tcph->check = 0; - iph->check = 0; - skb->csum = skb_checksum(skb, iph->ihl * 4, ntohs(iph->tot_len) - iph->ihl * 4, 0); - iph->check = ip_fast_csum(iph, iph->ihl); - tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, ntohs(iph->tot_len) - iph->ihl * 4, IPPROTO_TCP, skb->csum); - } - while((skb = skb -> next) != skb_end); - - skb = stm -> skb; - while(skb != 0) - { - dev_queue_xmit(skb); - skb = skb -> next; - } - - stm -> modify_finished = 1; -} - - - diff --git a/src/stm_vec.h b/src/stm_vec.h deleted file mode 100644 index 08362d2..0000000 --- a/src/stm_vec.h +++ /dev/null @@ -1,49 +0,0 @@ -#include "stm_info.h" - -struct stm_vec -// 用于存储一条条流的向量,包含了扩大等函数。 -{ - struct stm_info* data; - u_int16_t max_size; - u_int16_t size; -}; -void stm_vec_init(struct stm_vec* stmv) -// 初始化 skb_vec -{ - stmv -> max_size = 4; - stmv -> size = 0; - stmv -> data = kmalloc(sizeof(stm_info) * stmv -> max_size, GFP_KERNEL) -} -void stm_vec_insert(struct stm_vec* stmv) -// 为 skb_vec 增加一条流。不会初始化新增的流。 -{ - if(stmv -> size == stmv -> max_size) // 如果满了,就扩大 - { - struct stm_info* temp = stmv -> data; - stmv -> max_size *= 2; - stmv -> data = kmalloc(sizeof(stm_info) * stmv -> max_size, GFP_KERNEL) - memcpy(stmv -> data, temp, sizeof(stm_info) * stmv -> size); - kfree(temp); - printk("xmurp-ua: Streams buff expanded to %d.\n", stmv -> max_size); - } - stmv -> size++; -} -u_int16_t stm_vec_find(struct stm_vec* stmv, struct sk_buff* skb) -// 根据一个数据包来寻找它属于哪个流。数据包必须是客户端发给服务端的。如果没有找到,返回 0xffff。 -{ - int i; - u_int32_t saddr = ntohl(ip_hdr(skb) -> saddr; - u_int32_t sport = ntohs(tcp_hdr(skb) -> src); - for(i = 0; i < stmv -> size; i++) - if(saddr == stmv -> data[i].saddr && sport == stmv -> data[i].sport) - return i; - return 0xffff; -} -void stm_vec_del(struct stm_vec* stmv, u_int16_t pos) -// 删除指定流,后面的往前移。 -{ - int i; - for(i = pos; i < size - 1; i++) - stmv -> data[i] = stv -> data[i + 1]; - stmv -> size--; -} diff --git a/src/xmurp-ua.c b/src/xmurp-ua.c index 5de5e4e..a598be7 100644 --- a/src/xmurp-ua.c +++ b/src/xmurp-ua.c @@ -1,268 +1,73 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "rkpManager.h" static struct nf_hook_ops nfho; +static struct rkpManager* rkpm; +static time_t last_flush; -enum char_scan_enum -{ - next, - modified_and_next, - scan_finish, - reset, -}; - -enum skb_scan_ret -{ - need_next_frag = 1, - ua_modified = 2, -}; - -// 根据得到的指针尝试扫描,发现结尾或发现UA或更改UA后返回对应结果。 -// 输入零指针则为重置状态。 -inline u_int8_t char_scan(char *data) -{ - const char str_ua_head[] = "User-Agent: ", str_ua[] = "XMURP/1.0", str_end[] = "\r\n\r\n"; - // 不算'\0',长度分别为12、9、4 - static enum - { - nothing_matching, - ua_head_matching, - ua_modifying, - end_matching, - } status = nothing_matching; - static u_int8_t covered_length; - - if(data == 0) - { - status = nothing_matching; - covered_length = 0; - return reset; - } - - while(true) - { - if(status == nothing_matching) - { - if(*data == str_ua_head[0]) - { - status = ua_head_matching; - covered_length = 1; - return next; - } - else if(*data == str_end[0]) - { - status = end_matching; - covered_length = 1; - return next; - } - else - return next; - } - else if(status == ua_head_matching) - { - if(*data == str_ua_head[covered_length]) - { - covered_length++; - if(covered_length == 12) - { - status = ua_modifying; - covered_length = 0; - return next; - } - else - return next; - } - else - status = nothing_matching; - } - else if(status == ua_modifying) - { - if(*data == '\r') - { - status = nothing_matching; - return scan_finish; - } - else - { - if(covered_length < 9) - *data = str_ua[covered_length]; - else - *data = ' '; - covered_length++; - return modified_and_next; - } - } - else if(status == end_matching) - { - if(*data == str_end[covered_length]) - { - covered_length++; - if(covered_length == 4) - { - status = nothing_matching; - return scan_finish; - } - else - return next; - } - else - status = nothing_matching; - } - } -} - -// 将数据逐字节发送给下一层,根据下一层的结果(扫描到结尾、扫描到UA、已更改UA),确定是否扫描完毕,以及是否发生了改动,返回到上一层。 -inline u_int8_t skb_scan(char *data_start, char *data_end) -{ - register char *i; - register u_int8_t ret, modified = 0; - for(i = data_start; i < data_end; i++) - { - ret = char_scan(i); - if(ret == scan_finish) - return modified; - else if(ret == modified_and_next) - modified = ua_modified; - } - return modified + need_next_frag; -} - -// 捕获数据包,检查是否符合条件。如果符合,则送到下一层,并根据下一层返回的结果,如果必要的话,重新计算校验和以及继续捕获下一个分片。 -// ip地址、端口号、iph->tot_len需要网络顺序到主机顺序的转换。校验和时,除长度字段外,不需要手动进行网络顺序和主机顺序的转换。 unsigned int hook_funcion(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { - register struct tcphdr *tcph; - register struct iphdr *iph; - register char *data_start, *data_end; + u_int8_t rtn; + static u_int8_t crashed = 0; - static u_int8_t catch_next_frag = 0; - static u_int32_t saddr, daddr, seq; - static u_int16_t sport, dport; - - static u_int32_t n_ua_modified = 0, n_ua_modify_faild = 0; - static u_int8_t mark_matched = 0; - - register u_int8_t jump_to_next_function = 0, ret; - - // 过滤发往外网的HTTP请求的包,且要求包的应用层内容不短于3字节 - if(skb == 0) + if(crashed) return NF_ACCEPT; - iph = ip_hdr(skb); - if((ntohl(iph->daddr) & 0xffff0000) == 0xc0a80000) + if(!rkpSettings_capture(skb)) return NF_ACCEPT; - if(iph->protocol != IPPROTO_TCP) - return NF_ACCEPT; - tcph = tcp_hdr(skb); - if(ntohs(tcph->dest) != 80) - return NF_ACCEPT; - data_start = (char *)tcph + tcph->doff * 4; - data_end = (char *)tcph + ntohs(iph->tot_len) - iph->ihl * 4; - if(data_end - data_start < 4) - return NF_ACCEPT; - if(skb->mark & 0x100) + rtn = rkpManager_execute(rkpm, skb); + if(rtn == NF_DROP_ERR(1)) { - if(!mark_matched) - { - mark_matched = 1; - printk("xmurp-ua: Mark matched. Note that all packages with the mark will be ACCEPT without modify.\n"); - printk("xmurp-ua: If the mark is not set manually, it maybe a conflict there. " - "Find out which app is using the desired bit and let it use others, or modify and recompile me.\n"); - } - return NF_ACCEPT; + printk("rkp-ua: Crashed.\n"); + crashed = 1; + rkpManager_del(rkpm); + return NF_STOLEN; } - - // 决定是否发送到下一层 - if(catch_next_frag && iph->saddr == saddr && iph->daddr == daddr && - tcph->seq == seq && tcph->source == sport && tcph->dest == dport) - jump_to_next_function = 1; - else if(data_end - data_start > 3) - if(memcmp(data_start, "GET", 3) == 0 || memcmp(data_start, "POST", 4) == 0) - { - if(catch_next_frag) - { - n_ua_modify_faild++; - char_scan(0); - catch_next_frag = 0; - } - jump_to_next_function = 1; - } - if(!jump_to_next_function) - return NF_ACCEPT; - - // 发送到下一层,并回收数据 - ret = skb_scan(data_start, data_end); - - // 处理返回值 - if(ret & need_next_frag) - { - if(!catch_next_frag) - { - catch_next_frag = 1; - saddr = iph->saddr; - daddr = iph->daddr; - sport = tcph->source; - dport = tcph->dest; - } - seq = tcph->seq + (data_end - data_start); - } - else - catch_next_frag = 0; - if(ret & ua_modified) - { - n_ua_modified++; - if(n_ua_modified % 0x10000 == 0) - printk("xmurp-ua: Successfully modified %d packages, faild to modify %d packages.\n", - n_ua_modified, n_ua_modify_faild); - tcph->check = 0; - iph->check = 0; - skb->csum = skb_checksum(skb, iph->ihl * 4, ntohs(iph->tot_len) - iph->ihl * 4, 0); - iph->check = ip_fast_csum(iph, iph->ihl); - tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, ntohs(iph->tot_len) - iph->ihl * 4, IPPROTO_TCP, skb->csum); - } - - return NF_ACCEPT; + else + return rtn; } static int __init hook_init(void) { int ret; + rkpm = rkpManager_new(); + last_flush = now(); + nfho.hook = hook_funcion; nfho.pf = NFPROTO_IPV4; nfho.hooknum = NF_INET_POST_ROUTING; - nfho.priority = NF_IP_PRI_FILTER; + nfho.priority = NF_IP_PRI_NAT_SRC; + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) ret = nf_register_net_hook(&init_net, &nfho); #else ret = nf_register_hook(&nfho); #endif - printk("xmurp-ua: Started.\n"); - printk("xmurp-ua: nf_register_hook returnd %d.\n", ret); + + printk("rkp-ua: Started, version %s\n", VERSION); + printk("rkp-ua: mode_advanced=%c, mode_winPreserve=%c, mark_capture=0x%x, " + "mark_request=0x%x, mark_first=0x%x, mark_winPreserve=0x%x.\n", + 'n' + mode_advanced * ('y' - 'n'), 'n' + mode_winPreserve * ('y' - 'n'), + mark_capture, mark_request, mark_first, mark_winPreserve); + printk("rkp-ua: nf_register_hook returnd %d.\n", ret); return 0; } -//卸载模块 static void __exit hook_exit(void) { + rkpManager_del(rkpm); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) nf_unregister_net_hook(&init_net, &nfho); #else nf_unregister_hook(&nfho); #endif - printk("xmurp-ua: Stopped.\n"); + printk("rkp-ua: Stopped.\n"); } module_init(hook_init); module_exit(hook_exit); MODULE_AUTHOR("Haonan Chen"); -MODULE_DESCRIPTION("Modify UA in HTTP for anti-detection of router in XMU."); +MODULE_DESCRIPTION("Modify UA in HTTP for anti-detection about amount of devices behind NAT."); MODULE_LICENSE("GPL"); \ No newline at end of file