巡风xunfeng代码研究---threading线程(nascan)

 

一、

    def masscan(self, ip):
        try:
            if len(ip) == 0: return
            sys.path.append(sys.path[0] + "/plugin")
            m_scan = __import__("masscan")
            result = m_scan.run(ip, self.masscan_path, self.masscan_rate)
            return result
        except Exception, e:
            print e
            print 'No masscan plugin detected'

  我们知道import语句是用来导入外部模块的,当然还有from...import...也可以,但是其实import实际上是使用builtin函数__import__来工作的。 
    在一些程序中,我们可以动态地去调用函数,如果我们知道模块的名称(字符串)的时候,我们可以很方便的使用动态调用。 然后本例子里直接导入对应的module后运行,

 

 

二、

    def scan_start(self):
        for i in range(self.thread):  # 开始扫描
            t = ThreadNum(self.queue)
            t.setDaemon(True)
            t.mode = self.mode
            t.config_ini = self.config_ini
            t.statistics = self.statistics
            t.start()
        self.queue.join()

 

 

 

三、开启多线程的类

 

AC_PORT_LIST = {}
MASSCAN_AC = 0

class ThreadNum(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            if self.queue.empty(): break
            try:
                task_host = self.queue.get()
                if self.mode:
                    port_list = AC_PORT_LIST[task_host]
                else:
                    port_list = self.config_ini['Port_list'].split('|')[1].split('\n')
                _s = scan.scan(task_host, port_list)
                _s.config_ini = self.config_ini  # 提供配置信息
                _s.statistics = self.statistics  # 提供统计信息
                _s.run()
            except Exception,e:
                print e
            finally:
                self.queue.task_done()
每一个scan.scan扫描方法执行一次,生成一个线程

 

 

四、 Scan方法讲解(nascan,非mascan)

 1) 扫描端口: 

      

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   ---#定义socket类型,网络通信,TCP
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) --默认的socket选项不够用的时候,就必须要使用setsockopt来调整。就是使用setsockopt。
---有三个参数: level:选项定义的层次。支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
optname:需设置的选项。
value:设置选项的值。
sock.connect((self.ip, self.port))  --#要连接的IP与端口
time.sleep(0.2)


self.banner = sock.recv(1024)  --#把接收的数据定义为变量
sock.close() --然后关闭连接
if len(self.banner) <= 2: --前两个字节是换行符 \r \n
self.banner = 'NULL'

banner = ''
hostname = self.ip2hostname(self.ip) -- 将IP转换为主机名 (此功能是自动发现主机)
time_ = datetime.datetime.now()
date_ = time_.strftime('%Y-%m-%d')
try:
banner = unicode(self.banner, errors='replace')
if self.banner == 'NULL': banner = ''
mongo.NA_INFO.insert({"ip": self.ip, "port": self.port, "hostname": hostname, "banner": banner, "time": time_})
self.statistics[date_]['add'] += 1


ip2hostname   具体数据原理后续补上  

请注意: ip2hostname 里的

socket.socket(socket.AF_INET, socket.SOCK_DGRAM) --是指数据报式socket , for UDP
sendto(query_data, (ip, dport)) --将query_data数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
.recvfrom(1024) --与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。




# coding:utf-8
import mongo
import log
import socket
import datetime
import urllib2
import re
import time
import ssl
import gzip
import StringIO

try:
    _create_unverified_https_context = ssl._create_unverified_context  # 忽略证书错误
except AttributeError:
    pass
else:
    ssl._create_default_https_context = _create_unverified_https_context
