linux下通过伪造udp包来实现指定网卡发送数据
Posted 17岁boy想当攻城狮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux下通过伪造udp包来实现指定网卡发送数据相关的知识,希望对你有一定的参考价值。
1.包含头文件
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#include <net/if.h>
2.设置src和dest,同时定义一个全局的socket,我们以单列模式为列子
#define DEST_IP "192.168.1.53"
#define DEST_PORT 996
#define SRC_IP "192.168.1.56"
#define SRC_PORT 998
#define TEST_MESSAGE "This test udp message!"
#define NETWORK_CAP "ens33"
unsigned int m_OutSocket;
struct sockaddr_in m_SocketOut;
DEST:目的IP,要发送到哪个ip上
SRC:本机IP,也就是本机ip,这个决定了选择哪个ip发送包,哪个网卡绑定了这个ip就使用哪个网卡
3.设置初始化函数
void init_udp_socket(){
}
3.1 首先初始化socket
m_OutSocket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
AF_INET:ipv4的通讯协议
sock_raw:接收tcp udp icmp sctp协议,同时不收本机发出去的数据包的回应,其次是不会收不属于本IP的包
ipproto_udp:udp协议
3.2 设置socket属性为自己构造hand,告诉socket不要给我们构造hand头
bool flag = true;
setsockopt(m_OutSocket, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag));
IPPROTO_IP:ipv4协议
IP_HDRINCL:用户需要自己构造协议包头,这里用户可以自己指定选择使用哪个IP,若不使用这个协议,socket会自动选择一个ip使用
3.3 设置send延迟时间
需要注意linux下的send时间参数是一个结构体
struct timeval nTimeOver={10,0};
setsockopt(m_OutSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver,sizeof(struct timeval));
3.4 指定当前socket的dest ip与dest port
其实可以不指定,但是不指定不行,因为我们需要自己构造hand,如果这一步不指定则无法构造dest ip和prot这是socket发送包的协议规范
我们使用了SOCK_RAW协议,所以不会收到反馈的包,这个只是为了符合socket的规范
m_SocketOut.sin_family = AF_INET;
m_SocketOut.sin_addr.s_addr = inet_addr(DEST_IP);
m_SocketOut.sin_port = (u_short)DEST_PORT;
3.5 绑定网卡
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, NETWORK_CAP, strlen(NETWORK_CAP));
setsockopt(m_OutSocket, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr));
到这一步初始化函数就写完了
接下来我们写发送函数
发送函数比较复杂,因为我们要构造udp的hand包
4. 编写发送函数
函数原型
void send(const char* buff,int length){
if( buff == NULL){
return;
}
}
在开始之前我们在外面定义IP,UDP的hand包:
IP:
typedef struct stuIPHEADER
{
unsigned char h_lenver; // 8bit 4bit版本 + 4bit首部长度 (h_lenver &0xf) * 4
unsigned char tos; // 8bit 服务类型
unsigned short total_len; // 16bit 总长度(字节数)
unsigned short ident; // 16bit 标识
unsigned short frag_and_flags; // 16bit 3bit标志 + 13bit片偏移
unsigned char ttl; // 8bit 生存时间(TTL)
unsigned char proto; // 8bit 上层协议
unsigned short checksum; // 16bit 检验和
unsigned int sourceIP; // 32bit 源IP地址
unsigned int destIP; // 32bit 目的IP地址
}IPHEADER, *LPIPHEADER;
UDP:
typedef struct _UDP_HEADER {
unsigned short nSourPort ; // 源端口号
unsigned short nDestPort ; // 目的端口号
unsigned short nLength ; // 数据包长度
unsigned short nCheckSum ; // 校验和
} UDP_HEADER, *PUDP_HEADER ;
其次还有PSD的:
typedef struct _PSD_HEADER{
unsigned long saddr; //源IP地址
unsigned long daddr; //目的IP地址
char mbz; //置空(0)
char ptcl; //协议类型
unsigned short plen; //TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)
} UDP_PSDHEADER,*PUDP_PSDHEADER ;
然后在定义一个发送体
#define MAX_UDP_DATA_LEN 65536
char SendBuf[MAX_UDP_DATA_LEN];
然后构造在函数体里定义出来,并构造它
IPHEADER ipHeader;
memset(&ipHeader,0,sizeof(IPHEADER));
构造代码:
void ConstructIPHeader(IPHEADER *pIpHeader, int dataLength)
{
pIpHeader->h_lenver = 0x45; //ip v4
pIpHeader->tos = 0;
pIpHeader->total_len = htons(dataLength);
pIpHeader->ident = htons(rand());
pIpHeader->frag_and_flags = 0;
pIpHeader->ttl = 128;
pIpHeader->proto = IPPROTO_UDP;
pIpHeader->checksum = 0;
pIpHeader->sourceIP = inet_addr(SRC_IP);
pIpHeader->destIP = inet_addr(DEST_IP);
}
构造UDP包:
UDP_HEADER udpHeader;
memset(&udpHeader,0,sizeof(UDP_HEADER));
构造代码:
void ConstructUdpHeader(UDP_HEADER *pUdpHeader, int dataLength)
{
pUdpHeader->nSourPort = htons((unsigned short)(SRC_PORT));
pUdpHeader->nDestPort = htons((unsigned short)(DEST_PORT));
pUdpHeader->nLength = htons(sizeof(UDP_HEADER) + dataLength);
pUdpHeader->nCheckSum = 0;
}
初始化发送体,并把hand、buff copy进去
memset(SendBuf, 0, MAX_UDP_DATA_LEN);
memcpy(SendBuf, &ipHeader, sizeof(IPHEADER));
memcpy(SendBuf+sizeof(IPHEADER), &udpHeader, sizeof(UDP_HEADER));
memcpy(SendBuf+sizeof(IPHEADER)+sizeof(UDP_HEADER), buff, length);
最后构造报文
这段报文代码非本人原创,是由socket编程开源里构造udp报文代码里直接取得。
构造报文代码:
unsigned short CalculateChecksum(char *buffer1, int len1, char *buffer2, int len2)
{
unsigned long checksum=0;
unsigned short* buffer;
int i=0;
buffer = (unsigned short*) buffer1;
for (i=0; i<int(len1/sizeof(unsigned short)); i++)
checksum += buffer[i];
buffer = (unsigned short*) buffer2;
for (i=0; i<int(len2/sizeof(unsigned short)); i++)
checksum += buffer[i];
if ((len2 & 0x1) != 0)
checksum += (unsigned char) buffer2[len2-1];
checksum = (checksum >> 16) + (checksum & 0xffff);
checksum += (checksum >>16);
return (unsigned short)(~checksum);
}
然后在基于报文函数写IP报文与UDP报文
构造IP报文函数:
void FinalIPHeader(char *pIpAndDataBuffer, int length)
{
IPHEADER* pIpHeader = (IPHEADER*) pIpAndDataBuffer;
char* pDataBuffer = pIpAndDataBuffer + sizeof(IPHEADER);
int dataLen = length - sizeof(IPHEADER);
pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER), pDataBuffer, dataLen);
pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER), pDataBuffer, 0);
}
构造UDP报文函数:
void CRawSocket::FinalUdpHeader(char *pUdpAndDataBuffer, int length)
{
UDP_PSDHEADER UDP_PSD_HEADER;
memset(&UDP_PSD_HEADER,0,sizeof(UDP_PSDHEADER));
UDP_HEADER* pUdpHeader = (UDP_HEADER*) pUdpAndDataBuffer;
char* pDataBuffer = pUdpAndDataBuffer + sizeof(UDP_HEADER);
int dataLen = length - sizeof(UDP_HEADER);
UDP_PSD_HEADER.saddr = inet_addr(SRC_IP);
UDP_PSD_HEADER.daddr = inet_addr(DEST_IP);
UDP_PSD_HEADER.mbz = 0;
UDP_PSD_HEADER.ptcl = IPPROTO_UDP;
UDP_PSD_HEADER.plen = ::htons(length);
pUdpHeader->nCheckSum = CalculateChecksum((char*) &UDP_PSD_HEADER,
sizeof(UDP_PSD_HEADER), pUdpAndDataBuffer, length);
}
然后我们构造一下:
FinalIPHeader(SendBuf,length+sizeof(IPHEADER)+sizeof(UDP_HEADER));
FinalUdpHeader(SendBuf+sizeof(IPHEADER),length+sizeof(UDP_HEADER));
然后我们就可以调用send_to来为我们发送,因为send_to不会构造hand包,所以这里一定要用send_to
sendto(m_OutSocket,SendBuf,length+sizeof(IPHEADER)+sizeof(UDP_HEADER), 0,(struct sockaddr*) &m_SocketOut,sizeof(struct sockaddr_in));
至此就结束了。
完整代码+demo:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#include <net/if.h>
#define DEST_IP "192.168.1.53"
#define DEST_PORT 996
#define SRC_IP "192.168.1.56"
#define SRC_PORT 998
#define TEST_MESSAGE "This test udp message!"
#define NETWORK_CAP "ens33"
unsigned int m_OutSocket;
struct sockaddr_in m_SocketOut;
typedef struct stuIPHEADER
{
unsigned char h_lenver; // 8bit 4bit版本 + 4bit首部长度
// (h_lenver &0xf) * 4
unsigned char tos; // 8bit 服务类型
unsigned short total_len; // 16bit 总长度(字节数)
unsigned short ident; // 16bit 标识
unsigned short frag_and_flags; // 16bit 3bit标志 + 13bit片偏移
unsigned char ttl; // 8bit 生存时间(TTL)
unsigned char proto; // 8bit 上层协议
unsigned short checksum; // 16bit 检验和
unsigned int sourceIP; // 32bit 源IP地址
unsigned int destIP; // 32bit 目的IP地址
}IPHEADER, *LPIPHEADER;
typedef struct _UDP_HEADER {
unsigned short nSourPort ; // 源端口号
unsigned short nDestPort ; // 目的端口号
unsigned short nLength ; // 数据包长度
unsigned short nCheckSum ; // 校验和
} UDP_HEADER, *PUDP_HEADER ;
typedef struct _PSD_HEADER{
unsigned long saddr; //源IP地址
unsigned long daddr; //目的IP地址
char mbz; //置空(0)
char ptcl; //协议类型
unsigned short plen; //TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)
} UDP_PSDHEADER,*PUDP_PSDHEADER ;
#define MAX_UDP_DATA_LEN 65536
char SendBuf[MAX_UDP_DATA_LEN];
void init_udp_socket(){
m_OutSocket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
bool flag = true;
setsockopt(m_OutSocket, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag));
struct timeval nTimeOver={10,0};
setsockopt(m_OutSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver,sizeof(struct timeval));
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, NETWORK_CAP, strlen(NETWORK_CAP));
setsockopt(m_OutSocket, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr));
m_SocketOut.sin_family = AF_INET;
m_SocketOut.sin_addr.s_addr = inet_addr(DEST_IP);
m_SocketOut.sin_port = (u_short)DEST_PORT;
}
unsigned short CalculateChecksum(char *buffer1, int len1, char *buffer2, int len2)
{
unsigned long checksum=0;
unsigned short* buffer;
int i=0;
buffer = (unsigned short*) buffer1;
for (i=0; i<int(len1/sizeof(unsigned short)); i++)
checksum += buffer[i];
buffer = (unsigned short*) buffer2;
for (i=0; i<int(len2/sizeof(unsigned short)); i++)
checksum += buffer[i];
if ((len2 & 0x1) != 0)
checksum += (unsigned char) buffer2[len2-1];
checksum = (checksum >> 16) + (checksum & 0xffff);
checksum += (checksum >>16);
return (unsigned short)(~checksum);
}
void FinalIPHeader(char *pIpAndDataBuffer, int length)
{
IPHEADER* pIpHeader = (IPHEADER*) pIpAndDataBuffer;
char* pDataBuffer = pIpAndDataBuffer + sizeof(IPHEADER);
int dataLen = length - sizeof(IPHEADER);
pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER), pDataBuffer, dataLen);
pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER), pDataBuffer, 0);
}
void FinalUdpHeader(char *pUdpAndDataBuffer, int length)
{
UDP_PSDHEADER UDP_PSD_HEADER;
memset(&UDP_PSD_HEADER,0,sizeof(UDP_PSDHEADER));
UDP_HEADER* pUdpHeader = (UDP_HEADER*) pUdpAndDataBuffer;
char* pDataBuffer = pUdpAndDataBuffer + sizeof(UDP_HEADER);
int dataLen = length - sizeof(UDP_HEADER);
UDP_PSD_HEADER.saddr = inet_addr(SRC_IP);
UDP_PSD_HEADER.daddr = inet_addr(DEST_IP);
UDP_PSD_HEADER.mbz = 0;
UDP_PSD_HEADER.ptcl = IPPROTO_UDP;
UDP_PSD_HEADER.plen = htons(length);
pUdpHeader->nCheckSum = CalculateChecksum((char*) &UDP_PSD_HEADER, sizeof(UDP_PSD_HEADER), pUdpAndDataBuffer, length);
}
void ConstructUdpHeader(UDP_HEADER *pUdpHeader, int dataLength)
{
pUdpHeader->nSourPort = htons((unsigned short)(SRC_PORT));
pUdpHeader->nDestPort = htons((unsigned short)(DEST_PORT));
pUdpHeader->nLength = htons(sizeof(UDP_HEADER) + dataLength);
pUdpHeader->nCheckSum = 0;
}
void ConstructIPHeader(IPHEADER *pIpHeader, int dataLength)
{
pIpHeader->h_lenver = 0x45; //ip v4
pIpHeader->tos = 0;
pIpHeader->total_len = htons(dataLength);
pIpHeader->ident = htons(rand());
pIpHeader->frag_and_flags = 0;
pIpHeader->ttl = 128;
pIpHeader->proto = IPPROTO_UDP;
pIpHeader->checksum = 0;
pIpHeader->sourceIP = inet_addr(SRC_IP);
pIpHeader->destIP = inet_addr(DEST_IP);
}
void send_my(const char* buff,int length){
if( buff == NULL){
return;
}
IPHEADER ipHeader;
memset(&ipHeader,0,sizeof(IPHEADER));
ConstructIPHeader(&ipHeader,length+sizeof(IPHEADER)+sizeof(UDP_HEADER));
UDP_HEADER udpHeader;
memset(&udpHeader,0,sizeof(UDP_HEADER));
ConstructUdpHeader(&udpHeader,length);
memset(SendBuf, 0, MAX_UDP_DATA_LEN);
memcpy(SendBuf, &ipHeader, sizeof(IPHEADER));
memcpy(SendBuf+sizeof(IPHEADER), &udpHeader, sizeof(UDP_HEADER));
memcpy(SendBuf+sizeof(IPHEADER)+sizeof(UDP_HEADER), buff, length);
FinalIPHeader(SendBuf,length+sizeof(IPHEADER)+sizeof(UDP_HEADER));
FinalUdpHeader(SendBuf+sizeof(IPHEADER),length+sizeof(UDP_HEADER));
sendto(m_OutSocket,SendBuf,(length+sizeof(IPHEADER)+sizeof(UDP_HEADER)), 0,(struct sockaddr*) &m_SocketOut,sizeof(struct sockaddr_in));
}
int main(){
init_udp_socket();
while(1){
send_my(TEST_MESSAGE,strlen(TEST_MESSAGE));
usleep(200);
}
}
当程序运行期间你可以使用tcpdump来抓取包,或者使用wireshark来抓包
这里我为了方便大家观看演示,所以使用wireshark来抓包给大家演示一下:
因为是虚拟机,只有一个网卡,所以我用一个网卡做演示:
把宏定义里的SRC_IP改成网卡对应IP:
#define SRC_IP "192.168.1.56"
然后测试看下:
你也可以在网卡地址中可以看到:
wireshark里的网卡地址:
是对应起来的,说明我们没有被udp最优网包算法给优化掉。
udp里会有一个最优网包算法,当你使用udp去发包时,即便你绑定的是A网卡的IP,在协议栈里udp还是会调用算法去判断当前哪张网卡会最适合发送,路线不拥堵,即便IP没有变但是网卡地址会变,就是你绑定的A网卡的IP,但是协议包里却是B网卡的物理地址这会导致物理链路层选择B网卡发送你的包。
以上是关于linux下通过伪造udp包来实现指定网卡发送数据的主要内容,如果未能解决你的问题,请参考以下文章