Go红队开发—CLI框架(一)

news/2025/3/25 5:34:38/文章来源:https://www.cnblogs.com/dhan/p/18787942

CLI开发框架

命令行工具开发,主要是介绍开发用到的包,集成了一个框架,只要学会了基本每个人都能开发安全工具了。

该文章先学flags包,是比较经典的一个包,相比后面要学习的集成框架这个比较自由比较细化点,自定义可能高一些,后续会学到一个Cobra框架,这个很多安全工具都在使用,先学会flags包入门再去理解Cobra框架就比较好学。

flags包

  • 支持段选项长选项
    意思是使用的时候可以用简写也可以用完整的参数写,比如一个工具叫a.exe,用的时候可以a.exe -h 也可以 a.exe -help
  • 支持短选项组合写,比如:a.exe -p a.exe -s 两种可以写在一起:a.exe -ps
  • 一个参数选项可以给多个值(代码中很容易了解到这一点)
  • flags包默认有-h选项,解析完后--help和-h会返回你的所有参数解释,不用自己写help

安装

go get github.com/jessevdk/go-flags

个人感觉学习这个框架只需要理解两步:

  • 指定结构体选项
  • 解析结构体

基础的选项:随便写几个展示即可

  • short:短选项名字
  • long:长选项名字
  • description:显示你当前选项的一个解释,-help或者-h的时候显示
  • tips:
// 参数选项type Option struct {    //所有结构体首字母一定要大写,否则解析不了,但是short或者long的名字就随便你起    V     string   `short:"v" long:"verbose" description:"显示详细信息"`    Vlist []string `short:"V" long:"verbose-list" description:"显示详细信息列表"`    I     int      `short:"i" long:"input" description:"int类型测试"`    //这里可以给了一个默认值default:"xxx",不给的话在不使用该参数的时候就不用调用那个函数    P      func(string)   `short:"p" long:"print" description:"打印"` // default:"myPrint"    IntMap map[string]int `short:"m" long:"intmap" description:"intmap"`}

函数这里是要给一个函数传递进去使用,具体如下:
是在你实例化你的选项结构体后,将你写好的函数传递进去,或者你写一个匿名函数也行。

// 打印功能func myPrint(str string) {    fmt.Println("打印-p参数值:", str)}
func main() {var opt Option  //定义一个选项opt.P = myPrint //把打印函数赋值给P
}

使用的时候就可以用该参数了,传递的参数值就是给到函数变量使用的:

参数传递格式

  • -V传递:-V 123 -V 456 -V 789
    这样才能传递进map里面,不可以使用逗号或者空格隔开-V 123 456 / -V 123,456都是不行的
  • map的传递:-m key1:123 -m key2:456,不建议使用等号=,至少我在windows测试的时候等号不能作为键值对分割,只能使用:分割,同时多个map键值对也是多个-m才能传递添加进去。

剩下几个类型自己实践即可,这里就够用了。


选项设置

选项设置都是直接在结构体选项反引号中直接添加即可,他会解析的。

  • required:在结构体选项中可设置,true的时候,该选项必选,否则报错,一定要给这个选项一个值
    比如:
    V  string   short:"v" long:"verbose" description:"显示详细信息" required:true

  • default:表示你这个选项参数有一个默认值,就算你不加他也会以默认值形式作用本次运行
    比如:上面结构体代码注释其实有提到
    P  func(string)   short:"p" long:"print" description:"打印" default:"myPrint"
    这里就是可以给一个default,表示这个选项的默认值是多少,可以看到我这里给的是一个函数名,这个函数名我也在代码这种实现了吗,所以运行的时候肯定是可以找到我这个函数然后执行

分组

这个功能比较好用,至少对我来说以后开发构思中肯定需要用到这个,以及后面讲的子命令都是在一些比较完整的工具开发中用的比较多。

先看运行的效果图

这里和之前的不一样了,之前的没有分组的时候就是直接把一些参数打印出来,这里会有参数分组,对比之下会更加直观一点。

分组的结构体之间的联系是比较紧密的

  • 先创建好组名(组名也要用结构体)
    结构体内的字段都是你接下来要创建的结构体选项,所以在这里可以先提前想好选项的结构体名字
    比如下面的就是HostScan就是我们接下来要创建的分组的选项参数结构体
    group就是组名,到时候你在help中就能看到分组的组名
    namespace是空间名,在help中显示为Host.xxx选项,就是类似这样,主要是告诉你哪个组下的选项,重要是group是组名即可,显示的时候比较明显。
