天天看點

使用Java開發多線程端口掃描工具

一 掃描原理

其實原理非常簡單,就是使用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 &lt; 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 &lt;= 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&lt;Integer&gt; 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&lt;Integer&gt; 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&lt;Integer&gt; 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 &lt; </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 &lt;= 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&lt;Integer&gt; portSet = </code><code>new</code> <code>LinkedHashSet&lt;Integer&gt;();</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,如需轉載請自行聯系原作者