在進入raw socket多種強大的應用之前,我們先講解怎樣建立乙個raw socket及怎樣用建立的raw socket傳送和接收ip包。
建立raw socket
在windows平台上,為了使用raw socket,需先初始化winsock:
// 啟動 winsock
wsadata wsadata;
if (wsastartup(makeword(2, 1), &wsadata) != 0)
makeword(2, 1)組成乙個版本字段,2.1版,同樣的,makeword(2, 2)意味著2.2版。makeword本身定義為:
inline word makeword(const byte whigh, const byte wlow)
因此makeword(2, 1)實際等同於0x0201。同樣地,0x0101可等同於makeword(1, 1)。
與wsastartup()的函式為wsacleanup(),在所有的socket都使用完後呼叫,如:
void sock_cleanup()
接下來,定義乙個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)
其中使用的struct sockaddr_in(即sockaddr_in)為:
struct sockaddr_in
而bind()函式第二個引數的struct sockaddr型別定義為:
struct sockaddr;
實際上,bind()函式採用struct sockaddr是為了考慮相容性,最終struct sockaddr和struct sockaddr_in的記憶體占用是等同的。struct sockaddr_in中的struct in_addr成員占用4個位元組,為32位的ip位址,定義為:
typedef struct in_addr
s_un_b;
struct
s_un_w;
u_long s_addr;
}s_un;
} in_addr, *pin_addr, far *lpin_addr;
把32位的ip位址定義為上述聯合體將使使用者可以以位元組、半字或字方式讀寫同乙個ip位址。同志們,注意了,這個技巧在許多軟體開發中定義資料結構時被廣泛採用。
為了控制包的傳送方式,我們可能會用到如下的這個十分重要的函式來設定套接字選項:
int setsockopt(
socket s, //套接字控制代碼
int level, //選項level,如sol_socket
int optname, //選項名,如so_broadcast
const char* optval, //選項值buffer指標
int optlen //選項buffer長度);
例如,當level為sol_socket時,我們可以設定布林型選項so_broadcast從而控制套接字是否傳送和接收廣播訊息。
下面的**通過設定ipproto_ip level的ip_hdrincl選項為true從而使能程式設計師親自處理ip包報頭:
//設定 ip 頭操作選項
bool flag = true;
setsockopt(sd, ipproto_ip, ip_hdrincl, (char*) &flag, sizeof(flag);
下面的函式用於控制套接字:
int ioctlsocket(
socket s,
long cmd, //命令
u_long* argp //命令引數指標);
如下面的**讓socket接收所有報文(sniffer模式):
u_long imode = 1;
ioctlsocket(sd, sio_rcvall, & imode); //讓 sockraw 接受所有的資料
raw socket傳送報文
傳送報文的函式為:
int sendto(
socket s, //套接字控制代碼
const char* buf, //傳送緩衝區
int len, //要傳送的位元組數
int flags, //方式標誌
const struct sockaddr* to, //目標位址
int tolen //目標位址長度);
或int send(
socket s, //已經建立連線的套接字控制代碼
const char* buf,
int len,
int flags);
send()函式的第1個引數只能是乙個已經建立連線的套接字控制代碼,所以這個函式就不再需要目標位址引數輸入。
函式的返回值為實際傳送的位元組數,如果返回socket_error,可以通過wsagetlasterror()獲得錯誤原因。請看下面的示例:
int bwrote = sendto(sd, (char*)send_buf, packet_size, 0, (sockaddr*) &dest,
sizeof(dest));
if (bwrote == socket_error)
return - 1;
}else if (bwrote < packet_size)
raw socket接收報文
接收報文的函式為:
int recvfrom(
socket s, //套接字控制代碼
char* buf, //接收緩衝區
int len, //緩衝區位元組數
int flags, //方式標誌
struct sockaddr* from, //源位址
int* fromlen );
或int recv(
socket s, //已經建立連線的套接字控制代碼
char* buf,
int len,
int flags);
recv()函式的第1個引數只能是乙個已經建立連線的套接字控制代碼,所以這個函式就不再需要源位址引數輸入。
函式的返回值為實際接收的位元組數,如果返回socket_error,我們可以通過wsagetlasterror()函式獲得錯誤原因。請看下面的示例:
int bread = recvfrom(sd, (char*)recv_buf, packet_size + sizeof(ipheader), 0,
(sockaddr*) &source, &fromlen);
if (bread == socket_error)
return - 1;}
原始套接字按如下規則接收報文:若接收的報文中協議型別和定義的原始套接字匹配,那麼,接收的所有資料拷貝入套接字中;如果套接字繫結了本地位址,那麼只有接收資料ip頭中對應的目的位址等於本地位址,接收到的資料才拷貝到套接字中;如果套接字定義了遠端位址,那麼,只有接收資料ip頭中對應的源位址與遠端位址匹配,接收的資料才拷貝到套接字中。
建立報文
在利用raw socket傳送報文時,報文的ip頭、tcp頭、udp頭等需要程式設計師親自賦值,從而達到極大的靈活性。下面的程式利用raw socket傳送tcp報文,並完全手工建立報頭:
原始套接字
資料出處 實際上,我們常用的網路程式設計都是在應用層的報文的收發操作,也就是大多數程式設計師接觸到的流式套接字 sock stream 和資料報式套接字 sock dgram 而這些資料報都是由系統提供的協議棧實現,使用者只需要填充應用層報文即可,由系統完成底層報文頭的填充並傳送。然而在某些情況下需...
原始套接字
參考1 原始套接字能幹什麼?參考2 原始套接字抓包實踐 參考3 各層頭結構 通過原始套接字,我們可以抓取所有傳送到本機的ip包 包括ip頭和tcp udp icmp包頭 也可以抓取所有本機收到的幀 包括資料鏈路層協議頭 普通的套接字無法處理icmp igmp等網路報文,而sock raw可以。利用原...
原始套接字
利用原始套接字實現乙個tcp syn flooding 程式 要求 客戶端不斷向攻擊端傳送syn連線請求 客戶端在傳送資料時,通過源位址隨機位址的方式隱藏自己的 位址 檢驗 在shell下通過 netstat tn 檢查syn recv連線數檢驗自己的程式是否成功 好像有點問題。不能偽造mac位址。...