利用iptabls的NFLOG记录自己的HTTP HTTPS上网行为

想要记录上网行为(一天都访问了什么网站)势必要解析TCP数据包,第一种方法是自己写一个内核模块来做这件事,但调内核模块一崩溃系统就要重启,自己的真机上不方便调试。第二种方法是借助iptables的日志功能把数据包发到应用层来解析。

iptables有几个日志记录的target(LOG, ULOG, NFLOG), LOG只是简单的记录包头信息到系统日志,而ULOG, NFLOG则可以把整个数据包发到应用层,应用层可以通过解析数据包来记录自己想要的信息。

大致的过程是这样:内核数据包走到iptables的钩子点上,如果数据包和规则匹配,就通过netlink把这个数据包多播到多播组,然后应用层通过netlink来接受这个数据包。

可以通过iptables -j ULOG --help查看帮助信息,但自己添加iptables规则时(如:sudo iptables -A OUTPUT -j ULOG)发现提示:
iptables: No chain/target/match by that name.

使用命令cat /boot/config-$(uname -r) | grep ULOG得知内核没有编译ULOG进去,但是NFLOG是支持的。于是下面的程序使用NFLOG实现。

数据记录到文件,以后需要时可以再做分析:

1.tail /home/website_access_stat.txt
2.p1.music.126.net, 2017-07-19 22:17, 1500473873
3.p1.music.126.net, 2017-07-19 22:17, 1500473873
4.highlightjs.org, 2017-07-19 22:17, 1500473875
5.music.163.com, 2017-07-19 22:17, 1500473876
6.music.163.com, 2017-07-19 22:17, 1500473877
7.music.163.com, 2017-07-19 22:17, 1500473877
8.music.163.com, 2017-07-19 22:17, 1500473877
9.m8.music.126.net, 2017-07-19 22:17, 1500473877
10.music.163.com, 2017-07-19 22:17, 1500473877
11.www.gstatic.com, 2017-07-19 22:17, 1500473877

实现的代码:

