使用SpringBoot+nmap4j获取端口信息的代码详解
作者:发愤图强的羔羊
序言
今天在工作的时候,领导突然安排和我说一个需求,就是根据一个 ip 和 端口去获取对应服务上对应端口的信息,当时主要是为了确定数据库的版本和型号,比如 MySQL、Oracle 这些数据库,我后面尝试发现其他端口也可以获取信息。这个在公司里之前是通过 python 来写的,python 里面刚好有这个模块,但是 Java 没有,所以写这篇文章记录一下,帮助大家以后避免踩坑。
nmap4j
nmap4j 是一个用于 Java 语言的 Nmap 端口扫描器的包装库,它允许 Java 开发者在自己的项目中方便地调用 Nmap 的功能进行网络扫描和探测。
在运行这个代码之前,我们是需要下载 nmap 的可执行文件,地址为:Download the Free Nmap Security Scanner for Linux/Mac/Windows
这里下载第一个,下载后安装就行了。
代码说明
接下来和大家说一下 nmap4j 中的测试代码,这里面有个坑,我当时找了好久,代码在 test/org/nmap4j/Nmap4jTest.java
public class Nmap4jTest { @Test public void basicNmap4jUsageTest() { try { // 这里的路径要改为刚才 nmap 软件的安装路径 String path = "/usr/local" ; Nmap4j nmap4j = new Nmap4j( path ) ; // 这地方使用了 -oX 后面要跟文件名称 nmap4j.addFlags( "-sV -T5 -O -oX -" ) ; nmap4j.includeHosts( "localhost" ) ; nmap4j.execute() ; if( !nmap4j.hasError() ) { NMapRun nmapRun = nmap4j.getResult() ; // 这一行一定要注释掉,不然会一直报错 String output = nmap4j.getOutput() ; // 这一样代码意义也不大,我直接删掉了 if( output == null ) { fail() ; } String errors = nmap4j.getExecutionResults().getErrors() ; if (errors == null ) { fail() ; } } } catch (NMapInitializationException e) { // TODO Auto-generated catch block e.printStackTrace(); fail() ; } catch (NMapExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); fail() ; } } }
参数说明:
目标选择参数:
- -iL :从文件中读取扫描目标列表。例如,
nmap -iL targets.txt
,会从targets.txt
文件中读取每行一个的 IP 地址或域名作为扫描目标。 - -iR :随机选择指定数量的主机进行扫描。如
nmap -iR 100
,会随机选取 100 个主机进行扫描。 - --exclude <host1[,host2,...]> :排除指定的主机或网络不进行扫描。例如,
nmap 192.168.1.0/24 --exclude 192.168.1.100,192.168.1.200
,将扫描192.168.1.0/24
网段,但排除192.168.1.100
和192.168.1.200
这两台主机。 - --excludefile <exclude_file> :从文件中读取要排除的主机或网络列表。
- -iL :从文件中读取扫描目标列表。例如,
扫描类型参数
- -sS:TCP SYN 扫描,也称为半开放扫描。它发送 SYN 包到目标端口,如果收到 SYN/ACK 响应,就表示端口开放;如果收到 RST 响应,则表示端口关闭。这种扫描方式速度快,且不容易被目标系统记录,相对隐蔽,例如
nmap -sS 192.168.1.100
。 - -sT:TCP 连接扫描,通过完整的 TCP 三次握手来确定端口是否开放。这种扫描方式最准确,但也最容易被检测到,如
nmap -sT 192.168.1.100
。 - -sU:UDP 扫描,用于检测目标主机上的 UDP 端口是否开放。因为 UDP 是无连接协议,所以判断端口状态相对复杂,
nmap -sU 192.168.1.100
可对指定主机进行 UDP 扫描。 - -sF、-sX、-sN:分别是 FIN 扫描、XMAS 扫描和 NULL 扫描。这些扫描方式通过发送特殊标志位的 TCP 包来判断端口状态,常用于绕过一些简单的防火墙检测。
- -sS:TCP SYN 扫描,也称为半开放扫描。它发送 SYN 包到目标端口,如果收到 SYN/ACK 响应,就表示端口开放;如果收到 RST 响应,则表示端口关闭。这种扫描方式速度快,且不容易被目标系统记录,相对隐蔽,例如
端口指定参数
- -p :指定要扫描的端口范围。可以是单个端口,如
-p 80
;也可以是多个端口,如-p 80,443,8080
;还可以是端口范围,如-p 1-1000
表示扫描 1 到 1000 号端口。 - --top-ports :扫描最常用的指定数量的端口。例如,
nmap --top-ports 100 192.168.1.100
会扫描目标主机上最常用的 100 个端口。 - -F:快速扫描模式,只扫描一些常见的端口,相当于
-p 1-1024
加上一些其他常用端口。
- -p :指定要扫描的端口范围。可以是单个端口,如
服务探测参数
- -sV:启用服务版本探测,尝试确定目标主机上运行的服务及其版本信息。例如,
nmap -sV 192.168.1.100
可以扫描出目标主机开放端口上运行的服务名称和版本号。 - --version-intensity :设置服务版本探测的强度,级别越高,探测越全面,但耗时也越长,取值范围是 0 到 9。
- -sV:启用服务版本探测,尝试确定目标主机上运行的服务及其版本信息。例如,
操作系统探测参数
- -O:启用操作系统探测,尝试识别目标主机的操作系统类型和版本。如
nmap -O 192.168.1.100
。 - --osscan-limit:限制操作系统探测只对可能的目标进行,这样可以加快扫描速度,但可能会降低准确性。
- --osscan-guess:更积极地猜测操作系统类型,当 Nmap 不确定时会给出更宽泛的猜测结果。
- -O:启用操作系统探测,尝试识别目标主机的操作系统类型和版本。如
输出参数
- -oN :将扫描结果以正常格式保存到指定文件。例如,
nmap -oN scan_results.txt 192.168.1.100
会把扫描结果保存到scan_results.txt
文件中。 - -oX :将扫描结果以 XML 格式保存到指定文件,方便后续使用脚本或其他工具进行解析和处理。
- -oG :将扫描结果以 Grep 格式保存,这种格式便于使用文本处理工具进行分析。
- -oA :以多种格式(包括正常、XML 和 Grep 格式)保存扫描结果,文件名为指定的基本名称加上相应的扩展名。
- -v:详细输出模式,显示更多的扫描过程信息,如发送的数据包、收到的响应等。使用多个
v
可以增加详细程度,如-vv
、-vvv
。
- -oN :将扫描结果以正常格式保存到指定文件。例如,
这里给大家看一下改造后的代码
/** * 使用 nmap4j 工具进行扫描 * * @param ip 目标 ip * @param ports 目标端口 * @return 端口信息列表 */ @RequestMapping("/querydb") public List<NmapPortInfo> querydb(@RequestParam(value = "ip") String ip, @RequestParam("ports") List<String> ports) { ArrayList<NmapPortInfo> portInfos = new ArrayList<>(); // 1.拼接端口 String portStr = StrUtil.join(",", ports); //2. 指定 nmap 路径 String path = "D:/StudyApps/nmap"; String fileName = "temp_result.xml"; Nmap4j nmap4j = new Nmap4j(path); //3.读取端口耗时较长,可以使用异步 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { nmap4j.addFlags("-sV -p " + portStr + " -T5 -O -oX " + fileName); nmap4j.includeHosts(ip); try { nmap4j.execute(); } catch (Exception e) { throw new RuntimeException(e); } }, threadPoolExecutor); future.join(); //4. 获取端口信息 return getPortInfo(portInfos, fileName); }
/** * 获取 ip + 端口信息,封装为集合返回前端 * * @param portInfos 返回前端集合 * @param fileName 临时的 xml 文件 * @return 信息列表 */ @SneakyThrows private List<NmapPortInfo> getPortInfo(List<NmapPortInfo> portInfos, String fileName) { // 获取项目所在路径 String projectPath = System.getProperty("user.dir"); // 拼接文件路径 String filePath = projectPath + FileUtil.FILE_SEPARATOR + fileName; log.info("文件路径:{}", filePath); // nmap 返回 xml 格式固定,使用 dom4j 解析 SAXReader reader = new SAXReader(); org.dom4j.Document document = reader.read(FileUtil.file(filePath)); org.dom4j.Element rootElement = document.getRootElement(); org.dom4j.Element element = rootElement.element("host"); org.dom4j.Element xmlPorts = element.element("ports"); List<org.dom4j.Element> port = xmlPorts.elements("port"); for (org.dom4j.Element port1 : port) { Element service = port1.element("service"); String product = service.attributeValue("product"); String version = service.attributeValue("version"); NmapPortInfo nmapPortInfo = new NmapPortInfo(product, version); portInfos.add(nmapPortInfo); } // 删除临时文件 FileUtil.del(filePath); return portInfos; } }
我这里就是没有去按照官网上的写法,我的思路是文件已经下载了我直接去读取 xml 文件解决会更快,这里是使用 dom4j 来读取的 xml 文件。代码就这么多,最后请求是可以获取到数据的:
总结
忘记和大家说了,nmap4j 在maven仓库是搜不到的,所以只通过 jar 包(点击下载)的方式来引入。
以上就是使用SpringBoot+nmap4j获取端口信息的代码详解的详细内容,更多关于SpringBoot nmap4j获取端口信息的资料请关注脚本之家其它相关文章!