Java写黑软-端口扫描器篇
海啸天鸣(Ansty)
上次我们用Java写了一个“文件最后修改时间编辑器”的小黑软,现在我们来用Java来写我们平时经常用的端口扫描器。这次为了方便和避免GUI编程的麻烦,我们就直接做成命令行下面的工具,用参数来启动它。我们姑且把它命名为“Java版简单端口扫描工具”,因为本文只是提供Java写黑软的思路,许多算法优化和功能附加不在本文的讨论之列,使用的也是单线程。
程序界面:
程序原理:
利用java.net.Socket类来建立socket连接,如果无法与指定的IP和端口建立连接,将会抛出IOException。我们用try-catch对这个IOException这个异常进行捕获,以判断是否成功与指定的IP端口建立连接。如果成功建立了连接,说明指定IP的指定端口已经开放;如果程序抛出了一个IOException异常被我们捕获,则说明指定的IP没有开放指定的端口。扫描指定端口段则是利用循环不断与服务器的指定端口进行连接,供我们判断是否开放。
我一直坚信,世界上的所有问题只要有了明确的算法,我们就一定能用程序语言来实现它,无论什么语言!现在,我们有了原理就等于有了算法,你说我们除了技术以外还缺什么?只缺动手了!
因为我们要从程序启动的参数中获得服务器地址、起始端口、终止端口的信息,所以我们就要用到下面这段代码:
ip = args[0]; //获得我们指定的服务器地址
startPort = Integer.parseInt(args[1]); //获得起始端口号,因为args[]是String类型,所以要强制转换成int类型。
endPort = Integer.parseInt(args[2]); //获得终止端口号,同上
在得到端口和建立socket之前一定要判断端口的合法性,因为端口的范围是在1~65535,如果我们去建立范围外端口的连接是没必要的,而且是不可行的。既然是起始端口和终止端口,那么就要有个大小顺序问题,就是判断它们的大小。
Copy code
if(startPort<1||startPort>65535||endPort<1||endPort>65535){ //检查端口是否在合法范围1~65535
System.out.printf("端口范围必须在1~65535以内!");
return;
}else if(startPort>endPort){ //比较起始端口和终止端口
System.out.println("端口输入有误!/n起始端口必须小于终止端口");
return;
}
建立与服务器指定端口的连接,这里就要用到java.net.Socket类了,首先我们来看看它的构造方法:
Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程端口上的指定远程地址。
Socket(Proxy proxy) 根据不管其他设置如何都应使用的指定代理类型(如果有),创建一个未连接的套接字。
Socket(SocketImpl impl) 创建带有用户指定的 SocketImpl 的未连接 Socket。
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket(String host, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程主机上的指定远程端口。
可以看到,我们有很多中构造方法,我们现在只需要关心第二种构造方法Socket(InetAddress address, int port),因为我们并不需要与服务器运行在端口的服务进行交互,所以我们只要建立连接,然后关闭连接即可。即:
Socket s = new Socket(address,port);
我们在建立连接之前,首先要把ip转换成InetAddress类型,不是说String类型不能用,是为了能减少出现更多异常的可能。
static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。这是静态方法,直接InetAddress.getByName()就行了。
Copy code
try{
InetAddress address = InetAddress.getByName(ip); //转换类型
}catch(UnknownHostException e){
System.out.println("无法找到 "+ ip);
return;
}
下面是我们的核心算法了
循环指定端口段的所有端口,对所有端口建立连接。连接成功后我们就算完成了当前循环的任务,然后就调用close()方法关闭连接。因为在Socket s = new Socket(address,nport)执行的时候,如果成功建立连接,就不会执行到catch里面,就能执行到下面result.add(“”+nport)语句;如果不能连接上去就会抛出一个异常被我们捕获,程序就会运行到catch里面,执行catch里面的语句;最后继续下一个循环。
Copy code
for(int nport=startPort;nport<=endPort;nport++){ //从起始端口到终止端口进行循环
try{
System.out.print("Scanning "+nport); //打印扫描进度
Socket s=new Socket(address,nport); //建立连接
s.close(); //关闭连接
result.add(""+nport); //将打开的端口添加到ArrayList result里面
System.out.println(" : open"); //打印状态
}catch(IOException e){
System.out.println(":close"); //打印状态
}
}
打印结果,因为我们用到了ArrayList来存储扫描结果,Java里面没有C语言意义上的指针,所以我们在访问ArrayList里面的元素要用到ListIterator。
ListIterator li = result.listIterator(); //获得ArrayList的ListIterator
while(li.hasNext()){ // 如果li里面有元素
System.out.println(li.next().toString()+"/tOpen"); //打印出指向的元素,同时将指向下一个元素
}
好了,现在我们就已经把主要功能的程序代码介绍完了,相信读者看完以后能用Java编写自己的Java版黑软了。正如我前文说的,本文只提供一种思路。如果朋友有兴趣,可以自己在本文的基础上实现多线程、扩展一些有用的功能,把GUI界面做出来,或者做成仿SuperScan就更强大了。本人在黑防论坛的ID是Ansty,大家对本文有问题可以到论坛里M我