除了主函数以外,都修改完毕

This commit is contained in:
chn 2019-05-18 18:01:57 +08:00
parent 5f330d7656
commit b18e77f655
3 changed files with 305 additions and 77 deletions

View File

@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=xmurp-ua
PKG_RELEASE:=15
PKG_RELEASE:=16
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

View File

@ -6,4 +6,35 @@
iptables -t mangle -A PREROUTING -i br-lan_raw -j MARK --set-mark 1
```
  这是我第一次在 GitHub 上搞自己的仓库,可能会有些小问题哈。要是有的话,请各路大佬指正。
  这是我第一次在 GitHub 上搞自己的仓库,可能会有些小问题哈。要是有的话,请各路大佬指正。
---
第一层函数的思路:
* 如果没有在等待下一个分包则检测这个包是否是HTTP的开头部分。如果是则传递给下一层检查。
* 如果在等待下一个分包,则检测这个包是否是需要的下一个包。如果是,传递给下一层检查;如果不是,报错。
检查结果包含:
* 是否需要下一个分包。
* 是否需要保留当前包。
* 是否已经被修改(一旦被修改,就意味着是修改完成,所以肯定不需要下一个分包,也不需要保留当前包)。
* 如果被修改,是否有上一个分包。
对应的动作为:
* 如果需要下一个分包,并且保留当前包,如果这是第二个被保留的包,则出错。
* 如果需要下一个分包,并且保留当前包,如果这是第一个被保留的包,则返回 STOLEN并等待下一个包。
* 如果需要下一个分包,并且不保留当前包,返回 ACCEPT并且等待下一个包。
* 如果不需要下一个分包、没有被修改、没有上一个分包,则返回 ACCEPT不等待下一个包当前流修改完毕。
* 如果不需要下一个分包、没有被修改、有上一个分包,则返回 ACCEPT不等待下一个包上一个包标记后进入协议栈当前流修改完毕。
* 如果不需要下一个分包、被修改、没有上一个分包,则修改校验和并返回 ACCEPT当前流修改完毕。
* 如果不需要下一个分包、被修改、有上一个分包,则这个包修改校验和并返回 ACCEPT上一个包修改校验和、标记后进入协议栈。
第二层函数的思路:
逐个字节发送给下一层。下一层的返回有这些可能:
* 如果需要下一个字节。如果匹配到 UA

View File

@ -11,128 +11,321 @@
static struct nf_hook_ops nfho;
enum char_scan_enum
enum char_scan_ret // 逐字节扫描函数的返回值,互斥
{
next,
modified_and_next,
scan_finish,
reset,
need_next, // 需要下一个字节来进一步判断
ua_start, // 接下来就是ua的内容了
ua_scaning, // 正在扫描ua
ua_mobile, // 确认为移动版ua
ua_finish, // ua结束同时确认不是桌面版ua
httph_finish, // http头结束没有找到ua
reset, // 状态被重置
};
enum skb_scan_ret
struct skb_scan_ret // 逐分片扫描函数的返回值,共存
{
need_next_frag = 1,
ua_modified = 2,
u_int8_t ua_found:1 = 0, // 是否已经发现ua的位置
ua_exist:1 = 0, // 这个分片中是否存在部分或全部ua
need_next_frag:1 = 0, // 是否需要下一个分片
is_mobile:1 = 0; // 是否已经被判定为移动版ua
char *ua_start, *ua_end;
};
// 根据得到的指针尝试扫描发现结尾或发现UA或更改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
const char str_ua_head[] = "User-Agent: ", str_end[] = "\r\n\r\n",
str_wp[] = "Windows Phone", str_android = "Android", str_ipad = "iPad", str_iphone = "iPhone";
const u_int8_t len_ua_head = 12, len_end = 4, len_wp = 13, len_android = 7, len_ipad = 4, len_iphone = 6;
enum matching_status // 当前字节处在匹配什么的状态
{
nothing_matching,
ua_head_matching,
ua_modifying,
end_matching,
} status = nothing_matching;
static u_int8_t covered_length;
nothing, // 无
ua_head, // 正在匹配ua头
ua, // 正在匹配ua
end, // 正在匹配http头尾
};
enum ua_matching_status // 如果正在匹配ua那么正在匹配哪个关键字
{
nothing, // 无只是在匹配ua而已
wp, // 正在匹配windows phone
android, // 正在匹配安卓
ipad_or_iphone, // 正在匹配ipad或iphone因为两者都是ip开头所以需要这样一个东西。
ipad, // 正在匹配ipad
iphone, // 正在匹配iphone
mobile_matched, // 已经确认为移动端
};
static u_int8_t status = matching_status::nothing;
static u_int8_t ua_status = ua_matching_status::nothing;
static u_int8_t covered_length = 0;
if(data == 0)
if(data == 0) // 重置状态
{
status = nothing_matching;
status = nothing;
ua_status = ua_matching_status::nothing;
covered_length = 0;
return reset;
return char_scan_ret::reset;
}
while(true)
{
if(status == nothing_matching)
if(status == matching_status::nothing) // 如果没有在匹配什么就寻找ua头或者http头尾
{
if(*data == str_ua_head[0])
if(*data == str_ua_head[0]) // 发现疑似ua头
{
status = ua_head_matching;
status = matching_status::ua_head;
covered_length = 1;
return next;
return char_scan_ret::need_next;
}
else if(*data == str_end[0])
else if(*data == str_end[0]) // 发现疑似http头尾
{
status = end_matching;
status = matching_status::end;
covered_length = 1;
return next;
return char_scan_ret::need_next;
}
else
return next;
else // 啥都没发现
return char_scan_ret::need_next;
}
else if(status == ua_head_matching)
else if(status == matching_status::ua_head) // 如果正在匹配ua头尝试继续匹配或将状态置为nothing后重新匹配
{
if(*data == str_ua_head[covered_length])
if(*data == str_ua_head[covered_length]) // 继续匹配ua头
{
covered_length++;
if(covered_length == 12)
if(covered_length == len_ua_head) // ua头匹配完成
{
status = ua_modifying;
status = matching_status::ua;
ua_status = ua_matching_status::nothing;
covered_length = 0;
return next;
return char_scan_ret::ua_start;
}
else // 还没有匹配完
return char_scan_ret::need_next;
}
else
status = matching_status::nothing; // 将状态置为nothing重新匹配
}
else if(status == matching_status::ua) // 如果正在匹配ua需要进一步考虑是否正在匹配ua中的特殊字段
{
if(ua_status == ua_matching_status::nothing) // 如果没有在匹配特殊字段,就尝试匹配特殊字段或结尾
{
if(*data == '\r') // 如果发现ua结尾
{
status = nothing_matching;
ua_status = ua_matching_status::nothing;
return char_scan_ret::ua_finish;
}
else if(*data == str_wp[0]) // 如果疑似匹配到windows phone
{
ua_status = ua_matching_status::wp;
covered_length = 1;
return char_scan_ret::ua_scaning;
}
else if(*data == str_android[0]) // 如果疑似匹配到android
{
ua_status = ua_matching_status::android;
covered_length = 1;
return char_scan_ret::ua_scaning;
}
else if(*data == str_ipad[0]) // 如果疑似匹配到ipad或iphone
{
ua_status = ua_matching_status::ipad_or_iphone;
covered_length = 1;
return char_scan_ret::ua_scaning;
}
else
return next;
return char_scan_ret::ua_scaning;
}
else
status = nothing_matching;
}
else if(status == ua_modifying)
{
if(*data == '\r')
else if(ua_status == ua_matching_status::wp) // 如果正在匹配wp就尝试继续匹配或将ua状态置为nothing后重新匹配
{
status = nothing_matching;
return scan_finish;
if(*data == str_wp[covered_length]) // 如果可以继续匹配wp
{
covered_length++;
if(covered_length == len_wp) // 如果wp匹配完成
{
ua_status = ua_matching_status::mobile_matched;
covered_length = 0;
return char_scan_ret::ua_mobile;
}
}
else // 如果没有匹配上将ua状态置为nothing后重新匹配
ua_status = ua_matching_status::nothing;
}
else
else if(ua_status == ua_matching_status::android) // 如果正在匹配android就尝试继续匹配或将ua状态置为nothing后重新匹配
{
if(covered_length < 9)
*data = str_ua[covered_length];
else
*data = ' ';
covered_length++;
return modified_and_next;
if(*data == str_android[covered_length]) // 如果可以继续匹配android
{
covered_length++;
if(covered_length == len_android) // 如果匹配android完成
{
ua_status = ua_matching_status::mobile_matched;
covered_length = 0;
return char_scan_ret::ua_mobile;
}
}
else // 如果没有匹配上将ua状态置为nothing后重新匹配
ua_status = ua_matching_status::nothing;
}
else if(ua_status == ua_matching_status::ipad_or_iphone) // 如果正在匹配ipad或iphone就尝试继续匹配或将ua状态置为nothing后重新匹配
{
if(covered_length == 1 && *data == 'P') // 如果已经匹配i、可以匹配P
{
covered_length++;
return char_scan_ret::ua_scaning;
}
else if(covered_length == 2 && *data == 'a') // 如果已经匹配iP、可以匹配a
{
ua_status = ua_matching_status::ipad;
covered_length++;
return char_scan_ret::ua_scaning;
}
else if(covered_length == 2 && *data == 'h') // 如果已经匹配iP、可以匹配h
{
ua_status = ua_matching_status::iphone;
covered_length++;
return char_scan_ret::ua_scaning;
}
else // 如果没有匹配到将ua状态置为nothing后重新匹配
{
ua_status = ua_matching_status::nothing;
covered_length = 0;
}
}
else if(ua_status == ua_matching_status::ipad) // 如果正在匹配ipad就尝试继续匹配或将ua状态置为nothing后重新匹配
{
if(*data == str_ipad[covered_length]) // 如果可以继续匹配ipad
{
covered_length++;
if(covered_length == len_ipad) // 如果匹配ipad完成
{
ua_status = ua_matching_status::mobile_matched;
covered_length = 0;
return char_scan_ret::ua_mobile;
}
}
else // 如果没有匹配上将ua状态置为nothing后重新匹配
ua_status = ua_matching_status::nothing;
}
else if(ua_status == ua_matching_status::iphone) // 如果正在匹配iphone就尝试继续匹配或将ua状态置为nothing后重新匹配
{
if(*data == str_iphone[covered_length]) // 如果可以继续匹配iphone
{
covered_length++;
if(covered_length == len_iphone) // 如果匹配iphone完成
{
ua_status = ua_matching_status::mobile_matched;
covered_length = 0;
return char_scan_ret::ua_mobile;
}
}
else // 如果没有匹配上将ua状态置为nothing后重新匹配
ua_status = ua_matching_status::nothing;
}
}
else if(status == end_matching)
else if(status == matching_status::end)
{
if(*data == str_end[covered_length])
{
covered_length++;
if(covered_length == 4)
if(covered_length == len_end)
{
status = nothing_matching;
return scan_finish;
status = matching_status::nothing;
covered_length = 0;
return char_scan_ret::httph_finish;
}
else
return next;
return char_scan_ret::need_next;
}
else
status = nothing_matching;
status = matching_status::nothing;
}
}
}
// 将数据逐字节发送给下一层根据下一层的结果扫描到结尾、扫描到UA、已更改UA确定是否扫描完毕以及是否发生了改动返回到上一层。
inline u_int8_t skb_scan(char *data_start, char *data_end)
// 将传入的区域修改掉
inline void ua_modify(char *data1_start, char *data1_end, char *data2_start, char *data2_end)
{
register char *i;
register u_int8_t ret, modified = 0;
for(i = data_start; i < data_end; i++)
const char str_ua[] = "XMURP/1.0";
const u_int8_t len_ua = 9;
u_int8_t i = 0;
char *j = data1_start;
for(; i < len_ua && j < data1_end; i++, j++)
*j = str_ua[i];
for(; j < data1_end; j++)
*j = ' ';
for(j = data2_start; i < len_ua && j < data2_end; i++, j++)
*j = str_ua[i];
for(; j < data2_end; j++)
*j = ' ';
}
// 将数据逐字节发送给下一层根据下一层的结果确定是否改动以及改动哪里ua_found指明在上一个分片中是否找到了ua的位置
inline u_int8_t skb_scan(char *data_start, char *data_end, u_int8_t ua_found)
{
register char *i = data_start;
register u_int8_t ret, is_mobile = 0;
register struct skb_scan_ret skb_ret;
// 如果在上一个分片中已经找到了ua的位置那么这个分片的初始位置就已经是ua的一部分了因此需要做一些预处理
if(ua_found)
{
skb_ret.ua_found = 1;
skb_ret = char_scan(i);
if(ret == char_scan_ret:ua_scaning)
{
skb_ret.ua_exist = 1;
skb_ret.ua_start = i;
}
else if(ret == char_scan_ret::ua_mobile)
{
skb_ret.ua_exist = 1;
skb_ret.ua_start = i;
skb_ret.ua_mobile = 1;
}
else if(ret == char_scan_ret::ua_finish)
{
skb_ret.need_next_frag = 0;
return skb_ret;
}
i++;
}
// 扫描整个数据
for(; i < data_end; i++)
{
ret = char_scan(i);
if(ret == scan_finish)
return modified;
else if(ret == modified_and_next)
modified = ua_modified;
if(ret == char_scan_ret::need_next)
continue;
else if(ret == char_scan_ret::ua_start)
skb_ret.ua_found = 1;
else if(ret == char_scan_ret:ua_scaning)
if(!skb_ret.ua_exist)
{
skb_ret.ua_exist = 1;
skb_ret.ua_start = i;
}
else
continue;
else if(ret == char_scan_ret::ua_mobile)
skb_ret.ua_mobile = 1;
else if(ret == char_scan_ret::ua_finish)
{
skb_ret.need_next_frag = 0;
skb_ret.ua_end = i;
return skb_ret;
}
else if(ret == char_scan_ret::httph_finish)
{
skb_ret.need_next_frag = 0;
return skb_ret;
}
}
return modified + need_next_frag;
// 如果没有扫描到需要返回的地方
if(skb_ret.ua_exist)
skb_ret.ua_end = data_end;
skb_ret.need_next_frag = 1;
return skb_ret;
}
// 捕获数据包,检查是否符合条件。如果符合,则送到下一层,并根据下一层返回的结果,如果必要的话,重新计算校验和以及继续捕获下一个分片。
@ -143,13 +336,16 @@ unsigned int hook_funcion(void *priv, struct sk_buff *skb, const struct nf_hook_
register struct iphdr *iph;
register char *data_start, *data_end;
static u_int8_t catch_next_frag = 0;
static u_int32_t saddr, daddr, seq;
static u_int16_t sport, dport;
static struct skb_scan_ret ret_last;
static struct sk_buff *skb_last;
static u_int32_t n_ua_modified = 0, n_ua_modify_faild = 0;
register u_int8_t jump_to_next_function = 0, ret;
register u_int8_t jump_to_next_function = 0;
register struct skb_scan_ret ret;
// 过滤发往外网的HTTP请求的包且要求包的应用层内容不短于3字节
if(skb == 0)
@ -166,21 +362,22 @@ unsigned int hook_funcion(void *priv, struct sk_buff *skb, const struct nf_hook_
data_end = (char *)tcph + ntohs(iph->tot_len) - iph->ihl * 4;
if(data_end - data_start < 4)
return NF_ACCEPT;
if(skb->mark & 0x00000001)
if(skb->mark & 1)
return NF_ACCEPT;
// 决定是否发送到下一层
if(catch_next_frag && iph->saddr == saddr && iph->daddr == daddr &&
if(ret_last.need_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)
if(ret_last.need_next_frag)
{
n_ua_modify_faild++;
char_scan(0);
catch_next_frag = 0;
ret_last.need_next_frag = 0;
ret_last.ua_found = 0;
}
jump_to_next_function = 1;
}
@ -188,7 +385,7 @@ unsigned int hook_funcion(void *priv, struct sk_buff *skb, const struct nf_hook_
return NF_ACCEPT;
// 发送到下一层,并回收数据
ret = skb_scan(data_start, data_end);
ret = skb_scan(data_start, data_end, ret_last.ua_found);
// 处理返回值
if(ret & need_next_frag)