python使用scapy模塊實現ping掃描的過程詳解

關於scapy

Scapy 是一個可以讓用戶發送、偵聽和解析並偽裝網絡報文的Python程序。這些功能可以用於制作偵測、掃描和攻擊網絡的工具。
換言之, Scapy 是一個強大的操縱報文的交互程序。它可以偽造或者解析多種協議的報文,還具有發送、捕獲、匹配請求和響應這些報文以及更多的功能。 Scapy 可以輕松地做到像掃描(scanning)、路由跟蹤(tracerouting)、探測(probing)、單元測試(unit tests)、攻擊(attacks)和發現網絡(network discorvery)這樣的傳統任務。它可以代替 hping , arpspoof , arp-sk , arping , p0f 甚至是部分的 Namp , tcpdump 和 tshark 的功能

對於我來言,我認為scapy最強大的特點就是scapy解碼不解釋,比如我們在用nmap進行掃描的時候,結果往往是nmap已經根據返回包的特征,然後輸出自己推測的結果,但是scapy是直接輸出返回包的內容,這樣我們可以判斷的更加準確

實現過程

接下來我將詳細的介紹如何使用scapy模塊編寫一個簡單的內網掃描工具。

我先說一下我的腳本整個大致流程:

1.首先我們應該編寫出能夠像單個IP發送ICMP包並接受回包的代碼

2.其次我們應該開啟多進程,從而讓該腳本可以快速掃描整個網段的主機存活情況

那麼我們接下來就開始上面的流程。

單進程發包

首先我們需要導入scapy模塊,這裡要強調一下,scapy的v2版本的導入與別的模塊導入方式不同,需要特殊的書寫方式,否則scapy模塊中的某些功能將無法使用

from scapy.all import *
from random import randint

接下來需要構造我們的包,即我們要發送的ICMP包,首先瞭解一下scapy包的偽造

對於scapy我們可以直接輸入scapy進入一個交互模式,其實這個交互模式本質也還是python,我們來看一下包

讀者可以看看我構造的這個包,我寫瞭三個協議層,第一寫其實是mac地址,如果我們隻輸入Ether,那麼就是默認的結果,這裡的mac就是默認廣播形式,第二個我寫的是IP,也就是IP層的設置,可以發現其中的一些已經填寫瞭默認值,我們要設置的其實主要就的dst,即目的地址,當然其他的想改一下也是可以的,比如說把ttl生存周期改成128,這也是沒毛病的,第三個我寫的是TCP,即我發送的包是TCP包,其中也有一些默認值,我們看到有個window,這個其實就是程序的進程號。

那麼既然我們是ping包,我們就使用ICMP包,那麼我們來看一下ICMP包

看到ICMP的值更少一些,其中type表示這個包是請求包還是回應包。

那我們既然知道如何構造包瞭,那就動手構造一個

def scapy_ping_one(host):
 id_ip = randint(1, 65535)
 id_ping = randint(1, 65535)
 seq_ping = randint(1, 65535)
 packet = IP(dst=host, ttl=128, id=id_ip) / ICMP(id=id_ping, seq=seq_ping) / b'I am Ameng'

這裡我設置的id號其實不設置也可以,這個就是標識包的信息,後面的字節流也可以不填寫,不影響結果

既然構造完瞭,那麼我們就要發送,如何發送呢?這就用到瞭scapy中的一個函數sr

sr() 函數是用來發送數據包和接收應答。該函數返回一對數據包及其應答,還有無應答的數據包。 sr1() 函數是一種變體,用來返回一個應答數據包。發送的數據包必須是第3層報文(IP,ARP等)。 srp() 則是使用第2層報文(以太網,802.3等)

這裡使用sr1()

ping = sr1(packet, timeout=0.2, verbose=False)

介紹一下參數,其實看名字前兩個沒什麼問題,我這裡主要說一下第三個參數,第三個參數的意思是說關閉詳細信息,因為我們隻看結果,這樣界面更整潔

到這裡發送單個包並接受單個包的ping代碼已經實現,整體代碼如下:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:Ameng jlx-love.com


from scapy.all import *
from random import randint

def scapy_ping_one(host):
 id_ip = randint(1, 65535)
 id_ping = randint(1, 65535)
 seq_ping = randint(1, 65535)
 packet = IP(dst=host, ttl=128, id=id_ip) / ICMP(id=id_ping, seq=seq_ping) / b'I am Ameng'
 ping = sr1(packet, timeout=0.2, verbose=False)
 if ping:
  os._exit(3)

if __name__ == '__main__':
 scapy_ping_one(sys.argv[1])

多進程發包

這裡主要是將單進程的代碼實現多進程,那麼我們什麼時候會用到多進程呢?我們一般都是判斷一個網段有哪些主機存活,所以我們一般的輸入格式都是這樣子的,比如:192.168.10.0/24,所以我們首先需要將用戶的這個輸入變成一個這個網段的各個地址,這裡用到ipaddress模塊,可以自動實現這個功能

其次我們需要開啟多個進程來同時進行不同ip地址的ping檢測,所以我們引入multiprocessing模塊,開啟多個進程。

代碼如下:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:Ameng jlx-love.com
import time
import multiprocessing
from scapy_ping_one import scapy_ping_one
from scapy.all import *
import ipaddress

def scapy_ping_scan(network):
 net = ipaddress.ip_network(network)
 ip_processes = {}
 for ip in net:
  ip_addr = str(ip)
  ping_one = multiprocessing.Process(target = scapy_ping_one, args = (ip_addr,))
  ping_one.start()
  ip_processes[ip_addr] = ping_one
 ip_list = []
 for ip, process in ip_processes.items():
  if process.exitcode == 3:
   ip_list.append(ip)
  else:
   process.terminate()
 return sorted(ip_list)

if __name__ == '__main__':
 import time
 t1 = time.time()
 active_ip = scapy_ping_scan(sys.argv[1])
 print('存活的ip地址:')
 for ip in active_ip:
  print(ip)
 t2 = time.time()
 print('所用時間為:{}s'.format(int(t2 - t1)))

到此,一個簡單的ping掃描即可實現

運行結果

到此這篇關於python使用scapy模塊實現ping掃描的過程詳解的文章就介紹到這瞭,更多相關python scapy實現ping掃描內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: