Go红队开发—格式导出

news/2025/3/11 11:30:33/文章来源:https://www.cnblogs.com/dhan/p/18759096

目录
  • 输出功能
    • CSV输出
      • CSV 转 结构体
      • 结构体 转 CSV
      • 端口扫描结果使用CSV格式导出
    • HTML输出
    • Sqlite输出
      • nmap扫描
    • JSON
      • map转json
      • 结构体转json
      • json写入文件
      • json编解码
      • json转结构体
      • json转map
      • json转string
      • 练习:nmap扫描结果导出json格式

输出功能

在我们使用安全工具的时候基本都会有一个输出功能,同样也很重要,所以下面介绍csv、json、html、sqlite的输出格式。

CSV输出

下载包:go get -u github.com/gocarina/gocsv

使用之前先明确我们要csv格式干什么:

  • 首先一些数据可能就是存在csv文件里面,需要我们提取出来的话就需要另外写函数,但是现在有现成的包使用就很方便了
  • 其次我们使用一些安全工具的时候经常会有导出格式为csv格式的,所以在开发过程中也是一个很重要的需求,使用Gocsv包会很方便
    同理我们往后的其他格式也一样的需求。

CSV 转 结构体

test.csv文件内容为:

1.在CSV转结构体的时候,我们需要构造一个结构体,用来接收CSV文件中的表头

type Person struct {    Id   string `csv:"id"`    Name string `csv:"name"`    Age  int    `csv:"age"`}

2.解析csv文件

// 解析CSV文件func anlyzeCSV() {    file, err := os.OpenFile(        "test.csv",        os.O_RDWR|os.O_CREATE,        0666,    )    defer file.Close()    if err != nil {        fmt.Println("打开文件失败:", err)    }    person := []*Person{}    if err := gocsv.UnmarshalFile(file, &person); err != nil { //UnmarshalFile将文件解析为结构体        fmt.Println("解析文件失败:", err)    }    fmt.Println("id,name,age")    for _, p := range person {        fmt.Println(p.Id, p.Name, p.Age)    }}

结构体 转 CSV

// 写入CSV文件func writeCSV() {    file, err := os.OpenFile("test.csv", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)    if err != nil {        fmt.Println("打开文件失败:", err)    }    defer file.Close()    person := []*Person{}    person = append(person, &Person{        Id:   "1",        Name: "李四",        Age:  20,    })    err = gocsv.MarshalFile(&person, file) //MarshalFile将数据写入文件    if err != nil {        fmt.Println("写入文件失败:", err)    }}

端口扫描结果使用CSV格式导出

结合之前的端口扫描练习,将结果通过所学的知识CSV格式输出
这里我对之前的练习进行了一个改造,就是让输出美观一点,同时修改了传参与返回值,目的都是为了拿到扫描完成的端口结果。
为了写进csv还创建了一个结构体。


函数中大部分代码其实都是格式的转换,主要功能其实学会了上面基本都能做了。

所以这里我在放代码前说一下我遇到的问题:

  • csv的表头要修改的话是比较困难,我没有找到一个比较好的办法,就是在写入csv的代码过程中,修改csv表头,我的解决办法是通过开一个新的结构体,通过老结构体的数据传递到新的结构体中就能够修改csv头写进文件里了(这里有大佬知道解决办法可以告诉我一下)
  • 时间格式化有问题,时间格式一定只能用他给出的几种时间:- "2006-01-02 15:04:05":表示 年-月-日 时:分:秒 格式,如果你修改一下2006年改为2002年都是会格式化出错,这一点尝试了几回发现原来是格式时间数字也固定的表达的。
  • 你要写入的结构体的变量名首个字母要大写,一定要大写,否则他会报错,可能这也是一种规范吧,反正不大写就会报错。 最后的那个示例代码你可以尝试把HostPort结构体中的ScanTime修改首字母小写scanTime,很好的验证了小写的时候出现的错误,当然我运行的时候没有报错,但是他实际上是他没有吧你的时间写进csv中,也代码出错了,可以观察验证一下确实不能小写只能首字母大写。
  • 自定义格式实现MarshalCSV接口后,在写入的时候会自动调用该函数,你可以在该函数进行一些初始化或者格式化动作等等。

这补充一下时间格式化的代码:

// 格式化时间time.Time类型func timeFormat(t time.Time) string {    return t.Format("2006年1月2日") //转为string类型}// Parse将string类型转为time.Time类型func timeParse(t string) time.Time {    tm, _ := time.Parse("2006-01-02 15:04:05", t)    return tm}

代码示例:(成功将扫描结果存到csv中保存)


