SOCKET_RAW 手动封装TCP协议

SOCKET_RAW 手动封装TCP协议_Doub1's Blog-CSDN博客_rawsocket tcpSOCKET_RAW 手动封装TCP协议还是笔记,移到博客来了。/*使用IP头包含选项创建原始套接字之后,再打开IP_HDRINCL 选项,即可在IP头中封装自己的协议,而不是仅仅使用系统预定义的协议。一般可以使用这种方法来发送UDP和TCP数据IP数据报格式:Version域:这4位指定了数据报的IP版本。对IPV4来说此域值为4.IHL(IP header length 的缩写):因为IP头长度不是固定的,所以需要这4位来确定IP数据报中数据部分的开始位置。大多数IP数据报不包含此https://blog.csdn.net/slslslyxz/article/details/111145638

手动构建tcp数据包

SOCKET_RAW 手动封装TCP协议


/*
使用IP头包含选项

创建原始套接字之后,再打开IP_HDRINCL 选项,即可在IP头中封装自己的协议,而不是仅仅使用系统预定义的协议。
一般可以使用这种方法来发送UDP和TCP数据

IP数据报格式:
Version域:这4位指定了数据报的IP版本。对IPV4来说此域值为4.
IHL(IP header length 的缩写):因为IP头长度不是固定的,所以需要这4位来确定IP数据报中数据部分的开始位置。大多数IP数据报不包含此选项,所以通常IP数据报有20个字节的头长度。
Type of service(服务端类型,TOS):包含在IPV4头中,用来区分不同类型的IP数据报
Total length:这是IP数据报的总长度,IP头加数据。这个域是16位(2字节)长,所以IP数据报大小的理论最大值是65535字节。然而数据报的长度很少有超过1500字节的。
Identification域:用来表示已发送的IPV4封包。通常,系统每发送一次封包就增加一次这个值。
Falgs和Fragment offset域:当IPV4分包被分割为较小的包时使用这2个域。DF代表不要分割(dont fragment),这是一个给路由器的命令,告诉他们不要分割此数据报,因为目标主机没有能力将它们恢复回来(例如:机器在启动时)。MF代表更多的分割(More Fragments)。
Time to live(生存时间,TTL)域:包含TTL域是为了确保数据报不会永远呆在网络里打圈。每当数据报被路由器处理时,这个域就会减1,如果减到0,此数据报就会被丢弃。
Protocol域:当IP数据报到达目的地时才使用此域,它指定了IP数据报的数据部分将要传递给哪个传输层协议,例如,值6表示数据部分要被传递给TCP,值17表示数据部分要被传递给UDP。
Header checksum 域:头校验和帮助路由器检测接收到的IP数据报中的位错误。


Source address 和 Destianation address域:指定此数据报的源IP地址和目的IP地址。
Options域:选项域是一个长度可变的域,它包含了可选的信息。最大值是40字节。

*/

#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <ws2ipdef.h>
#include <process.h>
#pragma warning(disable:4996)
#pragma comment(lib,"ws2_32.lib")

typedef struct _IPHeader {
	u_char VIHL; //版本和首部长度 各占4bit
	u_char ToS; //服务类型
	u_short TotalLen; //总长度
	u_short ID; //标识号
	u_short Frag_Flags; //片偏移量
	u_char TTL; //生存时间
	u_char Protocol; //协议
	u_short Checksum; //首部校验和
	ULONG SrcIP;
	ULONG DestIP;
	//struct in_addr SrcIP; //源IP地址
	//struct in_addr DestIP; //目的地址
}IPHDR, *PIPHDR;

//伪tcp头 用来计算校验和
typedef struct _psdheader
{
	unsigned long saddr; //源地址
	unsigned long daddr; //目的地址
	char mbz; //强制置空
	char ptcl; //协议类型
	unsigned short tcpl; //TCP长度
}PSD_HDR, *PPSD_HDR;

/*TCP头*/
typedef struct _tcpheader {
	USHORT sourcePort; //来源端口
	USHORT destinationPort;//目标端口
	UINT sequence; //序列号
	UINT acknowledgement;//确认号

	USHORT len;//4位首部长度,6位保留字 所以下面的flag其实是用了6位
	UCHAR flag;//6位标志符
	USHORT win;//16位窗口大小
	USHORT checkSum;//校验和
	USHORT urp;//16位紧急数据偏移量
}TCP_HDR, *PTCP_HDR;


//计算校验和
WORD checkSum(WORD* wBuff, int nSize) {
	DWORD dwSum = 0;;
	//将数据以WORD为单位累加到wSum
	while (nSize > 1) {
		dwSum += *wBuff++;
		nSize -= sizeof(WORD);
	}

	//若出现最后还剩一个字节继续与前面结果相加(也就是为奇数的情况)。
	if (nSize) {
		dwSum += *(BYTE*)wBuff++;
	}
	while (dwSum >> 16) { //如果有高16位,将一直与低16位相加
		dwSum = (dwSum >> 16) + (dwSum & 0xFFFF);
	}
	//取反
	return (WORD)(~dwSum);
}


