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

打印 上一主题 下一主题

主题 976|帖子 976|积分 2928

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
安装
  1. go get github.com/jessevdk/go-flags
复制代码
个人感觉学习这个框架只需要理解两步:

  • 指定结构体选项
  • 解析结构体
基础的选项:随便写几个展示即可

  • short:短选项名字
  • long:长选项名字
  • description:表现你当前选项的一个解释,-help或者-h的时候表现
  • tips:

  1. // 参数选项
  2. type Option struct {
  3.     //所有结构体首字母一定要大写,否则解析不了,但是short或者long的名字就随便你起
  4.     V     string   `short:"v" long:"verbose" description:"显示详细信息"`
  5.     Vlist []string `short:"V" long:"verbose-list" description:"显示详细信息列表"`
  6.     I     int      `short:"i" long:"input" description:"int类型测试"`
  7.     //这里可以给了一个默认值default:"xxx",不给的话在不使用该参数的时候就不用调用那个函数
  8.     P      func(string)   `short:"p" long:"print" description:"打印"` // default:"myPrint"
  9.     IntMap map[string]int `short:"m" long:"intmap" description:"intmap"`
  10. }
复制代码
函数这里是要给一个函数传递进去使用,具体如下:
是在你实例化你的选项结构体后,将你写好的函数传递进去,或者你写一个匿名函数也行。
  1. // 打印功能
  2. func myPrint(str string) {
  3.     fmt.Println("打印-p参数值:", str)
  4. }
  5. func main() {
  6.         var opt Option  //定义一个选项
  7.         opt.P = myPrint //把打印函数赋值给P
  8. }
复制代码
使用的时候就可以用该参数了,传递的参数值就是给到函数变量使用的:

参数传递格式


  • -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,表示这个选项的默认值是多少,可以看到我这里给的是一个函数名,这个函数名我也在代码这种实现了吗,以是运行的时候肯定是可以找到我这个函数然后执行
分组

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

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

  • 先创建好组名(组名也要用结构体)
    结构体内的字段都是你接下来要创建的结构体选项,以是在这里可以先提前想好选项的结构体名字
    比如下面的就是Host和Scan就是我们接下来要创建的分组的选项参数结构体
    group就是组名,到时候你在help中就能看到分组的组名
    namespace是空间名,在help中表现为Host.xxx选项,就是类似如许,主要是告诉你哪个组下的选项,紧张是group是组名即可,表现的时候比较明显。
  1. // basic 分组
  2. type Basic struct {
  3.     HostOption Host `group:"host" namespace:"Host"`
  4.     ScanOption Scan `group:"scan" namespace:"Scan"`
  5. }
复制代码

  • 正常创建你在分组的时候想好的那几个结构体字段,我们给的名字是:Host和ScanOption,那么接下来就是写这两个结构体了
    留意:我在Scan中又开了一个组ScanType,以是还要写一个ScanType的结构体
    这是help结果图,应该很轻易理解是怎么分组的了,也知道那个namespace是什么了,留意区分Host和host,host是分组的组名,Host才是那个namespace。

  1. type Host struct {
  2.     HostName string `short:"N" long:"hostname" description:"主机名"`
  3.     HostMac  string `short:"M" long:"hostmac" description:"主机mac"`
  4. }
  5.   
  6. // 在扫描类型中继续分组
  7. type ScanType struct {
  8.     HttpType string `short:"T" long:"http" description:"http扫描"`
  9.     DataBase string `short:"D" long:"database" description:"数据库扫描"`
  10.     Other    string `short:"O" long:"other" description:"其他扫描"`
  11. }
  12. type Scan struct {
  13.     BasicScan ScanType `group:"scantype" namespace:"scantype"`
  14.     ScanPort  int      `short:"P" long:"scanport" description:"扫描端口"`
  15.     ScanIP    string   `short:"I" long:"scanip" description:"扫描ip"`
  16. }
复制代码
子命令

简单的来说:go version 这个 version就是子命令,不消带-,直接用的就是子命令,-version 这种带-的就是选项而不是子命令哈,留意一个符号的区别。
同时记住一点:子命令在flags包中也自动实现了-h /h命令,以是不消编写帮助信息。
(但是你想要实现不添加-h /h 就实现比如 xx.exe finger 也能给出帮助信息的话就要在接口中实现了,后续会在finger中讲明白)
version


  • 先实现version子命令
    继承了flags.Command的Execute接口就成功实现了子命令了,只剩下注册到主要的结构体中,也就是之前学到的需要一个结构体注册选项
    (tips:version的结构体为空,不是拿来切割或者分组,只是一个表现版本号,后面讲finger的时候他才是作为这个分组命令来弄)
  1. //给一个空的,因为version一般都不需要什么其他参数来辅助就可以看到版本了
  2. type ChildCommand struct {
  3. }
  4. // 继承了flags.Command即可自动调用
  5. func (c *ChildCommand) Execute(args []string) error {
  6.   
  7.     fmt.Println("version: 1.0.0")
  8.   
  9.     return nil
  10. }
复制代码

  • 注册子命令到主选项结构体中,这里才是给到flags解析的结构体,留意这里给的键不再是short/long,而是command
  1. type VOption struct {
  2.     Version ChildCommand `command:"version" description:"Version显示版本信息"`
  3. }
复制代码
运行结果没问题(源码稍后放)

finger(测试)

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

  • 仍旧是先做好一个子命令,但是这里要给子命令上两个选项,用来指纹识别的ip和端口
    同时要记得实现接口Execute
  1. // 模拟一下指纹扫描中常见的一个子命令
  2. type Finger struct {
  3.     U string `short:"u" long:"url" description:"url"`
  4.     P string `short:"p" long:"port" description:"port"`
  5. }
  6. func (c *Finger) Execute(args []string) error {
  7.     if c.U == "" || c.P == "" {
  8.         // 如果没有提供参数,显示帮助信息,尽量做的完美一点
  9.         parser := flags.NewParser(c, flags.Default)
  10.         parser.WriteHelp(os.Stdout)
  11.         return nil
  12.     }
  13.     return nil
  14. }
复制代码

  • 在主结构体中注册这个子命令
    我就直接在之前的VOption结构体注册了
  1. type VOption struct {
  2.     Version ChildCommand `command:"version" description:"Version显示版本信息"`
  3.     // 指纹扫描
  4.     Finger Finger `command:"finger" description:"Finger指纹扫描"`
  5. }
复制代码
注册完成就可以用了

  • 加帮助参数

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

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

test1 基础使用测试、test2分组测试 和 test3子命令 自己看着用就行。
  1. package main  import (    "fmt"    "log"    "os"      "github.com/jessevdk/go-flags")  // 参数选项
  2. type Option struct {
  3.     //所有结构体首字母一定要大写,否则解析不了,但是short或者long的名字就随便你起
  4.     V     string   `short:"v" long:"verbose" description:"显示详细信息"`
  5.     Vlist []string `short:"V" long:"verbose-list" description:"显示详细信息列表"`
  6.     I     int      `short:"i" long:"input" description:"int类型测试"`
  7.     //这里可以给了一个默认值default:"xxx",不给的话在不使用该参数的时候就不用调用那个函数
  8.     P      func(string)   `short:"p" long:"print" description:"打印"` // default:"myPrint"
  9.     IntMap map[string]int `short:"m" long:"intmap" description:"intmap"`
  10. }  // 打印功能func myPrint(str string) {    fmt.Println("打印-p参数值:", str)}  // basic 分组
  11. type Basic struct {
  12.     HostOption Host `group:"host" namespace:"Host"`
  13.     ScanOption Scan `group:"scan" namespace:"Scan"`
  14. }  type Host struct {
  15.     HostName string `short:"N" long:"hostname" description:"主机名"`
  16.     HostMac  string `short:"M" long:"hostmac" description:"主机mac"`
  17. }
  18.   
  19. // 在扫描类型中继续分组
  20. type ScanType struct {
  21.     HttpType string `short:"T" long:"http" description:"http扫描"`
  22.     DataBase string `short:"D" long:"database" description:"数据库扫描"`
  23.     Other    string `short:"O" long:"other" description:"其他扫描"`
  24. }
  25. type Scan struct {
  26.     BasicScan ScanType `group:"scantype" namespace:"scantype"`
  27.     ScanPort  int      `short:"P" long:"scanport" description:"扫描端口"`
  28.     ScanIP    string   `short:"I" long:"scanip" description:"扫描ip"`
  29. }  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()  }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

老婆出轨

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表