# -*- coding: utf-8 -*-
"""
@author:随时静听
@file: shodanForIp.py
@time: 2019/01/28
@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
''')
# shodan访问API key
API_KEY=""
CACHE_ON = True
CACHE_FILES=[]
CACHE_DIR = "./cache"
LOG_ON = True
# Excel格式配置
EXCEL_TILTE=[u"序号","IP","Shodan_PORTS"]
# 列宽设置
COL_WIDTH=[7,20,30]
# 行高设置
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',
}
FAILED_LST=[]
#
DATA_DIC={}
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
def parseArgs():
global API_KEY, CACHE_ON, LOG_ON , CACHE_DIR
parser = argparse.ArgumentParser(
usage='''
python shellFilename -l ip_lst.txt -o outfile.xlsx \n\n
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('-l', "--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)
index+=1
book.close()
# 处理缓存数据
def cache_processing(func):
@wraps(func)
def inner(*args,**kwargs):
if len(args)==2:
#如果开启缓存
if CACHE_ON:
ret=DATA_DIC.get(args[0],None)
#如果从缓存文件中获取不到数据就直接请求加入缓存数据中
if not ret:
ret = func(*args, **kwargs)
if ret:
DATA_DIC.update({args[0]:ret})#讲数据加入缓存字典
else:
ret=func(*args,**kwargs)
try:
if LOG_ON:
print "[-] INFO: " + args[0] +" : " +json.dumps(ret,sort_keys=True, indent=2)
except Exception as e:
print "[!] Failed: " + args[0]
return ret
else:
print "[!] Error: Too few function parameters! In function "+func.__name__
return None
return inner
@cache_processing
def load_data_from_shodan(ip,shodan_api):
try:
data_dic=shodan_api.host(ip,history=False)
return data_dic
except Exception as e:
pass
return None
def writeCache():
'''
缓存数据回写
:return:
'''
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 get_ip_lst(filename):
if os.path.exists(filename):
with open(filename,'r') as f:
return map(lambda x : x.strip(),f.readlines())
else:
exit("[!] INFO: IP file is zero IP! ("+filename+")")
def runMain():
env_init()
#统计信息
num=0
# 获取解析的xml和导出的文件名
ip_lst_file, outfile = parseArgs()
print "[-] INFO: Processing IP file: "+ip_lst_file
print "[-] INFO: Set output Excel file name : "+outfile
# 获取file 中的主机信息
host_lst=get_ip_lst(ip_lst_file)
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)
result_lst = []
if not host_lst :
exit("[!] Parse IP file ("+ip_lst_file+") 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
shodan_ports = host_dic.get('ports', [])
result_lst.append( (host,shodan_ports) )
printProgress(i + 1, len(host_lst), host)
ReportExcel(result_lst,outfile)
writeCache()
print "[-] INFO: All has Finished processing ! Total:" + str(num)
print "[-] INFO: Excel File save path: " + outfile
if __name__ == '__main__':
import datetime
t_start=datetime.datetime.now()
runMain()
t_end=datetime.datetime.now()
print u"[!] INFO: 用时 [ "+str((t_end-t_start).total_seconds())+" S ]"
# api=shodan.Shodan(API_KEY)
# print api.host("211.147.146.74",history=False)
pass