// 稍微改一下代码,return一个port回来,然后输出到csv中func start_WaitGroup_scan_port(host string) ([]int, time.Time) {    var (        wg      sync.WaitGroup        ch      = make(chan int, 1024) // 增加缓冲区,减少阻塞        count   int        workers = 100 // 控制并发数    )    var scanPort = func(hostname string, port int) {        defer wg.Done()        address := fmt.Sprintf("%s:%d", hostname, port)        conn, err := net.DialTimeout("tcp", address, 2*time.Second)        if err == nil {            conn.Close()            ch <- port        }    }    // 控制并发数    sem := make(chan int, workers)    for i := 0; i < 65536; i++ {        wg.Add(1)        sem <- 1        go func(port int) {            defer func() { <-sem }()            scanPort(host, port)        }(i)    }    go func() {        wg.Wait()        close(ch)    }()    ports := []int{}    for port := range ch {        //fmt.Printf("open: %d\n", port)        ports = append(ports, port) //开放端口添加进去        count++    }    fmt.Printf("-------------------------- host:%v --------------------------------\n", host)    fmt.Println("扫描完成,共开放端口:", count)    fmt.Println("开放端口:", ports)    t := time.Now()    fmt.Println("时间:", timeFormat(t))    fmt.Println("------------------------------------------------------------------")    return ports, t}// 自定义格式type myTime struct {    time.Time}// 当你的自定义类型实现了这个接口后,在csv写入的时候会自动帮你格式化func (m *myTime) MarshalCSV() (string, error) {    return m.Time.Format("2006-01-02 15:04:05"), nil}// 保存扫描的主机和端口type HostPort struct {    Host     string `csv:"Host"`    Ports    string `csv:"Ports"`    ScanTime myTime `csv:"Time"`}func scanhost(host []string) []*HostPort {    var ports []int     //接收扫描端口结果    var t time.Time     //接收扫描结束时间    plist := []string{} //接收每一个ip扫描的端口列表    for _, h := range host {        ports, t = start_WaitGroup_scan_port(h)        for _, p := range ports {            plist = append(plist, strconv.Itoa(p)) //strconv.Itoa将int转为string,添加进列表里面        }    }    hostports := []*HostPort{}    for _, h := range host {        hostports = append(hostports, &HostPort{            Host:     h,            Ports:    "=" + strings.Join(plist, ","), //将列表转为字符串,用逗号分隔            ScanTime: myTime{Time: t},                //自动格式化不用担心,因为实现了MarshalCSV方法        })    }    return hostports}// 输出结果到csv中func outputHostPortCSV(hostports []*HostPort) {    file, err := os.OpenFile("host_port.csv", os.O_RDWR|os.O_CREATE, 0666)    defer file.Close()    if err != nil {        fmt.Println("打开文件失败:", err)        return    }    err = gocsv.MarshalFile(&hostports, file)    if err != nil {        fmt.Println("写入文件失败:", err)        return    }    fmt.Println("写入成功")}// 格式化时间time.Time类型func timeFormat(t time.Time) string {    return t.Format("2006年1月2日") //转为string类型}// Parse将string类型转为time.Time类型func timeParse(t string) time.Time {    tm, _ := time.Parse("2006-01-02 15:04:05", t)    return tm}func main() {    //anlyzeCSV()    //writeCSV()    //timeFormat()    //timeFormat(time.Now())    //timeParse("2024-11-5 5:40:43")    //anlyzeAlipay()    outputHostPortCSV(scanhost([]string{"127.0.0.1"})) //这里可以通过参数来给一个ip列表,具体操作可以按自己需求来}

到这里不知道各位是否觉得逐渐有点安全工具内味了。

HTML输出

在html模板中就比较简单了,将结果传导模板渲染即可
这里我个人没遇到什么问题,拿来就用了

template.html文件直接复制就行,无所谓的,主要是看{{range .}}这个意思是循环,{{end}}表示循环结束,要输出你的结构体中的变量就是用{{.xxx变量名}}

{{range .}}
<tr><td style="word-wrap:break-word;word-break:break-all;">{{.Host}}</td><td style="word-wrap:break-word;word-break:break-all;">{{.Ports}}</td><td style="word-wrap:break-word;word-break:break-all;">{{.ScanTime}}</td>
</tr>
{{end}}

go代码中就主要用两个函数执行,你拿到数据之后无非就是渲染数据到html文件中:

ParseFiles获取模板文件
Execute执行渲染
  • 有一个无关紧要的细节:创建项目目录的时候不要和某些包重名,大小写不一样也算重名,重名了就无法使用你要导入的包了。


