使用Python打造一個Nmap xml文件解析工具


工作過程中經常遇到客戶讓對大量資產開放的端口以及服務進行統計,於是乎就寫了個nmap 導出xml批量處理腳本。

腳本基本功能:

  • 提取IP,端口 以及端口對應的服務
  • 將結果導出至Excel( xlsx)
  • 支持批量xml處理

包依賴

  • xlsxwriter

  • 安裝


pip install xlsxwriter

Python代碼

好久沒用過XML解析了,有點生疏

# -*- coding: utf-8 -*-

"""
@author:隨時靜聽
@file: parserXML.py
@time: 2018/08/23
@email:wang_di@topsec.com.cn
"""
# http://blog.51cto.com/maoyao/1772102
# https://xlsxwriter.readthedocs.io/format.html
import re
try:
    import xml.etree.cElementTree as ET
except:
    import xml.etree.ElementTree as ET
import glob

import os
import time

# import xlwt
import xlsxwriter

import argparse

from multiprocessing import Process,Pool,Lock

XMLPATH='report'

DEFAULT_STYLE={
        'font_size': 12,  # 字體大小
        'bold': False,  # 是否粗體
        # 'bg_color': '#101010',  # 表格背景顏色
        'font_color': 'black',  # 字體顏色
        'align': 'left',  # 居中對齊
        'valign':'vcenter',
        'font_name':'Courier New',
        'top': 2,  # 上邊框
        # 后面參數是線條寬度
        'left': 2,  # 左邊框
        'right': 2,  # 右邊框
        'bottom': 2  # 底邊框
}
TITLE=[
    (u'序號',8),
    ('IP',22),
    (u'端口',10),
    (u'服務',18),
    (u'開放狀態',15),
]


#獲取寫入格式
def get_style(default=DEFAULT_STYLE,**kw):
    return  default.update(**kw)



def parseNmap(filename):
    try:
        tree=ET.parse(filename)
        root=tree.getroot()
    except Exception as e:

        print e
        return {}
    data_lst=[]
    for host in root.iter('host'):
        if host.find('status').get('state') == 'down':
            continue
        address=host.find('address').get('addr',None)
        # print address
        if not address:
            continue
        ports=[]
        for port in host.iter('port'):
            state=port.find('state').get('state','')
            port_num= port.get('portid',None)
            serv=port.find('service')
    
            serv= serv.get('name','') if serv is not None else ""
            # print serv
            ports.append([port_num,serv,state])
        data_lst.append({address:ports})
    # return {address:ports}
    return data_lst



def reportEXCEL(filename,datalst,title=TITLE,style=DEFAULT_STYLE,**kwargs):
    if not datalst:
        return ''
    if  os.path.exists(filename):
        print u"%s 文件已經存在" % filename
        path,name=os.path.split(filename)
        filename=os.path.splitext(name)[0]
        filename=filename+str(time.strftime("%Y%m%d%H%M%S",time.localtime()))+'.xlsx'
        filename=os.path.join(path,filename)
        print 'data will save as new file named :%s ' % filename

    book=xlsxwriter.Workbook(filename)
    title_style= style if not kwargs.get('title',None) else kwargs.get('title')

    row_hight=[20,16] if not kwargs.get('row_set',None) else kwargs.get('row_set')    #標題題和常規的高度
    # col_width=[8,22] if not kwargs.get('col_set',None) else kwargs.get('col_set') #序號,其他寬度
    sheet_name= 'sheet' if not kwargs.get('sheet_name',None) else kwargs.get('sheet_name')
    sheet=book.add_worksheet(sheet_name)

    row_hight=row_hight+(2000-len(row_hight))*[row_hight[-1]]
    for row , h in enumerate(row_hight):
        sheet.set_row(row,h)
    col_width=map(lambda x:x[1],title)
    for col , w in enumerate(col_width):
        sheet.set_column(col,col,w)
    title_style = book.add_format(title_style)
    for index,t in enumerate(title):

        sheet.write(0,index,t[0],title_style)

    row=1
    col=0
    style=book.add_format(style)
    index2=0
    for index,item in enumerate(datalst):
        # print item
        for ip,ports in item.items():
            port_num=len(ports)
            if not ports:
                continue
            index2=index2+1
            for  i,data in enumerate(ports):


                sheet.write(row,2,data[0],style)
                sheet.write(row,3,data[1],style)
                sheet.write(row,4,data[2],style)
                row = row + 1
            if row-port_num+1 != row:
                sheet.merge_range('B'+str(row-port_num+1)+':B'+str(row),ip,style)
                sheet.merge_range('A'+str(row-port_num+1)+':A'+str(row),index2,style)
            else:
                # print index2
                sheet.write(row-1,0,index2,style)
                sheet.write(row-1,1,ip,style)
    print  'Reprot result of xml parser to file: %s' % filename
    book.close()




def main(XMLPATH,REPORTFILENAME):

    data_lst=[]
    for xml in get_xml(XMLPATH):

        data=parseNmap(xml)
        if data:
            data_lst.extend(data)
            # print data


    reportEXCEL(REPORTFILENAME,data_lst)


if __name__ == '__main__':
    import sys
    if len(sys.argv)<3:
        print '[!] Usage: parserXML.py XMLPATH [reportfilename]'
        print '[!] Demo: parserXML.py  xmldir  result.xlsx'
    else:

        XMLPATH=sys.argv[1]
        REPORTFILENAME = sys.argv[2]
        print '[-] set parser XML file dir: %s' % XMLPATH
        print '[-] set report Excel file name: %s' % REPORTFILENAME

        if not os.path.exists(XMLPATH):
                print "[!] '%s' path does not exists!" % XMLPATH
                exit(1)
        main(XMLPATH,REPORTFILENAME)
    pass

只放代碼不教使用就是在耍流氓,接下來,如何在命令行使用

新建getResult.bat

@echo off

python "%~dp0\parser_nmap_xml_2_excel.py" %1 %2 %3 %4 %5 %6 %7 %8 %9

注意:我這里腳本名稱是arser_nmap_xml_2_excel.py,你換成你的名稱,就是你復制上面python代碼放的那個文件的名字

將腳本和getResult.bat放在一個固定額路徑下,並將這個路徑加入環境變量中:


//在控制台就可以直接使用了
getResult 存放xml的文件夾 導出xlsx文件名

效果圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM