系统:centos 7

准备:安装libnetfilter_queue模块,可以yum安装,也可以网上下载rpm包安装

简介:使用iptables在NAT表上创建DNAT与SNAT规则,对数据包进行转发;在MANGLE表上的FORWARD链上创建NF_QUEUE规则对数据进行勾取并修改;(iptables只有mangle表可以修改数据)

示例规则:

//把到本机 50.24  8889端口的数据包,nat到50.4的8889端口
iptables -t nat -A PREROUTING -p udp -d 192.168.50.24 --dport 8889 -j DNAT --to 192.168.50.4
iptables -t nat -A POSTROUTING -p udp -d 192.168.50.4 --dport 8889 -j SNAT --to 192.168.50.24//把目的地址50.4,目的端口8889的数据包,入队列 1
iptables -t mangle -A FORWARD -d 192.168.50.4 -p udp --dport 8889  -j NFQUEUE --queue-num 1

示例代码:

主线程DoListenIptablesThread负责对QUEUE队列数据的读取,读取到的数据通过回调PacketHandler方法解析处理,传入参数为  queue的 ID号

static void *DoListenIptablesThread(void *pData)
{struct nfq_handle *h;struct nfq_q_handle *qh;struct nfnl_handle *nh;int fd;int rv;int i;pthread_t RecvPth[PthNUM];char buf[QUEUE_BUFSIZE];TCLEANFUNCT struTmp;int nTmpError = -1;int nNum = *(int *)pData;   free(pData);    pthread_detach(pthread_self());memset(&struTmp, 0, sizeof(struTmp));zlog_debug(cat,"opening library handle, nNum[%d]", nNum);h = nfq_open();if (!h){nTmpError = errno;zlog_debug(cat,"error during nfq_open(), nNum[%d]", nNum);zlog_debug(cat,"nfq_open() errno[%d][%s]", nTmpError, strerror(nTmpError));pthread_exit(0);}zlog_debug(cat,"unbinding existing nf_queue handler for AF_INET (if any), nNum[%d]", nNum);if (nfq_unbind_pf(h, AF_INET) < 0){nTmpError = errno;zlog_debug(cat,"error during nfq_unbind_pf(), nNum[%d]", nNum);zlog_debug(cat,"nfq_unbind_pf() errno[%d][%s]", nTmpError, strerror(nTmpError));nfq_close(h);pthread_exit(0);}zlog_debug(cat,"binding nfnetlink_queue as nf_queue handler for AF_INET, nNum[%d]", nNum);if (nfq_bind_pf(h, AF_INET) < 0){nTmpError = errno;zlog_debug(cat,"error during nfq_bind_pf(), nNum[%d]", nNum);zlog_debug(cat,"nfq_bind_pf() errno[%d][%s]", nTmpError, strerror(nTmpError));nfq_close(h);pthread_exit(0);}zlog_debug(cat,"binding this socket to queue [%d]", nNum);qh = nfq_create_queue(h, nNum, &PacketHandler, &nNum);if (!qh){nTmpError = errno;zlog_debug(cat,"error during nfq_create_queue(), nNum[%d]", nNum);zlog_debug(cat,"nfq_create_queue() errno[%d][%s]", nTmpError, strerror(nTmpError));nfq_close(h);pthread_exit(0);}zlog_debug(cat,"setting copy_packet mode, nNum[%d]", nNum);if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0){nTmpError = errno;zlog_debug(cat,"can't set packet_copy mode, nNum[%d]", nNum);zlog_debug(cat,"nfq_set_mode() errno[%d][%s]", nTmpError, strerror(nTmpError));nfq_destroy_queue(qh);nfq_close(h);pthread_exit(0);}nh = nfq_nfnlh(h);fd = nfnl_fd(nh);struTmp.qh = qh;struTmp.h = h;for(i = 0;i<PthNUM;i++){pthread_create(&RecvPth[i], NULL, DoRecvPacketThread,(void*)&struTmp);struTmp.RecvPth[i] = RecvPth[i];}pthread_cleanup_push(FreePorcessResource, (void*)&struTmp);zlog_debug(cat,"Waitting for message ..., nNum[%d]", nNum);while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0){// 开始处理数据//zlog_debug(cat,"-- New packet received -- rv[%d]", rv);
        nfq_handle_packet(h, buf, rv);     memset(buf,0x00,sizeof(buf));}if (rv < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {zlog_debug(cat, "error: [%s], wait for next event.", strerror(errno));} else {// recv error, free conncetion.zlog_error(cat,"recv error: [%s]",strerror(errno));}}pthread_cleanup_pop(0);zlog_error(cat,"-- New packet received -- rv[%d]  fd = [%d]", rv,fd);zlog_debug(cat,"Exit DoNetFilter");
}

static int PacketHandler(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,struct nfq_data *nfa, void *data)
{int id = 0;struct nfqnl_msg_packet_hdr *ph;u_int32_t mark,ifi;struct iphdr *iph;int iphdr_size;int ret;char *nf_packet;unsigned int nAppProto = -1;int nReturnValue = 0;char szHost[30] = {0};ph = nfq_get_msg_packet_hdr(nfa);if (ph){id = ntohl(ph->packet_id);}mark = nfq_get_nfmark(nfa);if (mark){//    DEBUG_LOG("mark=%u ", mark);
    }ifi = nfq_get_indev(nfa);if (ifi){//    DEBUG_LOG("indev=%u ", ifi);
    }ifi = nfq_get_outdev(nfa);if (ifi){//    DEBUG_LOG("outdev=%u ", ifi);
    }ret = nfq_get_payload(nfa, (unsigned char**)&nf_packet);if ((ret >= 0)){//DEBUG_LOG("payload_len=%d bytes", ret);//fputc('\n', stdout);
    }// parse the packet headersiph = ((struct iphdr *) nf_packet);iphdr_size = iph->ihl << 2;if (iph->protocol == TCP_PRO){struct tcphdr *tcp;int tcphdr_size;int clen;tcp = ((struct tcphdr *) (nf_packet + (iph->ihl << 2)));tcphdr_size = (tcp->doff << 2);clen = ret - iphdr_size - tcphdr_size;if(clen > 0){     //在此处修改数据包,修改数据包后执行下面两行代码,重新对数据进行校验,然后通知内核放行修改后的数据包//set_tcp_checksum1(iph);     //return nfq_set_verdict(qh, id, NF_ACCEPT,(u_int32_t)ret, nf_packet);}
    }// if protocol is udpif(iph->protocol == UDP_PRO){int clen;struct udphdr *udp;udp = ((struct udphdr *) (nf_packet + (iph->ihl << 2)));clen = ret - iphdr_size - UDP_HEADER_LEN;if(clen > 0){char* c;PACKETINFO  packinfo;memset(&packinfo,0x00, sizeof(struct PACKETINFO));c = nf_packet + iphdr_size + UDP_HEADER_LEN;      
            Length_dif = strlen(c) -clen;            zlog_debug(cat,"[UDP]Length_dif===> %d  clen ==>[%d]",Length_dif,clen);            iph->tot_len = htons(ntohs(iph->tot_len)+Length_dif);            iph->check = 0;            iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
       //在此处修改数据包,修改数据包后执行下面两行代码,重新对数据进行校验,然后通知内核放行修改后的数据包 //set_udp_checksum1(iph);//return nfq_set_verdict(qh, id, NF_ACCEPT,(u_int32_t)ret, nf_packet);}}return nfq_set_verdict(qh, id, NF_ACCEPT,0, NULL);
}

