51 lines
1.6 KiB
Go
51 lines
1.6 KiB
Go
//go:build windows
|
|
|
|
package winpwn
|
|
|
|
import (
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
const (
|
|
memCommit = 0x1000
|
|
memReserve = 0x2000
|
|
pageExecuteReadwrite = 0x40
|
|
)
|
|
|
|
var (
|
|
modNtdll = windows.NewLazySystemDLL("ntdll.dll")
|
|
procMoveMemory = modNtdll.NewProc("RtlMoveMemory")
|
|
)
|
|
|
|
// ExecuteShellcode VirtualAlloc's an RWX page, copies code into it, and
|
|
// calls into it directly on the current thread — for locally validating a
|
|
// shellcode template actually does what it claims before landing it via a
|
|
// real exploit primitive (ROP chain, overwritten function pointer, ...).
|
|
// Not something pwntools has a direct analogue for: Python can't call
|
|
// raw machine code in-process, it always shells out to a target.
|
|
func ExecuteShellcode(code []byte) error {
|
|
addr, err := windows.VirtualAlloc(0, uintptr(len(code)), memCommit|memReserve, pageExecuteReadwrite)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Copy via RtlMoveMemory instead of building a Go slice over the raw
|
|
// VirtualAlloc address: converting a bare uintptr (not derived from an
|
|
// existing Pointer) into unsafe.Pointer is exactly what `go vet`'s
|
|
// unsafeptr check exists to catch, even though it's safe here (the page
|
|
// is OS-owned, not GC-tracked). Passing addr straight through as a
|
|
// syscall argument sidesteps that conversion entirely.
|
|
procMoveMemory.Call(addr, uintptr(unsafe.Pointer(&code[0])), uintptr(len(code)))
|
|
|
|
// syscall.Syscall's first argument is the address to call directly on
|
|
// Windows (there's no syscall-number indirection here, unlike Unix).
|
|
_, _, errno := syscall.Syscall(addr, 0, 0, 0, 0)
|
|
if errno != 0 {
|
|
return errno
|
|
}
|
|
return nil
|
|
}
|