当前位置: 首页 > news >正文

Go红队开发—格式导出

文章目录

  • 输出功能
    • 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.WaitGroupch      = make(chan int, 1024) // 增加缓冲区,减少阻塞count   intworkers = 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 <- 1go 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     stringPorts    stringScanTime 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.WaitGroupch      = make(chan int, 1024) // 增加缓冲区,减少阻塞count   intworkers = 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 <- 1go 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,即张三的年龄更改为45updateSQL = `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") //先格式化再写入,这里的缩进采用tabif 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()//编码jsonencoder := json.NewEncoder(file)encoder.SetIndent("", "\t") //格式添加taberr = encoder.Encode(user)if err != nil {fmt.Println("转json失败:", err)}//解码var newUser personfile, 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 person2if 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格式化jsonres, 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.NmapRunvar r *nmap.NmapRunvar 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())
}

相关文章:

Go红队开发—格式导出

文章目录 输出功能CSV输出CSV 转 结构体结构体 转 CSV端口扫描结果使用CSV格式导出 HTML输出Sqlite输出nmap扫描 JSONmap转json结构体转jsonjson写入文件json编解码json转结构体json转mapjson转string练习&#xff1a;nmap扫描结果导出json格式 输出功能 在我们使用安全工具的…...

Sharp 存在任意文件读取漏洞( DVB-2025-8923)

免责声明 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 0x01…...

C++数组,链表,二叉树的内存排列是什么样的,结构体占多大内存如何计算,类占多大内存如何计算,空类的空间是多少,为什么?

C数组是连续存储的&#xff0c;C数组元素依次存放在相邻的内存地址之中&#xff0c;并且内存大小相同。 C链表是离散存储的&#xff0c;C链表是由节点构成的&#xff0c;每个节点之中存在节点的值以及指向下一个节点的指针&#xff0c;每个节点是动态分配的。 C二叉树也是离散…...

【vLLM 教程】使用 TPU 安装

vLLM 是一款专为大语言模型推理加速而设计的框架&#xff0c;实现了 KV 缓存内存几乎零浪费&#xff0c;解决了内存管理瓶颈问题。 更多 vLLM 中文文档及教程可访问 →https://vllm.hyper.ai/ vLLM 使用 PyTorch XLA 支持 Google Cloud TPU。 依赖环境​ Google Cloud TPU …...

【RAG】基于向量检索的 RAG (BGE示例)

RAG机器人 结构体 文本向量化: 使用 BGE 模型将文档和查询编码为向量。 &#xff08;BGE 是专为检索任务优化的开源 Embedding 模型&#xff0c;除了本文API调用&#xff0c;也可以通过Hugging Face 本地部署BGE 开源模型&#xff09; 向量检索: 从数据库中找到与查询相关的文…...

【RAG】RAG 系统的基本搭建流程(ES关键词检索示例)

RAG 系统的基本搭建流程 搭建过程&#xff1a; 文档加载&#xff0c;并按一定条件切割成片段将切割的文本片段灌入检索引擎封装检索接口构建调用流程&#xff1a;Query -> 检索 -> Prompt -> LLM -> 回复 1. 文档的加载与切割 # !pip install --upgrade openai…...

PSIM积累经验

1、三极管的部署报错。 出错信息&#xff1a; 元件&#xff1a; R 名称&#xff1a; R2 Error: The RLC branch R2 is connected to the gate node of the switch Q1. The gate node should be connected to an On-Off Controller output. Refer to the switch Help p…...

C++之vector类(超详解)

这节我们来学习一下&#xff0c;C中一个重要的工具——STL&#xff0c;这是C中自带的一个标准库&#xff0c;我们可以直接调用这个库中的函数或者容器&#xff0c;可以使效率大大提升。这节我们介绍STL中的vector。 文章目录 前言 一、标准库类型vector 二、vector的使用 2.…...

Go学习笔记

<!-- 注意* --> 初始化工程 go mod init GoDemo 结构体&#xff0c;接口 type i struct{} type i interface{} 条件&#xff0c;选择 循环 键值对 make(map[string]int) 切片&#xff0c;集合 make([]int,10) 函数 通道 Channel make(chan int) ​ ch <- v…...

前端杂的学习笔记

什么是nginx Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器 Nginx是一款轻量级的Web 服务器/反向代理服务器&#xff0c;处理高并发能力是十分强大的&#xff0c;并且支持热部署&#xff0c;启动简单&#xff0c;可以做到7*24不间断运行 正代和反代 学习nginx&a…...

痉挛性斜颈护理:全方位呵护,重燃生活希望