template.html

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>扫描结果</title>    <style>        body {            font-family: 'Courier New', Courier, monospace;            background-color: #0d0d0d;            color: #00ff00;            margin: 40px;        }        h1 {            text-align: center;            color: #00ff00;            font-size: 2.5em;            margin-bottom: 30px;        }        table {            width: 100%;            border-collapse: collapse;            margin-bottom: 30px;            border: 2px solid #00ff00;        }        th, td {            padding: 12px;            text-align: left;            border: 1px solid #00ff00;        }        th {            background-color: #1a1a1a;            font-size: 1.2em;            color: #00ff00;        }        tr:nth-child(even) {            background-color: #1a1a1a;        }        tr:nth-child(odd) {            background-color: #0d0d0d;        }        tr:hover {            background-color: #262626;        }    </style></head><body>    <h1>扫描结果</h1>    <table>        <tr>            <th>Host</th>            <th>Ports</th>            <th>Time</th>        </tr>        {{range .}}        <tr>            <td style="word-wrap:break-word;word-break:break-all;">{{.Host}}</td>            <td style="word-wrap:break-word;word-break:break-all;">{{.Ports}}</td>            <td style="word-wrap:break-word;word-break:break-all;">{{.ScanTime}}</td>        </tr>        {{end}}    </table></body></html>

参考代码:

package mainimport (    "fmt"    "html/template"    "net"    "os"    "strconv"    "strings"    "sync"    "time")// 格式化时间time.Time类型func timeFormat(t time.Time) string {    return t.Format("2006-01-02 15:04:05") //转为string类型}type HostPort struct {    Host     string    Ports    string    ScanTime time.Time}// 这里就和csv不同了,就需要自己写一个函数重载调用,这里是用来格式化时间func (r *HostPort) Time() string {    return r.ScanTime.Format("2006-01-02 15:04:05")}// 稍微改一下代码,return一个port回来,然后输出到csv中func start_WaitGroup_scan_port(host string) ([]int, time.Time) {    var (        wg      sync.WaitGroup        ch      = make(chan int, 1024) // 增加缓冲区,减少阻塞        count   int        workers = 100 // 控制并发数    )    var scanPort = func(hostname string, port int) {        defer wg.Done()        address := fmt.Sprintf("%s:%d", hostname, port)        conn, err := net.DialTimeout("tcp", address, 2*time.Second)        if err == nil {            conn.Close()            ch <- port        }    }    // 控制并发数    sem := make(chan int, workers)    for i := 0; i < 65536; i++ {        wg.Add(1)        sem <- 1        go func(port int) {            defer func() { <-sem }()            scanPort(host, port)        }(i)    }    go func() {        wg.Wait()        close(ch)    }()    ports := []int{}    for port := range ch {        //fmt.Printf("open: %d\n", port)        ports = append(ports, port) //开放端口添加进去        count++    }    fmt.Printf("-------------------------- host:%v --------------------------------\n", host)    fmt.Println("扫描完成,共开放端口:", count)    fmt.Println("开放端口:", ports)    t := time.Now()    fmt.Println("时间:", timeFormat(t))    fmt.Println("------------------------------------------------------------------")    return ports, t}func scanhost(host []string) []*HostPort {    var ports []int     //接收扫描端口结果    var t time.Time     //接收扫描结束时间    plist := []string{} //接收每一个ip扫描的端口列表    for _, h := range host {        ports, t = start_WaitGroup_scan_port(h)        for _, p := range ports {            plist = append(plist, strconv.Itoa(p)) //strconv.Itoa将int转为string,添加进列表里面        }    }    hostports := []*HostPort{}    for _, h := range host {        hostports = append(hostports, &HostPort{            Host:     h,            Ports:    strings.Join(plist, ","), //将列表转为字符串,用逗号分隔            ScanTime: t,                        //自动格式化不用担心,因为实现了MarshalCSV方法        })    }    return hostports}func anlyzeHtml() {    temphtml, err := template.ParseFiles("template.html")    if err != nil {        fmt.Println("打开模版失败", err)        return    }    file, err := os.Create("output.html")    defer file.Close()    if err != nil {        fmt.Println("创建文件失败:", err)        return    }    defer file.Close()    err = temphtml.Execute(file, scanhost([]string{"127.0.0.1"}))    if err != nil {        fmt.Println("渲染失败:", err)        return    }    fmt.Println("html结果导出成功!")}func main() {    anlyzeHtml()}

Sqlite输出

下载包

go get github.com/mattn/go-sqlite3

导入包的时候注意细节

github.com/mattn/go-sqlite3 导入包需要给一个匿名重命名一下
因为go-sqlite3 包在导⼊时会执⾏其 init 函数,该函数会注册 SQLite3 驱动到 database/sql 包中,所以为了使⽤ sql.Open("sqlite3", ...) 时,database/sql 包就能够找到并使⽤这个驱动就跟着做就行了。

_ "github.com/mattn/go-sqlite3"

在涉及到数据库的时候无非就是几件事情

  • 打开数据库连接
  • 写sql语句
  • 执行sql语句
  • 关闭连接

同理下面就按照这个顺序介绍

打开数据库连接(test.db不存在他会帮你创建的,不用担心)

db, err := sql.Open("sqlite3", "./test.db")    if err != nil {        fmt.Println("连接失败", err)    }//关闭数据库在这里,//但是因为使用了go中的defer所以他会自动帮你关闭连接
    defer db.Close()  

写sql语句执行语句
创建表:users表为例

createTableSQL := CREATE TABLE IF NOT EXISTS users (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"age" INTEGER
);_, err = db.Exec(createTableSQL)
if err != nil {fmt.Println("创建数据库失败:", err)
}

插入:参数值可以有两种方式给:

  • 第一种:直接给值在sql语句中
insertSQL = `INSERT INTO users (name,age) VALUES ("李四",15)`
db.Exec(insertSQL)
  • 第二种:可以通过占位符 ? 在执行语句的时候传递参数值
insertSQL := `INSERT INTO users (name, age) VALUES (?,?)` //可以通过??作为占位符,exec的时候就可以传参的方式传进去
_, err = db.Exec(insertSQL, "张三", 18)
if err != nil {fmt.Println("插入数据失败:", err)
}

补充一些细节:

  • 如果你sql执行了,代码运行没有报错,但是你在数据库中仍然没有看到变化,那大概率是你sql语句写错了。
  • sqlite在go中不用安装什么软件,直接使用即可,我个人是使用vscode中的sqlite插件查看数据的,下图中我两个插件都安装了,第一个安装完成后你在项目中点开db文件就能直接看到数据被解析可以看到内容了。如果你使用其他编辑器的话自行搜索方法打开即可,推荐https://www.navicat.com.cn/products/navicat-premium/

nmap扫描

这里我将之前自定义的端口扫描换成nmap扫描了

1.第一步:需要提前安装好nmap:
https://nmap.org/download.html
安装对应系统的版本后他会自己添加到系统变量中的,比如我windows安装完毕后再cmd窗口输入nmap就可以有提示出来了,如果没有自己就去安装的路径,将该路径复制到环境变量中去。(这里自行解决)

2.第二步:下载go-nmap
注意了,这个是辅助包,不包含nmap的,nmap前面我们已经安装了
(当然如果你要不安装nmap就使用的话也有对应的包是下载来就是go语言写的nmap: github.com/Ullaakut/nmap 库 ,这个可以解决你的需求,但是功能肯定没有nmap强大)

go get github.com/lair-framework/go-nmap

主要执行的还是调用我们的命令
注意:-oX 如果你不加没有报错的话就可以不用加,这涉及到的输出问题,如果输出对不上他总是报错,解决办法我只有这一个,有大佬有其他解决办法可以告诉我一下。

cmd := exec.Command("nmap", "-sV", "-T4", "-oX", "-", target) // -sV:服务探测,-T4:扫描速度

解析nmap的结果

result, err := nmap.Parse(output)
if err != nil {log.Fatalf("解析失败: %v", err)
}

重点是打印,对应的变量名也写的很清楚了

// 打印结果
for _, host := range result.Hosts {fmt.Printf("主机: %s\n", host.Addresses[0].Addr)for _, port := range host.Ports {fmt.Printf(" 端口 %d/%s: %s %s\n",port.PortId,port.Protocol,port.Service.Name,port.Service.Product)}}

运行后的结果与db数据库


示例代码:

package mainimport (    "database/sql"    "fmt"    "log"    "os/exec"    "time"    "github.com/lair-framework/go-nmap"    _ "github.com/mattn/go-sqlite3")// 测试sqlitefunc testSqlite() {    db, err := sql.Open("sqlite3", "./test.db")    if err != nil {        fmt.Println("连接失败", err)    }    defer db.Close()    // //创建表    createTableSQL := `CREATE TABLE IF NOT EXISTS users (    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,    "name" TEXT,    "age" INTEGER    );`    _, err = db.Exec(createTableSQL)    if err != nil {        fmt.Println("创建数据库失败:", err)    }    //插入数据    insertSQL := `INSERT INTO users (name, age) VALUES (?,?)` //可以通过??作为占位符,exec的时候就可以传参的方式传进去    _, err = db.Exec(insertSQL, "张三", 18)    if err != nil {        fmt.Println("插入数据失败:", err)    }    insertSQL = `INSERT INTO users (name,age) VALUES ("李四",15)`    db.Exec(insertSQL)    insertSQL = `INSERT INTO users (name,age) VALUES ("王五",23)`    db.Exec(insertSQL)    insertSQL = `INSERT INTO users (name,age) VALUES ("test1",23)`    db.Exec(insertSQL)    insertSQL = `INSERT INTO users (name,age) VALUES ("test1",23)` //插入两个test1作为测试数据    db.Exec(insertSQL)    insertSQL = `INSERT INTO users (name,age) VALUES ("test2",23)`    db.Exec(insertSQL)    //更新数据    updateSQL := `UPDATE users SET age = ? WHERE id = ?`    db.Exec(updateSQL, 45, 1)                                    //更新id为1的年龄为45,即张三的年龄更改为45    updateSQL = `UPDATE users SET name = "五福" where name = "王五"` //将所有叫王五的人更改名字为五福    db.Exec(updateSQL)    // //删除数据    // //删除name为test的数据    deleteSQL := `DELETE FROM users WHERE name = ?`    res, err := db.Exec(deleteSQL, "test1")    if err != nil {        fmt.Println("删除失败", err)    }    //查看删除了多少个数据    resRows, err := res.RowsAffected()    if err != nil {        fmt.Println("删除失败:", err)    }    fmt.Println("更新了:", resRows)}// 使用nmap扫描return结果func nmapScan(target string) (*nmap.NmapRun, time.Time) {    // 执行Nmap扫描    cmd := exec.Command("nmap", "-sV", "-T4", "-oX", "-", target) // -sV:服务探测,-T4:扫描速度    output, err := cmd.CombinedOutput()    if err != nil {        log.Fatalf("Nmap扫描失败: %v\n输出: %s", err, string(output))    }    // 解析Nmap输出    result, err := nmap.Parse(output)    if err != nil {        log.Fatalf("解析失败: %v", err)    }    // 打印结果    for _, host := range result.Hosts {        fmt.Printf("主机: %s\n", host.Addresses[0].Addr)        for _, port := range host.Ports {            fmt.Printf(" 端口 %d/%s: %s %s\n",                port.PortId,                port.Protocol,                port.Service.Name,                port.Service.Product)        }    }    return result, time.Now()}// 将结果写进sqlite中func outpuSqlite(res *nmap.NmapRun, t time.Time) {    db, err := sql.Open("sqlite3", "./test.db")    if err != nil {        fmt.Println("连接失败", err)    }    defer db.Close()    createTableSQL := `CREATE TABLE IF NOT EXISTS result (    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,    "ip_address" TEXT,    "port" INTEGER,    "protocol" TEXT,    "severity" TEXT,    "timestamp" DATETIME    );`    db.Exec(createTableSQL)    //将扫描结果插入数据库中    for _, host := range res.Hosts {        for _, port := range host.Ports {            insertSQL := `INSERT INTO result (ip_address, port, protocol, severity, timestamp) VALUES (?,?,?,?,?)`            //添加数据,注明:hight是我随便编的,可以搞一个对照表来确定是否高危端口            db.Exec(insertSQL, host.Addresses[0].Addr, port.PortId, port.Protocol, "hight", t.Format("2006年01月02日"))        }    }}func main() {    //nmapScan()    res, t := nmapScan("baidu.com") //扫描单个目标    outpuSqlite(res, t)}

JSON

爆肝json篇章...


没啥好说过了Sqlite这个坎后json不在话下了

map转json

// map数据转jsonfunc mapTojson() {    data := map[string]string{"name": "zhangsan", "person": "something info"}    jsonData, err := json.Marshal(data)    if err != nil {        fmt.Println("转json失败:", err)        return    }    fmt.Println(string(jsonData))}

结构体转json

type person struct {    Name string `json:name`    Age  int    `json:age`}
// 结构体转jsonfunc structTojson() {    user := person{Name: "lisi", Age: 18}    jsonData, err := json.Marshal(user)    if err != nil {        fmt.Println("转json失败:", err)        return    }    fmt.Println(string(jsonData))}

json写入文件

type person struct {    Name string `json:name`    Age  int    `json:age`}
// 将json数据写入文件func outputJson() {    user := person{Name: "lisi", Age: 18}    jsonData, err := json.MarshalIndent(user, "", "\t") //先格式化再写入,这里的缩进采用tab    if err != nil {        fmt.Println("转json失败:", err)        return    }    file, err := os.OpenFile("output.json", os.O_CREATE|os.O_RDWR, 0666)    if err != nil {        fmt.Println("打开文件失败:", err)        return    }    defer file.Close()    _, err = file.Write(jsonData)    if err != nil {        fmt.Println("写入文件失败:", err)        return    }}

json编解码

type person struct {    Name string `json:name`    Age  int    `json:age`}
// json编解码func jsonEncoderDecoder() {    user := person{        Name: "wangwu",        Age:  16,    }    file, err := os.OpenFile("test.json", os.O_CREATE|os.O_RDWR, 06666)    if err != nil {        fmt.Println("打开文件失败:", err)    }    defer file.Close()    //编码json    encoder := json.NewEncoder(file)    encoder.SetIndent("", "\t") //格式添加tab    err = encoder.Encode(user)    if err != nil {        fmt.Println("转json失败:", err)    }    //解码    var newUser person    file, err = os.OpenFile("test.json", os.O_RDONLY, 0666)    decoder := json.NewDecoder(file)    err = decoder.Decode(newUser) //将加载的file文件json数据解析到newUser中    if err != nil {        fmt.Println("转换失败:", err)    }    fmt.Printf("Name: %s, Age: %d\n", user.Name, user.Age)}

json转结构体


type person2 struct {    Name     string `json:name`    Age      int    `json:age`    Location struct {        City  string `json:city`        Other string `json:other`    } `json:location`}
// 使用Unmarshal读取到的json文本数据解析到struct中func jsonTostruct_Unmarshal() {    file, err := os.OpenFile("test2.json", os.O_CREATE|os.O_RDWR, 0666)    if err != nil {        fmt.Println("打开文件失败:", err)    }    res, err := ioutil.ReadAll(file)    if err != nil {        fmt.Println("读取失败:", err)    }    var user2 person2    if err := json.Unmarshal(res, &user2); err != nil {        fmt.Println("json转struct失败:", err)    }    fmt.Println("转换成功:", user2.Location.City) //验证一下即可    //结构体格式化json(MarshalIndent)    jsonData, err := json.MarshalIndent(user2, "", "\t") //记得给制表符    if err != nil {        fmt.Println("struct转json失败", err)    }    fmt.Println(string(jsonData)) //验证是否转换成功}

json转map

// json文本数据转mapfunc jsonTomap_Unmarshal() {    file, err := os.OpenFile("test2.json", os.O_CREATE|os.O_RDWR, 0666)    if err != nil {        fmt.Println("打开文件失败:", err)    }    defer file.Close()    data, err := io.ReadAll(file)    if err != nil {        fmt.Println("读取失败:", err)    }    var user2 map[string]interface{}    //同理上一次转struct一样,这里是转为map而已    if err := json.Unmarshal(data, &user2); err != nil {        fmt.Println("转换失败:", err)    }    fmt.Println("验证是否转成功:", user2["Location"])    //map格式化json    res, err := json.MarshalIndent(user2, "", "\t")    if err != nil {        fmt.Println("转换失败:", err)    }    //验证是否转回来成功    fmt.Println(string(res))}

json转string

// 直接从json文件转json字符串即可,// 不用其他什么自己写一个结构体啥的,// 如果贪图快就直接转字符串func jsonTostring() {    file, err := os.OpenFile("test2.json", os.O_CREATE|os.O_RDWR, 0666)    if err != nil {        fmt.Println("打开文件失败:", err)    }    defer file.Close()    res, err := io.ReadAll(file)    if err != nil {        fmt.Println("读取文件失败:", err)    }    var strjson interface{}    if err := json.Unmarshal(res, &strjson); err != nil {        fmt.Println("解析失败:", err)    }    fmt.Println("查看是否解析成功(还未格式化):", strjson) //这里还没格式化    //接下来进行格式化    res, err = json.MarshalIndent(strjson, "", "\t")    if err != nil {        fmt.Println("格式化失败:", err)    }    fmt.Println(string(res))}

练习:nmap扫描结果导出json格式

需求:
通过读取json配置文件,配置文件可以控制变量传到nmap扫描,扫描结果以json格式导出

{    "ip_addresses": ["127.0.0.1", "192.168.1.1"],    "port_range": "1-1024",    "timeout": 5   }

细节分块:

  • nmapScan函数:使用nmap扫描return结果
    通过传参形式,将最大延迟时间和端口范围给到函数内部namp进行扫描

  • getConfig函数:这没啥好讲,我单独拿出来只是为了代码容易读一点,就是读取配置文件返回一个map类型数据

  • startScan函数:这里有一个之前没学过的知识点,断言,用于从 interface{} 类型的值中提取其具体类型,比如:value, ok := interfaceValue.(具体类型)
    这样就是强制的将你interface不指定的类型变量强制指定一个类型使用(非常好用)
    还有一个细节就是在json文件中读取出来的数字默认为float64,他直接给了最大的浮点数范围了,怕你不够用,所以我这里进行了类型转换

    最后一个细节就是:return的*nmap.NmapRun是一个切片,因为我们扫描的ip可能是多个的,不然就是只返回最后扫描的那个ip了。

  • scanResultOutputJson函数
    这里我是使用结构体,根据json输出的字段定义了一下

    接收的result也是nmap刚刚讲的扫描的多个结果,同时我用时间戳作为文件名前缀以防多次不同扫描结果冲突或者覆盖,其他没啥问题了就正常写入json文件即可。


先看运行截图,后面放源代码
(ps:两张截图之间没有联系)


示例代码:

package mainimport (    "encoding/json"    "fmt"    "io"    "io/ioutil"    "log"    "os"    "os/exec"    "strconv"    "time"    "github.com/lair-framework/go-nmap")// 将json文件存储扫描目标,加载进来作为,进行nmap扫描结果输出output到json文件中// 使用nmap扫描return结果func nmapScan(target string, port_range string, timeout int) (*nmap.NmapRun, time.Time) {    // 执行Nmap扫描    // -sV:服务探测,-T4:扫描速度    //--max-rtt-timeout控制每一个端口最大超时时间    cmd := exec.Command("nmap", "-sV", "-T4", "--max-rtt-timeout", strconv.Itoa(timeout), "-p", port_range, "-oX", "-", target)    output, err := cmd.CombinedOutput()    if err != nil {        log.Fatalf("Nmap扫描失败: %v\n输出: %s", err, string(output))    }    // 解析Nmap输出    result, err := nmap.Parse(output)    if err != nil {        log.Fatalf("解析失败: %v", err)    }    // 打印结果    for _, host := range result.Hosts {        fmt.Printf("主机: %s\n", host.Addresses[0].Addr)        for _, port := range host.Ports {            fmt.Printf(" 端口 %d/%s: %s %s\n",                port.PortId,                port.Protocol,                port.Service.Name,                port.Service.Product)        }    }    return result, time.Now()}// 拿到配置数据func getConfig(configFile string) map[string]interface{} {    file, err := os.OpenFile(configFile, os.O_CREATE|os.O_RDONLY, 0666)    if err != nil {        fmt.Println("打开文件失败:", err)    }    defer file.Close()    data, err := io.ReadAll(file)    if err != nil {        fmt.Println("读取配置文件失败:", err)    }    var res map[string]interface{}    if err := json.Unmarshal(data, &res); err != nil {        fmt.Println("转map失败:", err)    }    // fmt.Println(res["ip_addresses"])    return res}func startScan() ([]*nmap.NmapRun, time.Time) {    config := getConfig("config.json")    var result []*nmap.NmapRun    var r *nmap.NmapRun    var scanTime time.Time    //断言,直接强制给定类型    if ipList, ok := config["ip_addresses"].([]interface{}); ok {        for _, host := range ipList {            timeoutFloat, _ := config["timeout"].(float64) //在json读取出来的是float64类型            r, scanTime = nmapScan(host.(string), config["port_range"].(string), int(timeoutFloat))            result = append(result, r)        }    } else {        fmt.Println("读取失败,ip_addresses应该为列表类型")    }    return result, scanTime}func scanResultOutputJson(result []*nmap.NmapRun, scanTime time.Time) {    type res_struct struct {        Id            int    `json:id`        Ip_address    string `json:ip_address`        Port          int    `json:port`        Vulnerability string `json:vulnerability`        Severity      string `json:severity`        Timestamp     string `json:timestamp`    }    var ress []res_struct        //存储数据,最终要写入json文件中    for _, res := range result { //遍历所有扫描结果        for _, host := range res.Hosts { //遍历扫描完成的结果数据            for index, port := range host.Ports {                ress = append(ress, res_struct{                    Id:            index + 1,                    Ip_address:    host.Addresses[0].Addr,                    Port:          port.PortId,                    Vulnerability: port.Protocol,                    Severity:      "high",                    Timestamp:     scanTime.Format("2006年01月02日"),                })            }        }    }    outputdata, err := json.MarshalIndent(ress, "", "\t")    if err != nil {        fmt.Println("格式化失败:", err)    }    //输出文件名通过时间戳来表示就不会出错了    fileName := fmt.Sprintf("%d_scan_result.json", time.Now().Unix())    //意思是清空该文件的内容先,其实没啥用这里,用来当一个知识点吧    file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)    if err != nil {        fmt.Println("打开文件失败:", err)    }    _, err = file.Write(outputdata)    if err != nil {        fmt.Println("导出json文件失败:", err)    }}func main() {//程序运行
    scanResultOutputJson(startScan())
}

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

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

