summaryrefslogtreecommitdiff
path: root/procname.c
diff options
context:
space:
mode:
Diffstat (limited to 'procname.c')
-rw-r--r--procname.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/procname.c b/procname.c
new file mode 100644
index 0000000..05ba9be
--- /dev/null
+++ b/procname.c
@@ -0,0 +1,677 @@
1/*
2 * Copyright (c) 2004 Security Architects Corporation. All rights reserved.
3 *
4 * Module Name:
5 *
6 * procname.c
7 *
8 * Abstract:
9 *
10 * This module defines various types used by process id to process name conversion routines.
11 *
12 * All processes are tracked in a global hash table.
13 * At startup ZwQuerySystemInformation(SystemProcessesAndThreadsInformation..) is used to
14 * enumerate all the existing processes. After that NtCreateProcess() is hooked and used
15 * to keep track of newly created processes while PsSetCreateProcessNotifyRoutine()
16 * callbacks are used to keep track of terminating processes.
17 *
18 * See http://www.microsoft.com/msj/0199/nerd/nerd0199.aspx for more info.
19 *
20 * Author:
21 *
22 * Eugene Tsyrklevich 23-Feb-2004
23 *
24 * Revision History:
25 *
26 * 07-Apr-2004 ET - Copied from process.h
27 */
28
29
30#include <NTDDK.h>
31#include "procname.h"
32#include "hookproc.h"
33#include "process.h"
34#include "policy.h"
35#include "sysinfo.h"
36#include "learn.h"
37#include "misc.h"
38#include "log.h"
39
40
41void FindProcessNameOffset();
42
43
44#ifdef ALLOC_PRAGMA
45#pragma alloc_text (INIT, InitProcessNameEntries)
46#pragma alloc_text (INIT, FindProcessNameOffset)
47#pragma alloc_text (INIT, EnumerateExistingProcesses)
48#pragma alloc_text (PAGE, RemoveProcessNameEntries)
49#endif
50
51
52ULONG SystemProcessId;
53
54/* 67 * 144 = 10 kilobytes */
55IMAGE_PID_ENTRY gImagePidHtbl[IMAGE_PID_HASHTABLE_SIZE];
56
57//XXX investigate KeAcquireInStackQueuedSpinLock
58KSPIN_LOCK gImagePidHtblSpinLock;
59
60
61/*
62 * FindImagePidEntry()
63 *
64 * Description:
65 * Looks for a process entry in the global process hash table with a specified process id.
66 *
67 * Parameters:
68 * ProcessId - process id of the process to look for.
69 *
70 * Returns:
71 * Pointer to a process entry if one is found, NULL otherwise.
72 */
73
74PIMAGE_PID_ENTRY
75FindImagePidEntry(ULONG ProcessId, ULONG ParentId)
76{
77 PIMAGE_PID_ENTRY p;
78 KIRQL irql;
79
80
81//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindImagePidEntry(%d %d) 1\n", ProcessId, ParentId));
82 KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql);
83//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindImagePidEntry 2\n"));
84
85
86 p = gImagePidHtbl[(ULONG) ProcessId % IMAGE_PID_HASHTABLE_SIZE].next;
87
88 while (p)
89 {
90 if (p->ProcessId == ProcessId)
91 {
92 if (ParentId == 0 || p->ParentId == ParentId)
93 {
94 if (ParentId != 0)
95 {
96 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d FindImagePidEntry(%d, %d) found an entry (%d)\n", CURRENT_PROCESS_PID, ProcessId, ParentId, p->ParentId));
97 }
98
99 KeReleaseSpinLock(&gImagePidHtblSpinLock, irql);
100 return p;
101 }
102
103 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d FindImagePidEntry: ProcessId %d clash. Parent id %d vs %d\n", CURRENT_PROCESS_PID, ProcessId, ParentId, p->ParentId));
104 }
105
106 p = p->next;
107 }
108
109
110 KeReleaseSpinLock(&gImagePidHtblSpinLock, irql);
111
112
113 return NULL;
114}
115
116
117
118/*
119 * ProcessInsertImagePidEntry()
120 *
121 * Description:
122 * Insert the new process entry into the global process hash table.
123 *
124 * Parameters:
125 * ProcessId - process id of the new process.
126 * NewProcessEntry - the entry to insert into the hash table.
127 *
128 * Returns:
129 * TRUE to indicate success, FALSE if failed.
130 */
131
132BOOLEAN
133ProcessInsertImagePidEntry(ULONG ProcessId, PIMAGE_PID_ENTRY NewProcessEntry)
134{
135 PIMAGE_PID_ENTRY p, prev;
136 KIRQL irql;
137
138
139//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("ProcessInsertImagePidEntry(%d %x) 1\n", ProcessId, NewProcessEntry));
140 KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql);
141//LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("ProcessInsertImagePidEntry 2\n"));
142
143 prev = &gImagePidHtbl[(ULONG) ProcessId % IMAGE_PID_HASHTABLE_SIZE];
144 p = prev->next;
145
146 while (p)
147 {
148 // if an entry with our ProcessId already exists, bail out
149
150 if (p->ProcessId == (ULONG) ProcessId)
151 {
152 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d ProcessInsertImagePidEntry: ProcessId (%d) clash. New image name is '%S' (%d). Old image name is '%S' (%d)\n", CURRENT_PROCESS_PID, ProcessId, NewProcessEntry->ImageName, NewProcessEntry->ParentId, p->ImageName, p->ParentId));
153
154 if (ProcessId != 0)
155 {
156 KeReleaseSpinLock(&gImagePidHtblSpinLock, irql);
157 return FALSE;
158 }
159 }
160
161 prev = p;
162 p = p->next;
163 }
164
165 prev->next = NewProcessEntry;
166
167
168 KeReleaseSpinLock(&gImagePidHtblSpinLock, irql);
169
170
171 return TRUE;
172}
173
174
175
176/*
177 * CreateNewProcessEntry()
178 *
179 * Description:
180 * Allocates and initializes a new process entry.
181 *
182 * Parameters:
183 * ProcessId - process id of the new process.
184 * ProcessName - process image name.
185 *
186 * Returns:
187 * Pointer to a heap allocated IMAGE_PID_ENTRY structure. NULL if failed.
188 */
189
190PIMAGE_PID_ENTRY
191CreateNewProcessEntry(ULONG ProcessId, ULONG ParentId, PUNICODE_STRING ProcessName, BOOLEAN NewProcess)
192{
193 PIMAGE_PID_ENTRY ProcessEntry;
194
195
196 ASSERT(ProcessName);
197
198
199 ProcessEntry = ExAllocatePoolWithTag(NonPagedPool, sizeof(IMAGE_PID_ENTRY) + ProcessName->Length, _POOL_TAG);
200 if (ProcessEntry == NULL)
201 {
202 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("CreateNewProcessEntry: Out of memory. Forgeting about process %d (%S)", ProcessId, ProcessName->Buffer));
203 return NULL;
204 }
205
206 RtlZeroMemory(ProcessEntry, sizeof(IMAGE_PID_ENTRY));
207
208 ProcessEntry->ProcessId = ProcessId;
209 ProcessEntry->ParentId = ParentId;
210 ProcessEntry->FirstThread = NewProcess;
211
212 KeInitializeEvent(&ProcessEntry->UserlandRequestDoneEvent, SynchronizationEvent, FALSE);
213
214 wcscpy(ProcessEntry->ImageName, ProcessName->Buffer);
215 ProcessEntry->ImageName[ ProcessName->Length/sizeof(WCHAR) ] = L'\0';
216
217 KeInitializeSpinLock(&ProcessEntry->SecPolicy.SpinLock);
218
219
220 return ProcessEntry;
221}
222
223
224
225/*
226 * CreateAndLoadNewProcessEntry()
227 *
228 * Description:
229 * Creates a new process entry and inserts it into the global process hash table.
230 *
231 * Parameters:
232 * ProcessId - process id of the new process.
233 * ProcessName - process image name.
234 *
235 * Returns:
236 * Pointer to a heap allocated IMAGE_PID_ENTRY structure. NULL if failed.
237 */
238
239PIMAGE_PID_ENTRY
240CreateAndLoadNewProcessEntry(ULONG ProcessId, PUNICODE_STRING ProcessName, BOOLEAN NewProcess)
241{
242 PIMAGE_PID_ENTRY ProcessEntry;
243
244
245 ASSERT(ProcessName);
246
247
248 ProcessEntry = CreateNewProcessEntry(ProcessId, 0, ProcessName, NewProcess);
249 if (ProcessEntry == NULL)
250 return NULL;
251
252
253 if (LearningMode == FALSE && FindAndLoadSecurityPolicy(&ProcessEntry->SecPolicy, ProcessName->Buffer, NULL) == FALSE)
254 {
255 LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("CreateAndLoadNewProcessEntry: Failed to load security policy for %S\n", ProcessName->Buffer));
256 }
257
258
259 if (ProcessInsertImagePidEntry(ProcessId, ProcessEntry) == FALSE)
260 {
261 ExFreePoolWithTag(ProcessEntry, _POOL_TAG);
262 return NULL;
263 }
264
265
266 return ProcessEntry;
267}
268
269
270
271/*
272 * CreateProcessNotifyProc()
273 *
274 * Description:
275 * PsSetCreateProcessNotifyRoutine() callback. Used to remove process entries from the global
276 * hashtable of currently running processes. On Windows 2000, this routine also replaces PIDs
277 * of 0 with the real PIDs. This is necessary since win2k does not assign PIDs at a point
278 * where process.c!PostProcessNtCreateProcess() is called.
279 *
280 * NOTE: PsSetCreateProcessNotifyRoutine() adds a driver-supplied callback routine to,
281 * or removes it from, a list of routines to be called whenever a process is created or deleted.
282 *
283 * Parameters:
284 * ParentId - the parent process ID.
285 * ProcessId - process ID that was created / deleted.
286 * Create - indicates whether the process was created (TRUE) or deleted (FALSE).
287 *
288 * Returns:
289 * Nothing.
290 */
291
292VOID
293CreateProcessNotifyProc
294(
295 IN HANDLE ParentId,
296 IN HANDLE ProcessId,
297 IN BOOLEAN Create
298)
299{
300 PIMAGE_PID_ENTRY p, prev, tmp;
301 ULONG RequiredPid;
302 BOOLEAN FoundNewProcess = FALSE;
303 KIRQL irql;
304
305
306 LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("%d CreateProccessNotifyProc(%d, %d, %d)\n", CURRENT_PROCESS_PID, ParentId, ProcessId, Create));
307
308
309 /* if the entry is being removed look for ProcessId, otherwise look for 0 */
310 RequiredPid = Create == FALSE ? (ULONG) ProcessId : 0;
311
312
313 /*
314 * find an entry with pid=ProcessId
315 */
316
317 KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql);
318
319 prev = &gImagePidHtbl[(ULONG) RequiredPid % IMAGE_PID_HASHTABLE_SIZE];
320 p = prev->next;
321
322 while (p)
323 {
324 if (p->ProcessId != (ULONG) RequiredPid)
325 {
326 prev = p;
327 p = p->next;
328 continue;
329 }
330
331 if (p->ParentId != 0 && p->ParentId != (ULONG) ParentId)
332 {
333 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d CreateProccessNotifyProc(): ProcessId %d collision. %d vs %d\n", CURRENT_PROCESS_PID, p->ProcessId, p->ParentId, ParentId));
334
335 prev = p;
336 p = p->next;
337 continue;
338 }
339
340
341 /* found the necessary entry */
342 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d CreateProccessNotifyProc(): Found (%s) %d %x %S\n", CURRENT_PROCESS_PID, Create == FALSE ? "delete" : "fix", p->ProcessId, p->next, p->ImageName));
343
344 tmp = p;
345
346 /* unlink the found entry */
347 prev->next = p->next;
348
349 if (Create == FALSE)
350 {
351 /* if the process is being deleted then free the entry */
352 PolicyDelete(&tmp->SecPolicy);
353 ExFreePoolWithTag(tmp, _POOL_TAG);
354 }
355 else
356 {
357 /*
358 * if the process is being executed and we found an entry with PID of 0 then
359 * replace 0 with the real pid
360 */
361 tmp->ProcessId = (ULONG) ProcessId;
362 tmp->next = NULL;
363 FoundNewProcess = TRUE;
364 }
365
366 break;
367 }
368
369 KeReleaseSpinLock(&gImagePidHtblSpinLock, irql);
370
371
372 /*
373 * If necessary, reinsert the new entry that used to have a PID of 0.
374 * We need to reinsert since the hashtable is indexed by PIDs.
375 */
376 if (FoundNewProcess == TRUE)
377 {
378//XXX at this point no locks are held. if tmp->ProcessId process quits before ProcessInsertImagePidEntry()
379 // executes, we will get a zombie entry (will never be removed)
380 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d Replacing 0 with %d for %S\n", CURRENT_PROCESS_PID, tmp->ProcessId, tmp->ImageName));
381 ProcessInsertImagePidEntry(tmp->ProcessId, tmp);
382 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("%d Done replacing %d\n", CURRENT_PROCESS_PID, tmp->ProcessId, tmp->ImageName));
383 }
384}
385
386
387
388USHORT ProcessNameOffset = 0, ThreadServiceTableOffset = 0;
389
390
391/*
392 * FindProcessNameOffset()
393 *
394 * Description:
395 * Identifies process name offset in the EPROCESS structure.
396 *
397 * The name offset is identified by searching for "System" string in the System EPROCESS structure
398 * (this function is called when the driver is loaded and thus runs in the "System" context).
399 *
400 * ThreadServiceTableOffset is the offset of the pointer to a service descriptor table (syscall table)
401 * in the Thread Environment Block (TEB). Used to determine whether a thread is GUI based or not.
402 *
403 * Parameters:
404 * None.
405 *
406 * Returns:
407 * Nothing.
408 */
409
410void
411FindProcessNameOffset()
412{
413 PEPROCESS SystemProcess = PsGetCurrentProcess(); // current "System" process
414 PETHREAD SystemThread = PsGetCurrentThread(); // current "System" thread
415 USHORT i;
416
417
418 /* Search for "System" process name in the current system process Process Environment Block */
419
420 for (i = 0; i < 1024; i++) // 372 on Windows XP SP1
421 {
422 if (_strnicmp((PCHAR) SystemProcess + i, "System", 6) == 0)
423 {
424 ProcessNameOffset = i;
425 break;
426 }
427 }
428
429
430 /* Search for KeServiceDescriptorTable address in the current system thread's Thread Environment Block */
431
432 for (i = 0; i < 500; i++) // 292 on Win2k3
433 {
434 if (* (PULONG) ((PCHAR) SystemThread + i) == (ULONG) &KeServiceDescriptorTable[0])
435 {
436 ThreadServiceTableOffset = i;
437 break;
438 }
439 }
440}
441
442
443
444/*
445 * GetCurrentProcessName()
446 *
447 * Description:
448 * Returns the current process pathname.
449 *
450 * Parameters:
451 * None.
452 *
453 * Returns:
454 * Pointer to the current process pathname ("Unknown" if not found).
455 */
456
457PWCHAR
458GetCurrentProcessName()
459{
460 PIMAGE_PID_ENTRY p;
461 PWCHAR name = NULL;
462
463
464 p = FindImagePidEntry(CURRENT_PROCESS_PID, 0);
465
466 if (p != NULL)
467 /*
468 * NOTE: we are returning a pointer to a member of a structure which is not locked at this point!
469 * Should be ok though since this function is only called in the context of a current process which
470 * cannot disappear from underneath us.
471 */
472 name = p->ImageName;
473 else
474 name = L"(Unknown)";
475
476
477 return name;
478}
479
480
481
482/*
483 * FindExistingProcesses()
484 *
485 * Description:
486 * Enumerates all the existing processes using ZwQuerySystemInformation(SystemProcessesAndThreadsInformation)
487 * and creates IMAGE_PID_ENTRY for all of them. Called once at startup.
488 *
489 * Parameters:
490 * None.
491 *
492 * Returns:
493 * Nothing.
494 */
495
496VOID
497EnumerateExistingProcesses()
498{
499 ULONG size = 65535, i;
500 PUCHAR SystemInfo;
501 PSYSTEM_PROCESSES pSystemProcess;
502 NTSTATUS status;
503
504
505 /*
506 * The format of the data returned to the SystemInformation buffer is a sequence of
507 * SYSTEM_PROCESSES structures, chained together via the NextEntryDelta member.
508 * The Threads member of each SYSTEM_PROCESSES structure is an array of ThreadCount
509 * SYSTEM_THREADS structures.The end of the process chain is marked by a NextEntryDelta
510 * value of zero.
511 */
512
513 /* first, find out the total amount of SystemProcessesAndThreadsInformation to be returned */
514/*
515 status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, &i, 0, &size);
516 if (size == 0 || size > 1024*1024)
517 {
518 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindExistingProcesses: ZwQuerySystemInformation failed. status=%x size=%d\n", status, size));
519 return;
520 }
521*/
522
523 /* second, allocate the required amount of memory */
524
525 SystemInfo = ExAllocatePoolWithTag(PagedPool, size, _POOL_TAG);
526 if (SystemInfo == NULL)
527 {
528 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("FindExistingProcesses: out of memory (requested %d bytes)\n", size));
529 return;
530 }
531
532 /* third, request the SystemProcessesAndThreadsInformation */
533
534 ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, SystemInfo, size, &i);
535
536
537 pSystemProcess = (PSYSTEM_PROCESSES) SystemInfo;
538
539
540 i = 0;
541
542 while (pSystemProcess->NextEntryDelta != 0)
543 {
544 if (pSystemProcess->ProcessName.Length != 0)
545 {
546 /* create a new process entry and load the associated security policy (if any) */
547
548 CreateAndLoadNewProcessEntry(pSystemProcess->ProcessId, &pSystemProcess->ProcessName, FALSE);
549
550 ++i;
551 }
552
553 pSystemProcess = (PSYSTEM_PROCESSES) ( ((PUCHAR) pSystemProcess) + pSystemProcess->NextEntryDelta);
554 }
555
556 ExFreePoolWithTag(SystemInfo, _POOL_TAG);
557
558
559 /* if no processes exist, the computer must be booting up */
560 if (i == 0)
561 {
562 UNICODE_STRING System;
563
564
565 BootingUp = TRUE;
566
567 /*
568 * when booting up no processes are listed as existing even though "System" and "Idle"
569 * have already been initialized. Initialize "System" process entry since it will
570 * never be created otherwise.
571 */
572
573 RtlInitUnicodeString(&System, L"System");
574
575 CreateAndLoadNewProcessEntry(SystemProcessId, &System, FALSE);
576 }
577}
578
579
580
581/*
582 * InitProcessNameEntries()
583 *
584 * Description:
585 * Initializes process id to process name related data.
586 *
587 * NOTE: Called once during driver initialization (DriverEntry()).
588 *
589 * Parameters:
590 * None.
591 *
592 * Returns:
593 * TRUE to indicate success, FALSE if failed.
594 */
595
596BOOLEAN
597InitProcessNameEntries()
598{
599 memset(gImagePidHtbl, 0, sizeof(gImagePidHtbl));
600
601 KeInitializeSpinLock(&gImagePidHtblSpinLock);
602
603 SystemProcessId = (ULONG) PsGetCurrentProcessId();
604
605 FindProcessNameOffset();
606
607
608 /* XXX investigate
609A driver's process-notify routine is also called with Create set to FALSE, usually when the last thread within a process has terminated and the process address space is about to be deleted. In very rare circumstances, for processes in which no thread was ever created, a driver's process-notify routine is called only at the destruction of the process. */
610 if (! NT_SUCCESS( PsSetCreateProcessNotifyRoutine(CreateProcessNotifyProc, FALSE) ))
611 {
612 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("InitProcessNameEntries: PsSetCreateProcessNotifyRoutine() failed\n"));
613 return FALSE;
614 }
615
616
617 return TRUE;
618}
619
620
621
622/*
623 * RemoveProcessNameEntries()
624 *
625 * Description:
626 * Removes the global hash table process entries and PsSetCreateProcessNotifyRoutine() callback.
627 *
628 * NOTE: Called once during driver unload (DriverUnload()).
629 *
630 * Parameters:
631 * None.
632 *
633 * Returns:
634 * Nothing.
635 */
636
637VOID
638RemoveProcessNameEntries()
639{
640 int i;
641 PIMAGE_PID_ENTRY p, tmp;
642 KIRQL irql;
643
644
645#if HOOK_PROCESS
646
647 if (! NT_SUCCESS( PsSetCreateProcessNotifyRoutine(CreateProcessNotifyProc, TRUE) ))
648 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("RemoveProcessEntries: PsSetCreateProcessNotifyRoutine remove failed\n"));
649
650#endif
651
652
653 KeAcquireSpinLock(&gImagePidHtblSpinLock, &irql);
654
655 for (i = 0; i < IMAGE_PID_HASHTABLE_SIZE; i++)
656 {
657 p = gImagePidHtbl[i].next;
658
659 while (p)
660 {
661 LOG(LOG_SS_PROCESS, LOG_PRIORITY_VERBOSE, ("RemoveProcessEntries: Removing %d %x %S\n", p->ProcessId, p->next, p->ImageName));
662
663 tmp = p;
664 p = p->next;
665
666 if (tmp->WaitingForUserRequestId != 0)
667 {
668 LOG(LOG_SS_PROCESS, LOG_PRIORITY_DEBUG, ("RemoveProcessEntries: Process (pid=%d) is still waiting for a user request id %d!\n", tmp->ProcessId, tmp->WaitingForUserRequestId));
669 }
670
671 PolicyDelete(&tmp->SecPolicy);
672 ExFreePoolWithTag(tmp, _POOL_TAG);
673 }
674 }
675
676 KeReleaseSpinLock(&gImagePidHtblSpinLock, irql);
677}