学习如何用python3编写一个命令行脚本,通过分析brutespray源码

为什么选取brutespray这个项目

  • star数超过1k——有使用量有口碑。
  • 单文件,代码行数500以内——不至于过于复杂,便于分析。
    顺便介绍一下,brutespray是一个爆破用的安全工具,nmap扫描日志为输入,medusa实施实际爆破过程。

先找到入口

brutespray.py的入口很容易找到,在336行的main。脚本的第一步接收入参,调用了parse_args()函数,存入args变量。

>>>
if __name__ == "__main__":
    args = parse_args()
>>>

接下判断参数args.quiet是否存在,若不存在就打印ascii art的banner。这里是纯好玩 ,不影响主逻辑。ascii art确实很有意思,linux上有不少这样的‘玩具‘工具。

    if args.quiet == False:
        print(banner)
    else:
        print(quiet_banner)

接下来的代码多数是判断某参数是否存在,这里关注一下384行到388行,生成临时目录。

  1. 写脚本的时候,可以多多运用tmppath、tmpfile,没必要创建永久文件的,坚决使用临时的。
  2. 使用exit()函数退出时,要提供退出值。好多人在写脚本时不提供错误信息就直接退出。这个脚本非常值得学习的一点,在使用exit()函数时都提供了exit code。这样方便使用者使用$?变量获取此脚本运行情况。
    try:
        tmppath = tempfile.mkdtemp(prefix="brutespray-tmp")
    except:
        sys.stderr.write("
Error while creating brutespray temp directory.")
        exit(4)

再看393行到395行,判断当前环境变量是否存在medusa命令,可以理解为判断某工具是否已安装。command是一个shell内置变量,用于运行命令,所运行的命令不包括shell自定义函数,包括shell内置变量,也包括从PATH变量搜索到的命令。-v参数,表示打印所调用命令的简要描述,所以当命令找到则返回0,未找到则返回非0。

日后编写脚本判断工具是否安装时,就可以使用此方法。

    if os.system("command -v medusa > /dev/null") != 0:
        sys.stderr.write("Command medusa not found. Please install medusa before using brutespray")
        exit(3)

408行到421行。这里有一个比较奇怪得写法,仔细分析一下还比较清楚,但是这种写法不是很易读。in_format变量赋值这一行,将输入文件的类型存储在in_format变量。接下来实际定义了一个字典,字典中key-value对应文件类型 - 函数,并且使用[]确定所需要的函数,然后()调用函数。

    if os.path.isfile(args.file):        
        try:
            t = threading.Thread(target=loading)
            t.start()
            in_format = getInput(args.file)
            {
                "gnmap": make_dic_gnmap,
                "xml":   make_dic_xml,
                "json":  make_dic_json
            }[in_format]()
        except:
            print("
Format failed!
")
            loading = True
            sys.exit(0)

423行到426行,调用了interactive()和animate()函数,后面再分析具体函数内容。

        if args.interactive is True:
            interactive()

        animate()

433行到444行,开始进行爆破并将结果写入文件。具体功能涉及到services变量保存的内容,看起来应该是个全局变量。

    to_scan = args.service.split(',')
    for service in services:
        if service in to_scan or to_scan == ['all']:
            for port in services[service]:
                fname = tmppath + '/' +service + '-' + port
                iplist = services[service][port]
                f = open(fname, 'w+')
                for ip in iplist:
                    f.write(ip + '
')
                f.close()
                brute_process = Process(target=brute, args=(service,port,fname,args.output))
                brute_process.start()

命令行参数argparse

argparse属于python3标准库,写脚本工具必备。

brutespray脚本中,parse_args()函数主要就是在处理命令行接口。每个参数提供了长短两种形式,以及帮助信息,是否为必须参数,是否有默认值等。

官方文档进行学习吧,再结合代码去看。The importance of studying offical documents cannot be overemphasized.

爆破

brute()函数,在根据输入来构造命令。爆破使用的工具为medusa。此工具支持很多种服务的爆破。

交互模式

interactive()函数,交互模式参考这里来写。

动画

animate()函数,动画。像有些命令行工具,有加载进度之类的,转圈什么的,参考这里。

美化

ascii art banner以及添加颜色。

原文地址:https://www.cnblogs.com/jamesnpu/p/13599178.html