线程退出时资源释放代码:
主线程DoListenIptablesThread中recv的行为为阻塞,所以强制通过其他方式强制退出时,无法有效关闭并释放资源,通过FreePorcessResource对其资源进行关闭回收,并杀掉其开辟的线程;

void FreePorcessResource(void *pData)
{TCLEANFUNCT *pTmp = NULL;int i;int kill_rc;pTmp = (TCLEANFUNCT *)pData;for(i = 0;i<PthNUM;i++){if(!pTmp->RecvPth[i]) continue;kill_rc = pthread_kill(pTmp->RecvPth[i], 0);if (kill_rc == ESRCH){zlog_debug(cat,"the specified thread did not exists or already quit --- ");}else if (kill_rc == EINVAL){zlog_debug(cat,"signal is invalid --- ");}else{zlog_debug(cat,"the specified thread is alive --- ");// 杀死该线程pthread_cancel(pTmp->RecvPth[i]);//pthread_join(m->second, NULL);usleep(50*1000);// 检测该线程是否存在kill_rc = pthread_kill(pTmp->RecvPth[i], 0);if (kill_rc == ESRCH){zlog_debug(cat,"the specified thread did not exists or already quit +++ ");}else if (kill_rc == EINVAL){zlog_debug(cat,"signal is invalid +++ ");}else{zlog_debug(cat,"signal is alive +++ ");}}}nfq_destroy_queue(pTmp->qh);nfq_close(pTmp->h);zlog_debug(cat,"closing pthread handle\n");
}

主线程DoListenIptablesThread创建的数据读取线程:(多核设备时,内核会通过多核接收数据,单线程recv数据时,系统接收缓存区会由于应用层recv过慢造成缓存区没有足够的空间,所以该处需要多线程recv处理)

static void *DoRecvPacketThread(void *pData){TCLEANFUNCT *pTmp = NULL;int rv;int fd;char buf[QUEUE_BUFSIZE];struct nfnl_handle *nh;pthread_detach(pthread_self());pTmp = (TCLEANFUNCT *)pData;nh = nfq_nfnlh(pTmp->h);fd = nfnl_fd(nh);while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0){// 开始处理数据//zlog_debug(cat,"-- New packet received -- rv[%d]", rv);nfq_handle_packet(pTmp->h, buf, rv);     memset(buf,0x00,sizeof(buf));}if (rv < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {zlog_debug(cat, "error: [%s], wait for next event.", strerror(errno));} else {// recv error, free conncetion.zlog_error(cat,"recv error: [%s]",strerror(errno));}}zlog_error(cat,"-- New packet received -- rv[%d]  fd = [%d]", rv,fd);
}

TCP与UDP数据修改后重新校验实现:

static u_int16_t checksum(u_int32_t init, u_int8_t *addr, size_t count){ /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ u_int32_t sum = init; while( count > 1 ) { /* This is the inner loop */sum += ntohs(* (u_int16_t*) addr);addr += 2;count -= 2;} /* Add left-over byte, if any */if( count > 0 )sum += ntohs(* (u_int8_t*) addr); /* Fold 32-bit sum to 16 bits */ while (sum>>16)sum = (sum & 0xffff) + (sum >> 16); return (u_int16_t)~sum;
}
static u_int16_t tcp_checksum2(struct iphdr* iphdrp, struct tcphdr* tcphdrp){ size_t tcplen = ntohs(iphdrp->tot_len) - (iphdrp->ihl<<2); u_int32_t cksum = 0;cksum += ntohs((iphdrp->saddr >> 16) & 0x0000ffff);cksum += ntohs(iphdrp->saddr & 0x0000ffff);cksum += ntohs((iphdrp->daddr >> 16) & 0x0000ffff);cksum += ntohs(iphdrp->daddr & 0x0000ffff);cksum += iphdrp->protocol & 0x00ff;cksum += tcplen; return checksum(cksum, (u_int8_t*)tcphdrp, tcplen);
} static u_int16_t tcp_checksum1(struct iphdr* iphdrp){ struct tcphdr *tcphdrp = (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2)); return tcp_checksum2(iphdrp, tcphdrp);
}
static void set_tcp_checksum2(struct iphdr* iphdrp, struct tcphdr* tcphdrp){tcphdrp->check = 0;tcphdrp->check = htons(tcp_checksum2(iphdrp, tcphdrp));
}
static void set_tcp_checksum1(struct iphdr* iphdrp){ struct tcphdr *tcphdrp = (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));set_tcp_checksum2(iphdrp, tcphdrp);
}static u_int16_t udp_checksum2(struct iphdr* iphdrp, struct udphdr* udphdrp){ size_t udplen = ntohs(iphdrp->tot_len) - (iphdrp->ihl<<2); u_int32_t cksum = 0;cksum += ntohs((iphdrp->saddr >> 16) & 0x0000ffff);cksum += ntohs(iphdrp->saddr & 0x0000ffff);cksum += ntohs((iphdrp->daddr >> 16) & 0x0000ffff);cksum += ntohs(iphdrp->daddr & 0x0000ffff);cksum += iphdrp->protocol & 0x00ff;cksum += udplen; return checksum(cksum, (u_int8_t*)udphdrp, udplen);
} static u_int16_t udp_checksum1(struct iphdr* iphdrp){ struct udphdr *udphdrp = (struct udphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2)); return udp_checksum2(iphdrp, udphdrp);
}
static void set_udp_checksum2(struct iphdr* iphdrp, struct udphdr* udphdrp){udphdrp->check = 0;udphdrp->check = htons(udp_checksum2(iphdrp, udphdrp));
}
static void set_udp_checksum1(struct iphdr* iphdrp){ struct udphdr *udphdrp = (struct udphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));set_udp_checksum2(iphdrp, udphdrp);
}

