前言

Nmap是一个非常用的网络/端口扫描工具,如果想将nmap集成进你的工具里。可以使用python-nmap这个python库,它提供了一个简单的接口来使用nmap进行扫描。

python-nmap的基本使用

在安装这个模块之前,请提前安装好nmap工具,python-nmap模块自身不提供任何扫描功能,只是提供一个接口来使用namp。

pip install python-nmap

目前最新版本是0.7.1支持python3,python2的版本详细参考:https://pypi.org/project/python-nmap/

PortScanner扫描

python-nmap其中的一个核心类是PortScanner,它负责与nmap扫描器进行交互,用于执行扫描并管理扫描结果。

首先需要创建一个PortScanner实例:

import nmap
nm = nmap.PortScanner()

执行扫描:

使用 scan() 方法执行网络扫描。可以指定目标主机、端口范围、扫描类型等参数。

def scan(  # NOQA: CFQ001, C901
self, hosts="127.0.0.1", ports=None, arguments="-sV", sudo=False, timeout=0
):
"""
:param hosts: 主机字符串,如 nmap 使用的 'scanme.nmap.org' 或 '198.116.0-255.1-127' 或 '216.163.128.20/20'
:param ports: 端口字符串,如 nmap 使用的 '22,53,110,143-4564'
:param arguments: nmap 参数字符串 '-sU -sX -sC'
:param sudo: 如果为 True,则使用 sudo 启动 nmap
:param timeout: 整数,如果大于零,将在指定秒数后终止扫描,否则将无限期等待
:returns: 扫描结果作为字典
"""

例如扫描主机和端口,scan() 返回一个字典作为扫描结果。

scan_result = nm.scan("192.168.88.150", "22")

获取扫描结果:

PortScanner封装了一系列方法,可以方便的获取扫描结果中我们想要的数据,而不需要去手动的解析上面返回的这一长串字典数据。

1、all_hosts() 获取所有扫描的主机

all_hosts = nm.all_hosts()

返回一个排序后的列表,包含所有扫描的ip地址。

['192.168.88.150']

2、command_line() 获取当前用于扫描的nmap命令

command_line = nm.command_line()

返回当前的扫描命令,这里的参数列表中的 -oX - 它会让nmap的把xml格式作为标准输出。

nmap -oX - -p 22 -sV 192.168.88.150

3、scaninfo() 获取当前扫描信息

scaninfo = nm.scaninfo()

返回一个当前扫描信息的字典。

{'tcp': {'method': 'syn', 'services': '22'}}

4、scanstats() 获取扫描统计信息

scan_stats = nm.scanstats()

返回一个当前描统计信息的字典,包括扫描时间等。

{'timestr': 'Mon Dec 30 17:25:17 2024', 'elapsed': '6.59', 'uphosts': '1', 'downhosts': '0', 'totalhosts': '1'}

5、has_host() 检查特定主机是否被扫描

has_host = nm.has_host("192.168.88.150")

如果有扫描结果返回True,否则返回False。

6、以 CSV 格式获取扫描结果

csv_result = nm.csv()

返回csv格式的文本输出。

host;hostname;hostname_type;protocol;port;name;state;product;extrainfo;reason;version;conf;cpe
192.168.88.150;;;tcp;22;ssh;open;OpenSSH;"Ubuntu Linux; protocol 2.0";syn-ack;8.9p1 Ubuntu 3ubuntu0.10;10;cpe:/o:linux:linux_kernel

写一个小案例,扫描一个网段内的所有存活主机。

import nmap

nm = nmap.PortScanner()
nm.scan(hosts='192.168.88.0/24', arguments='-sn')
hosts_list = [(x, nm[x]['status']['state']) for x in nm.all_hosts()] for host, status in hosts_list:
if status == 'up':
print(f'{host} status: {status}')

除了上述PortScanner类提供的几个基本方法,其实还可以更加灵活的运用。

PortScannerAsync异步扫描

对于需要同时扫描多个主机或端口范围的情况,使用PortScanner同步扫描,并不是一个好办法。好在python-nmap提供了一个异步扫描的方案,PortScannerAsync使用多进程技术异步扫描,避免同步扫描可能导致的阻塞,提高了扫描效率。

首先需要创建一个PortScannerAsync实例:

import nmap
nm_async = nmap.PortScannerAsync()

同样是使用 scan() 方法执行扫描:


def scan( # NOQA: CFQ002
self,
hosts="127.0.0.1",
ports=None,
arguments="-sV",
callback=None,
sudo=False,
timeout=0,
):
"""
:param hosts: 主机字符串,格式与 nmap 使用的格式相同,例如'scanme.nmap.org' 或 '198.116.0-255.1-127' 或 '216.163.128.20/20'。
:param ports: 端口字符串,格式与 nmap 使用的格式相同,例如 '22,53,110,143-4564'。
:param arguments: nmap 的参数字符串,例如 '-sU -sX -sC'。
:param callback: 回调函数,该函数以(主机,扫描数据)作为参数。
:param sudo: 如果为真,则使用 sudo 启动 nmap。
:param timeout: 整数,如果大于零,将会在指定秒数之后终止扫描,否则将无限期等待。
"""

和PortScanner的scan()很类似,但是多了一个callback参数,需要传一个回调函数,用于扫描结束后的结果处理。

import nmap

# 定义回调函数(扫描结果处理)
def scan_callback(host, data):
"""
:param host: 扫描完主机ip地址
:param data:扫描结果
"""
print(host, data) nm_async = nmap.PortScannerAsync()
hosts = '192.168.88.0/24'
ports = '1-1000'
arguments = '-sS' if __name__ == '__main__':
# 创建一个新的进程扫描,避免主进程阻塞
nm_async.scan(hosts, ports, arguments, callback=scan_callback) while nm_async.still_scanning():
# still_scanning判断是否还在扫描
print("scanning...")
nm_async.wait(1)

使用异步扫描,类似于放到后台扫描,避免了一直阻塞主进程。当然使用Process和PortScanner也可以实现一样的效果,PortScannerAsync是python-nmap封装好了,开箱即用。

python-nmap的源码分析

python-nmap其实已经比较完善了,但是如果想用做一些二次开发,不妨来看看源码,分析分析它的的工作流程。

这里主要看一下PortScanner和PortScannerAsync这两类。其中PortScanner是python-nmap的核心类,scan方法又是PortScanner的核心方法,其实只要了解了scan方法就知道python-nmap的整个逻辑了。

PortScanner的全貌:

后面6个函数前面介绍过,前面的几个函数主要也是为scan函数服务的,重点看scan函数。

scan函数的逻辑并不复杂,简单说,接收用户输入,构建完整的nmap命令行参数列表,交给nmap扫描, 获取nmap扫描结果,解析扫描结果并返回。

scan函数简化后的代码逻辑:


def scan(self, hosts="127.0.0.1", ports=None, arguments="-sV", sudo=False, timeout=0): # 对输入参数(主机、端口、扫描参数等)进行类型检查和合法性验证
if sys.version_info[0] == 2:
......
else:
...... # shlex模块对主机和扫描参数进行分割处理
h_args = shlex.split(hosts)
f_args = shlex.split(arguments) # 构建完整的nmap命令行参数列表
args = ([self._nmap_path, "-oX", "-"] + h_args + ["-p", ports] * (ports is not None) + f_args) # 启动nmap进程
p = subprocess.Popen(args, bufsize=100000, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE,) # 超时处理
if timeout == 0:
(self._nmap_last_output, nmap_err) = p.communicate()
else:
...... return self.analyse_nmap_xml_scan(nmap_xml_output=self._nmap_last_output, nmap_err=nmap_err)
  1. 它首先对输入参数(主机、端口、扫描参数等)进行类型检查和合法性验证,确保符合nmap命令的要求。

  2. 然后,使用shlex模块对主机和扫描参数进行分割处理,构建完整的nmap命令行参数列表。

  3. 在执行扫描过程中,通过subprocess.Popen启动nmap进程,并根据设置的超时时间等待扫描完成或进行超时处理。

  4. 构建nmap命令行参数列把XML格式作为标准输出。所以定义一个analyse_nmap_xml_scan函数来解析nmap的xml输出,该函数会将nmap生成的xml扫描结果解析为一个结构化的Python字典。

扫描结果的解析与存储也是重要一环,它在analyse_nmap_xml_scan函数实现。

analyse_nmap_xml_scan函数简化后的代码逻辑:


def analyse_nmap_xml_scan( self, nmap_xml_output=None, nmap_err=nmap_err): if nmap_xml_output is not None:
self._nmap_last_output = nmap_xml_output scan_result = {} # 将XML字符串转换为元素树
try:
dom = ET.fromstring(self._nmap_last_output)
except Exception:
pass # 扫描结果存储结构, get等方法拿到树中的数据
scan_result["nmap"] = {
"command_line": dom.get("args"),
"scaninfo": {
"timestr": dom.find("runstats/finished").get("timestr"),
......
},
} return scan_result

在analyse_nmap_xml_scan函数中,它会从nmap_xml_output拿到nmap的xml结果输出,再使用xml.etree.ElementTree模块对nmap扫描生成的xml输出进行解析。使用fromstring方法将xml字符串转换为可操作的元素树,然后遍历树的各个元素,提取关键信息。

通过遍历xml树结构,提取诸如扫描命令行信息、扫描统计数据、主机详细信息。对于每个主机,分别解析其地址信息、主机名信息、端口信息以及脚本输出信息,并将这些信息按照层次结构存储在scan_result字典中,也就是scan函数最后返回的内容。

其实nmap提供5种不同的输出格式,默认的方式是interactive output发送给标准输出。但-oX -会让nmap输出xml到标准输出stdout,而xml输出对于程序处理非常方便的。


PortScannerAsync类也很清晰,它使用PortScanner类进行端口扫描,multiprocessing库的Process类用于创建进程,以此实现异步扫描。

PortScannerAsync类简化后的代码逻辑:


def __scan_progressive__(self, hosts, ports, arguments, callback, timeout): # 此函数在一个单独的进程中执行扫描操作,并调用回调函数处理结果
try:
scan_data = self._nm.scan(host, ports, arguments, timeout)
except Exception:
scan_data = None if callback is not None:
callback(host, scan_data)
return class PortScannerAsync(object): def __init__(self):
self._process = None
self._nm = PortScanner() #创建一个PortScanner实例,用于执行扫描操作
return def scan(self, hosts="127.0.0.1", ports=None, arguments="-sV", callback=None, timeout=0): if sys.version_info[0] == 2:
......
else:
...... self._process = Process( # 创建一个新的进程对象,将 __scan_progressive__ 函数作为目标函数
target=__scan_progressive__,
args=(self, hosts, ports, arguments, callback, timeout),
)
self._process.daemon = True
self._process.start() return

__scan_progressive__函数,它是执行扫描任务和调用回调函数的核心部分,调用 self._nm.scan 方法进行扫描,调用 callback 函数以处理扫描结果。

scan函数会创建一个新的进程并指定 _scan_progressive_ 函数作为目标函数,将自身实例、扫描参数和回调函数等传递给该函数。

在目标函数中,针对每个发现的主机,调用PortScanner实例的scan方法进行扫描,并在扫描完成后调用用户提供的回调函数,将主机信息和扫描结果传递给回调函数进行处理。

PortScannerAsync还提供了一些管理进程的函数,用于扩展。

以上就是一个简单的分析,但愿有点用吧。

参考文章

https://pypi.org/project/python-nmap/


若有错误,欢迎指正!o( ̄▽ ̄)ブ