1./************************************************************
2.Copyright (C), 2017, Leon, All Rights Reserved.
3.FileName: http_trace.c
4.Description: 跟踪自己的http,https请求
5.Author: Leon
6.Version: 1.0
7.Date: 2017-7-19 13:47:04
8.Function:
9.
10.History:
11.<author>    <time>  <version>   <description>
12. Leon
13.
14. 安装依赖库:
15. apt install libnetfilter-log-dev libnfnetlink-dev
16.
17. 编译方法:
18. gcc http_trace.c -lnetfilter_log
19.
20. 需要以sudo权限运行
21. sudo iptables -I OUTPUT -o enp8s0 -p tcp -j NFLOG --nflog-group 1
22. sudo ./a.out &
23.
24. 统计方法:
25. cat /home/website_access_stat.txt | awk -F "," '{stat[$1]++} END {for(cmd in stat) print stat[cmd] " " cmd}' | sort -nr
26. ************************************************************/
27.
28.#include <stdio.h>
29.#include <stdlib.h>
30.#include <string.h>
31.#include <sys/socket.h>
32.#include <arpa/inet.h>
33.#include <sys/types.h>
34.#include <linux/netlink.h>
35.#include <sys/select.h>
36.#include <sys/time.h>
37.#include <unistd.h>
38.#include <time.h>
39.
40.#include <libnetfilter_log/libnetfilter_log.h>
41.
42.#include <linux/ip.h>
43.#include <linux/tcp.h>
44.#include <linux/in.h>
45.
46.#include "https.h"
47.
48.#define LISTEN_GROUP 1
49.#define BUFF_SIZE 4096
50.
51.#define DATA_FILE "/home/website_access_stat.txt"
52.
53.#define lprintf(format, argv...) printf("%s(%d): " format , __FUNCTION__, __LINE__, ##argv)
54.
55.static char host_data[BUFF_SIZE] = {0};
56.static int data_idx = 0;    /* 指向host_data的当前存储位置 */
57.
58.void print_pkt(unsigned char *pkt, int len, char *title)
59.{
60.    char hex[64] = {0};
61.    char assic[64] = {0};
62.    char *p_hex = hex, *p_assic = assic;
63.    int i = 0;
64.
65.    printf("*******************%s start*****************\n", title);
66.    while(len--)
67.    {
68.        if(i != 0 && !(i%16))
69.        {
70.            printf("%s %s\n", hex, assic);
71.            memset(hex, 0x0, sizeof(hex));
72.            memset(assic, 0x0, sizeof(assic));
73.            p_hex = hex;
74.            p_assic = assic;
75.            i = 0;
76.        }
77.        sprintf(p_hex, "%02x ", *pkt);
78.        p_hex += 3;
79.        sprintf(p_assic, "%c", *pkt < 32 || *pkt > 127? '.' : *pkt);
80.        p_assic += 1;
81.        i++;
82.        pkt++;
83.        if(!(i%8))
84.        {
85.            sprintf(p_hex, "  ");
86.            p_hex += 2;
87.            sprintf(p_assic, "  ");
88.            p_assic += 2;
89.        }
90.    }
91.    printf("%s %s\n", hex, assic);
92.    printf("\n\n*******************%s end*****************\n", title);
93.}
94.
95.void save_to_file(void)
96.{
97.    int len = data_idx;
98.    int write_len = 0;
99.
100.    if(!data_idx)
101.        return;
102.
103.    FILE *fp = fopen(DATA_FILE, "a+");
104.    if(!fp)
105.    {
106.        perror("fopen");
107.        return;
108.    }
109.    write_len = fwrite(host_data, len, 1, fp);
110.
111.    fclose(fp);
112.    data_idx = 0;
113.}
114.
115.void save_server_name(char *server_name)
116.{
117.    time_t sec = time(0);
118.    struct tm now;
119.    char buf[1024] = {0};
120.    int ret = 0;
121.
122.    localtime_r(&sec, &now);
123.    now.tm_year += 1900;
124.    now.tm_mon += 1;
125.
126.    ret = snprintf(buf, sizeof(buf) - 1, "%s, %d-%02d-%02d %02d:%02d, %u\n",
127.            server_name, now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour,
128.            now.tm_min, (unsigned int)sec);
129.
130.    if(ret + data_idx > BUFF_SIZE)
131.    {
132.        save_to_file();
133.    }
134.
135.    strcpy(host_data + data_idx, buf);
136.    data_idx += ret;
137.}
138.
139.int find_server_name(__u8 *edata, int data_len, char *server_name, int sn_len)
140.{
141.    __u8 *p = edata;
142.    __u16 type;
143.    __u16 len;
144.
145.    while(p < edata + data_len)
146.    {
147.        type = ntohs(*(__u16 *)p);
148.        p += 2;
149.        len = ntohs(*(__u16 *)p);
150.        p += 2;
151.
152.        if(type == SERVER_NAME)
153.        {
154.            p += 3; /* 跳过server name list length和server name type字段 */
155.            len = ntohs(*(__u16 *)p);
156.            p += 2;
157.            if(len > sn_len)
158.            {
159.                memcpy(server_name, p, sn_len);
160.                server_name[sn_len - 1] = '\0';
161.            }
162.            else
163.            {
164.                memcpy(server_name, p, len);
165.                server_name[len] = '\0';
166.            }
167.            return 1;
168.        }
169.        p += len;
170.    }
171.
172.    return 0;
173.}
174.
175.
176.#define POINT_OVERFLOW_CHECK(point, limit) do{\
177.        if((point) > (limit))   \
178.            goto point_overflow;    \
179.    }while(0)
180.
181.void https_handle(struct iphdr *iph)
182.{
183.    struct tcphdr *tcph = (void*)iph + iph->ihl*4;
184.    __u8 *tcpdata = (void*)tcph + tcph->doff*4;
185.    int len = ntohs(iph->tot_len) - (iph->ihl*4) - (tcph->doff*4);
186.
187.    struct https_head *https = (struct https_head *)tcpdata;
188.    __u8 *p = tcpdata;
189.    __u16 tmp_len;
190.    __u8 *point_limit = tcpdata + len;
191.    char server_name[HOSTNAME_MAX_LEN] = {0};
192.
193.    if(len < sizeof(struct https_head))
194.        return;
195.
196.    if(https->content_type != HANDSHAKE)
197.        return;
198.
199.    if(https->handshake_type != CLIENT_HELLO)
200.        return;
201.
202.    /* TLS1.0开始支持server name字段 */
203.    if(https->version < htons(TLS1_0_VERSION)
204.        && https->version2 < htons(TLS1_0_VERSION))
205.        return;
206.
207.    p = &(https->session_id_length);
208.
209.    /* 跳过session_id_length字段 */
210.    tmp_len = *p;
211.    p++;
212.    p += tmp_len;
213.    POINT_OVERFLOW_CHECK(p, point_limit);
214.
215.    /* 跳过cipher_suites_length字段 */
216.    tmp_len = ntohs(*(__u16 *)p);
217.    p += 2;
218.    p += tmp_len;
219.    POINT_OVERFLOW_CHECK(p, point_limit);
220.
221.    /* 跳过compression_methonds_length字段 */
222.    tmp_len = *p;
223.    p++;
224.    p += tmp_len;
225.    POINT_OVERFLOW_CHECK(p, point_limit);
226.
227.    /* 现在为扩展字段 */
228.    tmp_len = ntohs(*(__u16 *)p);
229.    p += 2;
230.
231.    if(0 == find_server_name(p, tmp_len, server_name, sizeof(server_name)))
232.    {
233.        return;
234.    }
235.    else
236.    {
237.        save_server_name(server_name);
238.        return;
239.    }
240.
241.point_overflow:
242.    lprintf("point overflow\n");
243.    return;
244.
245.}
246.
247./* 不区分大小写的strstr */
248.char *strncasestr(char *str, char *sub)
249.{
250.    if(!str || !sub)
251.        return NULL;
252.
253.    int len = strlen(sub);
254.    if (len == 0)
255.    {
256.        return NULL;
257.    }
258.
259.    while (*str)
260.    {
261.        if (strncasecmp(str, sub, len) == 0)
262.        {
263.            return str;
264.        }
265.        ++str;
266.    }
267.    return NULL;
268.}
269.
270./* 从字符串缓冲区读取一行数据(跳过空行),返回下一个字符串的开始位置 */
271.char *readline_from_buf(char *from, int from_len, char *to, int to_len)
272.{
273.    int i = 0;
274.
275.    memset(to, 0x0, to_len);
276.
277.    while(*from == '\r' || *from == '\n')
278.    {
279.        from++;
280.        from_len--;
281.    }
282.
283.    for(i = 0; i < to_len && i < from_len; i++)
284.    {
285.        if(from[i] == '\r' || from[i] == '\n')
286.        {
287.            memcpy(to, from, i);
288.            return from + i;
289.        }
290.    }
291.    return NULL;
292.}
293.
294.void http_handle(struct iphdr *iph)
295.{
296.    struct tcphdr *tcph = (void*)iph + iph->ihl*4;
297.    __u8 *tcpdata = (void*)tcph + tcph->doff*4;
298.    int len = ntohs(iph->tot_len) - (iph->ihl*4) - (tcph->doff*4);
299.    char line[1024];
300.    char *p = NULL;
301.
302.    if(len && tcpdata[0] != 'G' && tcpdata[0] != 'P')
303.        return;
304.
305.    while(tcpdata = readline_from_buf(tcpdata, len, line, sizeof(line)))
306.    {
307.        //printf("%s\n", line);
308.        if(strncasestr(line, "host:"))
309.        {
310.            p = (char *)line + strlen("host:");
311.            while(*p == ' ')
312.                p++;
313.            save_server_name(p);
314.            break;
315.        }
316.    }
317.}
318.
319.
320.static int cb(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg,
321.        struct nflog_data *nfa, void *data)
322.{
323.    struct nfulnl_msg_packet_hdr *ph = nflog_get_msg_packet_hdr(nfa);
324.    u_int32_t mark = nflog_get_nfmark(nfa);
325.    u_int32_t indev = nflog_get_indev(nfa);
326.    u_int32_t outdev = nflog_get_outdev(nfa);
327.    char *prefix = nflog_get_prefix(nfa);
328.    char *payload;
329.    int payload_len = nflog_get_payload(nfa, &payload);
330.    struct iphdr *iph = (struct iphdr *)payload;
331.
332.    struct tcphdr *tcph;
333.
334.    if (iph->protocol != IPPROTO_TCP) /* not TCP */
335.        return 0;
336.
337.    tcph = (void*)iph + iph->ihl*4;
338.
339.    if(tcph->dest == htons(443))
340.    {
341.        https_handle(iph);
342.        return 0;
343.    }
344.
345.    if(tcph->dest == htons(80))
346.    {
347.        http_handle(iph);
348.        return 0;
349.    }
350.
351.    return 0;
352.}
353.
354.int main(int argc, char *argv[])
355.{
356.    char buf[BUFF_SIZE] = {0};
357.    int len = 0;
358.    struct timeval tv = {10, 0};
359.    int ret = 0;
360.    fd_set rfds;
361.    struct nflog_handle *h;
362.    struct nflog_g_handle *gh;
363.    int fd;
364.
365.    h = nflog_open();
366.    if (!h) {
367.        fprintf(stderr, "error during nflog_open()\n");
368.        exit(1);
369.    }
370.
371.    if (nflog_bind_pf(h, AF_INET) < 0) {
372.        fprintf(stderr, "error during nflog_bind_pf()\n");
373.        exit(1);
374.    }
375.
376.    gh = nflog_bind_group(h, 1);
377.    if (!gh) {
378.        fprintf(stderr, "no handle for grup 1\n");
379.        exit(1);
380.    }
381.
382.    if (nflog_set_mode(gh, NFULNL_COPY_PACKET, 0xffff) < 0) {
383.        fprintf(stderr, "can't set packet copy mode\n");
384.        exit(1);
385.    }
386.
387.    fd = nflog_fd(h);
388.
389.    nflog_callback_register(gh, &cb, NULL);
390.
391.    while(1)
392.    {
393.        tv.tv_sec = 5;
394.        tv.tv_usec = 0;
395.        FD_ZERO(&rfds);
396.        FD_SET(fd, &rfds);
397.
398.        ret = select(fd + 1, &rfds, NULL, NULL, &tv);
399.        if(ret > 0)
400.        {
401.            len = recv(fd, buf, sizeof(buf), 0);
402.            if(len)
403.            {
404.                nflog_handle_packet(h, buf, len);
405.            }
406.        }
407.        else if(ret == 0)
408.        {
409.            save_to_file();
410.        }
411.    }
412.
413.    return 0;
414.}