相关文章

Spring Cloud史诗级插件!OpenFeign Assistant导航跳转效率飙升,网友:原来IDEA还能这么用

🌟 ​OpenFeign Assistant插件灵感来源:灵感源自 MyBatisX 的设计理念。针对JetBrains IDEA生态,专注于解决 ​OpenFeign 接口与远程服务映射的开发痛点,提供与 MyBatisX 相似的流畅导航体验,针对 Spring Feign 生态深度优化。 JetBrains IDEA插件市场:https://plugins.…

实验 1 C语言输入输出和简单程序编写

实验任务1 源代码1 #include<stdio.h>2 #include<stdlib.h>3 int main()4 {5 printf(" O \n");6 printf("<H>\n");7 printf("I I\n");8 printf(" O \n");9 printf("<H>\n");…

ABB机器人齿轮箱齿轮磨损维修技巧

在工业生产领域,ABB机器人发挥着至关重要的作用。然而,随着使用时间的增长和工作强度的增加,机器人齿轮箱齿轮可能会出现磨损现象,这将导致机器人故障,影响生产效率。因此,掌握ABB机器人齿轮箱齿轮磨损的维修技巧对于工业机器人维修至关重要。一、ABB机器人齿轮箱齿轮磨损…

持续绩效管理入门须知