痉挛性斜颈是一种以颈部肌肉不自主收缩导致头部向一侧扭转或倾斜为特征的疾病。对于痉挛性斜颈患者而言&#xff0c;科学有效的护理能够显著提升其生活质量&#xff0c;辅助病情的改善。 生活护理&#xff1a;在生活环境布置上&#xff0c;要充分考虑患者行动的便利性。确保室内…...

MySQL的安装以及数据库的基本配置

MySQL的安装及配置 MySQL的下载 选择想要安装的版本&#xff0c;点击Download下载 Mysql官网下载地址&#xff1a;​ ​https://downloads.mysql.com/archives/installer/​​ MySQL的安装 选择是自定义安装&#xff0c;所以直接选择“Custom”&#xff0c;点击“Next”​ …...

WangEditor快速实现版

WangEditor快速实现版 效果 案例代码 后端 package com.diy.springboot.controller;import cn.hutool.core.util.IdUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiImplicitParam; import org.sp…...

LeetCode Hot100刷题——反转链表(迭代+递归)

206.反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&#…...

10.2 继承与多态

文章目录 继承多态 继承 继承的作用是代码复用。派生类自动获得基类的除私有成员外的一切。基类描述一般特性&#xff0c;派生类提供更丰富的属性和行为。在构造派生类时&#xff0c;其基类构造函数先被调用&#xff0c;然后是派生类构造函数。在析构时顺序刚好相反。 // 基类…...

java项目之基于ssm的智能训练管理平台(源码+文档)

项目简介 智能训练管理平台实现了以下功能&#xff1a; 系统可以提供信息显示和相应服务&#xff0c;其管理员增删改查课程信息和课程信息资料&#xff0c;审核课程信息预订订单&#xff0c;查看订单评价和评分&#xff0c;通过留言功能回复用户提问。 &#x1f495;&#x1…...

29-验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &#xff1b;否则&#xf…...

(57)[HGAME 2023 week1]easyasm

nss&#xff1a;3477 [HGAME 2023 week1]easyasm 关于这个题吧&#xff0c;我还是和上一个题一样&#xff0c;我观察到了异或0x33 所以我就把result的结果跟0x33异或&#xff0c;然后我就就这样&#xff0c;做出来了...

FY-3D MWRI亮温绘制

1、FY-3D MWRI介绍 风云三号气象卫星&#xff08;FY-3&#xff09;是我国自行研制的第二代极轨气象卫星&#xff0c;其有效载荷覆 盖了紫外、可见光、红外、微波等频段&#xff0c;其目标是实现全球全天候、多光谱、三维定量 探测&#xff0c;为中期数值天气预报提供卫星观测数…...

Java集合面试题

引言 Java集合框架是Java编程中不可或缺的一部分&#xff0c;它提供了一系列用于存储和操作对象的接口和类。在Java面试中&#xff0c;集合框架的相关知识往往是必考的内容。本文将汇总一系列关于Java集合的面试题&#xff0c;帮助求职者更好地准备面试。 一、Java集合框架概…...

知识蒸馏综述Knowledge Distillation: A Survey解读

论文链接&#xff1a;Knowledge Distillation: A Survey 摘要&#xff1a;近年来&#xff0c;深度神经网络在工业界和学术界都取得了成功&#xff0c;尤其是在计算机视觉任务方面。深度学习的巨大成功主要归功于它能够扩展以对大规模数据进行编码&#xff0c;并且能够处理数十…...

ES映射知识

映射 映射类似于关系型数据库的Schema&#xff08;模式&#xff09;。 映射来定义字段列和存储的类型等基础信息。 {"mappings": {"properties": {"username": {"type": "keyword","ignore_above": 256 // 忽略…...

Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现与实战指南

Spring Boot拦截器&#xff08;Interceptor&#xff09;与过滤器&#xff08;Filter&#xff09;深度解析&#xff1a;区别、实现与实战指南 一、核心概念对比 1. 本质区别 维度过滤器&#xff08;Filter&#xff09;拦截器&#xff08;Interceptor&#xff09;规范层级Serv…...

Debian二次开发一体化工作站:提升科研效率的智能工具

在科研领域&#xff0c;数据处理是实验成功的关键环节之一。随着实验数据的复杂性和规模不断增加&#xff0c;传统的数据处理方法已经难以满足科研人员的需求。这时&#xff0c;一体化工作站应运而生&#xff0c;成为科研实验数据处理的 “智能大脑”。 一体化工作站&#xff…...

swift-5-汇编分析闭包本质

一、枚举、结构体、类都定义方法 方法占用对象的内存么&#xff1f; 不占用 方法的本质就是函数 方法、函数都存放在代码段&#xff0c;因为方法都是公共的&#xff0c;不管 对象一还是对对象二调用都是一样的&#xff0c;所以放在代码段&#xff0c;但是每个对象的成员不一样所…...

Linux安装升级docker

