From e8bdbc800a5626f42fad5f21fde0cdd77267d542 Mon Sep 17 00:00:00 2001 From: chn <897331845@qq.com> Date: Sun, 13 Oct 2019 20:52:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E6=A1=86=E6=9E=B6=E5=86=99=E5=AE=8C?= =?UTF-8?q?=E4=BA=86=20=E8=BF=98=E9=9C=80=E8=A6=81=E5=86=99main=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20=E7=BB=86=E8=8A=82=E4=B8=8A=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E4=B8=80=E4=B8=8B=20=E7=A6=81=E7=94=A8=20keep-alive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Makefile | 4 +- src/scanner.h | 182 +++++++++++++++++++++++++++++++++++++++++++++++ src/stm_info.h | 187 +++++++++++++++++++++++++++++++++++++++++++++++++ src/stm_vec.h | 49 +++++++++++++ src/xmurp-ua.c | 1 - 6 files changed, 421 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 src/scanner.h create mode 100644 src/stm_info.h create mode 100644 src/stm_vec.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..600d2d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile index 873b42f..9ca90fc 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=xmurp-ua -PKG_RELEASE:=27 +PKG_RELEASE:=28 include $(INCLUDE_DIR)/package.mk @@ -19,7 +19,7 @@ define KernelPackage/xmurp-ua SUBMENU:=Other modules TITLE:=xmurp-ua FILES:=$(PKG_BUILD_DIR)/xmurp-ua.ko - AUTOLOAD:=$(call AutoLoad,99,xmurp-ua) +# AUTOLOAD:=$(call AutoLoad,99,xmurp-ua) KCONFIG:= endef diff --git a/src/scanner.h b/src/scanner.h new file mode 100644 index 0000000..b3f13f8 --- /dev/null +++ b/src/scanner.h @@ -0,0 +1,182 @@ +#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 new file mode 100644 index 0000000..3082288 --- /dev/null +++ b/src/stm_info.h @@ -0,0 +1,187 @@ +#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 new file mode 100644 index 0000000..08362d2 --- /dev/null +++ b/src/stm_vec.h @@ -0,0 +1,49 @@ +#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 a4c14b3..5de5e4e 100644 --- a/src/xmurp-ua.c +++ b/src/xmurp-ua.c @@ -7,7 +7,6 @@ #include #include #include -#include static struct nf_hook_ops nfho;