summaryrefslogtreecommitdiff
path: root/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'process.c')
-rw-r--r--process.c1270
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
42fpZwCreateProcess OriginalNtCreateProcess = NULL;
43fpZwCreateProcessEx OriginalNtCreateProcessEx = NULL;
44fpZwOpenProcess OriginalNtOpenProcess = NULL;
45
46fpZwCreateThread OriginalNtCreateThread = NULL;
47fpZwOpenThread OriginalNtOpenThread = NULL;
48
49
50BOOLEAN AllowProcessesWithPoliciesOnly = FALSE;
51
52WCHAR OzoneInstallPath[MAX_PATH];
53USHORT OzoneInstallPathSize = 0;
54
55
56/* XXX this will not work on 64-bit architectures, due to assumption of size of ulong, pointers, etc */
57typedef struct _CONTROL_AREA { // must be quadword sized.
58 ULONG data[9];
59 PFILE_OBJECT FilePointer;
60} CONTROL_AREA, *PCONTROL_AREA;
61
62typedef struct _SEGMENT {
63 struct _CONTROL_AREA *ControlArea;
64} SEGMENT, *PSEGMENT;
65
66typedef 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
86ULONG
87rand(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
134ULONG
135PostProcessNtCreateProcess(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
504NTSTATUS
505NTAPI
506HookedNtCreateProcess
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
560NTSTATUS
561NTAPI
562HookedNtCreateProcessEx
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
617NTSTATUS
618NTAPI
619HookedNtOpenProcess
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/*
635PROCESS_TERMINATE Terminate process
636PROCESS_CREATE_THREAD Create threads in process
637PROCESS_SET_SESSIONID Set process session id
638PROCESS_VM_OPERATION Protect and lock memory of process
639PROCESS_VM_READ Read memory of process
640PROCESS_VM_WRITE Write memory of process
641PROCESS_DUP_HANDLE Duplicate handles of process
642PROCESS_CREATE_PROCESS Bequeath address space and handles to new process
643PROCESS_SET_QUOTA Set process quotas
644PROCESS_SET_INFORMATION Set information about process
645PROCESS_QUERY_INFORMATION Query information about process
646PROCESS_SET_PORT Set process exception or debug port
647PROCESS_ALL_ACCESS All of the preceding
648
649find 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
758done:
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
786NTSTATUS
787NTAPI
788HookedNtCreateThread
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
1075done:
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
1114NTSTATUS
1115NTAPI
1116HookedNtOpenThread
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
1173VOID
1174ProcessPostBootup()
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/*
1205VOID
1206LoadImageNotifyProc
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
1224BOOLEAN
1225InitProcessEntries()
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}