ip头部校验

static inline unsigned short ip_fast_csum(unsigned char* iph,unsigned int ihl){unsigned int sum;__asm__ __volatile__("movl (%1), %0 ;\n""subl $4, %2 ;\n""jbe 2f ;\n""addl 4(%1), %0 ;\n""adcl 8(%1), %0 ;\n""adcl 12(%1), %0 ;\n""1: adcl 16(%1), %0 ;\n""lea 4(%1), %1 ;\n""decl %2 ;\n""jne 1b ;\n""adcl $0, %0 ;\n""movl %0, %2 ;\n"   //保存sum的值到%2"shrl $16, %0 ;\n"  //右移16位(读取高16位)到%0"addw %w2, %w0 ;\n" //%0的16位加%2的16位"adcl $0, %0 ;\n"   //若进位加上进位"notl %0 ;\n" //取反"2: ;\n"
/* Since the input registers which are loaded with iph and ihl
are modified, we must also specify them as outputs, or gcc
will assume they contain their original values. */: "=r" (sum), "=r" (iph), "=r" (ihl): "1" (iph), "2" (ihl): "memory");return (sum);
}

程序内部宏定义整理:

#define        MAC_LEN        12
#define        UDP_PRO        17
#define        TCP_PRO        6
#define        VXLAN_HEADER_LEN    8
#define        UDP_HEADER_LEN    8
#define        TCP_HEADER_NO_OPERATION_LEN        20
#define     QUEUE_BUFSIZE    8192
#define     PthNUM            10typedef struct __CleanFunct
{struct nfq_q_handle *qh;struct nfq_handle *h;pthread_t RecvPth[PthNUM];
}TCLEANFUNCT;

