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练习:nmap扫描结果导出json格式 输出功能 在我们使用安全工具的…...

线性代数之矩阵特征值与特征向量的数值求解方法
文章目录 前言1. 幂迭代法(Power Iteration)幂法与反幂法求解矩阵特征值幂法求最大特征值编程实现补充说明 2. 逆幂迭代法(Inverse Iteration)移位反幂法 3. QR 算法(QR Algorithm)——稠密矩阵理论推导编程…...

Spring MVC源码分析のinit流程
文章目录 前言一、 init1.1、createWebApplicationContext1.2、onRefresh 二、请求处理器2.1、RequestMapping2.2、Controller接口2.3、HttpRequestHandler接口2.4、HandlerFunction 三、initHandlerMappings3.1、getDefaultStrategies3.1.1、RequestMappingHandlerMapping3.1.…...

【后端开发】go-zero微服务框架实践(goland框架对比,go-zero开发实践,文件上传问题优化等等)
【后端开发】go-zero微服务框架实践(goland框架对比,go-zero开发实践,文件上传问题优化等) 文章目录 1、go框架对比介绍2、go-zero 微服务开发实践3、go-zero 文件上传问题优化 1、go框架对比介绍 国内开源goland框架对比 1 go-…...

C#程序加密与解密Demo程序示例
目录 一、加密程序功能介绍 1、加密用途 2、功能 3、程序说明 4、加密过程 5、授权的注册文件保存方式 二、加密程序使用步骤 1、步骤一 编辑2、步骤二 3、步骤三 4、步骤四 三、核心代码说明 1、获取电脑CPU 信息 2、获取硬盘卷标号 3、机器码生成 3、 生成…...

小程序事件系统 —— 33 事件传参 - data-*自定义数据
事件传参:在触发事件时,将一些数据作为参数传递给事件处理函数的过程,就是事件传参; 在微信小程序中,我们经常会在组件上添加一些自定义数据,然后在事件处理函数中获取这些自定义数据,从而完成…...
深入解析 JavaScript 原型与原型链:从原理到应用
原型和原型链是 JavaScript 中实现对象继承和属性查找的核心机制。为了更深入地理解它们,我们需要从底层原理、实现机制以及实际应用等多个角度进行分析。 1. 原型(Prototype) 1.1 什么是原型? 每个 JavaScript 对象(…...

关于AI数据分析可行性的初步评估
一、结论:可在部分环节嵌入,无法直接处理大量数据 1.非本地部署的AI应用处理非机密文件没问题,内部文件要注意数据安全风险。 2.AI(指高规格大模型)十分适合探索性研究分析,对复杂报告无法全流程执行&…...

回归预测 | Matlab实现GWO-BP-Adaboost基于灰狼算法优化BP神经网络结合Adaboost思想的回归预测
回归预测 | Matlab实现GWO-BP-Adaboost基于灰狼算法优化BP神经网络结合Adaboost思想的回归预测 目录 回归预测 | Matlab实现GWO-BP-Adaboost基于灰狼算法优化BP神经网络结合Adaboost思想的回归预测回归效果基本介绍GWO-BP-Adaboost:基于灰狼算法优化BP神经网络结合Adaboost思想…...
ARM Cortex-M 内存映射详解:如何基于寄存器直接读写 寄存器映射方式编码程序 直接操作硬件寄存器来控制 MCU
ARM Cortex-M 的系统映射空间 在 STM32 等 ARM Cortex-M 系列 MCU 中,内存地址空间按照 存储功能 进行了严格划分,包括 Flash(程序存储)、RAM(数据存储)、外设寄存器(GPIO、UART、SPI 等&am…...

深度学习实战车辆目标跟踪与计数
本文采用YOLOv8作为核心算法框架,结合PyQt5构建用户界面,使用Python3进行开发。YOLOv8以其高效的实时检测能力,在多个目标检测任务中展现出卓越性能。本研究针对车辆目标数据集进行训练和优化,该数据集包含丰富的车辆目标图像样本…...
django中视图作用和视图功能 以及用法
在 Django REST Framework(DRF)中,视图(View)是处理 HTTP 请求并返回响应的核心组件。DRF 提供了多种视图类,适用于不同的场景和需求。以下是 DRF 中常见的视图类及其作用、使用方法的详细说明: 一、DRF 视图的分类 DRF 的视图可以分为以下几类: 基于函数的视图(Func…...

【每日学点HarmonyOS Next知识】输入框自动获取焦点、JS桥实现方式、Popup设置全屏蒙版、鼠标事件适配、Web跨域
1、HarmonyOS TextInput或TextArea如何自动获取焦点? 可以使用 focusControl.requestFocus 对需要获取焦点的组件设置焦点,具体可以参考文档: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-universal-attribut…...
【学习思维模型】
学习思维模型 一、理解类模型二、记忆类模型三、解决问题类模型四、结构化学习模型五、效率与习惯类模型六、高阶思维模型七、实践建议八、新增学习思维模型**1. 波利亚问题解决四步法****2. 主动回忆(Active Recall)****3. 鱼骨图(因果图/Ishikawa Diagram)****4. MECE原则…...

MyBatis-Plus分页控件使用及使用过程发现的一个坑
最近维护一个旧项目的时候,出现了一个BUG,经排查后发现是Mybatis-plus分页控件使用的时候需要注意的一个问题,故在本地使用MybatisPlus模拟出现了一下这个问题。 首先,先说一下MyBatis-Plus的使用: 1)引入…...
STM32的APB1和APB2的区别
STM32微控制器中的APB1和APB2的区别 STM32微控制器中的APB1和APB2是两种不同的外设总线,主要区别在于时钟速度、连接的外设以及用途。以下是它们的详细对比: 1. 时钟速度 APB1 (Advanced Peripheral Bus 1): 低速总线,时钟频率通常为系统时钟…...

JS一些小知识点
一、|| 运算符 plain this.ctx.body { type: type || 0, // ||在此处用法用于默认值填充,判断是否传参或该值是否存在,如果不存在就使用||后买你的值作为默认值 code: code || 0, msg: msg || SUCCESS, data: data || {}, ...others }; 二、trim() 方…...

手写Tomcat:实现基本功能
首先,Tomcat是一个软件,所有的项目都能在Tomcat上加载运行,Tomcat最核心的就是Servlet集合,本身就是HashMap。Tomcat需要支持Servlet,所以有servlet底层的资源:HttpServlet抽象类、HttpRequest和HttpRespon…...
C#变量与变量作用域详解
一、变量基础 1. 声明与初始化 声明语法:<数据类型> <变量名>(如 int age; string name)初始化要求: 1、 类或结构体中的字段变量(全局变量)无需显式初始化,默认值…...

SV学习笔记——数组、队列
一、定宽数组 定宽数组是静态变量,编译时便已经确定其大小,其可以分为压缩定宽数组和非压缩定宽数组:压缩数组是定义在类型后面,名字前面;非压缩数组定义在名字后面。Bit [7:0][3:0] name; bit[7:0] name [3:0]; 1.1定宽数组声明 数组的声…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...