w3af学习笔记(二)

3 分钟读完

#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)  
<pre style="padding-left: 30px;" class="lang:python decode:true">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()</pre>
<strong>(到此就返回一个console对象,下面是几个类的继承关系)</strong>
->(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']</pre>

(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)</pre>

对于字典的第一项’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)</pre>

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

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)</pre>

对于上面的最终执行函数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([])</pre>

比如命令 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启动成功。

留下评论