在进入Raw Socket多种强大的应用之前,我们先讲解怎样建立一个Raw Socket及怎样用建立的Raw Socket发送和接收IP包。
建立Raw Socket
在Windows平台上,为了使用Raw Socket,需先初始化WINSOCK:
// 启动 Winsock WSAData wsaData; if (WSAStartup(MAKEWord(2, 1), &wsaData) != 0) { cerr << "Failed to find Winsock 2.1 or better." << endl; return 1; } MAKEWORD(2, 1)组成一个版本字段,2.1版,同样的,MAKEWORD(2, 2)意味着2.2版。MAKEWORD本身定义为:
inline word MakeWord(const byte wHigh, const byte wLow) { return ((word)wHigh) << 8 wLow; } 因此MAKEWORD(2, 1)实际等同于0x0201。同样地,0x0101可等同于MAKEWORD(1, 1)。
与WSAStartup()的函数为WSACleanup(),在所有的socket都使用完后调用,如:
void sock_cleanup() { #ifdef WIN32 sockcount--; if (sockcount == 0) WSACleanup(); #endif } 接下来,定义一个Socket句柄:
SOCKET sd; // RAW Socket句柄 创建Socket并将句柄赋值给定义的sd,可以使用WSASocket()函数来完成,其原型为:
SOCKET WSASocket(int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags); 其中的参数定义为:
af:地址家族,一般为AF_INET,指代IPv4(The Internet Protocol version 4)地址家族。
type:套接字类型,如果创建原始套接字,应该使用SOCK_RAW;
Protocol:协议类型,如IPPROTO_TCP、IPPROTO_UDP等;
lpProtocolInfo :WSAPROTOCOL_INFO结构体指针;
dwFlags:套接字属性标志。
例如,下面的代码定义ICMP协议类型的原始套接字:
sd = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, 0); 创建Socket也可以使用socket()函数:
SOCKET WSAAPI socket( int af, int type, int protocol); 参数的定义与WSASocket()函数相同。
为了使用socket()函数创建的Socket,还需要将这个Socket与sockaddr绑定:
SOCKADDR_IN addr_in;
addr_in.sin_family = AF_INET; addr_in.sin_port = INADDR_ANY; addr_in.sin_addr.S_un.S_addr = GetLocalIP();
nRetCode = bind(sd, (strUCt sockaddr*) &addr_in, sizeof(addr_in)); if (SOCKET_ERROR == nRetCode) { printf("BIND Error!%d\n", WSAGetLastError()); } 其中使用的struct sockaddr_in(即SOCKADDR_IN)为:
struct sockaddr_in { unsigned short sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; } 而bind()函数第二个参数的struct sockaddr类型定义为:
struct sockaddr { unisgned short as_family; char sa_data[14]; }; 实际上,bind()函数采用struct sockaddr是为了考虑兼容性,最终struct sockaddr和struct sockaddr_in的内存占用是等同的。struct sockaddr_in中的struct in_addr成员占用4个字节,为32位的IP地址,定义为:
typedef struct in_addr { union { struct { u_char s_b1, s_b2, s_b3, s_b4; } S_un_b; struct { u_short s_w1, s_w2; } S_un_w; u_long S_addr; } S_un; } IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR; 把32位的IP地址定义为上述联合体将使用户可以以字节、半字或字方式读写同一个IP地址。同志们,注意了,这个技巧在许多软件开发中定义数据结构时被广泛采用。
上一篇:黑客之旅――原始套接字透析之前言
下一篇:原始套接字透析之ICMP拒绝服务攻击
|