NMAP xml文件系解析使用python-libnmap
pip install python-libnmap
参数解析使用 argparse
pip install argparse
导出Excel使用xlsxwriter模块
pip install xlsxwriter
json序列化成python 对象使用 simple-json模块
dumping 最快的是json内置模块
demsjon 性能不如simplejson
simplejson的loading数度比 dumping 快速
进度条的实现
#打印进度条,这个进度条打印思路很不错
def printProgress(cnt, tot, target='', previouslen=0):
percent = 100 * float(cnt) / float(tot)
if target and previouslen > len(target):
target = target + ' ' * (previouslen - len(target))
sys.stdout.write('[%-40s] %d%% %s\r' % ('='*int(float(percent)/100*40), percent, target))
sys.stdout.flush()
return ''
代码
# -*- coding: utf-8 -*-
"""
@author:随时静听
@file: SD2.py
@time: 2019/01/25
@email:d1314ziting@163.com
"""
try:
from libnmap.parser import NmapParser
import shodan
import os
import glob
import simplejson #用于loding
import json #用于 dumping
import argparse
from functools import wraps
import xlsxwriter
import sys
except :
exit(u'''
Python package dependency:(请检查包依赖)\n
pip install xlsxwriter\n
pip install simplejson\n
pip install python-libnmap\n
pip install argparse\n
''')
from collections import OrderedDict
# 脚本路径
BASE_DIR=os.path.dirname(os.path.abspath(__file__))
print BASE_DIR
# shodan访问API key
API_KEY=""
# 是否实时显示拉取的数据
LOG_ON=False
# 是否开启数据缓存
CACHE_ON=True
# 缓存数据存储位置配置
CACHE_DIR="./cache"
CACHE_FILES=[]
# 提取失败的任务记录
FAILED_FILE=os.path.join(CACHE_DIR+"Failed.lst")
FAILED_LST=[]
#
DATA_DIC={}
# Excel格式配置
EXCEL_TILTE=[u"序号","IP","NMAP_PORTS","Shodan_PORTS"]
# 列宽设置
COL_WIDTH=[7,20,30,35]
# 行高设置
ROW_HIGHT=[(0,17),("other",15)]#第一行和其他行高度
title_style={
'bold':True,#字体加粗
'align':'center',#水平位置设置:居中
'valign':'vcenter',#垂直位置设置,居中
'font_size':12,#'字体大小设置'
'font_name':'Courier New',
'border':1,#边框设置样式1
'border_color':'black',#边框颜色
'bg_color':'#009ad6',#背景颜色设置
}
body_style={
'align': 'left',
'valign': 'vcenter',
'font_size': 10,
'font_name':'Courier New',#字体设置
'border': 2,
'border_color': '#808080',
'font_name': 'Courier New',
}
def env_init():
'''
程序运行环境初始化:
1. 缓存路径检查,不存在就创建
2. 缓存文件见检查,如果存在将获取缓存文件名字,从现有文件中获取,加快获取数据
:return:
'''
if not os.path.exists(CACHE_DIR):
os.makedirs(CACHE_DIR)
CACHE_FILES.extend([os.path.join(CACHE_DIR,json_file) for json_file in glob.glob1(CACHE_DIR,"*.json")])
if CACHE_ON :#开启缓存加速
for filename in CACHE_FILES:
try:
with open(filename,'r') as fr:
data = simplejson.load(fr)
DATA_DIC.update(data)
except Exception as e:
print '[!] Error: loding data from file failed! '+filename
print '[!] Error: '+ e.message
# 解析nmap xml 文件
def parseNmapXml(filename = ""):
if not filename:
print "[!] Error: Nmap xml file is null !"
return None
# nmap xml 文件存在性校验
if not os.path.exists(filename):
print "[!] Error: Nmap xml file does not exist !"
#解析nmap xml数据
try:
nmap_obj = NmapParser.parse_fromfile(filename)
except Exception as e:
# xml解析失败异常处理
print "[!] Error: {} XML file parsing failed ! "
return []
return nmap_obj.hosts
# 处理缓存数据
def cache_processing(func):
@wraps(func)
def inner(*args,**kwargs):
if len(args)==2:
#如果开启缓存
if CACHE_ON:
ret=DATA_DIC.get(args[0].id,None)
#如果从缓存文件中获取不到数据就直接请求加入缓存数据中
if not ret:
ret = func(*args, **kwargs)
if ret:
DATA_DIC.update({args[0].id:ret})#讲数据加入缓存字典
else:
ret=func(*args,**kwargs)
try:
if LOG_ON:
print "[-] INFO: " + args[0].id +" : " +json.dumps(ret,sort_keys=True, indent=2)
except Exception as e:
print "[!] Failed: " + args[0].id
return ret
else:
print "[!] Error: Too few function parameters! In function "+func.__name__
return None
return inner
@cache_processing
def load_data_from_shodan(host,shodan_api):
try:
data_dic=shodan_api.host(host.id,history=False)
return data_dic
except Exception as e:
pass
return None
def parserArgs():
global API_KEY,CACHE_ON,LOG_ON,CACHE_DIR
parser=argparse.ArgumentParser(
usage="python shellFilename -f nmap_xml -o outfile.xlsx ",
description=u'''
Python package dependency:\n
\tpip install xlsxwriter\n
\tpip install python-libnmap\n
\tpip install argparse\n
程序说明:\n
\t1. 默认不开启开启缓存 ,请使用 --make-cache开启缓存\n
\t2. 如果需要实时查看信息获取,请使用 -v 参数\n
\t3. 使用 --api-key shodan-key 来初始化 shodan api \n
''',epilog=".."*50)
parser.add_argument('-f',"--file",help="A File Output scan in normal using nmap",required=True)
parser.add_argument('-o',"--output",help="Report xlsx filename",required=True)
parser.add_argument('-v',"--verbosity",help=u"Print data in real time (默认关闭)",action="store_true")
parser.add_argument('-c',"--make-cache",help=u"Create cached data (默认关闭)",action="store_true")
parser.add_argument("--cache-path",help="Cache file path",default="./cache")
parser.add_argument("--api-key",help="A key for Shodan API")
args = parser.parse_args()
if args.api_key:
API_KEY=args.api_key
if args.cache_path:
CACHE_DIR=args.cache_path
CACHE_ON=args.make_cache
LOG_ON=args.verbosity
if args.output:
if os.path.exists(args.output):
print u"[!] Error: 文件已经存在 ! ("+args.output+")."
exit(u"[!] 程序退出")
return (args.file,args.output)
def ReportExcel(data_lst,outfile,title_style=title_style,body_style=body_style,title=EXCEL_TILTE,c_w=COL_WIDTH,r_h=ROW_HIGHT):
if not outfile:
exit("[!] Error in Function : ReportExcel.")
book=xlsxwriter.Workbook(outfile)
sheet=book.add_worksheet("Result")
title_style=book.add_format(title_style)
body_style=book.add_format(body_style)
# 设置 列宽
for c, w in enumerate(c_w):
# print c, w
sheet.set_column(c, c + 1, w)
# 设置 行高
exculde_r=[]
other_h=0
for r,h in r_h:
if r!="other":
sheet.set_row(r,h)
exculde_r.append(r)
if r=="other":
other_h=h
for i in list(set(range(1000))-set(exculde_r)):
sheet.set_row(i,other_h)
#写入标题
for i,data in enumerate(title):
sheet.write(0,i,data,title_style)
# 数据写入
index=1
for r,data in enumerate(data_lst): # host,nmap_ports_lst,shodan_ports_lst
sheet.write(r+1,0,index,body_style)
#写入 IP
sheet.write(r+1,1,data[0],body_style)
if not data[1]:
sheet.write(r+1,2,"/",body_style)
else:
sheet.write(r+1,2,",".join([ str(p[0]) for p in data[1] ]),body_style)
if not data[2]:
sheet.write(r+1,3,"/",body_style)
else:
sheet.write(r+1,3,",".join([ str(p) for p in data[2] ]),body_style)
index+=1
book.close()
def writeCache():
'''
缓存数据回写
:return:
'''
import datetime
import time
cache_filename=time.strftime("%Y%m%d_%H%M%S",time.localtime())+".json"
if CACHE_ON:
with open(os.path.join(CACHE_DIR,cache_filename),"w") as f:
try:
json.dump(DATA_DIC,f)
except:
print "WARNING: CACHE DATA WRITE FAILED!"
#打印进度条,这个进度条打印思路很不错
def printProgress(cnt, tot, target='', previouslen=0):
percent = 100 * float(cnt) / float(tot)
if target and previouslen > len(target):
target = target + ' ' * (previouslen - len(target))
sys.stdout.write('[%-40s] %d%% %s\r' % ('='*int(float(percent)/100*40), percent, target))
sys.stdout.flush()
return ''
def runMain():
env_init()
#统计信息
num=0
# 获取解析的xml和导出的文件名
nmap_xml, outfile = parserArgs()
print "[-] INFO: Processing Nmap xml file: "+nmap_xml
print "[-] INFO: Set output Excel file name : "+outfile
# 获取xml 中的主机信息
host_lst = parseNmapXml(nmap_xml)
print "[-] INFO: Parser Nmap XML file completed! GET HOST NUM: "+ str(len(host_lst))
# 初始化shodan API
try:
api = shodan.Shodan(API_KEY)
except Exception as e:
print "[!] Error: Shodan API initialization failed! "
exit(u"[!] 程序异常退出:"+e.message)
# nmap_xml_lst=[]
# shodan_lst=[]
result_lst = []
if not host_lst :
exit("[!] Parse nmap xml file ("+nmap_xml+") 0 host!")
for i,host in enumerate(host_lst) :
host_dic = load_data_from_shodan(host,api)
# print DATA_DIC
if host_dic:
num+=1
nmap_ports = host.get_open_ports()
shodan_ports = host_dic.get('ports', [])
result_lst.append( (host.id, nmap_ports, shodan_ports) )
printProgress(i + 1, len(host_lst), host.id)
ReportExcel(result_lst,outfile)
writeCache()
print "[-] INFO: All has Finished processing ! Total:" + str(num)
print "[-] INFO: Excel File save path: " + outfile
if __name__ == '__main__':
# 测试
# ReportExcel([("192.168.1.1",[1,2,3],[]),["192.168.2.3",[77,8080,9001,8007],[999,3306,3389] ] ],"123.xlsx")
# print CACHE_ON
runMain()
效果图
![](https://img2018.cnblogs.com/blog/1473748/201901/1473748-20190128084717675-1558069891.png)