框架写好了

This commit is contained in:
chn
2019-11-01 20:24:08 +08:00
parent e848cb4402
commit 672e2be1c9
10 changed files with 341 additions and 684 deletions

View File

@@ -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` 中的部分数据包。

View File

@@ -1,2 +1,2 @@
config xmurp-ua
tristate "xmurp-ua"
config rkp-ua
tristate "rkp-ua"

View File

@@ -1 +1 @@
obj-${CONFIG_xmurp-ua} += xmurp-ua.o
obj-${CONFIG_rkp-ua} += rkp-ua.o

View File

@@ -12,10 +12,16 @@
#include <linux/random.h>
#include <linux/moduleparam.h>
#include <asm/limits.h>
#include <linux/time.h>
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;
}

15
src/rkpManager.h Normal file
View File

@@ -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出错1accept2stolen3drop
void rkpManager_refresh(struct rkpManager*); // 清理过时的流
struct rkpStream* __rkpManager_find(struct rkpManager*, struct sk_buff*); // 寻找一个数据包属于哪个流
void __rkpManager_stream_add(struct rkpManager*, struct sk_buff*); // 增加一个流

View File

@@ -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;
}

View File

@@ -1,182 +0,0 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/types.h>
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++;
}

View File

@@ -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: " 这一项就被置为 1ua_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;
}

View File

@@ -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--;
}

View File

@@ -1,268 +1,73 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/netdevice.h>
#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");