-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdrivermain.cpp
More file actions
363 lines (282 loc) · 10.6 KB
/
Copy pathdrivermain.cpp
File metadata and controls
363 lines (282 loc) · 10.6 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
#include <ntifs.h>
extern "C" {
NTKERNELAPI NTSTATUS IoCreateDriver(PUNICODE_STRING DriverName,
PDRIVER_INITIALIZE InitializationFunction);
NTKERNELAPI NTSTATUS MmCopyVirtualMemory(PEPROCESS SourceProcess, PVOID SourceAddress,
PEPROCESS TargetProcess, PVOID TargetAddress,
SIZE_T BufferSize, KPROCESSOR_MODE PreviousMode,
PSIZE_T ReturnSize);
}
void debug_print(PCSTR text) {
}
namespace driver {
namespace codes {
// Used to setup the driver.
constexpr ULONG attach =
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x696, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
// Read process memory.
constexpr ULONG read =
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x697, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
// Write process memory.
constexpr ULONG write =
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x698, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
// Read multiple process memory regions in one IOCTL.
constexpr ULONG batch_read =
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x699, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
// Get base address of a loaded module in the attached process.
constexpr ULONG get_module_base =
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x69A, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
} // namespace codes
constexpr ULONG kMaxBatchReadEntries = 64;
constexpr ULONG kMaxModuleNameLength = 260;
// Shared between user mode & kernel mode.
struct Request {
HANDLE process_id;
PVOID target;
PVOID buffer;
SIZE_T size;
SIZE_T return_size;
};
struct BatchReadEntry {
PVOID target;
PVOID buffer;
SIZE_T size;
SIZE_T return_size;
};
struct BatchReadRequest {
ULONG count;
BatchReadEntry entries[kMaxBatchReadEntries];
};
struct ModuleRequest {
WCHAR module_name[kMaxModuleNameLength];
PVOID base_address;
};
// EPROCESS.Peb offset for Win10 2004+ / Win11 x64.
constexpr ULONG kEprocessPebOffset = 0x550;
PVOID get_process_peb(PEPROCESS process) {
return *reinterpret_cast<PVOID*>(reinterpret_cast<PUCHAR>(process) + kEprocessPebOffset);
}
NTSTATUS copy_from_process(PEPROCESS process, PVOID source, PVOID destination, SIZE_T size,
PSIZE_T bytes_copied) {
return MmCopyVirtualMemory(process, source, PsGetCurrentProcess(), destination, size,
KernelMode, bytes_copied);
}
NTSTATUS get_module_base_address(PEPROCESS process, PCWSTR module_name, PVOID* base_address) {
if (process == nullptr || module_name == nullptr || base_address == nullptr) {
return STATUS_INVALID_PARAMETER;
}
*base_address = nullptr;
const PVOID peb = get_process_peb(process);
if (peb == nullptr) {
return STATUS_UNSUCCESSFUL;
}
PVOID ldr = nullptr;
SIZE_T bytes = 0;
NTSTATUS status = copy_from_process(process,
reinterpret_cast<PVOID>(reinterpret_cast<PUCHAR>(peb) + 0x18),
&ldr, sizeof(ldr), &bytes);
if (status != STATUS_SUCCESS || ldr == nullptr) {
return status != STATUS_SUCCESS ? status : STATUS_UNSUCCESSFUL;
}
LIST_ENTRY list_head{};
status = copy_from_process(process,
reinterpret_cast<PVOID>(reinterpret_cast<PUCHAR>(ldr) + 0x10),
&list_head, sizeof(list_head), &bytes);
if (status != STATUS_SUCCESS) {
return status;
}
UNICODE_STRING target_name{};
RtlInitUnicodeString(&target_name, module_name);
PVOID current_entry = list_head.Flink;
const PVOID list_head_address =
reinterpret_cast<PVOID>(reinterpret_cast<PUCHAR>(ldr) + 0x10);
for (ULONG i = 0; i < 512 && current_entry != list_head_address; ++i) {
PVOID dll_base = nullptr;
status = copy_from_process(process,
reinterpret_cast<PVOID>(reinterpret_cast<PUCHAR>(current_entry) + 0x30),
&dll_base, sizeof(dll_base), &bytes);
if (status != STATUS_SUCCESS) {
return status;
}
UNICODE_STRING base_dll_name{};
status = copy_from_process(process,
reinterpret_cast<PVOID>(reinterpret_cast<PUCHAR>(current_entry) + 0x58),
&base_dll_name, sizeof(base_dll_name), &bytes);
if (status != STATUS_SUCCESS) {
return status;
}
if (base_dll_name.Buffer != nullptr && base_dll_name.Length > 0 &&
base_dll_name.Length < static_cast<USHORT>(kMaxModuleNameLength * sizeof(WCHAR))) {
WCHAR name_buffer[kMaxModuleNameLength]{};
const SIZE_T name_bytes = base_dll_name.Length;
status = copy_from_process(process, base_dll_name.Buffer, name_buffer, name_bytes, &bytes);
if (status == STATUS_SUCCESS) {
UNICODE_STRING entry_name{};
entry_name.Length = base_dll_name.Length;
entry_name.MaximumLength = static_cast<USHORT>(name_bytes + sizeof(WCHAR));
entry_name.Buffer = name_buffer;
if (RtlCompareUnicodeString(&entry_name, &target_name, TRUE) == 0) {
*base_address = dll_base;
return STATUS_SUCCESS;
}
}
}
LIST_ENTRY entry_links{};
status = copy_from_process(process, current_entry, &entry_links, sizeof(entry_links), &bytes);
if (status != STATUS_SUCCESS) {
return status;
}
current_entry = entry_links.Flink;
}
return STATUS_NOT_FOUND;
}
NTSTATUS create(PDEVICE_OBJECT device_object, PIRP irp) {
UNREFERENCED_PARAMETER(device_object);
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS close(PDEVICE_OBJECT device_object, PIRP irp) {
UNREFERENCED_PARAMETER(device_object);
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// Note: Todo
NTSTATUS device_control(PDEVICE_OBJECT device_object, PIRP irp) {
UNREFERENCED_PARAMETER(device_object);
debug_print("[+] Device control called.\n");
NTSTATUS status = STATUS_UNSUCCESSFUL;
// We need this to determine which code was passed through.
PIO_STACK_LOCATION stack_irp = IoGetCurrentIrpStackLocation(irp);
// Access the request object sent from user mode.
auto request = reinterpret_cast<Request*>(irp->AssociatedIrp.SystemBuffer);
if (stack_irp == nullptr || request == nullptr) {
IoCompleteRequest(irp, IO_NO_INCREMENT);
return status;
}
// The target process we want access to.
static PEPROCESS target_process = nullptr;
const ULONG control_code = stack_irp->Parameters.DeviceIoControl.IoControlCode;
switch (control_code) {
case codes::attach:
status = PsLookupProcessByProcessId(request->process_id, &target_process);
break;
case codes::read:
if (target_process != nullptr)
status = MmCopyVirtualMemory(target_process, request->target,
PsGetCurrentProcess(), request->buffer,
request->size, KernelMode, &request->return_size);
break;
case codes::write:
if (target_process != nullptr)
status = MmCopyVirtualMemory(PsGetCurrentProcess(), request->buffer,
target_process, request->target,
request->size, KernelMode, &request->return_size);
break;
case codes::batch_read: {
auto batch_request = reinterpret_cast<BatchReadRequest*>(irp->AssociatedIrp.SystemBuffer);
const ULONG input_length = stack_irp->Parameters.DeviceIoControl.InputBufferLength;
if (batch_request == nullptr || target_process == nullptr) {
status = STATUS_INVALID_PARAMETER;
break;
}
if (batch_request->count == 0 || batch_request->count > kMaxBatchReadEntries) {
status = STATUS_INVALID_PARAMETER;
break;
}
const SIZE_T required_length =
sizeof(ULONG) + static_cast<SIZE_T>(batch_request->count) * sizeof(BatchReadEntry);
if (input_length < required_length) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
status = STATUS_SUCCESS;
for (ULONG i = 0; i < batch_request->count; ++i) {
auto& entry = batch_request->entries[i];
entry.return_size = 0;
const NTSTATUS entry_status = MmCopyVirtualMemory(
target_process, entry.target,
PsGetCurrentProcess(), entry.buffer,
entry.size, KernelMode, &entry.return_size);
if (entry_status != STATUS_SUCCESS) {
status = entry_status;
}
}
break;
}
case codes::get_module_base: {
auto module_request = reinterpret_cast<ModuleRequest*>(irp->AssociatedIrp.SystemBuffer);
if (module_request == nullptr || target_process == nullptr) {
status = STATUS_INVALID_PARAMETER;
break;
}
module_request->base_address = nullptr;
status = get_module_base_address(target_process, module_request->module_name,
&module_request->base_address);
break;
}
default:
break;
}
irp->IoStatus.Status = status;
if (control_code == codes::batch_read) {
const auto batch_request = reinterpret_cast<BatchReadRequest*>(irp->AssociatedIrp.SystemBuffer);
if (batch_request != nullptr && batch_request->count > 0 &&
batch_request->count <= kMaxBatchReadEntries) {
irp->IoStatus.Information =
sizeof(ULONG) + static_cast<ULONG>(batch_request->count) * sizeof(BatchReadEntry);
} else {
irp->IoStatus.Information = 0;
}
} else if (control_code == codes::get_module_base) {
irp->IoStatus.Information = sizeof(ModuleRequest);
} else {
irp->IoStatus.Information = sizeof(Request);
}
IoCompleteRequest(irp, IO_NO_INCREMENT);
return status;
}
} // namespace driver
// "Real" entry point.
NTSTATUS driver_main(PDRIVER_OBJECT driver_object, PUNICODE_STRING registry_path) {
UNREFERENCED_PARAMETER(registry_path);
UNICODE_STRING device_name = {};
RtlInitUnicodeString(&device_name, L"\\Device\\sorakmv1");
// Create driver device obj.
PDEVICE_OBJECT device_object = nullptr;
NTSTATUS status = IoCreateDevice(driver_object, 0, &device_name, FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN, FALSE, &device_object);
if (status != STATUS_SUCCESS) {
debug_print("[-] Failed to create driver device.\n");
return status;
}
debug_print("[+] Driver device succesfully created.\n");
UNICODE_STRING symbolic_link = {};
RtlInitUnicodeString(&symbolic_link, L"\\??\\sorakmv1");
status = IoCreateSymbolicLink(&symbolic_link, &device_name);
if (status != STATUS_SUCCESS) {
debug_print("[-] Failed to establish symboly link.\n");
return status;
}
debug_print("[+] Driver symbolic link succesfully established.\n");
// Allow us to send small amounts of data between um/km.
SetFlag(device_object->Flags, DO_BUFFERED_IO);
// set the driver handlers to our functions with our logic.
driver_object->MajorFunction[IRP_MJ_CREATE] = driver::create;
driver_object->MajorFunction[IRP_MJ_CLOSE] = driver::close;
driver_object->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver::device_control;
// we have initialized our device.
ClearFlag(device_object->Flags, DO_DEVICE_INITIALIZING);
debug_print("[+] Driver initialized succesfully.\n");
return status;
}
// KdMapper will call this "entry point" but params will be null.
extern "C" NTSTATUS DriverEntry() {
debug_print("[+] HelloWorld from the kernel!\n");
UNICODE_STRING driver_name = {};
RtlInitUnicodeString(&driver_name, L"\\Driver\\sorakmv1");
return IoCreateDriver(&driver_name, &driver_main);
}