29网络偷包
偷包,就是在网络传输过程中,截取某一数据包,进行解析获取其发送的数据。
原理与TCP通信类似。只需在创建套接字时,参数不同。
fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
PF_INET IPV4协议
SOCK_RAW 对原始网络协议访问
IPPROTO_TCP: TCP 协议
注意:第三参数必须指定协议的类型,不可以是0,只能接受某一种协议的数据,不可以是所有IP数据包。
注意:
char szBuf[1024];
iRet = read(fd, szBuf, sizeof(szBuf));
此时szBuf接受的包是数据,不是字符串,可能是乱码或其他乱七八的东东。
因此,对数据包进行解析:
IP数据报的格式:
(1)一个 IP 数据报由首部和数据两部分组成。
(2)首部的前一部分是固定长度,共 20 字节,是所有 IP 数据报必须具有的。
(3)在首部的固定部分的后面是一些可选字段,其长度是可变的。
定义IP数据包格式的一个结构体:
typedef struct tagIpHeader
{
unsigned char ucHeadLen:4;
unsigned char ucVer:4;
unsigned char ucTos;
unsigned short usLen;
unsigned short usIdent;
unsigned short usOffset:13;
unsigned short usFlag:3;
unsigned char ucTTL;
unsigned char ucProtocol;
unsigned short usChkSum;
unsigned int uiSrcIp;
unsigned int uiDstIp;
char data[0];
}IPHEAD_S;
再将接收到的szBuf,转换,再将结构体的信息输出
IPHEAD_S *pIpHead = (IPHEAD_S*)szBuf;
TCP 报文段首部的前 20 个字节是固定的,后面有 4n 字节是根据需要而增加的选项 (n 是整数)。
注意:TCP 首部的最小长度是 20 字节。而UDP是28字节。
定义TIP报文段格式的一个结构体:
typedef struct tagTcpHeader
{
unsigned short srcPort;
unsigned short dstPort;
unsigned int uiSeq;
unsigned int uiSeqQ;
unsigned short fin:1;
unsigned short syn:1;
unsigned short rst:1;
unsigned short psh:1;
unsigned short ack:1;
unsigned short urg:1;
unsigned short res:6;
unsigned short usHeadLen:4;
unsigned short usWinLen;
unsigned short usChkSum;
unsigned short usUrgPtr;
unsigned char data[0];
}TCPHEAD_S;
例子:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#define RAW_PORT 8888
//IP数据包格式
typedef struct tagIpHeader
{
unsigned char ucHeadLen:4;
unsigned char ucVer:4;
unsigned char ucTos;
unsigned short usLen;
unsigned short usIdent;
unsigned short usOffset:13;
unsigned short usFlag:3;
unsigned char ucTTL;
unsigned char ucProtocol;
unsigned short usChkSum;
unsigned int uiSrcIp;
unsigned int uiDstIp;
char data[0];
}IPHEAD_S;
//TCP数据报格式
typedef struct tagTcpHeader
{
unsigned short srcPort;
unsigned short dstPort;
unsigned int uiSeq;
unsigned int uiSeqQ;
unsigned short fin:1;
unsigned short syn:1;
unsigned short rst:1;
unsigned short psh:1;
unsigned short ack:1;
unsigned short urg:1;
unsigned short res:6;
unsigned short usHeadLen:4;
unsigned short usWinLen;
unsigned short usChkSum;
unsigned short usUrgPtr;
unsigned char data[0];
}TCPHEAD_S;
//IP信息
void PrintIpHead(IPHEAD_S *pIpHead)
{
printf("\n--------------- IP_HEAD -------------\n");
printf("srcPort : %d\n", pIpHead->ucVer);
printf("HeadLen : %d\n", pIpHead->ucHeadLen);
printf("TOS : %d\n", pIpHead->ucTos);
printf("DataLen : %d\n", ntohs(pIpHead->usLen));
printf("Ident : %d\n", ntohs(pIpHead->usIdent));
printf("Flag : %d\n", pIpHead->usFlag);
printf("Offset : %d\n", pIpHead->usOffset);
printf("TTL : %d\n", pIpHead->ucTTL);
printf("Protocol: %d\n", pIpHead->ucProtocol);
printf("ChkSum : %d\n", ntohs(pIpHead->usChkSum));
struct sockaddr_in addr;
addr.sin_addr.s_addr = pIpHead->uiSrcIp; //s_addr is network-endian
printf("Src IP : %s\n", inet_ntoa(addr.sin_addr));
addr.sin_addr.s_addr = pIpHead->uiDstIp;
printf("Dst IP : %s\n", inet_ntoa(addr.sin_addr));
printf("\n-------------------------------------\n");
}
//TCP信息
void PrintTcpHead(TCPHEAD_S *pTcpHead)
{
printf("\n--------------- TCP_HEAD -------------\n");
printf("srcPort : %d\n", htons(pTcpHead->srcPort));
printf("dstPort : %d\n", pTcpHead->dstPort);
printf("uiSeq : %d\n", pTcpHead->uiSeq);
printf("uiSeqQ : %d\n", htons(pTcpHead->uiSeqQ));
printf("fin:%d syn:%d tst:%d psh:%d ack:%d urg:%d res:%d:%d\n",
pTcpHead->fin,pTcpHead->syn,pTcpHead->rst,
pTcpHead->psh,pTcpHead->ack,pTcpHead->urg,pTcpHead->res);
printf("usHeadLen: %d\n", pTcpHead->usHeadLen);
printf("usWinLen : %d\n", htons(pTcpHead->usWinLen));
printf("usChkSum : %d\n", htons(pTcpHead->usChkSum));
printf("usUrgPtr: %d\n", htons(pTcpHead->usUrgPtr));
printf("ChkSum : %d\n", htons(pTcpHead->usChkSum));
printf("\n-------------------------------------\n");
}
void CaptureData()
{
int fd;
int iRet;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
fd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); //capture TCP data-packet
//fd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); //capture TCP data-packet
if (fd < 0)
{
perror("Fail to socket!");
return;
}
addr.sin_family = AF_INET;
addr.sin_port = htonl(RAW_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
iRet = bind(fd, (struct sockaddr*)&addr, addrlen);
if (iRet)
{
perror("Fail to bind!");
close(fd);
return;
}
char szBuf[1024];
while(1)
{
memset(szBuf, 0, sizeof(szBuf));
iRet = read(fd, szBuf, sizeof(szBuf));
if (iRet < 0)
{
perror("Fail to read!");
break;
}
//解析数据包
IPHEAD_S *pIpHead = (IPHEAD_S*)szBuf;
PrintIpHead(pIpHead);
//解析TCP报文的数据
TCPHEAD_S *pTcpHead=(TCPHEAD_S *)pIpHead;
printf("data: %s\n", pTcpHead->data);
//printf("Recv:%s\n", szBuf);
}
close(fd);
return;
}
int main()
{
CaptureData();
return 0;
}
大小序问题:
在数据包格式中看到,版本在前,首部长度在后。但是在结构体中,首部长度在前,版本在后。
解析:因此char是一个字节,而在数据包中,版本+首部长度=1字节。并且是大字节序,接受到后在内存中是小字节序。此时首部长度将被放在高位,版本被放在地位。因此进行调换。
DataLen和 Ident 的转换也是如此。
printf("DataLen : %d\n", ntohs(pIpHead->usLen));
printf("Ident : %d\n", ntohs(pIpHead->usIdent));