Files
go_pwner/shellcode_exec_windows.go
T
2026-07-18 21:37:15 +03:00

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
}