随着互联网改变了我们的工作方式,绩效管理正在经历一场重大的重构。全球的人力资源领导者正在重新思考他们的战略目标、运营模式和技术投资。为了有效应对新的工作场所需求,组织和行政领导层必须制定新的人才战略和流程,以保持领先。 随着个人和团队的生产力受到审视,持续绩…

推荐4本书《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》专著,非常感谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

可视化+图解:轻松搞定链表

对于链表的相关操作,我们总结了一套【可视化+图解】方法,依据此方法来解决链表相关问题,链表操作变得易于理解,写出来的代码可读性高也不容易出错。链表(Linked list)是一种常用的数据结构,它由一系列节点组成,每个节点包含数据域和指针域。指针域存储了下一个节点的地…

FastAPI+OpenAI实现Telegram问答机器人

首先要创建一个机器人,找到BotFather获取到机器人的Token设置后台地址,实现消息转发 curl -X POST "https://api.telegram.org/bot{机器人token}/setWebhook?url=https://chat.xxxxxxxx.com/chat"配置文件 .env OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx…

dat格式和mmdb格式IP数据库下载源

MaxMind 提供了免费的 IP 地域数据库,早期的dat格式的ip库MaxMind 官方已经停止支持。 现在MaxMind 官方提供的mmdb格式需要注册后才能下载。 本文记录找到的其它下载源。 dat格式下载:https://www.miyuru.lk/geoiplegacy下载国家IP库(包含ipv4和ipv6),解压并重命名:wget …