程序需要头文件:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <pthread.h>
#include <zlog.h>
#include <assert.h>
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>

代码编译需要链接内容:

-lpthread  -lnfnetlink -lnetfilter_queue

整理不易,转载请注明出处;

转载于:https://www.cnblogs.com/dpf-10/p/7899237.html

使用netfilter_queue改包笔记相关推荐

  1. IOI车机系统刷机和改包笔记

    前言 我自己21年买的雪佛兰探界者SUV,没想到到了23年,车机系统上的高德导航还是3.2版本,而官方的已经7.1. 中间出去玩,在高速上被车机上的高德坑了好多次,简直无力吐槽.单纯使用手机Carpl ...

  2. android工程改包名及多工程设置

    android工程更改包名: 1. 先改manifest中的包名 2. src目录上点右键,选择refactor->rename,改包名,然后选择continue. 3. 改了包名之后,要注意包 ...

  3. android改包名

    改包名方法: 打开android studio app-java-com.XXXX.XXX  右键 refactor-rename-显示最后一个单词,改掉这个词,refactor 底栏出现 提示一系列 ...

  4. Fiddler数据抓包试玩改包工具使用畅享

    Fiddler数据抓包改包工具使用畅享 简介:Fiddler(中文名称:小提琴)是一个HTTP的调试代理,以代理服务器的方式,监听系统的Http网络数据流动,Fiddler可以也可以让你检查所有的HT ...

  5. Bursuite简单抓包改包发包__超详细步骤

    burpsuite抓http请求包, 第一步,设置浏览器代理.设置代理是为了把浏览器的请求代理到某个端口,burpsuite拦截该端口的包,解析后放入软件中 我用chrome,其他浏览器类似 打开系统 ...

  6. burp放包_Burp Suite截断改包发包获取Webshell

    本帖最后由 wangxiulin 于 2016-11-17 12:11 编辑 难易程度  ★★★ 阅读点   截断发包方式 文章作者  wangxiulin 文章来源  i春秋论坛 前言 今天就是先来 ...

  7. Linux (centos) 安装anyproxy https手机设备抓包笔记

    Linux (centos) 安装anyproxy https手机设备抓包笔记  安装nodejs curl -sL https://rpm.nodesource.com/setup_14.x | b ...

  8. Burpsuite --- 抓包,截包,改包

    之前的博客有简单的介绍一下Burpsuit,今天介绍一下如何使用Burpsuite进行抓包,截包,改包.我这里是在Kali系统下测试的. 第一步:首先设置浏览器的代理,之前的博客有讲到,这里不再讲了. ...

  9. 使用burpsuite抓包和改包

    第一次使用到这个工具,是在上web安全课的时候,老师让我们进行CTF实验,采用burpsuite进行抓包改包,才发现这个工具的强大. 1 burpsuite工具下载 官网链接:https://port ...

