市面上用python封装Nmap的特别多,方法很简单,调用nmap的命令,执行并解析结果。
为了方便工具化以及微服务的调用,我用Java对Nmap进行了封装。
这里以实现存活性扫描和端口扫描为例,首先,我们创建一个类,里面包含了我们对命令的解析,以保证调用方可以直接调用函数从而实现对命令的调用。
/**
* nmap参数
*/
public class NmapArgs {
private boolean toXml;
private boolean allScan;
private boolean survival; // 存活性扫描
private boolean synPort; // syn端口扫描
private Long randomFile;
public String resultFile;
public NmapArgs toXml(){
toXml = true;
randomFile = new Double(Math.random()*1000000000L).longValue();
return this;
}
public NmapArgs allScan(){
allScan = true;
return this;
}
public NmapArgs survival(){
survival = true;
return this;
}
public NmapArgs synPort(){
synPort = true;
return this;
}
public String toArgs(){
StringBuilder command = new StringBuilder();
command.append("nmap");
if (toXml){
command.append(" -oX /root/nmap_test/").append(randomFile).append(".xml");
resultFile = "/root/nmap_test/" + randomFile + ".xml";
}
if (allScan){
command.append(" -A");
}
if (survival){
command.append(" -sP");
}
if (synPort){
command.append(" -sS");
}
return command.append(" ").toString();
}
}
... ...
// 调用方法
private static Nmap scan(String command, String resultFile) {
Nmap nmap = null;
try {
Process process = Runtime.getRuntime().exec(command);
int cnt = 0;
while (process.isAlive()) {
logger.info(command + " 进程执行中" + cnt * 5 + "秒");
Thread.sleep(5 * 1000);
cnt++;
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
int cnt = 5;
try {
while (cnt >= 0) {
nmap = readResult(resultFile);
if (Objects.nonNull(nmap.runstats.finished)) {
if (nmap.runstats.finished.exit.equals("success")) {
// TODO: 2022/3/29 成功
return nmap;
}
}
cnt--;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
这里首先利用NmapArgs来构造执行命令的参数,然后调用的scan方法里面使用
Process process = Runtime.getRuntime().exec(command);
来执行命令。等到结果出来了,解析xml即可。
使用就很简单了。例如:
实现端口扫描
public static Nmap scanPort(String targets) {
NmapArgs nmapArgs = new NmapArgs();
String command = nmapArgs.toXml().toArgs().concat(targets);
String file = nmapArgs.resultFile;
return scan(command, file);
}
实现存活性扫描
public static Nmap scanSurvival(String targets) {
NmapArgs nmapArgs = new NmapArgs();
String command = nmapArgs.toXml().survival().toArgs().concat(targets);
String file = nmapArgs.resultFile;
return scan(command,file);
}
命令行的调用是非常简单的,我们需要做的比较复杂的操作主要是对结果进行解析。根据上面代码中的指令,可以看见,我们最后是将结果导出到xml中,所以这里,我们需要实现对nmap结果进行解析。
对Nmap的xml进行解析是封装nmap最为繁琐的一步,这里我利用自己实现的一个自动化对接的工具,做到了对nmap结果的及解析。下面是我生成的结构:
import java.util.List;
public class Nmap {
public String scanner;
public String args;
public String start;
public String startstr;
public String version;
public String xmloutputversion;
public NmapScaninfo scaninfo;
public NmapVerbose verbose;
public NmapDebugging debugging;
public List<NmapHost> hosts;
public NmapRunstats runstats;
}
... ...
public class NmapDebugging {
public String level;
}
... ...
public class NmapHost {
public String starttime;
public String endtime;
public NmapHostStatus status;
public NmapHostAddress address;
public String hostnames;
public NmapHostPorts ports;
public NmapHostTimes times;
}
... ...
public class NmapHostAddress {
public String addr;
public String addrtype;
}
... ...
public class NmapHostPorts {
public NmapHostPortsExtraports extraports;
public List<NmapHostPortsPort> port;
}
... ...
public class NmapHostPortsExtraports {
public String state;
public String count;
public List<NmapHostPortsExtraportsExtrareasons> extrareasons;
}
... ...
public class NmapHostPortsExtraportsExtrareasons {
public String reason;
public String count;
}
... ...
public class NmapHostPortsPort {
public String protocol;
public NmapHostPortsPortService service;
public NmapHostPortsPortState state;
public String portid;
}
... ...
public class NmapHostPortsPortService {
public String name;
public String method;
public String conf;
}
... ...
public class NmapHostPortsPortState {
public String state;
public String reason;
public String reasonTtl;
}
... ...
public class NmapHostStatus {
public String state;
public String reason;
public String reasonTtl;
}
... ...
public class NmapHostTimes {
public String srtt;
public String rttvar;
public String to;
}
... ...
public class NmapRunstats {
public NmapRunstatsFinished finished;
public NmapRunstatsHosts hosts;
}
... ...
public class NmapRunstatsFinished {
public String time;
public String timestr;
public String elapsed;
public String summary;
public String exit;
}
... ...
public class NmapRunstatsHosts {
public String up;
public String down;
public String total;
}
... ...
public class NmapScaninfo {
public String type;
public String protocol;
public String numservices;
public String services;
}
... ...
public class NmapVerbose {
public String level;
}
也就是说,可以将Xml文件里面的内容,转化成Nmap这个大对象。转换的方法我也放在下面了(都是自动生成的,体力活)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class NmapExchange {
public static Nmap exchangeNmap(Map<String, Object> tmp) {
Nmap tmpDto = new Nmap();
tmpDto.scanner = (String) tmp.getOrDefault("scanner", "");
tmpDto.args = (String) tmp.getOrDefault("args", "");
tmpDto.start = (String) tmp.getOrDefault("start", "");
tmpDto.startstr = (String) tmp.getOrDefault("startstr", "");
tmpDto.version = (String) tmp.getOrDefault("version", "");
tmpDto.xmloutputversion = (String) tmp.getOrDefault("xmloutputversion", "");
tmpDto.scaninfo = exchangeNmapScaninfo((Map<String, Object>) tmp.getOrDefault("scaninfo", new HashMap<>()));
tmpDto.verbose = exchangeNmapVerbose((Map<String, Object>) tmp.getOrDefault("verbose", new HashMap<>()));
tmpDto.debugging = exchangeNmapDebugging((Map<String, Object>) tmp.getOrDefault("debugging", new HashMap<>()));
// tmpDto.host = exchangeNmapHost((Map<String, Object>) tmp.getOrDefault("host", new HashMap<>()));
tmpDto.runstats = exchangeNmapRunstats((Map<String, Object>) tmp.getOrDefault("runstats", new HashMap<>()));
return tmpDto;
}
public static NmapScaninfo exchangeNmapScaninfo(Map<String, Object> tmp) {
NmapScaninfo tmpDto = new NmapScaninfo();
tmpDto.type = (String) tmp.getOrDefault("type", "");
tmpDto.protocol = (String) tmp.getOrDefault("protocol", "");
tmpDto.numservices = (String) tmp.getOrDefault("numservices", "");
tmpDto.services = (String) tmp.getOrDefault("services", "");
return tmpDto;
}
public static NmapVerbose exchangeNmapVerbose(Map<String, Object> tmp) {
NmapVerbose tmpDto = new NmapVerbose();
tmpDto.level = (String) tmp.getOrDefault("level", "");
return tmpDto;
}
public static NmapDebugging exchangeNmapDebugging(Map<String, Object> tmp) {
NmapDebugging tmpDto = new NmapDebugging();
tmpDto.level = (String) tmp.getOrDefault("level", "");
return tmpDto;
}
public static NmapHost exchangeNmapHost(Map<String, Object> tmp) {
NmapHost tmpDto = new NmapHost();
tmpDto.starttime = (String) tmp.getOrDefault("starttime", "");
tmpDto.endtime = (String) tmp.getOrDefault("endtime", "");
tmpDto.status = exchangeNmapHostStatus((Map<String, Object>) tmp.getOrDefault("status", new HashMap<>()));
tmpDto.address = exchangeNmapHostAddress((Map<String, Object>) tmp.getOrDefault("address", new HashMap<>()));
tmpDto.hostnames = (String) tmp.getOrDefault("hostnames", "");
tmpDto.ports = exchangeNmapHostPorts((Map<String, Object>) tmp.getOrDefault("ports", new HashMap<>()));
tmpDto.times = exchangeNmapHostTimes((Map<String, Object>) tmp.getOrDefault("times", new HashMap<>()));
return tmpDto;
}
public static NmapHostStatus exchangeNmapHostStatus(Map<String, Object> tmp) {
NmapHostStatus tmpDto = new NmapHostStatus();
tmpDto.state = (String) tmp.getOrDefault("state", "");
tmpDto.reason = (String) tmp.getOrDefault("reason", "");
tmpDto.reasonTtl = (String) tmp.getOrDefault("reason_ttl", "");
return tmpDto;
}
public static NmapHostAddress exchangeNmapHostAddress(Map<String, Object> tmp) {
NmapHostAddress tmpDto = new NmapHostAddress();
tmpDto.addr = (String) tmp.getOrDefault("addr", "");
tmpDto.addrtype = (String) tmp.getOrDefault("addrtype", "");
return tmpDto;
}
public static NmapHostPorts exchangeNmapHostPorts(Map<String, Object> tmp) {
NmapHostPorts tmpDto = new NmapHostPorts();
tmpDto.extraports = exchangeNmapHostPortsExtraports((Map<String, Object>) tmp.getOrDefault("extraports", new HashMap<>()));
tmpDto.port = new ArrayList<>();
try {
List<Map<String, Object>> x = (List<Map<String, Object>>) tmp.getOrDefault("port", new ArrayList<>());
for (Map<String,Object> y : x){
tmpDto.port.add(exchangeNmapHostPortsPort(y));
}
}catch (Exception e){
}
return tmpDto;
}
public static NmapHostPortsExtraports exchangeNmapHostPortsExtraports(Map<String, Object> tmp) {
NmapHostPortsExtraports tmpDto = new NmapHostPortsExtraports();
tmpDto.state = (String) tmp.getOrDefault("state", "");
tmpDto.count = (String) tmp.getOrDefault("count", "");
tmpDto.extrareasons = new ArrayList<>();
try {
List<Map<String, Object>> x = (List<Map<String, Object>>) tmp.getOrDefault("extrareasons", new ArrayList<>());
for (Map<String, Object> y : x){
tmpDto.extrareasons.add(exchangeNmapHostPortsExtraportsExtrareasons(y));
}
}catch (Exception e){
}
return tmpDto;
}
public static NmapHostPortsExtraportsExtrareasons exchangeNmapHostPortsExtraportsExtrareasons(Map<String, Object> tmp) {
NmapHostPortsExtraportsExtrareasons tmpDto = new NmapHostPortsExtraportsExtrareasons();
tmpDto.reason = (String) tmp.getOrDefault("reason", "");
tmpDto.count = (String) tmp.getOrDefault("count", "");
return tmpDto;
}
public static NmapHostPortsPort exchangeNmapHostPortsPort(Map<String, Object> tmp) {
NmapHostPortsPort tmpDto = new NmapHostPortsPort();
tmpDto.protocol = (String) tmp.getOrDefault("protocol", "");
tmpDto.service = exchangeNmapHostPortsPortService((Map<String, Object>) tmp.getOrDefault("service", new HashMap<>()));
tmpDto.state = exchangeNmapHostPortsPortState((Map<String, Object>) tmp.getOrDefault("state", new HashMap<>()));
tmpDto.portid = (String) tmp.getOrDefault("portid", "");
return tmpDto;
}
public static NmapHostPortsPortService exchangeNmapHostPortsPortService(Map<String, Object> tmp) {
NmapHostPortsPortService tmpDto = new NmapHostPortsPortService();
tmpDto.name = (String) tmp.getOrDefault("name", "");
tmpDto.method = (String) tmp.getOrDefault("method", "");
tmpDto.conf = (String) tmp.getOrDefault("conf", "");
return tmpDto;
}
public static NmapHostPortsPortState exchangeNmapHostPortsPortState(Map<String, Object> tmp) {
NmapHostPortsPortState tmpDto = new NmapHostPortsPortState();
tmpDto.state = (String) tmp.getOrDefault("state", "");
tmpDto.reason = (String) tmp.getOrDefault("reason", "");
tmpDto.reasonTtl = (String) tmp.getOrDefault("reason_ttl", "");
return tmpDto;
}
public static NmapHostTimes exchangeNmapHostTimes(Map<String, Object> tmp) {
NmapHostTimes tmpDto = new NmapHostTimes();
tmpDto.srtt = (String) tmp.getOrDefault("srtt", "");
tmpDto.rttvar = (String) tmp.getOrDefault("rttvar", "");
tmpDto.to = (String) tmp.getOrDefault("to", "");
return tmpDto;
}
public static NmapRunstats exchangeNmapRunstats(Map<String, Object> tmp) {
NmapRunstats tmpDto = new NmapRunstats();
tmpDto.finished = exchangeNmapRunstatsFinished((Map<String, Object>) tmp.getOrDefault("finished", new HashMap<>()));
tmpDto.hosts = exchangeNmapRunstatsHosts((Map<String, Object>) tmp.getOrDefault("hosts", new HashMap<>()));
return tmpDto;
}
public static NmapRunstatsFinished exchangeNmapRunstatsFinished(Map<String, Object> tmp) {
NmapRunstatsFinished tmpDto = new NmapRunstatsFinished();
tmpDto.time = (String) tmp.getOrDefault("time", "");
tmpDto.timestr = (String) tmp.getOrDefault("timestr", "");
tmpDto.elapsed = (String) tmp.getOrDefault("elapsed", "");
tmpDto.summary = (String) tmp.getOrDefault("summary", "");
tmpDto.exit = (String) tmp.getOrDefault("exit", "");
return tmpDto;
}
public static NmapRunstatsHosts exchangeNmapRunstatsHosts(Map<String, Object> tmp) {
NmapRunstatsHosts tmpDto = new NmapRunstatsHosts();
tmpDto.up = (String) tmp.getOrDefault("up", "");
tmpDto.down = (String) tmp.getOrDefault("down", "");
tmpDto.total = (String) tmp.getOrDefault("total", "");
return tmpDto;
}
}
这里将xml转成hashmap结构之后,再转成对象。这里有个细节点:可能有的朋友会觉得,既然我都有HashMap了,为啥还需要这么复杂,我直接利用Json库里面的方法转成对象不就行了吗,为啥还得一个一个取出来放进去?这里这样做的目的是防止数据有结构错误,Json库会直接出错,从而造成容错性很差,特别是在一些字段无法进行直接强转的情况下。
那么问题来了,我们如何将xml转成HashMap呢?
这里就要用到相关的库了
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.12.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
然后最终将xmlStr转成Nmap对象的代码如下:
public static Nmap getNmapFromXmlStr(String xmlStr) {
ObjectMapper xmlMapper = new XmlMapper();
System.out.println(xmlStr);
Map y = null;
try {
y = xmlMapper.readValue(xmlStr, Map.class);
Nmap nmap = NmapExchange.exchangeNmap(y);
nmap.hosts = new ArrayList<>();
String[] a = xmlStr.split("\n");
int flag = 0;
StringBuilder builder = new StringBuilder();
for (String b : a) {
if (b.startsWith("<host ") || b.startsWith("<host>")) {
flag = 1;
}
if (flag == 1) {
builder.append(b).append("\n");
}
if (b.startsWith("</host>")) {
flag = 0;
NmapHost nmapHost = NmapExchange.exchangeNmapHost(xmlMapper.readValue(builder.toString(), Map.class));
nmap.hosts.add(nmapHost);
builder = new StringBuilder();
}
}
return nmap;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
这段代码在之前提到的scan方法中的readResult方法中被调用
private static Nmap readResult(String fileName) {
StringBuilder xmlStr = new StringBuilder();
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(fileName))));
String line = "";
while ((line = bufferedReader.readLine()) != null) {
xmlStr.append(line).append("\n");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Nmap nmap = XmlUtils.getNmapFromXmlStr(xmlStr.toString());
return nmap;
}
而此时我们就实现对nmap命令的封装和对其结果的反序列化,这一系列打包成一个jar给别人用就行了。