class scan:
    def __init__(self, task_host, port_list):
        self.ip = task_host
        self.port_list = port_list
        self.config_ini = {}

    def run(self):
        self.timeout = int(self.config_ini['Timeout'])
        for _port in self.port_list:
            self.server = ''
            self.banner = ''
            self.port = int(_port)
            self.scan_port()  # 端口扫描
            if not self.banner:continue
            self.server_discern()  # 服务识别
            if self.server == '':
                web_info = self.try_web()  # 尝试web访问
                if web_info:
                    log.write('web', self.ip, self.port, web_info)
                    time_ = datetime.datetime.now()
                    mongo.NA_INFO.update({'ip': self.ip, 'port': self.port},
                                         {"$set": {'banner': self.banner, 'server': 'web', 'webinfo': web_info,
                                                   'time': time_}})
    def scan_port(self):
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.connect((self.ip, self.port))
            time.sleep(0.2)
        except Exception, e:
            return
        try:
            self.banner = sock.recv(1024)
            sock.close()
            if len(self.banner) <= 2:
                self.banner = 'NULL'
        except Exception, e:
            self.banner = 'NULL'
        log.write('portscan', self.ip, self.port, None)
        banner = ''
        hostname = self.ip2hostname(self.ip)
        time_ = datetime.datetime.now()
        date_ = time_.strftime('%Y-%m-%d')
        try:
            banner = unicode(self.banner, errors='replace')
            if self.banner == 'NULL': banner = ''
            mongo.NA_INFO.insert({"ip": self.ip, "port": self.port, "hostname": hostname, "banner": banner, "time": time_})
            self.statistics[date_]['add'] += 1
        except:
            if banner:
                history_info = mongo.NA_INFO.find_and_modify(
                    query={"ip": self.ip, "port": self.port, "banner": {"$ne": banner}}, remove=True)
                if history_info:
                    mongo.NA_INFO.insert(
                        {"ip": self.ip, "port": self.port, "hostname": hostname, "banner": banner, "time": time_})
                    self.statistics[date_]['update'] += 1
                    del history_info["_id"]
                    history_info['del_time'] = time_
                    history_info['type'] = 'update'
                    mongo.NA_HISTORY.insert(history_info)
    def server_discern(self):
        for mark_info in self.config_ini['Discern_server']: # 快速识别
            try:
                name, default_port, mode, reg = mark_info
                if mode == 'default':
                    if int(default_port) == self.port:
                        self.server = name
                elif mode == 'banner':
                    matchObj = re.search(reg, self.banner, re.I | re.M)
                    if matchObj:
                        self.server = name
                if self.server:break
            except:
                continue
        if not self.server and self.port not in [80,443,8080]:
            for mark_info in self.config_ini['Discern_server']:  # 发包识别
                try:
                    name, default_port, mode, reg = mark_info
                    if mode not in ['default','banner']:
                        dis_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                        dis_sock.connect((self.ip, self.port))
                        mode = mode.decode('string_escape')
                        reg = reg.decode('string_escape')
                        dis_sock.send(mode)
                        time.sleep(0.3)
                        dis_recv = dis_sock.recv(1024)
                        matchObj = re.search(reg, dis_recv, re.I | re.M)
                        if matchObj:
                            self.server = name
                            break
                except:
                    pass
        if self.server:
            log.write("server", self.ip, self.port, self.server)
            mongo.NA_INFO.update({"ip": self.ip, "port": self.port}, {"$set": {"server": self.server}})

    def try_web(self):
        title_str, html = '', ''
        try:
            if self.port == 443:
                info = urllib2.urlopen("https://%s:%s" % (self.ip, self.port), timeout=self.timeout)
            else:
                info = urllib2.urlopen("http://%s:%s" % (self.ip, self.port), timeout=self.timeout)
            html = info.read()
            header = info.headers
        except urllib2.HTTPError, e:
            html = e.read()
            header = e.headers
        except:
            return
        if not header: return
        if 'Content-Encoding' in header and 'gzip' in header['Content-Encoding']:  # 解压gzip
            html_data = StringIO.StringIO(html)
            gz = gzip.GzipFile(fileobj=html_data)
            html = gz.read()
        try:
            html_code = self.get_code(header, html).strip()
            if html_code and len(html_code) < 12:
                html = html.decode(html_code).encode('utf-8')
        except: pass
        try:
            title = re.search(r'<title>(.*?)</title>', html, flags=re.I | re.M)
            if title: title_str = title.group(1)
        except: pass
        try:
            web_banner = str(header) + "\r\n\r\n" + html
            self.banner = web_banner
            history_info = mongo.NA_INFO.find_one({"ip": self.ip, "port": self.port})
            if 'server' not in history_info:
                tag = self.get_tag()
                web_info = {'title': title_str, 'tag': tag}
                return web_info
            else:
                if abs(len(history_info['banner'].encode('utf-8')) - len(web_banner)) > len(web_banner) / 60:
                    del history_info['_id']
                    history_info['del_time'] = datetime.datetime.now()
                    mongo.NA_HISTORY.insert(history_info)
                    tag = self.get_tag()
                    web_info = {'title': title_str, 'tag': tag}
                    date_ = datetime.datetime.now().strftime('%Y-%m-%d')
                    self.statistics[date_]['update'] += 1
                    log.write('info', None, 0, '%s:%s update web info'%(self.ip, self.port))
                    return web_info
        except:
            return

    def ip2hostname(self,ip):
        try:
            hostname = socket.gethostbyaddr(ip)[0]
            return hostname
        except:
            pass
        try:
            query_data = "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x20\x43\x4b\x41\x41" + \
                         "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" + \
                         "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00\x00\x21\x00\x01"
            dport = 137
            _s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            _s.sendto(query_data, (ip, dport))
            x = _s.recvfrom(1024)
            tmp = x[0][57:]
            hostname = tmp.split("\x00", 2)[0].strip()
            hostname = hostname.split()[0]
            return hostname
        except:
            pass

    def get_code(self, header, html):
        try:
            m = re.search(r'<meta.*?charset=(.*?)"(>| |/)', html, flags=re.I)
            if m: return m.group(1).replace('"', '')
        except:
            pass
        try:
            if 'Content-Type' in header:
                Content_Type = header['Content-Type']
                m = re.search(r'.*?charset=(.*?)(;|$)', Content_Type, flags=re.I)
                if m: return m.group(1)
        except:
            pass

    def get_tag(self):
        try:
            url = self.ip + ':' + str(self.port)
            tag = map(self.discern, ['Discern_cms', 'Discern_con', 'Discern_lang'], [url, url, url])
            return filter(None, tag)
        except Exception, e:
            return

    def discern(self, dis_type, domain):
        file_tmp = {}
        if int(domain.split(":")[1]) == 443:
            protocol = "https://"
        else:
            protocol = "http://"
        try:
            req = urllib2.urlopen(protocol + domain, timeout=self.timeout)
            header = req.headers
            html = req.read()
        except urllib2.HTTPError, e:
            html = e.read()
            header = e.headers
        except Exception, e:
            return
        for mark_info in self.config_ini[dis_type]:
            if mark_info[1] == 'header':
                try:
                    if not header: return
                    if re.search(mark_info[3], header[mark_info[2]], re.I):
                        return mark_info[0]
                except Exception, e:
                    continue
            elif mark_info[1] == 'file':
                if mark_info[2] == 'index':
                    try:
                        if not html: return
                        if re.search(mark_info[3], html, re.I):
                            return mark_info[0]
                    except Exception, e:
                        continue
                else:
                    if mark_info[2] in file_tmp:
                        re_html = file_tmp[mark_info[2]]
                    else:
                        try:
                            re_html = urllib2.urlopen(protocol + domain + "/" + mark_info[2],
                                                      timeout=self.timeout).read()
                        except urllib2.HTTPError, e:
                            re_html = e.read()
                        except Exception, e:
                            return
                        file_tmp[mark_info[2]] = re_html
                    try:
                        if re.search(mark_info[3], re_html, re.I):
                            return mark_info[0]
                    except Exception, e:
                        print mark_info[3]

 

posted @ 2017-03-27 18:53  八月的男人  阅读(1307)  评论(0编辑  收藏  举报