最新文章

  1. FastCGI模块(FastCGI)
  2. 疯狂java学习笔记1023---线程的同步
  3. openmp与openmpi区别
  4. Spring容器创建流程——总结
  5. Nagios 监控windows性能计数器
  6. 后端用java还是python_【后端开发】已学php再学java还是python?
  7. hdu 1087 Super Jumping! Jumping! Jumping! 解题报告
  8. 游戏开发中的数学和物理算法(18):缩放
  9. import sys是什么意思_学了半天,import 到底在干啥?
  10. 50条培养内心强大的励志语录
  11. 简单典型二阶系统_【文献选译】二阶弹性波动方程PML的简单实现
  12. 【已解决】script标签里提示Emmet Abbreviation问题(Html和CSS的语法)
  13. 深度学习之神经网络基础(1)——感知器,S型神经元,梯度下降法,神经网络架构
  14. java hypot_java – 为什么hypot()函数这么慢?
  15. 二进制十进制十六进制转换_二进制数制到十进制数制的转换
  16. CF Round231C (ZWJ 神构造)
  17. 某个版本的JavaSE (unbound)的问题解决
  18. VR全景拍摄的具体张数
  19. 评自主创新与自主实现
  20. PL/SQL Developer x64 官网下载、中文包、注册码

热门文章

  1. 麦芒8能安装鸿蒙系统吗,首颗5G基带芯片麦芒8发布鸿蒙最新进展
  2. noi2017初赛c语言试题,NOIP2017普及组初赛试题及答案
  3. python中关键字参数含义_python中接受任意关键字的参数
  4. 【Java核心面试宝典(1),程序员Javaweb源码
  5. 【深度学习笔记】ROC曲线 vs Precision-Recall曲线
  6. Linux 查看交换区内容,Unix: Unix/Linux/Win的虚拟交换区信息的查看
  7. python最终目标是什么_Python之父Guido谈Python的未来
  8. 在网络推广外包中企业网站排名优化在网络推广外包中如何实现?
  9. 如何设计网站导航更利于SEO优化?
  10. python pycurl_简单谈谈Python的pycurl模块_python