还可以通过sting模块直接匹配字符串,然后再传到应用层处理,这样可以避免传递太多的数据包到应用层
iptables -m string –help

一些有帮助的网页:
ip6tables中nflog的使用

github中保存位置:
https://github.com/leon0625/practice/tree/master/http_trace

利用iptabls的NFLOG记录自己的HTTP HTTPS上网行为相关推荐

  1. 搜索2.0:利用用户点击记录改善搜索结果

    /*版权声明:可以任意转载,转载时请务必标明文章原始出处和作者信息 .*/ 搜索2.0:利用用户点击记录改善搜索结果 Author:张俊林 timestamp:2007年7月 现在是WEB2.0时代了 ...

  2. QuickWAP V1.5利用ASP读取Access记录集一例

    QuickWAP V1.5利用ASP读取Access记录集一例 很多WAP开发人员除了掌握了QuickWAP V1.5提供的MdbList.MdbListPages等函数外,仍然感觉QuickWAP ...

  3. 利用 nslookup 解析 DNS 记录

    利用 nslookup 解析 DNS 记录 nslookup 是一个域名解析工具,在进行一些网页无法打开的问题上,能帮助我们进行更全面理解问题的所在! 0x01.直接查询 nslookup 域名 注意 ...

  4. 利用XSLT把ADO记录集转换成XML

    由于XML(可扩展标记语言:eXtensible Markup Language)真正的平台无关性,它正在逐渐成为数据传输的主要介质.XML是一种自描述的语言,数据本身就已经包含了元数据,即关于数据本 ...

  5. java记录访问次数_Java 利用监听器来实现记录用户访问网站次数(示例代码)

    假如有这么一个需求,要记录所有用户访问某一页面的次数. 最先想到的可能是在该Controller定义一个静态成员,然后在相应Action里自增.但这样有一个问题,就是Tomcat或者其他服务器重启的话 ...

  6. linux prompt模式,Linux利用PROMPT_COMMAND实现操作记录的功能

    Linux中的PROMPT_COMMAND会记录下出现提示符前面的命令,利用这个特性可以实现记录所有用户的操作记录. root用户身份下,进行以下操作 vi /etc/profile #在最后一行追加 ...

  7. 利用winston和morgan记录express日志信息

    最近忙着做毕业设计,项目是一个多用户博客程序.后端技术栈为express+mongodb,为了能够更为详细的记录程序运行日志,便开始研究如何使用winston和morgan.项目代码已托管到GitHu ...

  8. GridView利用FootTemplate插入新记录

    首先拖一个GridView到页面,然后把非自己生成的项转化为TemplateItemField的,然后在每一项的FootTemplate处拖上相应的填写新数据的控件,在Foot的合适位置放一块Butt ...

  9. java https post get请求_JAVA利用HttpClient进行POST和GET请求(HTTPS)

    目前,要为另一个项目提供接口,接口是用HTTP URL实现的,最初的想法是另一个项目用JQuery post进行请求. 但是,很可能另一个项目是部署在别的机器上,那么就存在跨域问题,而JQuery的p ...

