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