Linux 安装升级docker Linux 安装升级docker背景升级停止docker服务备份原docker数据目录移除旧版本docker安装docker ce恢复数据目录启动docker参考 安装找到docker官网找到docker文档删除旧版本docker配置docker yum源参考官网继续安装docker设置开机自启配置加速测试 Linux …...

小程序事件系统 —— 33 事件传参 - data-*自定义数据

事件传参&#xff1a;在触发事件时&#xff0c;将一些数据作为参数传递给事件处理函数的过程&#xff0c;就是事件传参&#xff1b; 在微信小程序中&#xff0c;我们经常会在组件上添加一些自定义数据&#xff0c;然后在事件处理函数中获取这些自定义数据&#xff0c;从而完成…...

推荐一些免费开源支持Vue3甘特图组件

文章目录 前言一、dhtmlxGantt二、frappe-gantt三、vue-ganttastic四、gantt-elastic五、v-gantt六、vue-gantt-schedule-timeline-calendar七、vue-gantt八、总结 前言 在现代项目管理和任务调度中&#xff0c;甘特图是一种非常实用的工具。它能够直观地展示任务的时间安排、…...

Dify 本地部署教程

目录 一、下载安装包 二、修改配置 三、启动容器 四、访问 Dify 五、总结 本篇文章主要记录 Dify 本地部署过程&#xff0c;有问题欢迎交流~ 一、下载安装包 从 Github 仓库下载最新稳定版软件包&#xff0c;点击下载~&#xff0c;当然也可以克隆仓库或者从仓库里直接下…...

nlp培训重点-5

1. LoRA微调 loader&#xff1a; # -*- coding: utf-8 -*-import json import re import os import torch import numpy as np from torch.utils.data import Dataset, DataLoader from transformers import BertTokenizer """ 数据加载 """cl…...

XWiki使用war部署在tomcat9

xwiki部署 官方文档&#xff0c;比较详细。 https://www.xwiki.org/xwiki/bin/view/Documentation/AdminGuide/Installation/InstallationWAR/ xwiki是基于java的开源知识库&#xff0c;可以替代Confluence。有多种部署方式&#xff0c;本文使用war方式部署在tomca下&#x…...

CTA策略【量化理论】

CTA策略演变史 全称&#xff1a;Commodity Trading Advisor &#xff08;商品交易顾问&#xff09; CTA最开始是指通过为客户提供期权、期货方面的交易建议&#xff0c;或者直接通过受管理的期货账户参与实际交易&#xff0c;来获得收益的机构或个人。 随着市场的发展&#…...

旋转编码器原理与应用详解:从结构到实战 | 零基础入门STM32第四十七步

主题内容教学目的/扩展视频旋转编码器电路原理&#xff0c;跳线设置&#xff0c;结构分析。驱动程序与调用。熟悉电路和驱动程序。 师从洋桃电子&#xff0c;杜洋老师 &#x1f4d1;文章目录 一、旋转编码器是什么&#xff1f;二、内部结构揭秘2.1 机械组件解剖2.2 核心部件说明…...

计算机视觉cv2入门之图像的读取,显示,与保存

在计算机视觉领域&#xff0c;Python的cv2库是一个不可或缺的工具&#xff0c;它提供了丰富的图像处理功能。作为OpenCV的Python接口&#xff0c;cv2使得图像处理的实现变得简单而高效。 示例图片 目录 opencv获取方式 图像基本知识 颜色空间 RGB HSV 图像格式 BMP格式 …...

基于Canvas和和原生JS实现俄罗斯方块小游戏

这里是一个完整的H5俄罗斯方块游戏&#xff0c;使用了 HTML CSS JavaScript (原生) 实现&#xff0c;支持基本的俄罗斯方块玩法&#xff0c;如&#xff1a; ✅ 方块自动下落 ✅ 方向键控制移动、旋转、加速下落 ✅ 方块堆叠、消行 ✅ 计分系统 在 canvas 上绘制游戏&#x…...

阿里云 QwQ-32B 模型调研文档

阿里云 QwQ-32B 模型调研文档 ——技术解析、部署实践与微调指南 一、模型概述 QwQ-32B 是阿里云开源的轻量化大语言模型,以 320 亿参数 实现与 DeepSeek-R1(6710 亿参数)相当的推理性能。其核心优势包括: 参数效率:1/20 参数量达成竞品性能,显存需求降低 70%部署灵活性…...

【玩转23种Java设计模式】结构型模式篇:组合模式

软件设计模式&#xff08;Design pattern&#xff09;&#xff0c;又称设计模式&#xff0c;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 汇总目录链接&…...

Eolink:专为开发者设计的API协作平台

