手写HTTP抓包工具------可视化界面
项目 | 描述 |
---|---|
语言 | golang |
可视化 | fynev2 |
功能 | 代理抓包、重发、记录 |
目录
- [1. 示例](#1. 示例)
-
- [1.1 主界面](#1.1 主界面)
- [1.2 开启反向代理](#1.2 开启反向代理)
- [1.3 抓包](#1.3 抓包)
- [1.4 历史记录](#1.4 历史记录)
- [1.5 重发](#1.5 重发)
- [2. 核心代码](#2. 核心代码)
-
- [2.1 GUI](#2.1 GUI)
- [2.1 抓包](#2.1 抓包)
- [3. 结语](#3. 结语)
-
- [3.1 传送门](#3.1 传送门)
1. 示例
1.1 主界面
1.2 开启反向代理
1.3 抓包
1.4 历史记录
1.5 重发
2. 核心代码
2.1 GUI
golang
func (cf *Config) CreateUi() {
myApp := app.New()
mainWin := myApp.NewWindow("Request Handling Tool v1.0")
// 创建数据绑定对象
cf.MainWin = &mainWin
// 创建可以多行输入的 Entry 并绑定数据
requestEntry := widget.NewEntryWithData(types.RequestData)
requestEntry.MultiLine = true
requestEntry.Wrapping = fyne.TextWrapBreak
responseEntry := widget.NewEntryWithData(types.ResponseData)
responseEntry.MultiLine = true
responseEntry.Wrapping = fyne.TextWrapBreak
ErrEntry := widget.NewEntryWithData(types.ErrData)
ErrEntry.MultiLine = true
ErrEntry.Wrapping = fyne.TextWrapBreak
hisreq1Entry := widget.NewEntryWithData(types.History_RequestD)
hisreq1Entry.MultiLine = true
hisreq1Entry.Wrapping = fyne.TextWrapBreak
hisres2Entry := widget.NewEntryWithData(types.History_ResponseD)
hisres2Entry.MultiLine = true
hisres2Entry.Wrapping = fyne.TextWrapBreak
repeat_req1Entry := widget.NewEntryWithData(types.Repeat_RequestD)
repeat_req1Entry.MultiLine = true
repeat_req1Entry.Wrapping = fyne.TextWrapBreak
repeat_res2Entry := widget.NewEntryWithData(types.Repeat_ResponseD)
repeat_res2Entry.MultiLine = true
repeat_res2Entry.Wrapping = fyne.TextWrapBreak
targetAddrEntry := widget.NewEntryWithData(types.TargetAD)
types.TargetAD.Set("127.0.0.1:3443")
serverAddrEntry := widget.NewEntryWithData(types.ServerAD)
types.ServerAD.Set("0.0.0.0:8089")
targetPMEntry := widget.NewEntryWithData(types.TargetPM)
types.TargetPM.Set("https")
targetAddrLabel := widget.NewLabel("TargetAddr:")
serverAddrLabel := widget.NewLabel("ServerAddr:")
targetPMLabel := widget.NewLabel("TargetPM:")
// 设置请求和响应 Entry 控件铺满父容器
check1 := widget.NewCheckWithData("AutoSend", types.AutoSData)
check1.SetChecked(true)
check_history := widget.NewCheckWithData("HistoryOK", types.HistoryOK)
check_history.SetChecked(true)
sendButton := widget.NewButton("Send", func() {
cf.GUI_Send()
})
send2Button := widget.NewButton("SendRAW", func() {
cf.GUI_Send_repeat()
})
discardButton := widget.NewButton("Discard", func() {
cf.GUI_Discard()
})
nextButton := widget.NewButton("Next", func() {
cf.GUI_Next()
})
sRepeatButton := widget.NewButton(">>Repeat", func() {
cf.GUI_sRepeat()
})
clearButton := widget.NewButton("ClearHistory", func() {
cf.GUI_Clear()
})
_history_dir_Button := widget.NewButton("ClearHistoryDirFile", func() {
cf.GUI_history_dir_Button()
})
nextButton.Disable()
sendButton.Disable()
discardButton.Disable()
cf.Sendbt = sendButton
cf.Nextbt = nextButton
cf.Discardbt = discardButton
OKconfig := widget.NewButton("Save", func() {
cf.GUI_Save_config()
})
OpenProxyconfig := widget.NewButton("OpenProxy", func() {
cf.GUI_OpenProxyconfig()
})
CloseProxyconfig := widget.NewButton("CloseProxy", func() {
cf.GUI_CloseProxyconfig()
})
CloseProxyconfig.Disable()
OKconfig.Disable()
cf.OpenProxybt = OpenProxyconfig
cf.CloseProxybt = CloseProxyconfig
cf.Savebt = OKconfig
// 创建 Grid 布局
grid_capture := container.NewGridWithColumns(3,
container.NewStack(requestEntry),
container.NewStack(responseEntry),
container.NewVBox(check1, sendButton, discardButton, nextButton, sRepeatButton),
)
grid_config := container.NewGridWithColumns(3,
container.NewVBox(
container.NewVBox(targetAddrLabel, targetAddrEntry),
container.NewVBox(serverAddrLabel, serverAddrEntry),
container.NewVBox(targetPMLabel, targetPMEntry)),
container.NewVBox(check_history, OKconfig, OpenProxyconfig, CloseProxyconfig, clearButton, _history_dir_Button),
container.NewStack(ErrEntry),
)
table := widget.NewTable(
// 返回表格的行数和列数
func() (int, int) {
return len(types.History_Data), len(types.History_Data[0]) // 第三列为按钮
},
// 返回每个单元格的 CanvasObject
func() fyne.CanvasObject {
entry := widget.NewEntry()
ccButton := widget.NewButton("Select", nil)
repeatButton := widget.NewButton(">>Repeat", nil)
buttonContainer := container.NewGridWithColumns(2, ccButton, repeatButton)
en1 := container.NewVBox(entry, buttonContainer)
return en1
},
// 更新每个单元格的内容
func(id widget.TableCellID, obj fyne.CanvasObject) {
en1 := obj.(*fyne.Container)
entry := en1.Objects[0].(*widget.Entry)
buttonContainer := en1.Objects[1].(*fyne.Container)
ccButton := buttonContainer.Objects[0].(*widget.Button)
repeatButton := buttonContainer.Objects[1].(*widget.Button)
if id.Col <= 1 { // 前两列显示数据
entry.Show() // 显示 Entry
buttonContainer.Hide() // 隐藏按钮容器
entry.SetText(types.History_Data[id.Row][id.Col])
} else if id.Col > 1 { // 第三列显示按钮
entry.Hide() // 隐藏 Entry
buttonContainer.Show() // 显示按钮容器
ccButton.OnTapped = func(row int) func() {
return func() {
cf.GUI_Table_bt(row)
}
}(id.Row)
repeatButton.OnTapped = func(row int) func() {
return func() {
cf.GUI_Table_Repeat_bt(row)
}
}(id.Row)
}
})
cf.Table_history = table
// 创建带有垂直滚动条的容器
tableContainer := container.NewVScroll(table)
// 创建histroy布局
grid_history := container.NewGridWithRows(2,
container.NewStack(tableContainer),
container.NewGridWithColumns(2,
container.NewStack(hisreq1Entry),
container.NewStack(hisres2Entry),
),
)
grid_repeat := container.NewGridWithColumns(3,
container.NewStack(repeat_req1Entry),
container.NewStack(repeat_res2Entry),
container.NewVBox(send2Button),
)
tab2 := container.NewTabItem("capture", grid_capture)
tab1 := container.NewTabItem("config", grid_config)
tab3 := container.NewTabItem("history", grid_history)
tab4 := container.NewTabItem("repeat", grid_repeat)
tabContainer := container.NewAppTabs(tab1, tab2, tab3, tab4)
tabContainer.SetTabLocation(container.TabLocationTop)
// 布局
// 拦截关闭事件
mainWin.SetCloseIntercept(func() {
mainWin.Hide() // 隐藏窗口
})
mainWin.SetContent(tabContainer)
// 添加窗口大小变化监听器
mainWin.Resize(fyne.NewSize(800, 600))
mainWin.ShowAndRun()
}
2.1 抓包
python
func Run(conf *Config) {
for {
select {
case <-conf.RunTH:
types.ErrMainData += fmt.Sprintln("[+] Close-Proxy-OK")
// fmt.Println(1)
return
default:
// fmt.Println(0)
}
if conf.ServerAddr == "" || conf.TargetAddr == "" || conf.TargetPM == "" || conf.Nextbt == nil {
time.Sleep(2 * time.Second)
types.ErrMainData += fmt.Sprintln("[-] Parameters not saved or initialization not completed")
} else {
break
}
}
Init(conf)
// 创建 HTTP 处理函数的中间件
middleware := func(http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
types.CHT <- true
hisD1 := []string{}
hisD2 := []string{}
defer func() { <-types.CHT }()
// 获取原始请求的字符串表示
dump, err := httputil.DumpRequest(r, true)
utils.HandleErr(utils.GCFN(), err)
dump, err = conf.HostFix(dump)
utils.HandleErr(utils.GCFN(), err)
if ok, err := types.HistoryOK.Get(); ok {
utils.HandleErr(utils.GCFN(), err)
hisD1 = append(hisD1, string(dump))
hisD2 = append(hisD2, fmt.Sprintf("%v %v", r.Method, r.URL.String()))
}
// // 打印原始请求字符串
// fmt.Printf("\033[32m%s\033[0m\n", dump)
if ok, err := types.AutoSData.Get(); !ok {
utils.HandleErr(utils.GCFN(), err)
err = types.RequestData.Set(string(dump))
utils.HandleErr(utils.GCFN(), err)
conf.Cf_Enable()
CC:
for {
select {
case <-types.SendCH:
rdata, err := types.RequestData.Get()
utils.HandleErr(utils.GCFN(), err)
// 将 rdata 解析成 *http.Request 对象
r, err = http.ReadRequest(bufio.NewReader(bytes.NewBufferString(rdata)))
utils.HandleErr(utils.GCFN(), err)
break CC
case <-types.DiscardCH:
return
default:
}
time.Sleep(100 * time.Millisecond)
}
} else {
err = types.RequestData.Set("")
utils.HandleErr(utils.GCFN(), err)
err = types.ResponseData.Set("")
utils.HandleErr(utils.GCFN(), err)
}
recorder := httptest.NewRecorder()
recorder.Header().Add("Waf", "Coraza-v3")
// 使用反向代理转发请求
conf.Proxy.ServeHTTP(recorder, r)
// 创建 http.Response 对象
response := recorder.Result()
// 使用 httputil.DumpResponse 来获取完整响应包
res1, err := httputil.DumpResponse(response, true)
utils.HandleErr(utils.GCFN(), err)
if ok, _ := types.HistoryOK.Get(); ok {
hisD1 = append(hisD1, string(res1))
ff := utils.Wrtie_history_response(hisD1, conf.TargetAddr)
hisD2 = append(hisD2, ff)
types.History_Data = append(types.History_Data, hisD2)
}
if ok, err := types.AutoSData.Get(); !ok {
utils.HandleErr(utils.GCFN(), err)
err = types.ResponseData.Set(string(res1))
utils.HandleErr(utils.GCFN(), err)
}
conf.TxProcessResponse(recorder, w, r)
if ok, err := types.AutoSData.Get(); !ok {
utils.HandleErr(utils.GCFN(), err)
BB:
for {
select {
case <-types.NextCH:
err = types.RequestData.Set("")
utils.HandleErr(utils.GCFN(), err)
err = types.ResponseData.Set("")
utils.HandleErr(utils.GCFN(), err)
break BB
default:
}
time.Sleep(100 * time.Millisecond)
}
} else {
err = types.RequestData.Set("")
utils.HandleErr(utils.GCFN(), err)
err = types.ResponseData.Set("")
utils.HandleErr(utils.GCFN(), err)
conf.Cf_Disable()
}
})
}
server := &http.Server{
Addr: conf.ServerAddr,
Handler: middleware(http.DefaultServeMux), // 使用中间件包裹默认的 ServeMux
}
go func() {
types.ErrMainData += fmt.Sprintf("[+] Starting server at %v\n", conf.ServerAddr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
utils.HandleErr(utils.GCFN(), err)
}
}()
AA:
for {
select {
case <-conf.RunTH:
break AA
default:
time.Sleep(500 * time.Millisecond)
}
}
// 通过 context 实现优雅关闭
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
types.ErrMainData += fmt.Sprintf("[-] Server shutdown failed:%v\n", err)
if err := server.Close(); err != nil {
types.ErrMainData += fmt.Sprintf("[!] Server Close failed:%v\n", err)
} else {
types.ErrMainData += fmt.Sprintln("[+] Close-Proxy-OK")
}
} else {
types.ErrMainData += fmt.Sprintln("[+] Close-Proxy-OK")
}
}