//go:build windows package winpwn import ( "syscall" "testing" "time" "unsafe" "golang.org/x/sys/windows" ) var ( user32ForTest = windows.NewLazySystemDLL("user32.dll") procEnumWindows = user32ForTest.NewProc("EnumWindows") procGetWindowTextW = user32ForTest.NewProc("GetWindowTextW") procPostMessageW = user32ForTest.NewProc("PostMessageW") ) const wmClose = 0x0010 // TestShellcodeMessageBoxA runs the real shellcode (via ExecuteShellcode) // and checks for a real window with the expected title -- not just "didn't // crash". This proves resolve_export actually found LoadLibraryA in // kernel32, loaded user32.dll (not guaranteed loaded in a test binary), // resolved MessageBoxA inside it, and called it with the right calling // convention. The window is dismissed programmatically (WM_CLOSE) so the // test doesn't hang waiting for a human. func TestShellcodeMessageBoxA(t *testing.T) { const wantTitle = "winpwn test" code, err := ShellcodeMessageBoxA("hello from winpwn", wantTitle) if err != nil { t.Fatal(err) } myPID := windows.GetCurrentProcessId() done := make(chan error, 1) go func() { done <- ExecuteShellcode(code) }() var found windows.HWND deadline := time.Now().Add(10 * time.Second) for time.Now().Before(deadline) && found == 0 { cb := syscall.NewCallback(func(hwnd windows.HWND, _ uintptr) uintptr { var pid uint32 windows.GetWindowThreadProcessId(hwnd, &pid) if pid != myPID { return 1 // continue enumeration } buf := make([]uint16, 256) procGetWindowTextW.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) if windows.UTF16ToString(buf) == wantTitle { found = hwnd return 0 // stop enumeration } return 1 }) procEnumWindows.Call(cb, 0) if found == 0 { time.Sleep(100 * time.Millisecond) } } if found == 0 { t.Fatalf("never saw a real window titled %q -- MessageBoxA shellcode did not pop a window", wantTitle) } procPostMessageW.Call(uintptr(found), wmClose, 0, 0) select { case err := <-done: if err != nil { t.Errorf("ExecuteShellcode returned an error after dismissal: %v", err) } case <-time.After(5 * time.Second): t.Fatal("shellcode goroutine never returned after WM_CLOSE") } }