Eolink Apikit 是一款集 API 设计、管理、自动化测试、Mock 和异常监控于一体的全生命周期智能协作平台&#xff0c;旨在提升 API 研发和管理的效率。以下是对其功能和特点的详细介绍&#xff1a; 核心功能&#xff1a; API 设计与文档管理&#xff1a;Apikit 提供了强大的 API…...

【Python】为什么要写__init__.py

文章目录 PackageA(__init__特性)应该往__init__.py里放什么东西&#xff1f;1、包的初始化2、管理包的公共接口3、包的信息 正常我们直接导入就可以执行&#xff0c;但是在package的时候&#xff0c;有一种__init__.py的特殊存在 引入moduleA.py&#xff0c;执行main.py&…...

golang 从零单排 (一) 安装环境

1.下载安装 打开网址The Go Programming Language 直接点击下载go1.24.1.windows-amd64.msi 下载完成 直接双击下一步 下一步 安装完成 环境变量自动设置不必配置 2.验证 win r 输入cmd 打开命令行 输入go version...

30-判断子序列

给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是"abcde"的一个子序列&#…...

AI 驱动的软件测试革命:从自动化到智能化的进阶之路

&#x1f680;引言&#xff1a;软件测试的智能化转型浪潮 在数字化转型加速的今天&#xff0c;软件产品的迭代速度与复杂度呈指数级增长。传统软件测试依赖人工编写用例、执行测试的模式&#xff0c;已难以应对快速交付与高质量要求的双重挑战。人工智能技术的突破为测试领域注…...

深度相机进行目标物体的空间姿态(位姿)估计

利用深度相机&#xff08;如Kinect、Intel Realsense、Zed相机等&#xff09;进行目标物体的空间姿态&#xff08;位姿&#xff09;估计&#xff0c;通常结合了3D点云处理、目标识别和位姿优化算法。以下是完整的实现流程、算法选择及注意事项&#xff1a; 一、实现流程 1. 目…...

3月8日实验

拓扑&#xff1a; 需求&#xff1a; 1.学校内部的HTTP客户端可以正常通过域名www.baidu.com访问到白度网络中的HTTP服务器 2.学校网络内部网段基于192.168.1.0/24划分&#xff0c;PC1可以正常访问3.3.3.0/24网段&#xff0c;但是PC2不允许 3.学校内部路由使用静态路由&#…...

GO语言学习笔记

一、viper笔记【七米】 https://liwenzhou.com/posts/Go/viper/ 二、优雅关机和平滑重启 https://liwenzhou.com/posts/Go/graceful-shutdown/ 三、gin使用zap https://liwenzhou.com/posts/Go/zap-in-gin/ 四、flag 用于命令行传参 https://liwenzhou.com/posts/Go/flag/ 五、…...

Autosar技术栈总目录

总目录 Autosar架构理解Autosar Mcal配置开发&#xff08;TC3xx系列 基于EB&#xff09;Autosar Mcal配置开发&#xff08;S32K3xx系列 基于EB&#xff09;Autosar BSW服务开发&#xff08;基于Davinci CFG &Dev&#xff09;Makefile编译自动化脚本 持续更新中… Autosar架…...

开发指南107-谷歌内核浏览器滚动条设置

平台上统一制定了滚动条样式(仅限于webkit内核)&#xff1a;/* ------美化谷歌浏览器滚动条 开始-----------*/ ::-webkit-scrollbar{width:12px;height:12px;background-color: #E1E1E1;} ::-webkit-scrollbar-button:single-button { background-color:#E1E1E1; display: …...

25年携程校招社招求职能力北森测评材料计算部分:备考要点与误区解析

在求职过程中&#xff0c;能力测评是筛选候选人的重要环节之一。对于携程这样的知名企业&#xff0c;其能力测评中的材料计算部分尤为关键。许多求职者在备考时容易陷入误区&#xff0c;导致在考试中表现不佳。本文将深入解析材料计算部分的实际考察方向&#xff0c;并提供针对…...

Linux系统编程--线程同步

目录 一、前言 二、线程饥饿 三、线程同步 四、条件变量 1、cond 2、条件变量的使用 五、条件变量与互斥锁 一、前言 上篇文章我们讲解了线程互斥的概念&#xff0c;为了防止多个线程同时访问一份临界资源而出问题&#xff0c;我们引入了线程互斥&#xff0c;线程互斥其实…...

李沐《动手学深度学习》——14.9. 用于预训练BERT的数据集——wiki数据集问题以及存在的其他问题

问题1&#xff1a;出现"file is not a zip file" 原因是链接已经失效。 解决方法&#xff1a;打开下面链接自行下载&#xff0c;需要魔法。下载完解压到特定位置。 下载链接&#xff1a;项目首页 - Wikitext-2-v1数据包下载:Wikitext-2-v1 数据包下载本仓库提供了一…...