大框架写完了 还需要写main函数 细节上检查一下 禁用 keep-alive

This commit is contained in:
chn
2019-10-13 20:52:09 +08:00
parent 7066eafaa3
commit e8bdbc800a
6 changed files with 421 additions and 3 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.vscode

View File

@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=xmurp-ua PKG_NAME:=xmurp-ua
PKG_RELEASE:=27 PKG_RELEASE:=28
include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/package.mk
@@ -19,7 +19,7 @@ define KernelPackage/xmurp-ua
SUBMENU:=Other modules SUBMENU:=Other modules
TITLE:=xmurp-ua TITLE:=xmurp-ua
FILES:=$(PKG_BUILD_DIR)/xmurp-ua.ko FILES:=$(PKG_BUILD_DIR)/xmurp-ua.ko
AUTOLOAD:=$(call AutoLoad,99,xmurp-ua) # AUTOLOAD:=$(call AutoLoad,99,xmurp-ua)
KCONFIG:= KCONFIG:=
endef endef

182
src/scanner.h Normal file
View File

@@ -0,0 +1,182 @@
#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++;
}

187
src/stm_info.h Normal file
View File

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

49
src/stm_vec.h Normal file
View File

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

View File

@@ -7,7 +7,6 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/random.h>
static struct nf_hook_ops nfho; static struct nf_hook_ops nfho;