這兩天在做一個(gè)實(shí)驗(yàn)需要自己構(gòu)造ip首部,遇到諸多問(wèn)題,搞了一天終于搞定。
關(guān)于socket的介紹網(wǎng)上一大堆,我只記錄構(gòu)造IP頭時(shí)我遇到的問(wèn)題。由于沒(méi)玩過(guò)socket構(gòu)造IP首部,網(wǎng)上找了段代碼研究下,無(wú)奈代碼跑不動(dòng),各種問(wèn)題,網(wǎng)上搜集資料無(wú)果,從基礎(chǔ)學(xué)起,加上自己的腦洞總算解決了。
我想自己構(gòu)造一個(gè)自定義IP頭的ICMP回送請(qǐng)求,網(wǎng)上找了段代碼,自己改了改,現(xiàn)在長(zhǎng)這個(gè)樣子:
1 import socket 2 import struct 3 def checksum(source_string): 4 sum = 0 5 countTo = (len(source_string)/2)*2 6 count = 0 7 while count<countTo: 8 thisVal = ord(source_string[count + 1])*256 + ord(source_string[count]) 9 sum = sum + thisVal10 sum = sum & 0xffffffff 11 count = count + 212 if countTo<len(source_string):13 sum = sum + ord(source_string[len(source_string) - 1])14 sum = sum & 0xffffffff 15 sum = (sum >> 16) + (sum & 0xffff)16 sum = sum + (sum >> 16)17 answer = ~sum18 answer = answer & 0xffff19 answer = answer >> 8 | (answer << 8 & 0xff00)20 return answer21 def ping(ip):22 s=socket.socket(socket.AF_INET,socket.SOCK_RAW,255)24 s.setsockopt(0, socket.IP_HDRINCL, 1)25 # now start constructing the packet26 27 source_ip = '172.16.12.1'28 dest_ip = ip29 30 # ip header fields31 ihl = 532 version = 433 tos = 034 tot_len = 2835 id = 036 frag_off = 037 ttl = 25538 PRotocol = 139 check = 0 40 saddr =socket.inet_aton ( source_ip ) #Spoof the source ip address if you want to41 daddr = socket.inet_aton ( dest_ip )44 ihl_version = (version << 4) + ihl45 # the ! in the pack format string means network order46 ip_header = struct.pack('!BBHHHBBH4s4s', ihl_version, tos, tot_len, id, frag_off, ttl, protocol, check, saddr, daddr)47 packet = struct.pack(48 "!BBHHH", 8, 0, 0, 0, 049 )50 chksum=checksum(packet)51 packet = struct.pack(52 "!BBHHH", 8, 0, chksum, 0, 053 )54 packet=ip_header+packet55 s.sendto(packet,(ip,1))56 print "done"57 58 if __name__=='__main__':59 ping('172.31.0.1')
步驟很簡(jiǎn)單,就是自己創(chuàng)建個(gè)套接字,然后把頭構(gòu)造好再發(fā)送就行了,但是一般的套接字是無(wú)法自己更改IP頭的,只能從IP數(shù)據(jù)報(bào)的數(shù)據(jù)部分開(kāi)始構(gòu)造,想要構(gòu)造IP首部就要用到原始套接字,用原始套接字可以從IP首部開(kāi)始構(gòu)造,但是如果用原始套接字需要root權(quán)限,開(kāi)始我在OS X下用IDE,程序總是報(bào)錯(cuò)socket.error: [Errno 1] Operation not permitted,就是因?yàn)闄?quán)限的問(wèn)題,在終端里sudo運(yùn)行就沒(méi)有權(quán)限問(wèn)題了(Ps:如果想用root權(quán)限打開(kāi)IDE,又不想切換賬戶的話,終端里sudo ./IDE就行了)現(xiàn)在有了權(quán)限,開(kāi)始報(bào)別的錯(cuò)了,提示socket.error: [Errno 22]Invalid argument。
這是創(chuàng)建原始套接字的代碼,第一行第三個(gè)值255是IPPROTO_RAW的值,如果要構(gòu)造IP頭,就要加上第二行代碼設(shè)置IP_HDRINCL,第一個(gè)值0是IPPROTO_IP的值
s=socket.socket(socket.AF_INET,socket.SOCK_RAW,255)
s.setsockopt(0, socket.IP_HDRINCL, 1)如果這樣設(shè)置在OS X下就會(huì)在調(diào)用sendto()的位置報(bào)Invalid argument錯(cuò)誤,后來(lái)發(fā)現(xiàn)問(wèn)題出在第一行的第三個(gè)參數(shù)255上,經(jīng)測(cè)試發(fā)現(xiàn)
在OS X下,這個(gè)參數(shù)置成0或255都會(huì)報(bào)錯(cuò)
在WINDOWS下 ,這個(gè)參數(shù)置成0或255都不會(huì)報(bào)錯(cuò)
在linux下,這個(gè)參數(shù)置成0會(huì)報(bào)錯(cuò),置成255不會(huì)報(bào)錯(cuò)
現(xiàn)在可以構(gòu)造任意的源IP和目的IP的ICMP回送請(qǐng)求了,IP首部字段的ID,長(zhǎng)度,校驗(yàn)和置成0就可以,內(nèi)核協(xié)議棧會(huì)修正。
在linux上抓包發(fā)現(xiàn)目的IP為同子網(wǎng)下不存在的主機(jī)IP時(shí),是抓不到ICMP包的,這是因?yàn)橹鳈C(jī)先發(fā)送ARP包請(qǐng)求目的IP的MAC地址得不到回應(yīng),而不能進(jìn)一步發(fā)送ICMP回送請(qǐng)求,也就是說(shuō)PING命令中提示的Request timeout for icmp_seq是因?yàn)锳RP請(qǐng)求得不到應(yīng)答而產(chǎn)生的。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注