python-nmap实现python利用nmap扫描分析的更多相关文章

  1. 20145322 Exp5 利用nmap扫描

    20145322 Exp5 利用nmap扫描 实验过程 使用命令创建一个msf所需的数据库 service postgresql start msfdb start 使用命令msfconsole开启m ...

  2. 20145333茹翔 Exp5 利用nmap扫描

    20145333茹翔 Exp5 利用nmap扫描 实验过程 首先使用命令创建一个msf所需的数据库 service postgresql start msfdb start 使用命令msfconsol ...

  3. 20145335郝昊《网络攻防》Exp 4 利用nmap扫描

    20145335郝昊<网络攻防>Exp 4 利用nmap扫描 实验原理 使用msf辅助模块,nmap来扫描发现局域网中的主机ip 实验步骤 首先使用命令创建一个msf所需的数据库 serv ...

  4. Nmap 常用命令及抓包分析

    1.主机发现:主机发现也称为ping扫描,但是Nmap中主机发现的技术已经不是简单的采用ping工具发送简单的ICMP回声请求报文.用户完全可以通过使用列表扫描(-sL)或者通过关闭ping(-P0) ...

  5. Python调用百度接口(情感倾向分析)和讯飞接口(语音识别、关键词提取)处理音频文件

    本示例的过程是: 1. 音频转文本 2. 利用文本获取情感倾向分析结果 3. 利用文本获取关键词提取 首先是讯飞的语音识别模块.在这里可以找到非实时语音转写的相关文档以及 Python 示例.我略作了 ...

  6. 用Python做词云可视化带你分析海贼王、火影和死神三大经典动漫

    对于动漫爱好者来说,海贼王.火影.死神三大动漫神作你肯定肯定不陌生了.小编身边很多的同事仍然深爱着这些经典神作,可见"中毒"至深.今天小编利用Python大法带大家分析一下这些神作 ...

  7. 理解 python metaclass使用技巧与应用场景分析

    理解python metaclass使用技巧与应用场景分析       参考: decorator与metaclass:http://jfine-python-classes.readthedocs. ...

  8. 老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具

    老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具 poptest是业内唯一的测试开发工程师培训机构,测试开发工程师主要是为测试服务开发测试工具,在工作中要求你做网络级别的安全 ...

  9. Nmap 7.70新增功能——扫描主机所有IP

     Nmap 7.70新增功能——扫描主机所有IP 有时,一个主机可能存在多个IP地址,如网站服务器.用户可以使用nmap提供的--resolve-all选项进行扫描.其语法格式如下:nmap --re ...

  10. python学习笔记(二十九)为什么python的多线程不能利用多核CPU

    问题:为什么python的多线程不能利用多核CPU,但是咱们在写代码的时候,多线程的确是在并发,而且还比单线程快原因:因为GIL,python只有一个GIL,运行python时,就要拿到这个锁才能执行 ...

随机推荐

  1. Squid设置用户名密码

    在ubutnu上设置squid代理认证 为了在Ubuntu上设置Squid代理身份验证,您需要对Squid配置文件进行以下一些调整: 生成Squid代理身份验证密码 htpasswd是两种可用于生成代 ...

  2. 关于C++当中的指针悬空问题

    一.哪里遇到了这个问题 在进行MNN机器学习框架的MFC应用开发的时候遇到了这个问题,在窗口控件代码段 "MNN_Inference_BarCode_MFCDlg.cpp" 当中需 ...

  3. Redis学习笔记整理

    一.Redis概述 1.redis简介 Redis(REmote DIctionary Server 远程字典服务器)是一款开源的,用ANSI C编写.支持网络.基于内存.亦可持久化的日志型.Key- ...

  4. 剖析Air724UG的硬件设计,还有大发现?01部分

    ​ 一.绪论 Air724UG是一款基于紫光展锐UIS8910DM平台设计的LTE Cat 1无线通信模组.支持FDD-LTE/TDD-LTE的4G远距离通讯和Bluetooth近距离无线传输技术,支 ...

  5. linux下文件夹文件名称最大长度

    今天突发奇想,如果创建一个文件,不写入内容,就如我们之前说的写入扩展属性能快速查找数据,但是在SSD下只能写4000个左右的字符,那么有没有更快速的方法存储这样的信息呢? 我想到可以同文件名来存储信息 ...

  6. 关于meta-analysis的一些评论

    当提到meta-analysis,很多人的反应是,水文章的神器. 一方面是因为Meta分析作为系统综述里一个定量分析方法,能把各种研究结果有组织有纪律地综合起来,证据档次瞬间飙升,能甩传统综述好几条街 ...

  7. ScheduledThreadPoolExecutor与System#nanoTime

    一直流传着Timer使用的是绝对时间,ScheduledThreadPoolExecutor使用的是相对时间,那么ScheduledThreadPoolExecutor是如何实现相对时间的? 先看看S ...

  8. Prometheus之系统安装,启动

    Prometheus简介Prometheus是最初在SoundCloud上构建的开源系统监视和警报工具包. 自2012年成立以来,许多公司和组织都采用了Prometheus,该项目拥有非常活跃的开发人 ...

  9. Linux之轨迹记录(script)

    使用命令: script 编辑文件: vim /etc/profile 在最后一行添加命令 if [ $UID -ge 0 ]; then exec /usr/bin/script -t 2>/ ...

  10. 基于.NetCore开发博客项目 StarBlog - (32) 第一期完结

    前言 本文记录一下 StarBlog 项目的当前状态与接下来 v2 版本的开发规划. StarBlog 项目从 2022 年开始至今已经 2 年多了,本来早就该给第一期做个小结的,但这种博客类型的项目 ...