-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathmem.c
More file actions
523 lines (452 loc) · 14.7 KB
/
Copy pathmem.c
File metadata and controls
523 lines (452 loc) · 14.7 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
/*
* mem.c — Stealthy cross-process memory read/write.
*
* Three methods, rotated to avoid detection:
* Method 0: MmCopyVirtualMemory (classic, fast)
* Method 1: MDL mapping (IoAllocateMdl + MmProbeAndLockPages)
* Method 2: KeStackAttachProcess + RtlCopyMemory (direct)
*
* Anti-detection:
* - Inline hook check on MmCopyVirtualMemory before use
* - Method rotation per call to avoid consistent patterns
* - Scatter mode: large reads split into random-sized chunks
* - Timing jitter between chunks
*/
#include <ntifs.h>
#include <fltKernel.h>
#include "edr_shared.h"
#include "core.h"
/* Undocumented but stable APIs */
NTSTATUS NTAPI MmCopyVirtualMemory(
_In_ PEPROCESS SourceProcess,
_In_ PVOID SourceAddress,
_In_ PEPROCESS TargetProcess,
_Out_ PVOID TargetAddress,
_In_ SIZE_T BufferSize,
_In_ KPROCESSOR_MODE PreviousMode,
_Out_ PSIZE_T ReturnSize
);
NTKERNELAPI PVOID NTAPI PsGetProcessSectionBaseAddress(
_In_ PEPROCESS Process
);
NTKERNELAPI PPEB NTAPI PsGetProcessPeb(
_In_ PEPROCESS Process
);
#define MEM_METHOD_MMCOPY 0
#define MEM_METHOD_MDL 1
#define MEM_METHOD_ATTACH 2
#define MEM_METHOD_COUNT 3
static volatile LONG g_MethodCounter = 0;
#define MEM_SCATTER_THRESHOLD 512
#define MEM_SCATTER_MIN_CHUNK 64
#define MEM_SCATTER_MAX_CHUNK 256
static NTSTATUS MemReadInternal(
_In_ PEPROCESS Target, _In_ PVOID Address,
_Out_ PVOID Buffer, _In_ SIZE_T Length, _Out_ PSIZE_T Copied
);
static NTSTATUS MemWriteInternal(
_In_ PEPROCESS Target, _In_ PVOID Address,
_In_ PVOID Buffer, _In_ SIZE_T Length, _Out_ PSIZE_T Copied
);
static NTSTATUS MemReadViaMmCopy(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_Out_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
);
static NTSTATUS MemReadViaMdl(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_Out_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
);
static NTSTATUS MemReadViaAttach(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_Out_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
);
static NTSTATUS MemWriteViaMmCopy(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_In_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
);
static NTSTATUS MemWriteViaMdl(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_In_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
);
static NTSTATUS MemWriteViaAttach(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_In_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
);
static BOOLEAN MemIsHooked(_In_ PVOID FuncAddr);
static ULONG MemPickMethod(VOID);
static ULONG MemPseudoRandom(VOID);
static NTSTATUS MemGetBase(_In_ ULONG Pid, _Out_ PULONGLONG BaseAddress);
static NTSTATUS MemGetPeb(_In_ ULONG Pid, _Out_ PULONGLONG PebAddress);
NTSTATUS MemModuleInitialize(VOID)
{
g_MethodCounter = 0;
return STATUS_SUCCESS;
}
VOID MemModuleUninitialize(VOID)
{
}
NTSTATUS MemHandleCommand(_Inout_ PRING_MESSAGE Msg)
{
switch (Msg->Command) {
case CMD_MEM_READ: {
if (Msg->DataLength == 0 || Msg->DataLength > RING_DATA_SIZE)
return STATUS_INVALID_PARAMETER;
if ((PVOID)Msg->Address >= MmHighestUserAddress)
return STATUS_ACCESS_VIOLATION;
if (Msg->Address + Msg->DataLength < Msg->Address)
return STATUS_INTEGER_OVERFLOW;
PEPROCESS target = NULL;
NTSTATUS st = PsLookupProcessByProcessId(
(HANDLE)(ULONG_PTR)Msg->ProcessId, &target);
if (!NT_SUCCESS(st)) return st;
SIZE_T copied = 0;
st = MemReadInternal(target, (PVOID)Msg->Address,
Msg->Data, (SIZE_T)Msg->DataLength, &copied);
ObDereferenceObject(target);
Msg->DataLength = (ULONG)copied;
return st;
}
case CMD_MEM_WRITE: {
if (Msg->DataLength == 0 || Msg->DataLength > RING_DATA_SIZE)
return STATUS_INVALID_PARAMETER;
if ((PVOID)Msg->Address >= MmHighestUserAddress)
return STATUS_ACCESS_VIOLATION;
if (Msg->Address + Msg->DataLength < Msg->Address)
return STATUS_INTEGER_OVERFLOW;
PEPROCESS target = NULL;
NTSTATUS st = PsLookupProcessByProcessId(
(HANDLE)(ULONG_PTR)Msg->ProcessId, &target);
if (!NT_SUCCESS(st)) return st;
SIZE_T copied = 0;
st = MemWriteInternal(target, (PVOID)Msg->Address,
Msg->Data, (SIZE_T)Msg->DataLength, &copied);
ObDereferenceObject(target);
Msg->DataLength = (ULONG)copied;
return st;
}
case CMD_MEM_GET_BASE: {
ULONGLONG base = 0;
NTSTATUS st = MemGetBase(Msg->ProcessId, &base);
Msg->Address = base;
return st;
}
case CMD_MEM_GET_PEB: {
ULONGLONG peb = 0;
NTSTATUS st = MemGetPeb(Msg->ProcessId, &peb);
Msg->Address = peb;
return st;
}
default:
return STATUS_INVALID_PARAMETER;
}
}
static BOOLEAN MemIsHooked(_In_ PVOID FuncAddr)
{
if (!FuncAddr) return TRUE;
__try {
PUCHAR p = (PUCHAR)FuncAddr;
if (p[0] == 0xE9) return TRUE; /* JMP rel32 */
if (p[0] == 0xFF && p[1] == 0x25) return TRUE; /* JMP [rip+x] */
if (p[0] == 0xCC) return TRUE; /* INT3 */
if (p[0] == 0x48 && p[1] == 0xB8) { /* MOV RAX, imm64 */
if (p[10] == 0xFF && p[11] == 0xE0) return TRUE; /* JMP RAX */
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return TRUE;
}
return FALSE;
}
static ULONG MemPseudoRandom(VOID)
{
LARGE_INTEGER perf = KeQueryPerformanceCounter(NULL);
ULONG val = (ULONG)(perf.LowPart ^ perf.HighPart);
val ^= (ULONG)(ULONG_PTR)PsGetCurrentThread();
val = val * 1103515245 + 12345;
return (val >> 16);
}
static ULONG MemPickMethod(VOID)
{
ULONG base = (ULONG)InterlockedIncrement(&g_MethodCounter);
ULONG method = base % MEM_METHOD_COUNT;
if (method == MEM_METHOD_MMCOPY &&
MemIsHooked((PVOID)MmCopyVirtualMemory)) {
method = MEM_METHOD_MDL + (MemPseudoRandom() % 2);
}
return method;
}
static NTSTATUS MemReadViaMmCopy(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_Out_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
)
{
return MmCopyVirtualMemory(
Target, Addr, PsGetCurrentProcess(), Buf,
Len, KernelMode, Copied
);
}
static NTSTATUS MemReadViaMdl(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_Out_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
KAPC_STATE apc;
PMDL mdl = NULL;
PVOID mapped = NULL;
BOOLEAN locked = FALSE;
*Copied = 0;
KeStackAttachProcess(Target, &apc);
__try {
mdl = IoAllocateMdl(Addr, (ULONG)Len, FALSE, FALSE, NULL);
if (!mdl) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
locked = TRUE;
mapped = MmMapLockedPagesSpecifyCache(
mdl, KernelMode, MmNonCached,
NULL, FALSE, NormalPagePriority
);
if (!mapped) {
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
RtlCopyMemory(Buf, mapped, Len);
*Copied = Len;
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
if (mapped && mdl) MmUnmapLockedPages(mapped, mdl);
if (mdl) {
if (locked) MmUnlockPages(mdl);
IoFreeMdl(mdl);
}
KeUnstackDetachProcess(&apc);
return status;
}
static NTSTATUS MemReadViaAttach(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_Out_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
)
{
KAPC_STATE apc;
NTSTATUS status;
*Copied = 0;
KeStackAttachProcess(Target, &apc);
__try {
ProbeForRead(Addr, Len, 1);
RtlCopyMemory(Buf, Addr, Len);
*Copied = Len;
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
KeUnstackDetachProcess(&apc);
return status;
}
static NTSTATUS MemWriteViaMmCopy(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_In_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
)
{
return MmCopyVirtualMemory(
PsGetCurrentProcess(), Buf, Target, Addr,
Len, KernelMode, Copied
);
}
static NTSTATUS MemWriteViaMdl(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_In_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
KAPC_STATE apc;
PMDL mdl = NULL;
PVOID mapped = NULL;
BOOLEAN locked = FALSE;
*Copied = 0;
KeStackAttachProcess(Target, &apc);
__try {
mdl = IoAllocateMdl(Addr, (ULONG)Len, FALSE, FALSE, NULL);
if (!mdl) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; }
MmProbeAndLockPages(mdl, UserMode, IoWriteAccess);
locked = TRUE;
mapped = MmMapLockedPagesSpecifyCache(
mdl, KernelMode, MmNonCached,
NULL, FALSE, NormalPagePriority);
if (!mapped) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; }
RtlCopyMemory(mapped, Buf, Len);
*Copied = Len;
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
if (mapped && mdl) MmUnmapLockedPages(mapped, mdl);
if (mdl) {
if (locked) MmUnlockPages(mdl);
IoFreeMdl(mdl);
}
KeUnstackDetachProcess(&apc);
return status;
}
static NTSTATUS MemWriteViaAttach(
_In_ PEPROCESS Target, _In_ PVOID Addr,
_In_ PVOID Buf, _In_ SIZE_T Len, _Out_ PSIZE_T Copied
)
{
KAPC_STATE apc;
NTSTATUS status;
*Copied = 0;
KeStackAttachProcess(Target, &apc);
__try {
ProbeForWrite(Addr, Len, 1);
RtlCopyMemory(Addr, Buf, Len);
*Copied = Len;
status = STATUS_SUCCESS;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
KeUnstackDetachProcess(&apc);
return status;
}
static NTSTATUS MemReadInternal(
_In_ PEPROCESS Target, _In_ PVOID Address,
_Out_ PVOID Buffer, _In_ SIZE_T Length, _Out_ PSIZE_T Copied
)
{
*Copied = 0;
if (Length <= MEM_SCATTER_THRESHOLD) {
ULONG method = MemPickMethod();
switch (method) {
case MEM_METHOD_MMCOPY:
return MemReadViaMmCopy(Target, Address, Buffer, Length, Copied);
case MEM_METHOD_MDL:
return MemReadViaMdl(Target, Address, Buffer, Length, Copied);
default:
return MemReadViaAttach(Target, Address, Buffer, Length, Copied);
}
}
SIZE_T offset = 0;
while (offset < Length) {
SIZE_T remaining = Length - offset;
ULONG chunkRand = MemPseudoRandom();
SIZE_T chunk = MEM_SCATTER_MIN_CHUNK +
(chunkRand % (MEM_SCATTER_MAX_CHUNK - MEM_SCATTER_MIN_CHUNK + 1));
if (chunk > remaining) chunk = remaining;
ULONG method = MemPickMethod();
SIZE_T chunkCopied = 0;
NTSTATUS st;
switch (method) {
case MEM_METHOD_MMCOPY:
st = MemReadViaMmCopy(Target,
(PVOID)((ULONG_PTR)Address + offset),
(PVOID)((ULONG_PTR)Buffer + offset), chunk, &chunkCopied);
break;
case MEM_METHOD_MDL:
st = MemReadViaMdl(Target,
(PVOID)((ULONG_PTR)Address + offset),
(PVOID)((ULONG_PTR)Buffer + offset), chunk, &chunkCopied);
break;
default:
st = MemReadViaAttach(Target,
(PVOID)((ULONG_PTR)Address + offset),
(PVOID)((ULONG_PTR)Buffer + offset), chunk, &chunkCopied);
break;
}
*Copied += chunkCopied;
if (!NT_SUCCESS(st)) return st;
offset += chunk;
if (offset < Length) {
LARGE_INTEGER delay;
delay.QuadPart = -(LONGLONG)(10 * (MemPseudoRandom() % 50 + 1));
KeDelayExecutionThread(KernelMode, FALSE, &delay);
}
}
return STATUS_SUCCESS;
}
static NTSTATUS MemGetBase(_In_ ULONG Pid, _Out_ PULONGLONG BaseAddress)
{
PEPROCESS target = NULL;
NTSTATUS st;
*BaseAddress = 0;
st = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)Pid, &target);
if (!NT_SUCCESS(st)) return st;
PVOID base = PsGetProcessSectionBaseAddress(target);
*BaseAddress = (ULONGLONG)base;
ObDereferenceObject(target);
return STATUS_SUCCESS;
}
static NTSTATUS MemGetPeb(_In_ ULONG Pid, _Out_ PULONGLONG PebAddress)
{
PEPROCESS target = NULL;
NTSTATUS st;
*PebAddress = 0;
st = PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)Pid, &target);
if (!NT_SUCCESS(st)) return st;
PPEB peb = PsGetProcessPeb(target);
*PebAddress = (ULONGLONG)peb;
ObDereferenceObject(target);
return STATUS_SUCCESS;
}
static NTSTATUS MemWriteInternal(
_In_ PEPROCESS Target, _In_ PVOID Address,
_In_ PVOID Buffer, _In_ SIZE_T Length, _Out_ PSIZE_T Copied
)
{
*Copied = 0;
if (Length <= MEM_SCATTER_THRESHOLD) {
ULONG method = MemPickMethod();
switch (method) {
case MEM_METHOD_MMCOPY:
return MemWriteViaMmCopy(Target, Address, Buffer, Length, Copied);
case MEM_METHOD_MDL:
return MemWriteViaMdl(Target, Address, Buffer, Length, Copied);
default:
return MemWriteViaAttach(Target, Address, Buffer, Length, Copied);
}
}
SIZE_T offset = 0;
while (offset < Length) {
SIZE_T remaining = Length - offset;
ULONG chunkRand = MemPseudoRandom();
SIZE_T chunk = MEM_SCATTER_MIN_CHUNK +
(chunkRand % (MEM_SCATTER_MAX_CHUNK - MEM_SCATTER_MIN_CHUNK + 1));
if (chunk > remaining) chunk = remaining;
ULONG method = MemPickMethod();
SIZE_T chunkCopied = 0;
NTSTATUS st;
switch (method) {
case MEM_METHOD_MMCOPY:
st = MemWriteViaMmCopy(Target,
(PVOID)((ULONG_PTR)Address + offset),
(PVOID)((ULONG_PTR)Buffer + offset), chunk, &chunkCopied);
break;
case MEM_METHOD_MDL:
st = MemWriteViaMdl(Target,
(PVOID)((ULONG_PTR)Address + offset),
(PVOID)((ULONG_PTR)Buffer + offset), chunk, &chunkCopied);
break;
default:
st = MemWriteViaAttach(Target,
(PVOID)((ULONG_PTR)Address + offset),
(PVOID)((ULONG_PTR)Buffer + offset), chunk, &chunkCopied);
break;
}
*Copied += chunkCopied;
if (!NT_SUCCESS(st)) return st;
offset += chunk;
if (offset < Length) {
LARGE_INTEGER delay;
delay.QuadPart = -(LONGLONG)(10 * (MemPseudoRandom() % 50 + 1));
KeDelayExecutionThread(KernelMode, FALSE, &delay);
}
}
return STATUS_SUCCESS;
}