219 lines
15 KiB
Plaintext
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
|