IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    w3af学习笔记(二)

    一根稻草发表于 2014-09-05 00:00:00
    love 0

    0.引言

    本文深入分析了w3af_console启动过程,详细说明了函数调用关系,有助于理解w3af框架。下面是w3af_console入口函数_main()

    def _main():
        _configure_output_manager()
        sys.exit(main())
    

    1._configure_output_manager深入分析

    w3af_console执行的第一个函数_configure_output_manager()分析,执行的结果是创建的console对象保存在全局变量om.out中,om.out在main()函数中会用到,如console.sh()。下面用'->'表示调用或继承关系,一步一步的分析到源头。

    ->(file:w3af_console)  
    _configure_output_manager()  
    
    ->(file:w3af_console)  
    om.out.set_output_plugins( ['console'] )  
    
    ps: om就是out_manager module; out是类out_manager的一个全局对象,定义在\w3af\core\controllers\out_manager.py中  
    
    ->(file:\w3af\core\controllers\out_manager.py)  
    set_output_plugins( ['console'] )  
    
    ->(file:\w3af\core\controllers\out_manager.py)  
    _add_output_plugins( 'console')  
    
    ->(file:\w3af\core\controllers\out_manager.py)  
    plugin = factory('w3af.plugins.output.' + OutputPluginName)  
    plugin.set_options(self._plugin_options[OutputPluginName])  
    self._output_plugin_instances.append(plugin)  
    
    ->(file:\w3af\core\controllers\misc\factory.py)  
    
    factory('w3af.plugins.output.console')
            #主要过程
            __import__('w3af.plugins.output.console')
            class_name = module_name.split('.')[-1]  #class_name='console'
            module_inst = sys.modules['w3af.plugins.output.console']
            a_class = getattr(module_inst, 'console')  # a_class 现在等价于console类
            return a_class()
    (到此就返回一个console对象,下面是几个类的继承关系) ->(file:\w3af\plugins\output\console.py) class console(OutputPlugin): ... ->(file:\w3af\core\controllers\plugins\output_plugin.py) class OutputPlugin(Plugin): """ This is the base class for data output, all output plugins should inherit from it and implement the following methods : 1. debug( message, verbose ) 2. information( message, verbose ) 3. error( message, verbose ) 4. vulnerability( message, verbose ) """ ->(file:\w3af\core\controllers\plugins\plugins.py) class Plugin(Configurable): ... ->(file:\w3af\core\controllers\configurable.py) class Configurable(object): #This is mostly "an interface"

    2.main函数深入分析

    假定启动w3af的命令为

    ./w3af_console -s script_file
    

    2.1 ConsoleUI

    main()函数中首先解析script_file,生成commands_to_run[],根据commands,初始化一个ConsoleUI对象console,这是用户操作的UI. 有两个关键操作,self.handlers初始化和self.initRoot()

    #_handlers是一个字典,键盘输入字符是key,函数是value 
    self._handlers = {
            '\t': self._onTab,
            '\r': self._onEnter,
            term.KEY_BACKSPACE: self._onBackspace,
            term.KEY_LEFT: self._onLeft,
            term.KEY_RIGHT: self._onRight,
            term.KEY_UP: self._onUp,
            term.KEY_DOWN: self._onDown,
            '^C': self._backOrExit,
            '^D': self._backOrExit,
            '^L': self._clearScreen,
            '^W': self._delWord,
            '^H': self._onBackspace,
            '^A': self._toLineStart,
            '^E': self._toLineEnd
        }
    
    def __initRoot(self, do_upd):
        """
        Root menu init routine.
        """
        cons_upd = ConsoleUIUpdater(force=do_upd)
        cons_upd.update()
        # Core initialization
        self._w3af = w3afCore()
        self._w3af.plugins.set_plugins(['console'], 'output') #关键
        ...
        #一直递归下去,在类w3af_core_plugins(实例是上面的self._w3af.plugins)发现调用的是
        self._set_plugin_generic(self, 'output', ['console'])
            self._plugins_names_dict['output'] = ['console']

    (ps: file:\w3af\core\ui\console\console_ui.py

    ConsoleUI class represents the console.It handles the keys pressed and delegate the completion and execution tasks to the current menu.)

    2.2 accept_disclaimer

    创建完console实例后,console调用accept_disclaimer方法,它输出w3af的免责条款,如果用户接受,就可以继续使用,否则退出。

    2.3 sh

    最后调用console.sh(),进行主循环,创建一个w3af shell,执行w3af的命令。

    ->(file:\w3af\core\ui\console\console_ui.py) 删除异常处理语句等,sh()完成的主要工作如下

    def sh(self,name="w3af",callback=None)
        self._context = rootMenu(name, self, self._w3af)
        self._showPrompt()
        self._active = True
        term.setRawInputMode(True)
    
        self._executePending()
    
        while self._active:#主循环,等待用户输入,解析命令及参数
            c = term.getch()
            self._handleKey(c) #根据字典self._handlers调用相关方法
    

    2.4 rootMenu

    对于sh()的第一条语句:

    self._context = rootMenu(name, self, self._w3af)
    

    初始化一个rootMenu类的实例_context,顾名思义,包括plugins,target,exploit等根菜单;rootMenu初始化的关键代码如下 ->(file:\w3af\core\ui\console\rootMenu.py)

    class rootMenu(menu):
        def __init__(self, name, console, core, parent=None):
            menu.__init__(self, name, console, core, parent)
            self._load_help('root')
    
            #   At first, there is no scan thread
            self._scan_thread = None
    
            mapDict(self.addChild, {
                'plugins': pluginsMenu,
                'target': (ConfigMenu, self._w3af.target),
                'misc-settings': (ConfigMenu, MiscSettings()),
                'http-settings': (ConfigMenu, self._w3af.uri_opener.settings),
                'profiles': profilesMenu,
                'bug-report': bug_report_menu,
                'exploit': exploit,
                'kb': kbMenu
            })
        def mapDict(fun, dct):
            for p in dct:
                fun(p, dct[p])
    
        def addChild(self, name, constructor):
            if type(constructor) in (tuple, list):
                constructor, params = constructor[0], constructor[1:]
            else:
                params = []
    
            self._children[name] = constructor(
                name, self._console, self._w3af, self, *params)

    对于字典的第一项'plugins': pluginsMenu,mapDict()执行过程如下 self.addchild('plugins',pluginsMenu) --> self.children['plugins']=pluginsMenu('plugins', self.console, self._w3af, self,[])

    -->(file:\w3af\core\ui\console\plugins.py) 类pluginsMenu init中首先得到plugins目录下的插件类型types(除去attack,tests,.gits的所有目录名)

    types=['audit','auth','bruteforce','crawl','evasion','grep','infrastructure','mangle','output']
    for t in types:
    self.addChild(t, pluginsTypeMenu)
    self._children['audit']=pluginsTypeMenu('audit',...)
    

    -->(file:\w3af\core\ui\console\plugins.py) 类pluginsTypeMenu init中将plugins/plugin_types/目录下所有插件读入到plugins[]中 最后保存到一个插件字典中self.plugins={} self.plugins[plugin_name]=plugin_option_num

    2.5 _showPrompt

    功能输出提示符[plugins...]>>>

    ->(file:\w3af\core\ui\console\console_ui.py)
    term.write(self._context.get_path() + ">>> ")
    
    ->(file:\w3af\core\ui\console\menu.py)
    get_path()
    

    2.6 _executePending

    调用executePending(),它顺序读取console初始化时的输入参数commands指令curCmd
    paste(curCmd)将指令在终端stdout显示出来,同时保存至self.line[]
    onEnter()依次:执行指令,初始化当前行position=0和line=[],在新一行输出">>>";
    (ps:在当前类中找不到方法,去父类中找,如rootMenu中没有execute,在其父类menu中找)

    ->(file:\w3af\core\ui\console\console_ui.py)

    def _executePending(self):
        while (self._commands):
            curCmd, self._commands = self._commands[0], self._commands[1:]
            self._paste(curCmd)
            self._onEnter()
    def _paste(self, text):
        tail = self._line[self._position:]
        for c in text:
            self._line.insert(self._position, c)
            self._position += 1
    
        term.write(text)
        term.write(''.join(tail))
        term.moveBack(len(tail))
    
    def _onEnter(self):
        self._execute()  #关键
        self._initPrompt()
        self._showPrompt()
    
    def _execute(self):
        line = self._getLineStr()
        term.setRawInputMode(False)
        om.out.console('')
        if len(line) and not line.isspace():
            self._get_history().remember(self._line)
    
            params = self.in_raw_line_mode() and line or self._parseLine(line)
            menu = self._context.execute(params)
    
            if menu:
                if callable(menu):
                    menu = menu()
                elif menu != self._context:
                    # Remember this for the back command
                    self._trace.append(self._context)
                if menu is not None:
                    self._context = menu
        term.setRawInputMode(True)

    从上述代码发现函数调用路线如下

    self._executePending()->self._onEnter()->self._execute()->self._context.execute()   
    

    self._context是类rootMenu的对象,但是rootMenu类中没有execute()方法,那么去其父类menu中查找。

    2.7 menu.execute

    (file:\w3af\core\ui\console\menu.py)

    execute方法完整注释如下

    #class menu
    def execute(self, tokens):
        if len(tokens) == 0:
            return self  
    
        command, params = tokens[0], tokens[1:]
        handler = self.get_handler(command)
        # hander 可能是:
        # _cmd_back(), _cmd_exit(), _cmd_keys(), _cmd_help(), _cmd_print()
        # 子类中按需要增加了各自特殊_cmd__XXX,如ConfigMenu类中增加了_cmd_set(),_cmd_view()..
        if handler:   # 递归出口
            return handler(params)
    
        children = self.get_children() 
        '''
        return self._children, it's  a dict
        在menu的子类rootMenu中对self._children进行了初始化,代码见2.4 rootMenu
    
        '''
        if command in children:  # in children.keys()
            child = children[command]
            #child可能是一个pluginsMenu, ConfigMenu
    
            child.set_child_call(True)
            #set_child_call函数处理 set命令,如w3af>>> target set target http://w3af.org/
    
            try:
                return child.execute(params) 
                # 按上例,params = set target http://w3af.org/
                # 由于child也是继承自menu类,所以execute是同一个方法
                # 下次调用execute时在handler中找到了_cmd_set()方法
                # 直接执行_cmd_set(' target http://w3af.org/'),不再向下递归
            finally:
                child.set_child_call(False)
    
        raise BaseFrameworkException("Unknown command '%s'" % command)

    对于上面的最终执行函数handler(),仅分析一个set命令:类ConfigMenu的_cmd_set()方法。

    2.8 _cmd_set

    (file: \w3af\core\ui\console\config.py)

    _cmd_set()的主干如下

    #去掉的异常处理等
    def _cmd_set(self, params):
        name = params[0]
        value = ' '.join(params[1:])
    
        self._options[name].set_value(value)
        self._unsaved_options[name] = value
    
        if value not in self._memory[name]:
            self._memory[name].append(value)
    
        if self._child_call:
            self._cmd_save([])

    比如命令 w3af>>> target set target http://w3af.org/

    从execute()方法执行到_cmd_set()方法时

    params=['target','http://w3af.org/']

    即name='target', value='http://w3af.org/'

    下面关键的就是self._options

    2.9 w3af_core_target

    (file:\w3af\core\ui\console\config.py)

    class ConfigMenu(menu)
    def __init__(self, name, console, w3af, parent, configurable):
    self._configurable = configurable
    self._options = self._configurable.get_options()
    

    通过回滚,找到'target'插件对象创建的时刻就是在rootMenu对象创建时,去前文找mapDict()。核对参数发现当时传入的configurable是self._w3af.target(file:\w3af\core\ui\console\rootMenu.py)

    ->(file:\w3af\core\controllers\w3afCore.py) class w3afCore的init中有target的创建 self.target = w3af_core_target() ->(file:\w3af\core\controllers\core_helpers\target.py)

    get_options()涉及到的细节过多,跟w3af_console主干没有太多关系,因此不再深挖下去,以后再做分析。

    3. 总结

    w3af启动时,首先添加console插件,配置一个output_manager全局对象om.out,这是创建ConsoleUI,与用户交互的基础。然后在main函数中创建ConsoleUI对象,执行初始化,进入最重要的环节,生成一个w3af shell,创建rootMenu,读取命令行参数script,进行相关设置,最后进行死循环while self._active,等待用户输入,解析输入,如果是正确的命令,就(有些需要调用插件)执行相应操作,至此用户才能跟w3af进行正常交互,w3af启动成功。



沪ICP备19023445号-2号
友情链接