// basic 分组type Basic struct {    HostOption Host `group:"host" namespace:"Host"`    ScanOption Scan `group:"scan" namespace:"Scan"`}
  • 正常创建你在分组的时候想好的那几个结构体字段,我们给的名字是:HostScanOption,那么接下来就是写这两个结构体了
    注意:我在Scan中又开了一个组ScanType,所以还要写一个ScanType的结构体
    这是help效果图,应该很容易理解是怎么分组的了,也知道那个namespace是什么了,注意区分Hosthost,host是分组的组名,Host才是那个namespace。
type Host struct {    HostName string `short:"N" long:"hostname" description:"主机名"`    HostMac  string `short:"M" long:"hostmac" description:"主机mac"`}// 在扫描类型中继续分组type ScanType struct {    HttpType string `short:"T" long:"http" description:"http扫描"`    DataBase string `short:"D" long:"database" description:"数据库扫描"`    Other    string `short:"O" long:"other" description:"其他扫描"`}type Scan struct {    BasicScan ScanType `group:"scantype" namespace:"scantype"`    ScanPort  int      `short:"P" long:"scanport" description:"扫描端口"`    ScanIP    string   `short:"I" long:"scanip" description:"扫描ip"`}

子命令

简单的来说:go version 这个 version就是子命令,不用带-,直接用的就是子命令,-version 这种带-的就是选项而不是子命令哈,注意一个符号的区别。


同时记住一点:子命令在flags包中也自动实现了-h /h命令,所以不用编写帮助信息。
(但是你想要实现不添加-h /h 就实现比如 xx.exe finger 也能给出帮助信息的话就要在接口中实现了,后续会在finger中讲明白)

version

  • 先实现version子命令
    继承了flags.Command的Execute接口就成功实现了子命令了,只剩下注册到主要的结构体中,也就是之前学到的需要一个结构体注册选项
    (tips:version的结构体为空,不是拿来切割或者分组,只是一个显示版本号,后面讲finger的时候他才是作为这个分组命令来弄)
//给一个空的,因为version一般都不需要什么其他参数来辅助就可以看到版本了
type ChildCommand struct {}// 继承了flags.Command即可自动调用func (c *ChildCommand) Execute(args []string) error {    fmt.Println("version: 1.0.0")    return nil}
  • 注册子命令到主选项结构体中,这里才是给到flags解析的结构体,注意这里给的键不再是short/long,而是command
type VOption struct {    Version ChildCommand `command:"version" description:"Version显示版本信息"`}

运行效果没问题(源码稍后放)

finger(测试)

finger来加深理解
一般指纹识别的都是xx.exe finger -u xxx -p xxx
所以我认为用这个例子非常好

  • 依旧是先做好一个子命令,但是这里要给子命令上两个选项,用来指纹识别的ip和端口
    同时要记得实现接口Execute
// 模拟一下指纹扫描中常见的一个子命令type Finger struct {    U string `short:"u" long:"url" description:"url"`    P string `short:"p" long:"port" description:"port"`}func (c *Finger) Execute(args []string) error {
    if c.U == "" || c.P == "" {
        // 如果没有提供参数,显示帮助信息,尽量做的完美一点
        parser := flags.NewParser(c, flags.Default)
        parser.WriteHelp(os.Stdout)
        return nil
    }
    return nil}
  • 在主结构体中注册这个子命令
    我就直接在之前的VOption结构体注册了
type VOption struct {
    Version ChildCommand `command:"version" description:"Version显示版本信息"`    // 指纹扫描
    Finger Finger `command:"finger" description:"Finger指纹扫描"`}

注册完成就可以用了

  • 加帮助参数
  • 这个没有加参数 -h 或者 /h等等

以上就是flags包的一些基础常用的内容了,拿到参数之后就是往后丢给你自己写的功能函数即可。

所有测试源码

test1 基础使用测试、test2分组测试 和 test3子命令 自己看着用就行。