//原始套接字 发送TCP封包
//没有写接收,在win中我们不能用recv来接收原始套接字上的数据,因为IP包都是先递交给系统核心,再传输到用户程序。
//当发送一个raw socket包时,比如syn,核心并不知道,也没有这个数据被发送或者连接建立的记录,
//因此当远端主机回应的时候,系统核心就把这些包全部丢弃,从而到不了应用程序上。
//要达到接收的目的,需要使用嗅探来完成,之前我写过,简单的嗅探器。
int main()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);


	SOCKET s = WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, NULL, 0, WSA_FLAG_OVERLAPPED);
	//SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (s == INVALID_SOCKET) {
		printf_s("请使用管理员运行\n");
		return false;
	}
		

	BOOL flag = TRUE;
	if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)) == SOCKET_ERROR) {
		printf_s("设置flag失败\n");
		return false;
	}
		

	int nTimeOver = 1000;
	if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver, sizeof(nTimeOver)) == SOCKET_ERROR) {
		printf_s("设置发送时间失败\n");
		return false;
	}
		

	SOCKADDR_IN sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(4567);
	sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	//填充IP首部
	IPHDR ipHdr;
	ipHdr.VIHL = (4 << 4 | (sizeof(IPHDR) / sizeof(ULONG)));
	ipHdr.TotalLen = htons(sizeof(IPHDR) + sizeof(TCP_HDR));
	ipHdr.ID = 1;
	ipHdr.Frag_Flags = 0;
	ipHdr.TTL = 128;
	ipHdr.Protocol = IPPROTO_TCP;
	ipHdr.Checksum = 0;  //稍后计算
	ipHdr.SrcIP = inet_addr("127.0.0.1");
	ipHdr.DestIP = inet_addr("127.0.0.1");

	//构造tcp伪首部
	PSD_HDR pdsHdr;
	pdsHdr.saddr = ipHdr.SrcIP;
	pdsHdr.daddr = ipHdr.DestIP;
	pdsHdr.mbz = 0; //必须留空一个字节
	pdsHdr.ptcl = ipHdr.Protocol;
	pdsHdr.tcpl = 0; //稍后计算 因为需要发送内容长度
	
	//填充tcp
	TCP_HDR tcpHdr;
	tcpHdr.destinationPort = htons(4567);
	tcpHdr.sourcePort = htons(8888);
	tcpHdr.sequence = htonl(0x12345678);
	tcpHdr.acknowledgement = 0;
	tcpHdr.len = (sizeof(tcpHdr) / 4 << 4 | 0);
	tcpHdr.flag = 0x2;//这里用来实现不同标志位探测,2是SYN,1是FIN,16是ACK探测 等等.
	tcpHdr.win = htons(512);
	tcpHdr.urp = 0;
	tcpHdr.checkSum = 0; //稍后计算

	
	char szSendBuff[1024] = { 0 };
	char msgBuff[4] = { 0 };
	int iBuffLen = 1024;
	
	//拷贝IP头和计算IP头校验和
	memcpy_s(szSendBuff, iBuffLen, &ipHdr, sizeof(ipHdr));
	iBuffLen -= sizeof(ipHdr);
	ipHdr.Checksum = checkSum((WORD*)szSendBuff, sizeof(ipHdr));
	
	//清空,因为上面其实只是用来计算IP头的校验和
	memset(szSendBuff,0,iBuffLen);
	
	//计算机TCP校验和
	const char tcpSendData[]={"发送的内容!"};
	pdsHdr.tcpl = htons(sizeof(tcpHdr)+sizeof(tcpSendData)); //修改长度
	memcpy(szSendBuff,&pdsHdr,sizeof(pdsHdr)); //拷贝伪TCP头
	memcpy(szSendBuff+sizeof(pdsHdr),&tcpHdr,sizeof(tcpHdr)); //拼接真实tcp头
	memcpy(szSendBuff+sizeof(pdsHdr)+sizeof(tcpHdr),tcpSendData,sizeof(tcpSendData)); //再拼接内容
	tcpHdr.checkSum= checkSum((WORD*)szSendBuff,sizeof(pdsHdr)+sizeof(tcpHdr)+sizeof(tcpSendData)); //计算TCP校验和
	
	//清空,因为上面其实只是用来计算TCP头的校验和
	memset(szSendBuff,0,iBuffLen);
	
	//构造真实数据
	memcpy(szSendBuff,&ipHdr,sizeof(ipHdr));//拷贝计算好的IP头
	memcpy(szSendBuff +sizeof(ipHdr),&tcpHdr,sizeof(tcpHdr)); //拼接计算好的TCP头
	memcpy(szSendBuff +sizeof(ipHdr)+sizeof(tcpHdr),tcpSendData,sizeof(tcpSendData)); //拼接内容
	
	int nRet = sendto(s, szSendBuff, sizeof(ipHdr) + sizeof(tcpHdr) + sizeof(msgBuff), 0, (sockaddr*)&sin, sizeof(sin));
	if (nRet == SOCKET_ERROR)
		printf_s("发送失败\n");

	printf_s("发送程序完成\n");
	Sleep(10000);

	WSACleanup();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值