一 掃描原理
其實原理非常簡單,就是使用Socket去連接配接目标IP或者域名的指定端口,如果能夠連上則說明該端口是打開的。反之,要是在連接配接逾時之前都沒有連上,則将該端口判斷為關閉狀态。下面我将分别說明兩種基本的掃描方式:(1)掃描一個連續的端口段;(2)僅掃描一個指定的端口集合
二 使用多線程掃描目标主機一個段的端口開放情況
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<code>/**</code>
<code> </code><code>* 多線程掃描目标主機一個段的端口開放情況</code>
<code> </code><code>* </code>
<code> </code><code>* @param ip</code>
<code> </code><code>* 待掃描IP或域名,eg:180.97.161.184 www.zifangsky.cn</code>
<code> </code><code>* @param startPort</code>
<code> </code><code>* 起始端口</code>
<code> </code><code>* @param endPort</code>
<code> </code><code>* 結束端口</code>
<code> </code><code>* @param threadNumber</code>
<code> </code><code>* 線程數</code>
<code> </code><code>* @param timeout</code>
<code> </code><code>* 連接配接逾時時間</code>
<code> </code><code>* */</code>
<code> </code><code>public</code> <code>void</code> <code>scanLargePorts(String ip, </code><code>int</code> <code>startPort, </code><code>int</code> <code>endPort,</code>
<code> </code><code>int</code> <code>threadNumber, </code><code>int</code> <code>timeout) {</code>
<code> </code><code>ExecutorService threadPool = Executors.newCachedThreadPool();</code>
<code> </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i < threadNumber; i++) {</code>
<code> </code><code>ScanMethod1 scanMethod1 = </code><code>new</code> <code>ScanMethod1(ip, startPort, endPort,</code>
<code> </code><code>threadNumber, i, timeout);</code>
<code> </code><code>threadPool.execute(scanMethod1);</code>
<code> </code><code>}</code>
<code> </code><code>threadPool.shutdown();</code>
<code> </code><code>// 每秒中檢視一次是否已經掃描結束</code>
<code> </code><code>while</code> <code>(</code><code>true</code><code>) {</code>
<code> </code><code>if</code> <code>(threadPool.isTerminated()) {</code>
<code> </code><code>System.out.println(</code><code>"掃描結束"</code><code>);</code>
<code> </code><code>break</code><code>;</code>
<code> </code><code>}</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>Thread.sleep(</code><code>1000</code><code>);</code>
<code> </code><code>} </code><code>catch</code> <code>(InterruptedException e) {</code>
<code> </code><code>e.printStackTrace();</code>
<code> </code><code>}</code>
然後是一個内部類ScanMethod1實作了Runnable接口:
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<code> </code><code>* 掃描方式一:針對起始結束端口,進行逐個掃描</code>
<code> </code><code>class</code> <code>ScanMethod1 </code><code>implements</code> <code>Runnable {</code>
<code> </code><code>private</code> <code>String ip; </code><code>// 目标IP</code>
<code> </code><code>private</code> <code>int</code> <code>startPort, endPort, threadNumber, serial, timeout; </code><code>// 起始和結束端口,線程數,這是第幾個線程,逾時時間</code>
<code> </code><code>/**</code>
<code> </code><code>* 初始化</code>
<code> </code><code>* </code>
<code> </code><code>* @param ip</code>
<code> </code><code>* 待掃描IP或域名</code>
<code> </code><code>* @param startPort</code>
<code> </code><code>* 起始端口</code>
<code> </code><code>* @param endPort</code>
<code> </code><code>* 結束端口</code>
<code> </code><code>* @param threadNumber</code>
<code> </code><code>* 線程數</code>
<code> </code><code>* @param serial</code>
<code> </code><code>* 标記是第幾個線程</code>
<code> </code><code>* @param timeout</code>
<code> </code><code>* 連接配接逾時時間</code>
<code> </code><code>* */</code>
<code> </code><code>public</code> <code>ScanMethod1(String ip, </code><code>int</code> <code>startPort, </code><code>int</code> <code>endPort,</code>
<code> </code><code>int</code> <code>threadNumber, </code><code>int</code> <code>serial, </code><code>int</code> <code>timeout) {</code>
<code> </code><code>this</code><code>.ip = ip;</code>
<code> </code><code>this</code><code>.startPort = startPort;</code>
<code> </code><code>this</code><code>.endPort = endPort;</code>
<code> </code><code>this</code><code>.threadNumber = threadNumber;</code>
<code> </code><code>this</code><code>.serial = serial;</code>
<code> </code><code>this</code><code>.timeout = timeout;</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code> </code><code>int</code> <code>port = </code><code>0</code><code>;</code>
<code> </code><code>InetAddress address = InetAddress.getByName(ip);</code>
<code> </code><code>Socket socket;</code>
<code> </code><code>SocketAddress socketAddress;</code>
<code> </code><code>for</code> <code>(port = startPort + serial; port <= endPort; port += threadNumber) {</code>
<code> </code><code>socket = </code><code>new</code> <code>Socket();</code>
<code> </code><code>socketAddress = </code><code>new</code> <code>InetSocketAddress(address, port);</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>socket.connect(socketAddress, timeout); </code><code>// 逾時時間</code>
<code> </code><code>socket.close();</code>
<code> </code><code>System.out.println(</code><code>"端口 "</code> <code>+ port + </code><code>" :開放"</code><code>);</code>
<code> </code><code>} </code><code>catch</code> <code>(IOException e) {</code>
<code> </code><code>// System.out.println("端口 " + port + " :關閉");</code>
<code> </code><code>}</code>
<code> </code><code>}</code>
<code> </code><code>} </code><code>catch</code> <code>(UnknownHostException e) {</code>
三 使用多線程掃描目标主機指定Set端口集合的開放情況
<code> </code><code>* 多線程掃描目标主機指定Set端口集合的開放情況</code>
<code> </code><code>* @param portSet</code>
<code> </code><code>* 待掃描的端口的Set集合</code>
<code> </code><code>public</code> <code>void</code> <code>scanLargePorts(String ip, Set<Integer> portSet,</code>
<code> </code><code>ScanMethod2 scanMethod2 = </code><code>new</code> <code>ScanMethod2(ip, portSet,</code>
<code> </code><code>threadPool.execute(scanMethod2);</code>
具體的線程内部類跟上面類似,代碼如下:
<code> </code><code>* 掃描方式二:針對一個待掃描的端口的Set集合進行掃描</code>
<code> </code><code>private</code> <code>class</code> <code>ScanMethod2 </code><code>implements</code> <code>Runnable {</code>
<code> </code><code>private</code> <code>Set<Integer> portSet; </code><code>// 待掃描的端口的Set集合</code>
<code> </code><code>private</code> <code>int</code> <code>threadNumber, serial, timeout; </code><code>// 線程數,這是第幾個線程,逾時時間</code>
<code> </code><code>public</code> <code>ScanMethod2(String ip, Set<Integer> portSet, </code><code>int</code> <code>threadNumber,</code>
<code> </code><code>int</code> <code>serial, </code><code>int</code> <code>timeout) {</code>
<code> </code><code>this</code><code>.portSet = portSet;</code>
<code> </code><code>Integer[] ports = portSet.toArray(</code><code>new</code> <code>Integer[portSet.size()]); </code><code>// Set轉數組</code>
<code> </code><code>if</code> <code>(ports.length < </code><code>1</code><code>)</code>
<code> </code><code>return</code><code>;</code>
<code> </code><code>for</code> <code>(port = </code><code>0</code> <code>+ serial; port <= ports.length - </code><code>1</code><code>; port += threadNumber) {</code>
<code> </code><code>socketAddress = </code><code>new</code> <code>InetSocketAddress(address, ports[port]);</code>
<code> </code><code>socket.connect(socketAddress, timeout);</code>
<code> </code><code>System.out.println(</code><code>"端口 "</code> <code>+ ports[port] + </code><code>" :開放"</code><code>);</code>
<code> </code><code>// System.out.println("端口 " + ports[port] + " :關閉");</code>
四 兩種掃描方式的測試用例
<code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code>
<code> </code><code>PortScanDemo portScanDemo = </code><code>new</code> <code>PortScanDemo();</code>
<code> </code><code>//方式1</code>
<code> </code><code>// portScanDemo.scanLargePorts("ultra-book.co", 20, 10000, 5,800);</code>
<code> </code><code>// portScanDemo.scanLargePorts("180.97.161.184", 1, 100, 5);</code>
<code> </code><code>//方式2</code>
<code> </code><code>Set<Integer> portSet = </code><code>new</code> <code>LinkedHashSet<Integer>();</code>
<code> </code><code>Integer[] ports = </code><code>new</code> <code>Integer[] { </code><code>21</code><code>, </code><code>22</code><code>, </code><code>23</code><code>, </code><code>25</code><code>, </code><code>26</code><code>, </code><code>69</code><code>, </code><code>80</code><code>, </code><code>110</code><code>, </code><code>143</code><code>,</code>
<code> </code><code>443</code><code>, </code><code>465</code><code>, </code><code>995</code><code>, </code><code>1080</code><code>, </code><code>1158</code><code>, </code><code>1433</code><code>, </code><code>1521</code><code>, </code><code>2100</code><code>, </code><code>3128</code><code>, </code><code>3306</code><code>, </code><code>3389</code><code>,</code>
<code> </code><code>7001</code><code>, </code><code>8080</code><code>, </code><code>8081</code><code>, </code><code>9080</code><code>, </code><code>9090</code><code>,</code><code>43958</code><code>};</code>
<code> </code><code>portSet.addAll(Arrays.asList(ports));</code>
<code> </code><code>portScanDemo.scanLargePorts(</code><code>"ultra-book.co"</code><code>, portSet, </code><code>5</code><code>, </code><code>800</code><code>);</code>
五 測試結果
<a href="http://s5.51cto.com/wyfs02/M00/78/3A/wKioL1Z4oyXB_3cYAAFDqJgRSFQ285.png" target="_blank"></a>
<a href="http://s5.51cto.com/wyfs02/M02/78/3A/wKioL1Z4oyWxTyktAABNvxk7sL4968.png" target="_blank"></a>
注:1 逾時時間是以毫秒為機關,其中要是掃描國内的IP可以把這個時間适當設定低一點,200~500左右。相反,要是掃描國外IP就需要把這個時間适當設定大一點,不然有可能把本來打開的端口也漏掉了
本文轉自 pangfc 51CTO部落格,原文連結:http://blog.51cto.com/983836259/1727023,如需轉載請自行聯系原作者