最新文章

  1. libuv 中文编程指南(零)前言
  2. 嵌入式Linux C笔试题积累(转)
  3. 阿里新晋 CNCF TOC 委员张磊:“云原生”为什么对云计算生态充满吸引力?
  4. 大咖白话 Serverless 训练营,限时报名开启!
  5. XML Tree(树形结构)
  6. sp_executesql介绍和使用
  7. openmp并行编程_OpenMP实现生产者消费者问题
  8. CentOS 5.9 yum安装LAMP(Apache+MySQL+PHP)
  9. mongodb和SQL语句对应查找表
  10. 程序员熬夜写代码,用C/C++打造一个安全的即时聊天系统
  11. VS2010编程助手
  12. 关于大麦网接口抢票构造的一些思路
  13. conda 速度慢 解决方案
  14. Unity3D怪物基本AI
  15. 美国人口与种族变迁史
  16. 页面各手机屏幕的尺寸
  17. 为什么用线程池?解释下线程池参数?
  18. 2.5 C语言入职例程二:指针
  19. 02-CSS基础知识梳理(I)
  20. P1873 砍树(二分查找模板)

热门文章

  1. 接收 go run main.go 后面的参数_厦门油泥水三相离心分离机功能和参数
  2. php 日期算法,php日期时间计算,转载
  3. 如何在ps中调整文字的行距和间距_Wps如何调整文字字符的间距
  4. verilog coding style_韩嫕:坚持coding
  5. unity android 版本,Unity2019与Android混合开发
  6. Codevs 3342 绿色通道
  7. 如何在rul中添加图片
  8. 每隔一段时间自动执行一次某个方法(使用线程)[C#]
  9. 很口语I'll be back
  10. SQL Server 2005导入导出存储过程