//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 }