def cmdLineParser():
    parser = argparse.ArgumentParser(description='Example: python sepia.py -s s2045-rce -baidu "inurl:login.action"', add_help=False)

    #第一项optional_arguments，基础相关项
    optional_arguments = parser.add_argument_group('Options')
    optional_arguments.add_argument('-h', '--help', action='help', help='Show help message and exit')
    optional_arguments.add_argument('-v', '--version', action='version', version='Version:'+VERSION, help="Show program's version number and exit")

    #第二项engine_mode，扫描引擎配置项
    engine_mode = parser.add_argument_group('Engine')
    engine_mode.add_argument('--threads', dest="engine_thread", default=False, action='store_true', help='Multi-Threaded engine (default choice)')
    engine_mode.add_argument('--gevent', dest="engine_gevent", default=False, action='store_true', help='Gevent engine (single-threaded with asynchronous)')
    engine_mode.add_argument('--number', metavar='NUMBER', dest="thread_num", type=int, default=10, help='number of threads/concurrent (default:10)')
    
    #第三项script，脚本配置项
    script = parser.add_argument_group('Script')
    script.add_argument('-s', metavar='SCRIPT', dest="script_name", type=str, default='', help='Load script by name (-s jboss-rce) or path (-s ./script/jboss.py)')
    script.add_argument('--search', metavar='KEYWORDS', dest="search_script", type=str, help='Fuzzy search script by keywords (--search jboss)')
    script.add_argument('--list', dest="list_scripts", default=False, action='store_true', help='List available script names in ./script/ and exit')

    #第四项target，扫描目标配置项
    target = parser.add_argument_group('Target')
    target.add_argument('-t', '--target', metavar='URL&IP', dest="target_urlip", type=str, default='', help='Target URL or IP(e.g. http://www.targetsite.com/)')
    target.add_argument('-f', '--file', metavar='URLFILE', dest="target_file", type=str, default='', help='Load targets from targetFile (e.g. ./data/wooyun_domain)')
    target.add_argument('-c', '--cidr', metavar='CIDR', dest="target_cidr", type=str, default='', help='Read a CIDR (e.g. 10.0.1.0/24)')

    #第五项api，扫描数据接口配置项
    api = parser.add_argument_group('API')
    api.add_argument('-zoomeye', metavar='DORK', dest="zoomeye_dork", type=str, default='', help='ZoomEye dork (e.g. "zabbix port:8080")')
    api.add_argument('-baidu', metavar='DORK', dest="baidu_dork", type=str, default='', help='Baidu dork (e.g. "inurl:login.action")')
    api.add_argument('--limit', metavar='NUMBER', dest="api_limit", type=int, default=10, help='Maximum searching results (default:10)')
    api.add_argument('--offset', metavar='OFFSET', dest="api_offset", type=int, default=0, help="Search offset to begin getting results from (default:0)")
    api.add_argument('--search-type', metavar='TYPE', dest="search_type", action="store", default='host', help="[ZoomEye] search type used in ZoomEye API, web or host (default:host)")

    if len(sys.argv) == 1:
        sys.exit('sepia.py: error: missing a mandatory option (-s, -t|-f|-c|-zoomeye|-baidu), use -h for help')
    args = parser.parse_args()
    return args def initOptions(args): #初始化操作
    checkShow(args) #检查是否要求列出脚本
    checkSearch(args) #检查是否在搜索脚本
    EngineRegister(args) #检查配置扫描引擎，注册互斥量，扫描线程数
    ScriptRegister(args) #检查配置脚本项参数
    TargetRegister(args) #检查配置扫描目标信息
    ApiRegister(args) #检查API配置信息
    '''
    print "#########以下是conf字典#########"
    print conf
    ''' def loadModule():
    _name = conf.MODULE_NAME #加载的脚本名
    msg = 'Load script: %s' % _name
    logger.info(msg)
    #在脚本文件夹中查找_name文件
    fp, pathname, description = imp.find_module(os.path.splitext(_name)[0], [paths.SCRIPT_PATH])
    try:
        th.module_obj = imp.load_module("_", fp, pathname, description) #加载找到的模块
        for each in ESSENTIAL_MODULE_METHODS: #ESSENTIAL_MODULE_METHODS=['poc']
            if not hasattr(th.module_obj, each): #如果模块中不存在'poc'方法就提醒并退出
                errorMsg = "Can't find essential method:'%s()' in current script，Please modify your script/PoC".format(each)
                sys.exit(logger.error(errorMsg))
    except ImportError, e: #模块加载失败就抛出异常
        errorMsg = "Your current scipt [%s.py] caused this exception\n%s\n%s" \
                   % (_name, '[Error Msg]: ' + str(e), 'Maybe you can download this module from pip or easy_install')
        sys.exit(logger.error(errorMsg)) def loadPayloads():
    infoMsg = 'Initialize targets...'
    logger.info(infoMsg)
    th.queue = Queue.Queue() #创建扫描目标队列
    if conf.TARGET_MODE is TARGET_MODE_STATUS.FILE: #conf.TARGET_MOD=9 -f模式
        file_mode()
    elif conf.TARGET_MODE is TARGET_MODE_STATUS.IPMASK: #conf.TARGET_MOD=7 -c模式
        cidr_mode()
    elif conf.TARGET_MODE is TARGET_MODE_STATUS.SINGLE: #conf.TARGET_MOD=8 -u模式
        single_target_mode()
    elif conf.TARGET_MODE is TARGET_MODE_STATUS.API: #conf.TARGET_MOD=5 API模式
        api_mode()

    else:
        raise ToolkitValueException('conf.TARGET_MODE value ERROR.')
    logger.info('Total: %s' % str(th.queue.qsize())) def setPaths():#设置POC-T的文件路径和目录
    """
    设置Sepia的绝对路径
    """
    root_path = paths.ROOT_PATH
    paths.DATA_PATH = os.path.join(root_path, "data") #设置data文件夹路径
    paths.SCRIPT_PATH = os.path.join(root_path, "script") #设置script文件夹路径
    paths.CONFIG_PATH = os.path.join(root_path, "toolkit.conf") #设置toolkit.conf文件路径
    #如果data、script、output三个文件夹都不存在就创建
    if not os.path.exists(paths.SCRIPT_PATH):
        os.mkdir(paths.SCRIPT_PATH)
    if not os.path.exists(paths.DATA_PATH):
        os.mkdir(paths.DATA_PATH)

    paths.WEAK_PASS = os.path.join(paths.DATA_PATH, "pass100.txt") #设置弱口令文件pass100.txt的路径
    paths.LARGE_WEAK_PASS = os.path.join(paths.DATA_PATH, "pass1000.txt") #设置弱口令文件pass1000.txt的路径
    paths.UA_LIST_PATH = os.path.join(paths.DATA_PATH, "user-agents.txt") #设置user-agents.txt文件的路径

    if os.path.isfile(paths.CONFIG_PATH) and os.path.isfile(paths.WEAK_PASS) and os.path.isfile(
            paths.LARGE_WEAK_PASS) and os.path.isfile(paths.UA_LIST_PATH):
        pass
    else:
        #如果toolkit.conf、pass100.txt、pass1000.txt、user-agent.txt四个缺失任何一个，就抛出异常
        msg = 'Some files missing, it may cause an issue'
        raise ToolkitMissingPrivileges(msg) def banner():
    """
    Function prints banner with its version
    """
    _ = BANNER
    if not getattr(LOGGER_HANDLER, "is_tty", False):
        _ = re.sub("\033.+?m", "", _)
    dataToStdout(_) def systemQuit(status=EXIT_STATUS.SYSETM_EXIT):
    if status == EXIT_STATUS.USER_QUIT and "is_continue" not in th:
        logger.info('User quit')
        logger.error('System exit')
    elif status == EXIT_STATUS.USER_QUIT and "exploit_mode" in th:
        if th.exploit_mode:
            logger.error('Exit exploit mode')
            logger.info('System exit')
    else:
        if status == EXIT_STATUS.SYSETM_EXIT:
            printResult()
            if conf.TARGET_MODE == TARGET_MODE_STATUS.SINGLE: #如果是扫描单个目标就启动attack
                attack()
            logger.info('System exit')
        elif status == EXIT_STATUS.USER_QUIT and th.is_continue:
            dataToStdout('\n')
            printResult()
            logger.info('User quit')
            logger.error('System exit')
        elif status == EXIT_STATUS.ERROR_EXIT and th.is_continue:
            dataToStdout('\n')
            printResult()
            logger.error('System exit')
        else:
            raise ToolkitValueException('Invalid status code: %s' % str(status))
    sys.exit(0) class EXIT_STATUS:
    SYSETM_EXIT = 0
    ERROR_EXIT = 1
    USER_QUIT = 2 IS_WIN = subprocess.mswindows #判断当前环境是否为Windows class ToolkitUserQuitException(ToolkitBaseException):
    pass class ToolkitMissingPrivileges(ToolkitBaseException):
    pass class ToolkitSystemException(ToolkitBaseException):
    pass def run():
    initEngine()
    if conf.ENGINE is ENGINE_MODE_STATUS.THREAD: #多线程模式conf.ENGINE=9
        for i in range(th.threads_num):
            t = threading.Thread(target=scan, name=str(i))
            setThreadDaemon(t)
            t.start()
        # It can quit with Ctrl-C
        while 1: #如果未扫描结束，主线程一直死循环等待，取决于线程数量和th.is_continue的值
            if th.thread_count > 0 and th.is_continue:
                time.sleep(0.01)
            else:
                break

    elif conf.ENGINE is ENGINE_MODE_STATUS.GEVENT: #协程模式conf.ENGINE=8
        from gevent import monkey
        monkey.patch_all()
        import gevent
        while th.queue.qsize() > 0 and th.is_continue:
            gevent.joinall([gevent.spawn(scan) for i in xrange(0, th.threads_num) if #生成10个concurrent
                            th.queue.qsize() > 0])

    if 'errmsg' in th:
        logger.error(th.errmsg)
