diff options
Diffstat (limited to 'process.c')
| -rw-r--r-- | process.c | 1270 |
1 files changed, 1270 insertions, 0 deletions
diff --git a/process.c b/process.c new file mode 100644 index 0000000..c83603f --- /dev/null +++ b/process.c | |||
| @@ -0,0 +1,1270 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2004 Security Architects Corporation. All rights reserved. | ||
| 3 | * | ||
| 4 | * Module Name: | ||
| 5 | * | ||
| 6 | * process.c | ||
| 7 | * | ||
| 8 | * Abstract: | ||
| 9 | * | ||
| 10 | * This module defines various types used by process and thread hooking routines. | ||
| 11 | * | ||
| 12 | * Author: | ||
| 13 | * | ||
| 14 | * Eugene Tsyrklevich 23-Feb-2004 | ||
| 15 | * | ||
| 16 | * Revision History: | ||
| 17 | * | ||
| 18 | * None. | ||
| 19 | */ | ||
| 20 | |||
| 21 | |||
| 22 | #include <NTDDK.h> | ||
| 23 | #include "process.h" | ||
| 24 | #include "driver.h" | ||
| 25 | #include "policy.h" | ||
| 26 | #include "hookproc.h" | ||
| 27 | #include "userland.h" | ||
| 28 | #include "procname.h" | ||
| 29 | #include "accessmask.h" | ||
| 30 | #include "learn.h" | ||
| 31 | #include "misc.h" | ||
| 32 | #include "i386.h" | ||
| 33 | #include "log.h" | ||
| 34 | |||
| 35 | |||
| 36 | #ifdef ALLOC_PRAGMA | ||
| 37 | #pragma alloc_text (INIT, InitProcessEntries) | ||
| 38 | #pragma alloc_text (PAGE, ProcessPostBootup) | ||
| 39 | #endif | ||
| 40 | |||
| 41 | |||
| 42 | fpZwCreateProcess OriginalNtCreateProcess = NULL; | ||
| 43 | fpZwCreateProcessEx OriginalNtCreateProcessEx = NULL; | ||
| 44 | fpZwOpenProcess OriginalNtOpenProcess = NULL; | ||
| 45 | |||
| 46 | fpZwCreateThread OriginalNtCreateThread = NULL; | ||
| 47 | fpZwOpenThread OriginalNtOpenThread = NULL; | ||
| 48 | |||
| 49 | |||
| 50 | BOOLEAN AllowProcessesWithPoliciesOnly = FALSE; | ||
| 51 | |||
| 52 | WCHAR OzoneInstallPath[MAX_PATH]; | ||
| 53 | USHORT OzoneInstallPathSize = 0; | ||
| 54 | |||
| 55 | |||
| 56 | /* XXX this will not work on 64-bit architectures, due to assumption of size of ulong, pointers, etc */ | ||
| 57 | typedef struct _CONTROL_AREA { // must be quadword sized. | ||
| 58 | ULONG data[9]; | ||
| 59 | PFILE_OBJECT FilePointer; | ||
| 60 | } CONTROL_AREA, *PCONTROL_AREA; | ||
| 61 | |||
| 62 | typedef struct _SEGMENT { | ||
| 63 | struct _CONTROL_AREA *ControlArea; | ||
| 64 | } SEGMENT, *PSEGMENT; | ||
| 65 | |||
| 66 | typedef struct _SECTION { | ||
| 67 | ULONG_PTR data[5]; | ||
| 68 | PSEGMENT Segment; | ||
| 69 | } SECTION, *PSECTION; | ||
| 70 | |||
| 71 | |||
| 72 | |||
| 73 | /* | ||
| 74 | * rand() | ||
| 75 | * | ||
| 76 | * Description: | ||
| 77 | * Returns a random number that is calculated by XOR'ing together often changing system values. | ||
| 78 | * | ||
| 79 | * Parameters: | ||
| 80 | * randval - An optional random value. | ||
| 81 | * | ||
| 82 | * Returns: | ||
| 83 | * A random ULONG value. | ||
| 84 | */ | ||
| 85 | |||
| 86 | ULONG | ||
| 87 | rand(ULONG randval) | ||
| 88 | { | ||
| 89 | LARGE_INTEGER perf, TickCount; | ||
| 90 | ULONG r; | ||
| 91 | |||
| 92 | |||
| 93 | KeQueryPerformanceCounter(&perf); | ||
| 94 | // KdPrint(("perf: %d %d\n", perf.LowPart, perf.HighPart)); | ||
| 95 | |||
| 96 | KeQueryTickCount(&TickCount); | ||
| 97 | // KdPrint(("tick: %d %d\n", TickCount.LowPart, TickCount.HighPart)); | ||
| 98 | |||
| 99 | // KdPrint(("int: %I64d\n", KeQueryInterruptTime())); | ||
| 100 | |||
| 101 | r = randval ^ | ||
| 102 | perf.LowPart ^ perf.HighPart ^ | ||
| 103 | TickCount.HighPart ^ TickCount.LowPart ^ | ||
| 104 | (ULONG) KeQueryInterruptTime(); | ||
| 105 | |||
| 106 | // KdPrint(("rand = %x\n", r)); | ||
| 107 | |||
| 108 | |||
| 109 | return r; | ||
| 110 | } | ||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | /* | ||
| 115 | * PostProcessNtCreateProcess() | ||
| 116 | * | ||
| 117 | * Description: | ||
| 118 | * This function is called by the mediated NtCreateProcess() system service. | ||
| 119 | * It is responsible for saving the information about the newly created process in a | ||
| 120 | * global process hash table. | ||
| 121 | * | ||
| 122 | * Parameters: | ||
| 123 | * ProcessHandle - process object handle initialized by NtCreateProcess(). | ||
| 124 | * SectionHandle - specifies a handle to an image section that grants SECTION_MAP_EXECUTE | ||
| 125 | * access. If this value is zero, the new process inherits the address space from the process | ||
| 126 | * referred to by InheritFromProcessHandle. In Windows 2000 the lowest bit specifies | ||
| 127 | * (when set) that the process should not be associated with the job of the | ||
| 128 | * InheritFromProcessHandle process. | ||
| 129 | * | ||
| 130 | * Returns: | ||
| 131 | * ULONG indicating an action to take (ACTION_DENY to disallow process createion, ACTION_PERMIT to allow). | ||
| 132 | */ | ||
| 133 | |||
| 134 | ULONG | ||
| 135 | PostProcessNtCreateProcess(PHANDLE ProcessHandle, HANDLE SectionHandle) | ||
| 136 | { | ||
| 137 | NTSTATUS status, rc; | ||
| 138 | PIMAGE_PID_ENTRY p, prev, NewProcess; | ||
| 139 | ULONG ProcessId, Size; | ||
| 140 | PULONG_PTR Base; | ||
| 141 | UNICODE_STRING usPath; | ||
| 142 | ANSI_STRING asPath; | ||
| 143 | KIRQL irql; | ||
| 144 | PROCESS_BASIC_INFORMATION ProcessBasicInfo; | ||
| 145 | CHAR ProcessPathUnresolved[MAX_PATH]; | ||
| 146 | CHAR PROCESSNAME[MAX_PATH]; | ||
| 147 | PCHAR FunctionName = "PostProcessNtCreateProcess"; | ||
| 148 | |||
| 149 | |||
| 150 | if (ProcessHandle == NULL) | ||
| 151 | { | ||
| 152 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%s: ProcessHandle is NULL\n", FunctionName)); | ||
| 153 | return ACTION_NONE; | ||
| 154 | } | ||
| 155 | |||
| 156 | |||
| 157 | /* | ||
| 158 | * Try to retrieve the image name from a section object | ||
| 159 | */ | ||
| 160 | |||
| 161 | if (!SectionHandle) | ||
| 162 | { | ||
| 163 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%s: SectionHandle is NULL\n", FunctionName)); | ||
| 164 | return ACTION_NONE; | ||
| 165 | } | ||
| 166 | |||
| 167 | |||
| 168 | do | ||
| 169 | { | ||
| 170 | PSECTION SectionToMap; | ||
| 171 | PSEGMENT seg; | ||
| 172 | PCONTROL_AREA pca; | ||
| 173 | PFILE_OBJECT pfo; | ||
| 174 | |||
| 175 | |||
| 176 | status = ObReferenceObjectByHandle( | ||
| 177 | SectionHandle, | ||
| 178 | 0, | ||
| 179 | 0, | ||
| 180 | KernelMode, | ||
| 181 | (PSECTION *) &SectionToMap, | ||
| 182 | NULL | ||
| 183 | ); | ||
| 184 | |||
| 185 | /* macro shortcut for bailing out of PostProcessNtCreateProcess do {} while loop in case of an error */ | ||
| 186 | |||
| 187 | #define ABORT_PostProcessNtCreateProcess(msg) \ | ||
| 188 | { \ | ||
| 189 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("Error occurred in %s:", FunctionName)); \ | ||
| 190 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, (msg)); \ | ||
| 191 | return ACTION_NONE; /* XXX */ \ | ||
| 192 | } | ||
| 193 | |||
| 194 | if (! NT_SUCCESS(status)) | ||
| 195 | |||
| 196 | ABORT_PostProcessNtCreateProcess(("ObReferenceObjectByHandle(SectionHandle) failed")); | ||
| 197 | |||
| 198 | |||
| 199 | if (SectionToMap == NULL) | ||
| 200 | |||
| 201 | ABORT_PostProcessNtCreateProcess(("SectionToMap is NULL")); | ||
| 202 | |||
| 203 | |||
| 204 | if ( (seg = ((PSECTION)SectionToMap)->Segment) == NULL) | ||
| 205 | |||
| 206 | ABORT_PostProcessNtCreateProcess(("Segment is NULL")); | ||
| 207 | |||
| 208 | if ( (pca = seg->ControlArea) == NULL) | ||
| 209 | |||
| 210 | ABORT_PostProcessNtCreateProcess(("ControlArea is NULL")); | ||
| 211 | |||
| 212 | |||
| 213 | if ( (pfo = pca->FilePointer) == NULL) | ||
| 214 | |||
| 215 | ABORT_PostProcessNtCreateProcess(("FilePointer is NULL")); | ||
| 216 | |||
| 217 | |||
| 218 | usPath = pfo->FileName; | ||
| 219 | |||
| 220 | if (usPath.Length == 0) | ||
| 221 | |||
| 222 | ABORT_PostProcessNtCreateProcess(("FileName length is 0")); | ||
| 223 | |||
| 224 | |||
| 225 | _snprintf(ProcessPathUnresolved, MAX_PATH, "%S", usPath.Buffer); | ||
| 226 | ProcessPathUnresolved[MAX_PATH - 1] = 0; | ||
| 227 | |||
| 228 | |||
| 229 | ObDereferenceObject(SectionToMap); | ||
| 230 | |||
| 231 | } while (0); | ||
| 232 | |||
| 233 | |||
| 234 | /* | ||
| 235 | * Now verify the executable name against the security policy | ||
| 236 | */ | ||
| 237 | |||
| 238 | VerifyExecutableName(ProcessPathUnresolved); | ||
| 239 | |||
| 240 | |||
| 241 | if (LearningMode == FALSE) | ||
| 242 | { | ||
| 243 | ACTION_TYPE Action; | ||
| 244 | |||
| 245 | |||
| 246 | VerifyUserReturnAddress(); | ||
| 247 | |||
| 248 | |||
| 249 | FixupFilename(ProcessPathUnresolved, PROCESSNAME, MAX_PATH); | ||
| 250 | |||
| 251 | |||
| 252 | /* | ||
| 253 | * We manually inc/dec HookedRoutineRunning since POLICY_CHECK_OPTYPE_NAME() can call | ||
| 254 | * HOOK_ROUTINE_EXIT() which will decrement HookedRoutineRunning and then it will get | ||
| 255 | * decremented the second time in HookedNtCreateProcess() | ||
| 256 | */ | ||
| 257 | |||
| 258 | #if DBG | ||
| 259 | InterlockedIncrement(&HookedRoutineRunning); | ||
| 260 | #endif | ||
| 261 | |||
| 262 | POLICY_CHECK_OPTYPE_NAME(PROCESS, OP_PROC_EXECUTE); | ||
| 263 | |||
| 264 | #if DBG | ||
| 265 | InterlockedDecrement(&HookedRoutineRunning); | ||
| 266 | #endif | ||
| 267 | } | ||
| 268 | else | ||
| 269 | { | ||
| 270 | // learning mode | ||
| 271 | AddRule(RULE_PROCESS, ProcessPathUnresolved, OP_PROC_EXECUTE); | ||
| 272 | } | ||
| 273 | |||
| 274 | |||
| 275 | /* | ||
| 276 | * retrieve the Process ID | ||
| 277 | */ | ||
| 278 | |||
| 279 | status = ZwQueryInformationProcess(*ProcessHandle, ProcessBasicInformation, &ProcessBasicInfo, sizeof(ProcessBasicInfo), &Size); | ||
| 280 | |||
| 281 | if (! NT_SUCCESS(status)) | ||
| 282 | { | ||
| 283 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwQueryInformationProcess failed with status %x\n", status)); | ||
| 284 | return ACTION_NONE; | ||
| 285 | } | ||
| 286 | |||
| 287 | ProcessId = ProcessBasicInfo.UniqueProcessId; | ||
| 288 | |||
| 289 | /* | ||
| 290 | * On win2k ProcessId is not available at this stage yet. | ||
| 291 | * ProcessId of 0 will get replaced by a real value in CreateProcessNotifyProc. | ||
| 292 | */ | ||
| 293 | |||
| 294 | |||
| 295 | /* once winlogon.exe executes, we consider the boot process to be complete */ | ||
| 296 | if (BootingUp == TRUE) | ||
| 297 | { | ||
| 298 | PCHAR ProcessName; | ||
| 299 | |||
| 300 | |||
| 301 | ProcessName = strrchr(ProcessPathUnresolved, '\\'); | ||
| 302 | |||
| 303 | if (ProcessName == NULL) | ||
| 304 | ProcessName = ProcessPathUnresolved; | ||
| 305 | else | ||
| 306 | ++ProcessName; /* skip past the slash */ | ||
| 307 | |||
| 308 | if (_strnicmp(ProcessName, "winlogon.exe", 12) == 0) | ||
| 309 | { | ||
| 310 | BootingUp = FALSE; | ||
| 311 | InitPostBootup(); | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | |||
| 316 | /* | ||
| 317 | * Now create a new process entry and load the associated security policy (if any) | ||
| 318 | */ | ||
| 319 | |||
| 320 | NewProcess = CreateNewProcessEntry(ProcessId, CURRENT_PROCESS_PID, &usPath, TRUE); | ||
| 321 | |||
| 322 | |||
| 323 | if (ProcessInsertImagePidEntry(ProcessId, NewProcess) == FALSE) | ||
| 324 | { | ||
| 325 | ExFreePoolWithTag(NewProcess, _POOL_TAG); | ||
| 326 | |||
| 327 | return ACTION_NONE; | ||
| 328 | } | ||
| 329 | |||
| 330 | |||
| 331 | /* | ||
| 332 | * Now find and load appropriate security policy. | ||
| 333 | * | ||
| 334 | * Look for a per-user policy first. To do that, we send an SID resolve request | ||
| 335 | * to userland Ozone Agent Service. | ||
| 336 | */ | ||
| 337 | |||
| 338 | if (LearningMode == FALSE) | ||
| 339 | { | ||
| 340 | PSID_RESOLVE_REPLY pSidResolveReply = NULL; | ||
| 341 | PWSTR UserName = NULL; | ||
| 342 | |||
| 343 | |||
| 344 | if (IssueUserlandSidResolveRequest(NewProcess) != FALSE) | ||
| 345 | { | ||
| 346 | if (NewProcess->UserlandReply) | ||
| 347 | { | ||
| 348 | pSidResolveReply = (PSID_RESOLVE_REPLY) NewProcess->UserlandReply; | ||
| 349 | |||
| 350 | if (pSidResolveReply->UserNameLength) | ||
| 351 | { | ||
| 352 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: SID resolved to %S\n", pSidResolveReply->UserName)); | ||
| 353 | UserName = pSidResolveReply->UserName; | ||
| 354 | } | ||
| 355 | else | ||
| 356 | { | ||
| 357 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess(): SID resolve error\n")); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | |||
| 363 | if (! FindAndLoadSecurityPolicy(&NewProcess->SecPolicy, usPath.Buffer, UserName)) | ||
| 364 | { | ||
| 365 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%d PostProcessNtCreateProcess(): no policy, %d, %S\n", CURRENT_PROCESS_PID, ProcessId, usPath.Buffer)); | ||
| 366 | |||
| 367 | //XXX have an option where only processes with existing policies (even if they are empty.. policy_default: allow) are allowed to run | ||
| 368 | //interactive session is excluded?!?! | ||
| 369 | if (AllowProcessesWithPoliciesOnly == TRUE) | ||
| 370 | { | ||
| 371 | ExFreePoolWithTag(NewProcess, _POOL_TAG); | ||
| 372 | return ACTION_DENY; | ||
| 373 | } | ||
| 374 | } | ||
| 375 | else | ||
| 376 | { | ||
| 377 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_WARNING, ("%d PostProcessNtCreateProcess(): with policy, %d, %S\n", CURRENT_PROCESS_PID, ProcessId, usPath.Buffer)); | ||
| 378 | } | ||
| 379 | |||
| 380 | |||
| 381 | if (NewProcess->UserlandReply) | ||
| 382 | { | ||
| 383 | ExFreePoolWithTag(NewProcess->UserlandReply, _POOL_TAG); | ||
| 384 | NewProcess->UserlandReply = NULL; | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | |||
| 389 | /* | ||
| 390 | * Stack Buffer Overflow Protection (Part 1). | ||
| 391 | * | ||
| 392 | * 1. Allocate/reserve a random (< 0x4000000) chunk of virtual memory starting at 0xFFFF. | ||
| 393 | * This causes the stack of the main thread to be moved by a random amount. | ||
| 394 | * | ||
| 395 | * (note1: first 64K of memory are allocated as a guard against NULL pointers dereferencing). | ||
| 396 | * (note2: main() is mapped at 0x4000000 and thus we cannot allocate anything that will cause the | ||
| 397 | * stack of the main thread to move past the 0x400000 boundary). | ||
| 398 | * | ||
| 399 | * | ||
| 400 | * Stack Buffer Overflow Protection (Part 2). | ||
| 401 | * | ||
| 402 | * 2. Allocate a random (> 4 Megs && < 10 Megs) chunk of virtual memory after the process code segment. | ||
| 403 | * This causes the heap and stacks non-main threads to be moved by a random amount. | ||
| 404 | * | ||
| 405 | * (note: as mentioned above, main() is mapped at 0x4000000, by reserving a large chunk of virtual | ||
| 406 | * we force Windows to find the first available address beyond the code segment and reserve | ||
| 407 | * a random amount of memory past it causing other thread stacks and heaps to shift). | ||
| 408 | */ | ||
| 409 | |||
| 410 | #define ONE_MEGABYTE (1024 * 1024) | ||
| 411 | |||
| 412 | if (IS_OVERFLOW_PROTECTION_ON(gSecPolicy) && IS_OVERFLOW_PROTECTION_ON(NewProcess->SecPolicy) && LearningMode == FALSE && BootingUp == FALSE) | ||
| 413 | { | ||
| 414 | //XXX verify that the image entry is actually at 4 meg (not true for most of system processes) | ||
| 415 | |||
| 416 | /* | ||
| 417 | * allocate up to 3 megs of virtual address space before the code segment, | ||
| 418 | * this affects main thread stack as well as some heaps | ||
| 419 | */ | ||
| 420 | |||
| 421 | #define FIRST_AVAILABLE_ADDRESS 0xFFFF | ||
| 422 | |||
| 423 | Size = PAGE_SIZE + (rand(ProcessId) % (3 * ONE_MEGABYTE)); | ||
| 424 | Base = (PULONG_PTR) FIRST_AVAILABLE_ADDRESS; | ||
| 425 | |||
| 426 | status = ZwAllocateVirtualMemory(*ProcessHandle, &Base, 0L, &Size, MEM_RESERVE, PAGE_NOACCESS); | ||
| 427 | |||
| 428 | if (! NT_SUCCESS(status)) | ||
| 429 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwAllocateVirtualMemory1(%x, %x) failed with status %x\n", Base, Size, status)); | ||
| 430 | else | ||
| 431 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("PostProcessNtCreateProcess: size=%u base=%x status=%d\n", Size, Base, status)); | ||
| 432 | |||
| 433 | /* | ||
| 434 | * allocate up to 10 megs of virtual address space after the code segment, | ||
| 435 | * this affects non-main thread stack as well as some heaps | ||
| 436 | */ | ||
| 437 | |||
| 438 | #define IMAGE_BASE (4 * ONE_MEGABYTE) | ||
| 439 | |||
| 440 | Size = IMAGE_BASE + (rand(ProcessId) % (10 * ONE_MEGABYTE)); | ||
| 441 | Base = (PULONG_PTR) NULL; | ||
| 442 | |||
| 443 | status = ZwAllocateVirtualMemory(*ProcessHandle, &Base, 0L, &Size, MEM_RESERVE, PAGE_NOACCESS); | ||
| 444 | |||
| 445 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("PostProcessNtCreateProcess: size=%u base=%x status=%d\n", Size, Base, status)); | ||
| 446 | |||
| 447 | if (! NT_SUCCESS(status)) | ||
| 448 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwAllocateVirtualMemory2(%x, %x) failed with status %x\n", Base, Size, status)); | ||
| 449 | |||
| 450 | |||
| 451 | |||
| 452 | /* | ||
| 453 | * allocate the entire KnownDll space | ||
| 454 | */ | ||
| 455 | //#if 0 | ||
| 456 | // Size = (4 * ONE_MEGABYTE) + (rand(ProcessId) % (100 * ONE_MEGABYTE)); | ||
| 457 | // Base = (PULONG_PTR) 0x71bf0000; | ||
| 458 | |||
| 459 | // Size = 0x7000000;//(125 * ONE_MEGABYTE); | ||
| 460 | // Base = (PULONG_PTR) 0x70000000; | ||
| 461 | |||
| 462 | #if HOOK_BOPROT | ||
| 463 | if (strstr(ProcessPathUnresolved, "stack.exe") != NULL) | ||
| 464 | { | ||
| 465 | Size = PAGE_SIZE; | ||
| 466 | // Base = (PULONG_PTR) 0x77d00000; //user32 | ||
| 467 | Base = (PULONG_PTR) 0x77e30000; //kernel32 on win2k3 | ||
| 468 | // Base = (PULONG_PTR) 0x77e80000; //kernel32 on win2k | ||
| 469 | |||
| 470 | status = ZwAllocateVirtualMemory(*ProcessHandle, &Base, 0L, &Size, MEM_RESERVE, PAGE_NOACCESS); | ||
| 471 | |||
| 472 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: kernel32.dll size=%u base=%x status=%d\n", Size, Base, status)); | ||
| 473 | |||
| 474 | if (! NT_SUCCESS(status)) | ||
| 475 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("PostProcessNtCreateProcess: ZwAllocateVirtualMemory1(%x, %x) failed with status %x\n", Base, Size, status)); | ||
| 476 | } | ||
| 477 | else | ||
| 478 | NewProcess->FirstThread = FALSE;//XXX remove | ||
| 479 | #endif | ||
| 480 | } | ||
| 481 | |||
| 482 | |||
| 483 | return ACTION_PERMIT; | ||
| 484 | } | ||
| 485 | |||
| 486 | |||
| 487 | |||
| 488 | /* | ||
| 489 | * HookedNtCreateProcess() | ||
| 490 | * | ||
| 491 | * Description: | ||
| 492 | * This function mediates the NtCreateProcess() system service in order to keep track of all | ||
| 493 | * the newly created processes. | ||
| 494 | * | ||
| 495 | * NOTE: ZwCreateProcess creates a process object. [NAR] | ||
| 496 | * | ||
| 497 | * Parameters: | ||
| 498 | * Those of NtCreateProcess(). | ||
| 499 | * | ||
| 500 | * Returns: | ||
| 501 | * NTSTATUS returned by NtCreateProcess(). | ||
| 502 | */ | ||
| 503 | |||
| 504 | NTSTATUS | ||
| 505 | NTAPI | ||
| 506 | HookedNtCreateProcess | ||
| 507 | ( | ||
| 508 | OUT PHANDLE ProcessHandle, | ||
| 509 | IN ACCESS_MASK DesiredAccess, | ||
| 510 | IN POBJECT_ATTRIBUTES ObjectAttributes, | ||
| 511 | IN HANDLE InheritFromProcessHandle, | ||
| 512 | IN BOOLEAN InheritHandles, | ||
| 513 | IN HANDLE SectionHandle OPTIONAL, | ||
| 514 | IN HANDLE DebugPort OPTIONAL, | ||
| 515 | IN HANDLE ExceptionPort OPTIONAL | ||
| 516 | ) | ||
| 517 | { | ||
| 518 | HOOK_ROUTINE_ENTER(); | ||
| 519 | |||
| 520 | |||
| 521 | ASSERT(OriginalNtCreateProcess); | ||
| 522 | |||
| 523 | rc = OriginalNtCreateProcess(ProcessHandle, DesiredAccess, ObjectAttributes, InheritFromProcessHandle, | ||
| 524 | InheritHandles, SectionHandle, DebugPort, ExceptionPort); | ||
| 525 | |||
| 526 | |||
| 527 | if (NT_SUCCESS(rc)) | ||
| 528 | { | ||
| 529 | ULONG ret = PostProcessNtCreateProcess(ProcessHandle, SectionHandle); | ||
| 530 | |||
| 531 | if (ret == ACTION_DENY || ret == STATUS_ACCESS_DENIED) | ||
| 532 | { | ||
| 533 | ZwClose(*ProcessHandle); | ||
| 534 | rc = STATUS_ACCESS_DENIED; | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | |||
| 539 | HOOK_ROUTINE_EXIT(rc); | ||
| 540 | } | ||
| 541 | |||
| 542 | |||
| 543 | |||
| 544 | /* | ||
| 545 | * HookedNtCreateProcessEx() | ||
| 546 | * | ||
| 547 | * Description: | ||
| 548 | * This function mediates the NtCreateProcessEx() system service in order to keep track of all | ||
| 549 | * the newly created processes. | ||
| 550 | * | ||
| 551 | * NOTE: ZwCreateProcessEx creates a process object. [NAR] | ||
| 552 | * | ||
| 553 | * Parameters: | ||
| 554 | * Those of NtCreateProcessEx(). | ||
| 555 | * | ||
| 556 | * Returns: | ||
| 557 | * NTSTATUS returned by NtCreateProcessEx(). | ||
| 558 | */ | ||
| 559 | |||
| 560 | NTSTATUS | ||
| 561 | NTAPI | ||
| 562 | HookedNtCreateProcessEx | ||
| 563 | ( | ||
| 564 | OUT PHANDLE ProcessHandle, | ||
| 565 | IN ACCESS_MASK DesiredAccess, | ||
| 566 | IN POBJECT_ATTRIBUTES ObjectAttributes, | ||
| 567 | IN HANDLE InheritFromProcessHandle, | ||
| 568 | IN ULONG Unknown1, | ||
| 569 | IN HANDLE SectionHandle OPTIONAL, | ||
| 570 | IN HANDLE DebugPort OPTIONAL, | ||
| 571 | IN HANDLE ExceptionPort OPTIONAL, | ||
| 572 | IN ULONG Unknown2 | ||
| 573 | ) | ||
| 574 | { | ||
| 575 | HOOK_ROUTINE_ENTER(); | ||
| 576 | |||
| 577 | |||
| 578 | ASSERT(OriginalNtCreateProcessEx); | ||
| 579 | |||
| 580 | rc = OriginalNtCreateProcessEx(ProcessHandle, DesiredAccess, ObjectAttributes, InheritFromProcessHandle, | ||
| 581 | Unknown1, SectionHandle, DebugPort, ExceptionPort, Unknown2); | ||
| 582 | |||
| 583 | |||
| 584 | if (NT_SUCCESS(rc)) | ||
| 585 | { | ||
| 586 | ULONG ret = PostProcessNtCreateProcess(ProcessHandle, SectionHandle); | ||
| 587 | |||
| 588 | if (ret == ACTION_DENY || ret == STATUS_ACCESS_DENIED) | ||
| 589 | { | ||
| 590 | ZwClose(*ProcessHandle); | ||
| 591 | rc = STATUS_ACCESS_DENIED; | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | |||
| 596 | HOOK_ROUTINE_EXIT(rc); | ||
| 597 | } | ||
| 598 | |||
| 599 | |||
| 600 | |||
| 601 | /* | ||
| 602 | * HookedNtOpenProcess() | ||
| 603 | * | ||
| 604 | * Description: | ||
| 605 | * This function mediates the NtOpenProcess() system service and disallows certain operations such as | ||
| 606 | * PROCESS_VM_WRITE and PROCESS_CREATE_THREAD. | ||
| 607 | * | ||
| 608 | * NOTE: ZwOpenProcess opens a process object. [NAR] | ||
| 609 | * | ||
| 610 | * Parameters: | ||
| 611 | * Those of NtOpenProcess(). | ||
| 612 | * | ||
| 613 | * Returns: | ||
| 614 | * NTSTATUS returned by NtOpenProcess(). | ||
| 615 | */ | ||
| 616 | |||
| 617 | NTSTATUS | ||
| 618 | NTAPI | ||
| 619 | HookedNtOpenProcess | ||
| 620 | ( | ||
| 621 | OUT PHANDLE ProcessHandle, | ||
| 622 | IN ACCESS_MASK DesiredAccess, | ||
| 623 | IN POBJECT_ATTRIBUTES ObjectAttributes, | ||
| 624 | IN PCLIENT_ID ClientId OPTIONAL | ||
| 625 | ) | ||
| 626 | { | ||
| 627 | PCHAR FunctionName = "HookedNtOpenProcess"; | ||
| 628 | CHAR PROCESSNAME[MAX_PATH]; | ||
| 629 | |||
| 630 | |||
| 631 | //taskmgr uses PROCESS_TERMINATE to kill processes | ||
| 632 | //XXX IPD disallows PROCESS_CREATE_THREAD|PROCESS_VM_WRITE | ||
| 633 | |||
| 634 | /* | ||
| 635 | PROCESS_TERMINATE Terminate process | ||
| 636 | PROCESS_CREATE_THREAD Create threads in process | ||
| 637 | PROCESS_SET_SESSIONID Set process session id | ||
| 638 | PROCESS_VM_OPERATION Protect and lock memory of process | ||
| 639 | PROCESS_VM_READ Read memory of process | ||
| 640 | PROCESS_VM_WRITE Write memory of process | ||
| 641 | PROCESS_DUP_HANDLE Duplicate handles of process | ||
| 642 | PROCESS_CREATE_PROCESS Bequeath address space and handles to new process | ||
| 643 | PROCESS_SET_QUOTA Set process quotas | ||
| 644 | PROCESS_SET_INFORMATION Set information about process | ||
| 645 | PROCESS_QUERY_INFORMATION Query information about process | ||
| 646 | PROCESS_SET_PORT Set process exception or debug port | ||
| 647 | PROCESS_ALL_ACCESS All of the preceding | ||
| 648 | |||
| 649 | find out who uses which flags, i.e. VM_READ, etc.. filter out accordingly | ||
| 650 | */ | ||
| 651 | |||
| 652 | HOOK_ROUTINE_ENTER(); | ||
| 653 | |||
| 654 | |||
| 655 | // if (! IS_BIT_SET(DesiredAccess, PROCESS_QUERY_INFORMATION) && | ||
| 656 | // ! IS_BIT_SET(DesiredAccess, PROCESS_DUP_HANDLE) && | ||
| 657 | // ! IS_BIT_SET(DesiredAccess, SYNCHRONIZE) ) | ||
| 658 | |||
| 659 | |||
| 660 | if (! ARGUMENT_PRESENT(ClientId) || KeGetPreviousMode() != UserMode || KeGetCurrentIrql() != PASSIVE_LEVEL) | ||
| 661 | goto done; | ||
| 662 | |||
| 663 | |||
| 664 | __try | ||
| 665 | { | ||
| 666 | ProbeForRead(ClientId, sizeof(*ClientId), sizeof(PULONG_PTR)); | ||
| 667 | } | ||
| 668 | |||
| 669 | __except(EXCEPTION_EXECUTE_HANDLER) | ||
| 670 | { | ||
| 671 | NTSTATUS status = GetExceptionCode(); | ||
| 672 | |||
| 673 | LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("%s: caught an exception. address=%x, status = 0x%x\n", FunctionName, ClientId, status)); | ||
| 674 | |||
| 675 | goto done; | ||
| 676 | } | ||
| 677 | |||
| 678 | |||
| 679 | if (PsGetCurrentProcessId() != ClientId->UniqueProcess && | ||
| 680 | (ULONG) PsGetCurrentProcessId() != SystemProcessId && | ||
| 681 | ClientId->UniqueProcess != 0) | ||
| 682 | { | ||
| 683 | PIMAGE_PID_ENTRY p; | ||
| 684 | |||
| 685 | /* can't access ClientId (pageable memory) while holding spinlonk */ | ||
| 686 | ULONG RequestedProcessId = (ULONG) ClientId->UniqueProcess; | ||
| 687 | |||
| 688 | |||
| 689 | //XXX | ||
| 690 | /* | ||
| 691 | if (RequestedProcessId == UserAgentServicePid) | ||
| 692 | { | ||
| 693 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("%s: FindImagePidEntry(%d) UserAgent %x\n", FunctionName, RequestedProcessId, DesiredAccess)); | ||
| 694 | |||
| 695 | if (IS_BIT_SET(DesiredAccess, PROCESS_TERMINATE)) | ||
| 696 | { | ||
| 697 | HOOK_ROUTINE_EXIT( STATUS_ACCESS_DENIED ); | ||
| 698 | } | ||
| 699 | } | ||
| 700 | */ | ||
| 701 | |||
| 702 | |||
| 703 | p = FindImagePidEntry(RequestedProcessId, 0); | ||
| 704 | |||
| 705 | if (p == NULL) | ||
| 706 | { | ||
| 707 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: FindImagePidEntry(%d) failed\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId)); | ||
| 708 | goto done; | ||
| 709 | } | ||
| 710 | |||
| 711 | /* | ||
| 712 | if (DesiredAccess & PROCESS_CREATE_THREAD) | ||
| 713 | { | ||
| 714 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s(%d): %x (PROCESS_CREATE_THREAD). (%S)\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId, DesiredAccess, p->ImageName)); | ||
| 715 | } | ||
| 716 | else if (DesiredAccess & PROCESS_VM_WRITE) | ||
| 717 | { | ||
| 718 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s(%d): %x (PROCESS_VM_WRITE). (%S)\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId, DesiredAccess, p->ImageName)); | ||
| 719 | } | ||
| 720 | else | ||
| 721 | { | ||
| 722 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("%d %s(%d): %x. (%S)\n", CURRENT_PROCESS_PID, FunctionName, RequestedProcessId, DesiredAccess, p->ImageName)); | ||
| 723 | } | ||
| 724 | */ | ||
| 725 | |||
| 726 | |||
| 727 | if (LearningMode == FALSE) | ||
| 728 | { | ||
| 729 | CHAR ProcessPathUnresolved[MAX_PATH]; | ||
| 730 | |||
| 731 | |||
| 732 | _snprintf(ProcessPathUnresolved, MAX_PATH, "%S", p->ImageName); | ||
| 733 | ProcessPathUnresolved[ MAX_PATH - 1 ] = 0; | ||
| 734 | |||
| 735 | |||
| 736 | FixupFilename(ProcessPathUnresolved, PROCESSNAME, MAX_PATH); | ||
| 737 | |||
| 738 | |||
| 739 | POLICY_CHECK_OPTYPE_NAME(PROCESS, OP_PROC_OPEN); | ||
| 740 | } | ||
| 741 | else | ||
| 742 | { | ||
| 743 | // learning mode | ||
| 744 | _snprintf(PROCESSNAME, MAX_PATH, "%S", p->ImageName); | ||
| 745 | PROCESSNAME[ MAX_PATH - 1 ] = 0; | ||
| 746 | |||
| 747 | AddRule(RULE_PROCESS, PROCESSNAME, OP_PROC_OPEN); | ||
| 748 | } | ||
| 749 | } | ||
| 750 | |||
| 751 | |||
| 752 | if (LearningMode == FALSE && GetPathFromOA(ObjectAttributes, PROCESSNAME, MAX_PATH, RESOLVE_LINKS)) | ||
| 753 | { | ||
| 754 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: %s SPECIAL CASE\n", (ULONG) PsGetCurrentProcessId(), FunctionName, PROCESSNAME)); | ||
| 755 | } | ||
| 756 | |||
| 757 | |||
| 758 | done: | ||
| 759 | |||
| 760 | ASSERT(OriginalNtOpenProcess); | ||
| 761 | |||
| 762 | rc = OriginalNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); | ||
| 763 | |||
| 764 | |||
| 765 | HOOK_ROUTINE_EXIT(rc); | ||
| 766 | } | ||
| 767 | |||
| 768 | |||
| 769 | |||
| 770 | /* | ||
| 771 | * HookedNtCreateThread() | ||
| 772 | * | ||
| 773 | * Description: | ||
| 774 | * This function mediates the NtCreateThread() system service in order to randomize thread stack | ||
| 775 | * and inject userland dll into newly created main threads. | ||
| 776 | * | ||
| 777 | * NOTE: ZwCreateThread creates a thread in a process. [NAR] | ||
| 778 | * | ||
| 779 | * Parameters: | ||
| 780 | * Those of NtCreateThread(). | ||
| 781 | * | ||
| 782 | * Returns: | ||
| 783 | * NTSTATUS returned by NtCreateThread(). | ||
| 784 | */ | ||
| 785 | |||
| 786 | NTSTATUS | ||
| 787 | NTAPI | ||
| 788 | HookedNtCreateThread | ||
| 789 | ( | ||
| 790 | OUT PHANDLE ThreadHandle, | ||
| 791 | IN ACCESS_MASK DesiredAccess, | ||
| 792 | IN POBJECT_ATTRIBUTES ObjectAttributes, | ||
| 793 | IN HANDLE ProcessHandle, | ||
| 794 | OUT PCLIENT_ID ClientId, | ||
| 795 | IN PCONTEXT ThreadContext, | ||
| 796 | IN PUSER_STACK UserStack, | ||
| 797 | IN BOOLEAN CreateSuspended | ||
| 798 | ) | ||
| 799 | { | ||
| 800 | PEPROCESS proc = NULL; | ||
| 801 | USHORT StackOffset = 0; | ||
| 802 | PCHAR FunctionName = "HookedNtCreateThread"; | ||
| 803 | |||
| 804 | |||
| 805 | HOOK_ROUTINE_ENTER(); | ||
| 806 | |||
| 807 | |||
| 808 | if (ARGUMENT_PRESENT(ThreadContext) && KeGetPreviousMode() == UserMode && LearningMode == FALSE && BootingUp == FALSE) | ||
| 809 | { | ||
| 810 | NTSTATUS status; | ||
| 811 | ULONG ProcessId; | ||
| 812 | PCHAR InstructionAddress; | ||
| 813 | ULONG Size; | ||
| 814 | PCHAR Base; | ||
| 815 | PROCESS_BASIC_INFORMATION ProcessBasicInfo; | ||
| 816 | ULONG ret; | ||
| 817 | PIMAGE_PID_ENTRY p; | ||
| 818 | |||
| 819 | |||
| 820 | VerifyUserReturnAddress(); | ||
| 821 | |||
| 822 | |||
| 823 | /* verify userland parameter threadcontext */ | ||
| 824 | __try | ||
| 825 | { | ||
| 826 | ProbeForRead(ThreadContext, sizeof(*ThreadContext), sizeof(ULONG)); | ||
| 827 | ProbeForWrite(ThreadContext, sizeof(*ThreadContext), sizeof(ULONG)); | ||
| 828 | } | ||
| 829 | |||
| 830 | __except(EXCEPTION_EXECUTE_HANDLER) | ||
| 831 | { | ||
| 832 | NTSTATUS status = GetExceptionCode(); | ||
| 833 | |||
| 834 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: caught an exception. status = 0x%x\n", FunctionName, status)); | ||
| 835 | |||
| 836 | goto done; | ||
| 837 | } | ||
| 838 | |||
| 839 | |||
| 840 | if (ThreadContext->Eax > SystemAddressStart) | ||
| 841 | { | ||
| 842 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: eax=%x > %x (SystemAddressStart)\n", FunctionName, ThreadContext->Eax, SystemAddressStart)); | ||
| 843 | goto done; | ||
| 844 | } | ||
| 845 | |||
| 846 | |||
| 847 | /* retrieve the Process ID */ | ||
| 848 | status = ZwQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &ProcessBasicInfo, sizeof(ProcessBasicInfo), &Size); | ||
| 849 | |||
| 850 | if (! NT_SUCCESS(status)) | ||
| 851 | { | ||
| 852 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: ZwQueryInformationProcess failed with status %x\n", FunctionName, status)); | ||
| 853 | goto done; | ||
| 854 | } | ||
| 855 | |||
| 856 | ProcessId = ProcessBasicInfo.UniqueProcessId; | ||
| 857 | |||
| 858 | /* | ||
| 859 | * if ProcessId is 0 then the pid has not been assigned yet and we are the primary thread. | ||
| 860 | * in that case pass our pid (we are still running in the context of the parent process) as the ParentId | ||
| 861 | * to make sure we find the right process (since theoretically there can be more than one process with a | ||
| 862 | * pid of 0) | ||
| 863 | */ | ||
| 864 | p = FindImagePidEntry(ProcessId, ProcessId == 0 ? CURRENT_PROCESS_PID : 0); | ||
| 865 | |||
| 866 | if (p == NULL) | ||
| 867 | { | ||
| 868 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: FindImagePidEntry(%d) failed\n", CURRENT_PROCESS_PID, FunctionName, ProcessId)); | ||
| 869 | goto done; | ||
| 870 | } | ||
| 871 | |||
| 872 | |||
| 873 | /* | ||
| 874 | * Stack Buffer Overflow Protection (Part 3). | ||
| 875 | * | ||
| 876 | * Allocate/reserve a random (< 1024 bytes) part of a thread's stack space. | ||
| 877 | * This causes the least significant 10 bits to be randomized as well. | ||
| 878 | * (without this, the least significant 16 bits are always the same). | ||
| 879 | */ | ||
| 880 | |||
| 881 | /* save the stackoffset for now since we are holding a spinlock and cannot touch pageable memory */ | ||
| 882 | //XXX we are not holding the spinlock here but are still accessing various p-> fields | ||
| 883 | if (IS_OVERFLOW_PROTECTION_ON(gSecPolicy) && IS_OVERFLOW_PROTECTION_ON(p->SecPolicy)) | ||
| 884 | |||
| 885 | StackOffset = (USHORT) (16 + (rand(ThreadContext->Eax) % 63) * 16); | ||
| 886 | |||
| 887 | |||
| 888 | if (! IS_USERLAND_PROTECTION_ON(gSecPolicy) || ! IS_USERLAND_PROTECTION_ON(p->SecPolicy)) | ||
| 889 | goto done; | ||
| 890 | |||
| 891 | |||
| 892 | /* Userland DLL needs to be loaded only once (by the first/main thread) */ | ||
| 893 | |||
| 894 | if (p->FirstThread != TRUE) | ||
| 895 | goto done; | ||
| 896 | |||
| 897 | p->FirstThread = FALSE; | ||
| 898 | |||
| 899 | //XXX investigate MEM_WRITE_WATCH (supported on i386?) | ||
| 900 | |||
| 901 | |||
| 902 | /* | ||
| 903 | * Inject the userland DLL into the process. | ||
| 904 | * | ||
| 905 | * This is achieved by allocating 1 page of memory in the address space of a "victim" process. | ||
| 906 | * Then the LdrLoadDll(our_dll) code is written to the allocated page and victim's thread EIP | ||
| 907 | * is changed to point to our code. | ||
| 908 | * As soon as the thread executes, it will load our DLL and then jump to the original entry point. | ||
| 909 | * | ||
| 910 | * When not given explicit directions, the Microsoft linker creates each DLL with a | ||
| 911 | * preferred load address of 0x10000000. By loading our DLL first, we cause the rest of | ||
| 912 | * the application DLLs to load at a different address. | ||
| 913 | */ | ||
| 914 | |||
| 915 | /* allocate 1 page of commited rwx memory */ | ||
| 916 | |||
| 917 | Size = PAGE_SIZE; | ||
| 918 | Base = NULL; | ||
| 919 | |||
| 920 | status = ZwAllocateVirtualMemory(ProcessHandle, &Base, 0L, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
| 921 | |||
| 922 | if (! NT_SUCCESS(status)) | ||
| 923 | { | ||
| 924 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: ZwAllocateVirtualMemory(%x, %x) failed with status %x\n", FunctionName, Base, Size, status)); | ||
| 925 | goto done; | ||
| 926 | } | ||
| 927 | |||
| 928 | |||
| 929 | status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_ALL_ACCESS, 0, KernelMode, &proc, NULL); | ||
| 930 | |||
| 931 | if (! NT_SUCCESS(status)) | ||
| 932 | { | ||
| 933 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: ObReferenceObjectByHandle(ProcessHandle) failed with status %x\n", FunctionName, status)); | ||
| 934 | goto done; | ||
| 935 | } | ||
| 936 | |||
| 937 | |||
| 938 | try | ||
| 939 | { | ||
| 940 | ULONG CodeAddress, DllPathAddress; | ||
| 941 | ULONG ThreadEntryPoint = ThreadContext->Eax; /* thread entry point is stored in the EAX register */ | ||
| 942 | PWSTR InjectDllName = L"ozoneusr.dll"; | ||
| 943 | USHORT InjectDllNameSize = (wcslen(InjectDllName) + 1) * sizeof(WCHAR); | ||
| 944 | |||
| 945 | |||
| 946 | /* | ||
| 947 | * Execute the DLL inject in the memory context of a "victim" process | ||
| 948 | */ | ||
| 949 | |||
| 950 | KeAttachProcess(proc); | ||
| 951 | { | ||
| 952 | /* probe the memory, just in case */ | ||
| 953 | ProbeForRead(Base, PAGE_SIZE, 1); | ||
| 954 | ProbeForWrite(Base, PAGE_SIZE, 1); | ||
| 955 | |||
| 956 | |||
| 957 | /* | ||
| 958 | * Memory Layout used for DLL injection | ||
| 959 | * | ||
| 960 | * Byte Value | ||
| 961 | * | ||
| 962 | * 0..4 Original EIP | ||
| 963 | * 4..8 LdrLoadDll() output handle | ||
| 964 | * 8..16 LdrLoadDll() DllName UNICODE_STRING structure | ||
| 965 | * 16..? DllName.Buffer WCHAR string | ||
| 966 | * ?..? DllPath WCHAR string | ||
| 967 | * .... assembler code (to call LdrLoadDll() and then jmp to the original EIP) | ||
| 968 | */ | ||
| 969 | |||
| 970 | #define BASE_PTR Base | ||
| 971 | #define ADDRESS_ORIGINAL_EIP (BASE_PTR + 0) | ||
| 972 | #define ADDRESS_DLL_HANDLE (BASE_PTR + 4) | ||
| 973 | #define ADDRESS_DLL_NAME (BASE_PTR + 8) | ||
| 974 | |||
| 975 | |||
| 976 | /* save the original thread entry point */ | ||
| 977 | * (PULONG) ADDRESS_ORIGINAL_EIP = ThreadEntryPoint; | ||
| 978 | |||
| 979 | /* skip past eip and output handle (8 bytes) */ | ||
| 980 | InstructionAddress = ADDRESS_DLL_NAME; | ||
| 981 | |||
| 982 | /* | ||
| 983 | * Create a UNICODE_STRING structure | ||
| 984 | */ | ||
| 985 | |||
| 986 | * ((PUSHORT) InstructionAddress)++ = InjectDllNameSize; /* UNICODE_STRING.Length */ | ||
| 987 | * ((PUSHORT) InstructionAddress)++ = InjectDllNameSize; /* UNICODE_STRING.MaximumLength */ | ||
| 988 | |||
| 989 | /* UNICODE_STRING.Buffer (points to unicode string directly following the buffer) */ | ||
| 990 | |||
| 991 | * ((PULONG) InstructionAddress)++ = (ULONG) (InstructionAddress + sizeof(PWSTR)); | ||
| 992 | |||
| 993 | |||
| 994 | /* the actual DllName.Buffer value */ | ||
| 995 | |||
| 996 | wcscpy((PWSTR) InstructionAddress, InjectDllName); | ||
| 997 | |||
| 998 | InstructionAddress += InjectDllNameSize; | ||
| 999 | |||
| 1000 | |||
| 1001 | /* DllPathValue value */ | ||
| 1002 | |||
| 1003 | DllPathAddress = (ULONG) InstructionAddress; | ||
| 1004 | |||
| 1005 | wcscpy((PWSTR) InstructionAddress, OzoneInstallPath); | ||
| 1006 | |||
| 1007 | InstructionAddress += OzoneInstallPathSize; | ||
| 1008 | |||
| 1009 | |||
| 1010 | CodeAddress = (ULONG) InstructionAddress; | ||
| 1011 | |||
| 1012 | |||
| 1013 | /* | ||
| 1014 | * Generate code that will call LdrLoadDll and then jmp to the original entry point. | ||
| 1015 | */ | ||
| 1016 | |||
| 1017 | /* | ||
| 1018 | * NTSTATUS | ||
| 1019 | * LdrLoadDll ( | ||
| 1020 | * IN PWSTR DllPath OPTIONAL, | ||
| 1021 | * IN PULONG DllCharacteristics OPTIONAL, | ||
| 1022 | * IN PUNICODE_STRING DllName, | ||
| 1023 | * OUT PVOID *DllHandle | ||
| 1024 | * ); | ||
| 1025 | * | ||
| 1026 | * Save LdrLoadDll parameters on stack (last to first). | ||
| 1027 | */ | ||
| 1028 | |||
| 1029 | ASM_PUSH(InstructionAddress, ADDRESS_DLL_HANDLE); /* DllHandle */ | ||
| 1030 | ASM_PUSH(InstructionAddress, ADDRESS_DLL_NAME); /* DllName */ | ||
| 1031 | ASM_PUSH(InstructionAddress, 0); /* DllCharacteristics */ | ||
| 1032 | // ASM_PUSH(InstructionAddress, NULL); /* DllPath */ | ||
| 1033 | ASM_PUSH(InstructionAddress, DllPathAddress); /* DllPath */ | ||
| 1034 | |||
| 1035 | ASM_CALL(InstructionAddress, FindFunctionBase(NTDLL_Base, "LdrLoadDll")); | ||
| 1036 | |||
| 1037 | |||
| 1038 | //XXX now clear out all the data up to this instruction | ||
| 1039 | //be careful first 4 bytes are used by the next instruction | ||
| 1040 | /* | ||
| 1041 | mov ecx, 16 | ||
| 1042 | lea edi, Base + 4 | ||
| 1043 | rep stosw | ||
| 1044 | */ | ||
| 1045 | |||
| 1046 | ASM_JMP(InstructionAddress, Base); | ||
| 1047 | } | ||
| 1048 | KeDetachProcess(); | ||
| 1049 | |||
| 1050 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d %s: Replacing original thread entry point %x with %x\n", CURRENT_PROCESS_PID, FunctionName, ThreadContext->Eax, CodeAddress)); | ||
| 1051 | |||
| 1052 | /* hijack the thread instruction pointer (saved in EAX) */ | ||
| 1053 | // LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("eip %x eax %x\n", ThreadContext->Eip, ThreadContext->Eax)); | ||
| 1054 | |||
| 1055 | ThreadContext->Eax = (ULONG) CodeAddress; | ||
| 1056 | |||
| 1057 | ThreadContext->Eip = ThreadContext->Eax; | ||
| 1058 | |||
| 1059 | ObDereferenceObject(proc); | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | except(EXCEPTION_EXECUTE_HANDLER) | ||
| 1063 | { | ||
| 1064 | NTSTATUS status = GetExceptionCode(); | ||
| 1065 | |||
| 1066 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%s: caught an exception. status = 0x%x\n", FunctionName, status)); | ||
| 1067 | |||
| 1068 | KeDetachProcess(); | ||
| 1069 | |||
| 1070 | ObDereferenceObject(proc); | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | |||
| 1075 | done: | ||
| 1076 | |||
| 1077 | /* | ||
| 1078 | * finally adjust the stack. | ||
| 1079 | * could not have done it before since we were holding a spinlock and weren't allowed to access pageable memory | ||
| 1080 | */ | ||
| 1081 | |||
| 1082 | if (StackOffset) | ||
| 1083 | |||
| 1084 | ThreadContext->Esp -= StackOffset; | ||
| 1085 | |||
| 1086 | |||
| 1087 | ASSERT(OriginalNtCreateThread); | ||
| 1088 | |||
| 1089 | rc = OriginalNtCreateThread(ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, | ||
| 1090 | ClientId, ThreadContext, UserStack, CreateSuspended); | ||
| 1091 | |||
| 1092 | |||
| 1093 | HOOK_ROUTINE_EXIT(rc); | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | |||
| 1097 | |||
| 1098 | /* | ||
| 1099 | * HookedNtOpenThread() | ||
| 1100 | * | ||
| 1101 | * Description: | ||
| 1102 | * This function mediates the NtOpenThread() system service in order to XXX | ||
| 1103 | * . | ||
| 1104 | * | ||
| 1105 | * NOTE: ZwOpenThread opens a thread object. [NAR] | ||
| 1106 | * | ||
| 1107 | * Parameters: | ||
| 1108 | * Those of NtOpenThread(). | ||
| 1109 | * | ||
| 1110 | * Returns: | ||
| 1111 | * NTSTATUS returned by NtOpenThread(). | ||
| 1112 | */ | ||
| 1113 | |||
| 1114 | NTSTATUS | ||
| 1115 | NTAPI | ||
| 1116 | HookedNtOpenThread | ||
| 1117 | ( | ||
| 1118 | OUT PHANDLE ThreadHandle, | ||
| 1119 | IN ACCESS_MASK DesiredAccess, | ||
| 1120 | IN POBJECT_ATTRIBUTES ObjectAttributes, | ||
| 1121 | IN PCLIENT_ID ClientId | ||
| 1122 | ) | ||
| 1123 | { | ||
| 1124 | CHAR THREADNAME[MAX_PATH]; | ||
| 1125 | |||
| 1126 | HOOK_ROUTINE_ENTER(); | ||
| 1127 | |||
| 1128 | |||
| 1129 | if (LearningMode == FALSE && GetPathFromOA(ObjectAttributes, THREADNAME, MAX_PATH, RESOLVE_LINKS)) | ||
| 1130 | { | ||
| 1131 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d HookedNtOpenThread: %s\n", (ULONG) PsGetCurrentProcessId(), THREADNAME)); | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | |||
| 1135 | if (! IS_BIT_SET(DesiredAccess, THREAD_QUERY_INFORMATION)) | ||
| 1136 | { | ||
| 1137 | // ClientId->UniqueProcess = 0 if process opens a thread in the same process (wmplayer.exe) | ||
| 1138 | if (ClientId) | ||
| 1139 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d HookedNtOpenThread(%d %d): %x\n", (ULONG) PsGetCurrentProcessId(), ClientId->UniqueProcess, ClientId->UniqueThread, DesiredAccess)); | ||
| 1140 | else | ||
| 1141 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d HookedNtOpenThread(): %x\n", (ULONG) PsGetCurrentProcessId(), DesiredAccess)); | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | ASSERT(OriginalNtOpenThread); | ||
| 1145 | |||
| 1146 | rc = OriginalNtOpenThread(ThreadHandle, DesiredAccess, ObjectAttributes, ClientId); | ||
| 1147 | |||
| 1148 | |||
| 1149 | HOOK_ROUTINE_EXIT(rc); | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | |||
| 1153 | |||
| 1154 | /* | ||
| 1155 | * ProcessPostBootup() | ||
| 1156 | * | ||
| 1157 | * Description: | ||
| 1158 | * Find out where userland Ozone files are installed once the bootup process is complete. | ||
| 1159 | * We are unable to read some parts of the registry before the bootup is complete since | ||
| 1160 | * they have not been initialized yet. | ||
| 1161 | * | ||
| 1162 | * ProcessPostBootup() might run even before bootup is complete in order to setup up the | ||
| 1163 | * default values. When ProcessPostBootup() runs again after bootup, the default values | ||
| 1164 | * will be overwritten with the correct ones. | ||
| 1165 | * | ||
| 1166 | * Parameters: | ||
| 1167 | * None. | ||
| 1168 | * | ||
| 1169 | * Returns: | ||
| 1170 | * Nothing. | ||
| 1171 | */ | ||
| 1172 | |||
| 1173 | VOID | ||
| 1174 | ProcessPostBootup() | ||
| 1175 | { | ||
| 1176 | if (ReadStringRegistryValueW(L"\\Registry\\Machine\\Software\\Security Architects\\Ozone Agent", L"InstallPath", OzoneInstallPath, MAX_PATH) == FALSE) | ||
| 1177 | { | ||
| 1178 | LOG(LOG_SS_MISC, LOG_PRIORITY_DEBUG, ("ProcessPostBootup: Failed to open InstallPath registry key\n")); | ||
| 1179 | |||
| 1180 | // use the default path | ||
| 1181 | wcscpy(OzoneInstallPath, L"C:\\Program Files\\Security Architects\\Ozone Agent"); | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | OzoneInstallPathSize = (wcslen(OzoneInstallPath) + 1) * sizeof(WCHAR); | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | |||
| 1188 | |||
| 1189 | /* | ||
| 1190 | * InitProcessEntries() | ||
| 1191 | * | ||
| 1192 | * Description: | ||
| 1193 | * Initializes all the mediated process operation pointers. The "OriginalFunction" pointers | ||
| 1194 | * are initialized by InstallSyscallsHooks() that must be called prior to this function. | ||
| 1195 | * | ||
| 1196 | * NOTE: Called once during driver initialization (DriverEntry()). | ||
| 1197 | * | ||
| 1198 | * Parameters: | ||
| 1199 | * None. | ||
| 1200 | * | ||
| 1201 | * Returns: | ||
| 1202 | * TRUE to indicate success, FALSE if failed. | ||
| 1203 | */ | ||
| 1204 | /* | ||
| 1205 | VOID | ||
| 1206 | LoadImageNotifyProc | ||
| 1207 | ( | ||
| 1208 | IN PUNICODE_STRING FullImageName, | ||
| 1209 | IN HANDLE ProcessId, // where image is mapped | ||
| 1210 | IN PIMAGE_INFO ImageInfo | ||
| 1211 | ) | ||
| 1212 | { | ||
| 1213 | if (FullImageName && ImageInfo) | ||
| 1214 | { | ||
| 1215 | KdPrint(("LoadImageNotifyProc: %d %S %x %x\n", ProcessId, FullImageName->Buffer, ImageInfo->ImageBase, ImageInfo->ImageSize)); | ||
| 1216 | } | ||
| 1217 | else | ||
| 1218 | { | ||
| 1219 | KdPrint(("LoadImageNotifyProc: NULL Pointer %x %x\n", FullImageName, ImageInfo)); | ||
| 1220 | } | ||
| 1221 | } | ||
| 1222 | */ | ||
| 1223 | |||
| 1224 | BOOLEAN | ||
| 1225 | InitProcessEntries() | ||
| 1226 | { | ||
| 1227 | // PsSetLoadImageNotifyRoutine(LoadImageNotifyProc); | ||
| 1228 | |||
| 1229 | if ( (OriginalNtCreateProcess = (fpZwCreateProcess) ZwCalls[ZW_CREATE_PROCESS_INDEX].OriginalFunction) == NULL) | ||
| 1230 | { | ||
| 1231 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtCreateProcess is NULL\n")); | ||
| 1232 | return FALSE; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | if ( (OriginalNtCreateProcessEx = (fpZwCreateProcessEx) ZwCalls[ZW_CREATE_PROCESSEX_INDEX].OriginalFunction) == NULL) | ||
| 1236 | { | ||
| 1237 | /* does not exist on Win2K */ | ||
| 1238 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtCreateProcessEx is NULL\n")); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | if ( (OriginalNtOpenProcess = (fpZwOpenProcess) ZwCalls[ZW_OPEN_PROCESS_INDEX].OriginalFunction) == NULL) | ||
| 1242 | { | ||
| 1243 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtOpenProcess is NULL\n")); | ||
| 1244 | return FALSE; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | if ( (OriginalNtCreateThread = (fpZwCreateThread) ZwCalls[ZW_CREATE_THREAD_INDEX].OriginalFunction) == NULL) | ||
| 1248 | { | ||
| 1249 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtCreateThread is NULL\n")); | ||
| 1250 | return FALSE; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | if ( (OriginalNtOpenThread = (fpZwOpenThread) ZwCalls[ZW_OPEN_THREAD_INDEX].OriginalFunction) == NULL) | ||
| 1254 | { | ||
| 1255 | LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessEntries: OriginalNtOpenThread is NULL\n")); | ||
| 1256 | return FALSE; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | |||
| 1260 | /* | ||
| 1261 | * run ProcessPostBootup() even if we are still booting up, this way we will at least setup | ||
| 1262 | * the default values. When ProcessPostBootup() runs again after bootup, the default values | ||
| 1263 | * will be overwritten with the correct ones. | ||
| 1264 | */ | ||
| 1265 | |||
| 1266 | ProcessPostBootup(); | ||
| 1267 | |||
| 1268 | |||
| 1269 | return TRUE; | ||
| 1270 | } | ||