Linux 平均负载 Load Average 详解

转载自Linux 平均负载 Load Average 详解_load average多少是正常-CSDN博客 一、什么是Load Average? 系统负载(System Load)是系统CPU繁忙程度的度量,即有多少进程在等待被CPU调度(进程等待队列的长度)。 平均负载(Load Average)是一段时间内系统的平均负载,这个一段…

Linux 中 sh -c

001、[root@PC1 test]# ls a.sh [root@PC1 test]# cat a.sh #!/bin/bash echo hello world [root@PC1 test]# sh -c bash a.sh ## 更新了系统环境变量? (base) [root@PC1 test]# (base) [root@PC1 test]# conda deactivate [root@PC1 test]# ls a.sh [root@PC1 test]#…

跑步名词

跑步名词 1. 跑步机上的速度6指的是多少跑步机上的速度指的是每小时几公里(km/h)所以跑步机上的速度“6”通常指的是每小时6公里(km/h)。 2. 配速是什么意思 配速(Pace)是跑步运动中常用的术语,指的是完成每公里(或每英里)需要几分钟。 它是衡量跑步速度的一种方式,通…

探秘Transformer系列之(11)--- 掩码

从零开始解析Transformer,目标是:(1) 解析Transformer如何运作,以及为何如此运作,让新同学可以入门;(2) 力争融入一些比较新的或者有特色的论文或者理念,让老鸟也可以有所收获。探秘Transformer系列之(11)--- 掩码 目录探秘Transformer系列之(11)--- 掩码0x00 概述0x…