import os.path
import traceback
from lib.parse.cmdline import cmdLineParser #导入命令行解析模块
from lib.core.option import initOptions
from lib.controller.loader import loadModule, loadPayloads #导入脚本加载模块
from lib.core.common import setPaths, banner, systemQuit
from lib.core.data import paths, logger, cmdLineOptions
from lib.core.enums import EXIT_STATUS
from lib.core.settings import IS_WIN #判断是否为Windows
from lib.core.exception import ToolkitUserQuitException
from lib.core.exception import ToolkitMissingPrivileges
from lib.core.exception import ToolkitSystemException
from lib.controller.engine import run
from thirdparty.colorama.initialise import init as winowsColorInit #导入第三方插件Colorama
#!/usr/bin/env python
#-*- coding:utf-8 -*-



def main():
    try:
        paths.ROOT_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
        #得到当前py文件所在文件夹上一个文件夹目录赋值给paths.ROOT_PATH，也就是Sepia的根目录
        try:
            os.path.isdir(paths.ROOT_PATH) #此处判断path.ROOT_PATH得到的路径编码是否正常
        except UnicodeEncodeError: #出现编码错误就退出
            errMsg = "Your system does not properly handle non-ASCII paths. "
            errMsg += "Please move the project root directory to another location"
            logger.error(errMsg)
            raise SystemExit
        setPaths() #设置Sepia的文件路径和目录
        banner() #打印Sepia的logo
        '''
        print "########以下为paths字典#########"
        print paths
        '''
        #存储原始命令行选项，以备恢复
        '''
        print "########以下为原始命令行参数#########"
        print cmdLineParser().__dict__
        '''
        #cmdLineParser().__dict__获得命令行参数数据字典并赋值给cmdLineOptions字典对象
        cmdLineOptions.update(cmdLineParser().__dict__)

        initOptions(cmdLineOptions)