一 扫描原理
其实原理非常简单,就是使用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
|
/**
* 多线程扫描目标主机一个段的端口开放情况
*
* @param ip
* 待扫描IP或域名,eg:180.97.161.184 www.zifangsky.cn
* @param startPort
* 起始端口
* @param endPort
* 结束端口
* @param threadNumber
* 线程数
* @param timeout
* 连接超时时间
* */
public
void
scanLargePorts(String ip,
int
startPort,
int
endPort,
int
threadNumber,
int
timeout) {
ExecutorService threadPool = Executors.newCachedThreadPool();
for
(
int
i =
0
; i < threadNumber; i++) {
ScanMethod1 scanMethod1 =
new
ScanMethod1(ip, startPort, endPort,
threadNumber, i, timeout);
threadPool.execute(scanMethod1);
}
threadPool.shutdown();
// 每秒中查看一次是否已经扫描结束
while
(
true
) {
if
(threadPool.isTerminated()) {
System.out.println(
"扫描结束"
);
break
;
}
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
|
然后是一个内部类ScanMethod1实现了Runnable接口:
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
/**
* 扫描方式一:针对起始结束端口,进行逐个扫描
*
* */
class
ScanMethod1
implements
Runnable {
private
String ip;
// 目标IP
private
int
startPort, endPort, threadNumber, serial, timeout;
// 起始和结束端口,线程数,这是第几个线程,超时时间
/**
* 初始化
*
* @param ip
* 待扫描IP或域名
* @param startPort
* 起始端口
* @param endPort
* 结束端口
* @param threadNumber
* 线程数
* @param serial
* 标记是第几个线程
* @param timeout
* 连接超时时间
* */
public
ScanMethod1(String ip,
int
startPort,
int
endPort,
int
threadNumber,
int
serial,
int
timeout) {
this
.ip = ip;
this
.startPort = startPort;
this
.endPort = endPort;
this
.threadNumber = threadNumber;
this
.serial = serial;
this
.timeout = timeout;
}
public
void
run() {
int
port =
0
;
try
{
InetAddress address = InetAddress.getByName(ip);
Socket socket;
SocketAddress socketAddress;
for
(port = startPort + serial; port <= endPort; port += threadNumber) {
socket =
new
Socket();
socketAddress =
new
InetSocketAddress(address, port);
try
{
socket.connect(socketAddress, timeout);
// 超时时间
socket.close();
System.out.println(
"端口 "
+ port +
" :开放"
);
}
catch
(IOException e) {
// System.out.println("端口 " + port + " :关闭");
}
}
}
catch
(UnknownHostException e) {
e.printStackTrace();
}
}
}
|
三 使用多线程扫描目标主机指定Set端口集合的开放情况
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
|
/**
* 多线程扫描目标主机指定Set端口集合的开放情况
*
* @param ip
* 待扫描IP或域名,eg:180.97.161.184 www.zifangsky.cn
* @param portSet
* 待扫描的端口的Set集合
* @param threadNumber
* 线程数
* @param timeout
* 连接超时时间
* */
public
void
scanLargePorts(String ip, Set<Integer> portSet,
int
threadNumber,
int
timeout) {
ExecutorService threadPool = Executors.newCachedThreadPool();
for
(
int
i =
0
; i < threadNumber; i++) {
ScanMethod2 scanMethod2 =
new
ScanMethod2(ip, portSet,
threadNumber, i, timeout);
threadPool.execute(scanMethod2);
}
threadPool.shutdown();
while
(
true
) {
if
(threadPool.isTerminated()) {
System.out.println(
"扫描结束"
);
break
;
}
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
|
具体的线程内部类跟上面类似,代码如下:
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
37
38
39
40
41
42
43
44
45
|
/**
* 扫描方式二:针对一个待扫描的端口的Set集合进行扫描
*
* */
private
class
ScanMethod2
implements
Runnable {
private
String ip;
// 目标IP
private
Set<Integer> portSet;
// 待扫描的端口的Set集合
private
int
threadNumber, serial, timeout;
// 线程数,这是第几个线程,超时时间
public
ScanMethod2(String ip, Set<Integer> portSet,
int
threadNumber,
int
serial,
int
timeout) {
this
.ip = ip;
this
.portSet = portSet;
this
.threadNumber = threadNumber;
this
.serial = serial;
this
.timeout = timeout;
}
public
void
run() {
int
port =
0
;
Integer[] ports = portSet.toArray(
new
Integer[portSet.size()]);
// Set转数组
try
{
InetAddress address = InetAddress.getByName(ip);
Socket socket;
SocketAddress socketAddress;
if
(ports.length <
1
)
return
;
for
(port =
0
+ serial; port <= ports.length -
1
; port += threadNumber) {
socket =
new
Socket();
socketAddress =
new
InetSocketAddress(address, ports[port]);
try
{
socket.connect(socketAddress, timeout);
socket.close();
System.out.println(
"端口 "
+ ports[port] +
" :开放"
);
}
catch
(IOException e) {
// System.out.println("端口 " + ports[port] + " :关闭");
}
}
}
catch
(UnknownHostException e) {
e.printStackTrace();
}
}
}
|
四 两种扫描方式的测试用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
static
void
main(String[] args) {
PortScanDemo portScanDemo =
new
PortScanDemo();
//方式1
// portScanDemo.scanLargePorts("ultra-book.co", 20, 10000, 5,800);
// portScanDemo.scanLargePorts("180.97.161.184", 1, 100, 5);
//方式2
Set<Integer> portSet =
new
LinkedHashSet<Integer>();
Integer[] ports =
new
Integer[] {
21
,
22
,
23
,
25
,
26
,
69
,
80
,
110
,
143
,
443
,
465
,
995
,
1080
,
1158
,
1433
,
1521
,
2100
,
3128
,
3306
,
3389
,
7001
,
8080
,
8081
,
9080
,
9090
,
43958
};
portSet.addAll(Arrays.asList(ports));
portScanDemo.scanLargePorts(
"ultra-book.co"
, portSet,
5
,
800
);
}
|
五 测试结果
注:1 超时时间是以毫秒为单位,其中要是扫描国内的IP可以把这个时间适当设置低一点,200~500左右。相反,要是扫描国外IP就需要把这个时间适当设置大一点,不然有可能把本来打开的端口也漏掉了
本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1727023,如需转载请自行联系原作者