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 align 8 45 text_buf: 46 00000188 00 times 256 db 0 47 48 align 8 49 caption_buf: 50 00000288 00 times 64 db 0