package mainimport (    "fmt"    "log"    "os"    "github.com/jessevdk/go-flags")// 参数选项type Option struct {    //所有结构体首字母一定要大写,否则解析不了,但是short或者long的名字就随便你起    V     string   `short:"v" long:"verbose" description:"显示详细信息"`    Vlist []string `short:"V" long:"verbose-list" description:"显示详细信息列表"`    I     int      `short:"i" long:"input" description:"int类型测试"`    //这里可以给了一个默认值default:"xxx",不给的话在不使用该参数的时候就不用调用那个函数    P      func(string)   `short:"p" long:"print" description:"打印"` // default:"myPrint"    IntMap map[string]int `short:"m" long:"intmap" description:"intmap"`}// 打印功能func myPrint(str string) {    fmt.Println("打印-p参数值:", str)}// basic 分组type Basic struct {    HostOption Host `group:"host" namespace:"Host"`    ScanOption Scan `group:"scan" namespace:"Scan"`}type Host struct {    HostName string `short:"N" long:"hostname" description:"主机名"`    HostMac  string `short:"M" long:"hostmac" description:"主机mac"`}// 在扫描类型中继续分组type ScanType struct {    HttpType string `short:"T" long:"http" description:"http扫描"`    DataBase string `short:"D" long:"database" description:"数据库扫描"`    Other    string `short:"O" long:"other" description:"其他扫描"`}type Scan struct {    BasicScan ScanType `group:"scantype" namespace:"scantype"`    ScanPort  int      `short:"P" long:"scanport" description:"扫描端口"`    ScanIP    string   `short:"I" long:"scanip" description:"扫描ip"`}type ChildCommand struct {}// 继承了flags.Command即可自动调用func (c *ChildCommand) Execute(args []string) error {    fmt.Println("version: 1.0.0")    return nil}// 模拟一下指纹扫描中常见的一个子命令type Finger struct {    U string `short:"u" long:"url" description:"url"`    P string `short:"p" long:"port" description:"port"`}func (c *Finger) Execute(args []string) error {    if c.U == "" || c.P == "" {        // 如果没有提供参数,显示帮助信息        parser := flags.NewParser(c, flags.Default)        parser.WriteHelp(os.Stdout)        return nil    }    return nil}type VOption struct {    Version ChildCommand `command:"version" description:"Version显示版本信息"`    // 指纹扫描    Finger Finger `command:"finger" description:"Finger指纹扫描"`}func test3() {    parser := flags.NewParser(&VOption{}, flags.Default)    _, err := parser.Parse()    if err != nil {        if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {            return // 帮助信息已打印,直接退出        }        //如果不是打印的帮助信息报错的话就直接log就行        log.Println("parses failed: ", err)        return    }}func test1() {    var opt Option  //定义一个选项    opt.P = myPrint //把打印函数赋值给P    parser := flags.NewParser(&opt, flags.Default)    _, err := parser.Parse()    if err != nil {        if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {            return // 帮助信息已打印,直接退出        }        //如果不是打印的帮助信息报错的话就直接log就行        log.Println("parses failed: ", err)        return    }    fmt.Println("--------------------------")    fmt.Println("打印-v参数值:", opt.V)    fmt.Println("打印-V 列表所有的参数值:", opt.Vlist)    fmt.Println("打印-i参数值:", opt.I)    fmt.Println("打印--intmap参数值:", opt.IntMap)}func test2() {    var BasicOption Basic    parser := flags.NewParser(&BasicOption, flags.Default)    _, err := parser.Parse()    if err != nil {        if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {            return // 帮助信息已打印,直接退出        }        //如果不是打印的帮助信息报错的话就直接log就行        log.Println("parses failed: ", err)        return    }    fmt.Println("--------------------------")}func main() {    // test1()    // test2()    test3()}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/904243.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WSL2安装Ubuntu

本文是介绍如何使用任意一台Windows主机借助WSL2创建Ubuntu虚拟系统并开放局域网内SSH连接的操作指南!先决条件拥有能够访问github.com的网络环境; 必须运行 Windows 10 版本 2004 及更高版本(内部版本 19041 及更高版本)或 Windows 11版本;安装WSL2 以管理员身份运行Powe…

人工‘够好就行’智能(AGEI)就快来了!

比尔弗兰克斯 前言:本文作者的观点是在真正的通用人工智能(AGI)到来之前,一种“够好就行”的人工智能(AGEI)就已经足以引发我们原本担心 AGI 才会带来的重大社会变革、正面效益,甚至潜在危机。 换句话说: AI 不需要达到像人类一样全面智能的程度; 只要在足够多的任务上…

List集合--java进阶day09

1.List集合以下面的例子来解释存储有序和存储重复如上图,我们是怎么添加这些字符串的,打印的时候就是按照这个顺序打印的--存取有序 并且“张三”出现了两次,也存入了两次--存储重复 因为List有索引,所以该接口有关于索引的独特的api..1.remove注意事项 List有两个remove方…

Pydantic字段级校验:解锁@validator的12种应用

title: Pydantic字段级校验:解锁@validator的12种应用 date: 2025/3/23 updated: 2025/3/23 author: cmdragon excerpt: Pydantic校验系统支持通过pre验证器实现原始数据预处理,在类型转换前完成字符清洗等操作。格式验证涵盖正则表达式匹配与枚举值约束,确保护照编号等字…

20244126 2024-2025-2 《python程序设计》实验一报告

课程:《Python程序设计》 班级:2441 姓名:马晓霞 学号:20244126 实验教师:王志强 实验日期:2025年3月23日 必修/选修:公选课 (一)实验内容 1.熟悉Python开发环境 2.练习Python运行,调试技能 3.编写技能,练习变量和类型、字符串、对象、缩进和注释等 4.编写一个猜数字…

1.6K star!这个开源文本提取神器,5分钟搞定PDF/图片/Office文档!

Kreuzberg 是一个基于 Python 的文本提取库,支持从 PDF、图像、Office 文档等 20+ 格式中提取文本内容。采用 MIT 开源协议,具备本地处理、异步架构、智能 OCR 等特性,特别适合需要隐私保护的文档处理场景。嗨,大家好,我是小华同学,关注我们获得“最新、最全、最优质”开…

leetcode每日一题:判断一个括号字符串是否有效

题目 一个括号字符串是只由 ( 和 ) 组成的 非空 字符串。如果一个字符串满足下面 任意 一个条件,那么它就是有效的:字符串为 (). 它可以表示为 AB(A 与 B 连接),其中A 和 B 都是有效括号字符串。 它可以表示为 (A) ,其中 A 是一个有效括号字符串。给你一个括号字符串 s 和…

实验2c语言分支与循环基础应用编程

任务一:1 #include <stdio.h>2 #include <stdlib.h>3 #include <time.h>4 5 #define N 56 7 int main() {8 int number;9 int i; 10 11 srand(time(0)); // 以当前系统时间作为随机种子 12 for(i = 0; i < N; ++i) { 13 nu…

矿用人员违规闯入监控报警系统

矿用人员违规闯入监控报警系统,设置警戒预警功能,巷道正在行车时,当有行人闯入时,及时抓拍、识别现场违章入侵人员同时闪光警示和音箱报警提示禁止进入,在绞车运行期间人员误入斜巷能够及时报警,自动停止绞车运行。该系统做到绞车运行和行人的安全隔离,有力保障了煤矿斜…

工厂车间人员违规闯入禁区报警系统

工厂车间人员违规闯入禁区报警系统前端设备嵌入AI人体识别算法,对人员的检测、跟踪,实现对人体检测分析识别,实时预警周界区域内人员入侵事件。当有可疑人员进入监测范围内可对其自动识别,即对其抓拍并将当时图像传输到管理中心,在管理中心输出报警信号。工厂车间人员违规…

iis备份还原工具,想要备份iis有哪些工具可以实现?

想要备份IIS(Internet Information Services),可以使用多种专门的备份还原工具。以下是一些常用的IIS备份还原工具及其简要介绍:IISBackUp 功能:专注于备份和恢复IIS站点设置,包括网站配置文件、站点绑定信息、应用程序池设置、SSL证书和密钥信息等。 特点: 提供一种方便…

集合的通用遍历方法--java进阶day09

1.集合的三种通用遍历方法之前我们学习过集合的遍历方法,为什么这里还要再学呢? 这是因为,之前我们用的遍历方法使用了索引,但我们知道set接口的实现类的集合均无索引,所以我们要学习通用的遍历方法 2.迭代器遍历 1.迭代器 迭代器叫做Iterator,是一个接口,我们知道接口是…