Process Hacker
modprv.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * module provider
4  *
5  * Copyright (C) 2009-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phapp.h>
24 #include <verify.h>
25 #include <extmgri.h>
26 
27 typedef struct _PH_MODULE_QUERY_DATA
28 {
29  SLIST_ENTRY ListEntry;
30  PPH_MODULE_PROVIDER ModuleProvider;
31  PPH_MODULE_ITEM ModuleItem;
32 
33  VERIFY_RESULT VerifyResult;
34  PPH_STRING VerifySignerName;
36 
38  _In_ PVOID Object,
39  _In_ ULONG Flags
40  );
41 
43  _In_ PVOID Object,
44  _In_ ULONG Flags
45  );
46 
48  _In_ PVOID Entry1,
49  _In_ PVOID Entry2
50  );
51 
53  _In_ PVOID Entry
54  );
55 
58 
60  VOID
61  )
62 {
63  PhModuleProviderType = PhCreateObjectType(L"ModuleProvider", 0, PhpModuleProviderDeleteProcedure);
64  PhModuleItemType = PhCreateObjectType(L"ModuleItem", 0, PhpModuleItemDeleteProcedure);
65 
66  return TRUE;
67 }
68 
70  _In_ HANDLE ProcessId
71  )
72 {
73  NTSTATUS status;
74  PPH_MODULE_PROVIDER moduleProvider;
75 
76  moduleProvider = PhCreateObject(
78  PhModuleProviderType
79  );
80 
81  moduleProvider->ModuleHashtable = PhCreateHashtable(
82  sizeof(PPH_MODULE_ITEM),
85  20
86  );
87  PhInitializeFastLock(&moduleProvider->ModuleHashtableLock);
88 
89  PhInitializeCallback(&moduleProvider->ModuleAddedEvent);
90  PhInitializeCallback(&moduleProvider->ModuleModifiedEvent);
91  PhInitializeCallback(&moduleProvider->ModuleRemovedEvent);
92  PhInitializeCallback(&moduleProvider->UpdatedEvent);
93 
94  moduleProvider->ProcessId = ProcessId;
95  moduleProvider->ProcessHandle = NULL;
96  moduleProvider->PackageFullName = NULL;
97  moduleProvider->RunStatus = STATUS_SUCCESS;
98 
99  // It doesn't matter if we can't get a process handle.
100 
101  // Try to get a handle with query information + vm read access.
102  if (!NT_SUCCESS(status = PhOpenProcess(
103  &moduleProvider->ProcessHandle,
105  ProcessId
106  )))
107  {
109  {
110  // Try to get a handle with query limited information + vm read access.
111  status = PhOpenProcess(
112  &moduleProvider->ProcessHandle,
114  ProcessId
115  );
116  }
117 
118  moduleProvider->RunStatus = status;
119  }
120 
121  if (moduleProvider->ProcessHandle)
122  moduleProvider->PackageFullName = PhGetProcessPackageFullName(moduleProvider->ProcessHandle);
123 
124  RtlInitializeSListHead(&moduleProvider->QueryListHead);
125 
127 
128  return moduleProvider;
129 }
130 
132  _In_ PVOID Object,
133  _In_ ULONG Flags
134  )
135 {
136  PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object;
137 
139 
140  // Dereference all module items (we referenced them
141  // when we added them to the hashtable).
142  PhDereferenceAllModuleItems(moduleProvider);
143 
144  PhDereferenceObject(moduleProvider->ModuleHashtable);
145  PhDeleteFastLock(&moduleProvider->ModuleHashtableLock);
146  PhDeleteCallback(&moduleProvider->ModuleAddedEvent);
147  PhDeleteCallback(&moduleProvider->ModuleModifiedEvent);
148  PhDeleteCallback(&moduleProvider->ModuleRemovedEvent);
149  PhDeleteCallback(&moduleProvider->UpdatedEvent);
150 
151  // Destroy all queue items.
152  {
153  PSLIST_ENTRY entry;
155 
156  entry = RtlInterlockedFlushSList(&moduleProvider->QueryListHead);
157 
158  while (entry)
159  {
160  data = CONTAINING_RECORD(entry, PH_MODULE_QUERY_DATA, ListEntry);
161  entry = entry->Next;
162 
163  if (data->VerifySignerName) PhDereferenceObject(data->VerifySignerName);
164  PhDereferenceObject(data->ModuleItem);
165  PhFree(data);
166  }
167  }
168 
169  if (moduleProvider->PackageFullName) PhDereferenceObject(moduleProvider->PackageFullName);
170  if (moduleProvider->ProcessHandle) NtClose(moduleProvider->ProcessHandle);
171 }
172 
174  VOID
175  )
176 {
177  PPH_MODULE_ITEM moduleItem;
178 
179  moduleItem = PhCreateObject(
181  PhModuleItemType
182  );
183  memset(moduleItem, 0, sizeof(PH_MODULE_ITEM));
185 
186  return moduleItem;
187 }
188 
190  _In_ PVOID Object,
191  _In_ ULONG Flags
192  )
193 {
194  PPH_MODULE_ITEM moduleItem = (PPH_MODULE_ITEM)Object;
195 
197 
198  if (moduleItem->Name) PhDereferenceObject(moduleItem->Name);
199  if (moduleItem->FileName) PhDereferenceObject(moduleItem->FileName);
200  if (moduleItem->VerifySignerName) PhDereferenceObject(moduleItem->VerifySignerName);
202 }
203 
205  _In_ PVOID Entry1,
206  _In_ PVOID Entry2
207  )
208 {
209  return
210  (*(PPH_MODULE_ITEM *)Entry1)->BaseAddress ==
211  (*(PPH_MODULE_ITEM *)Entry2)->BaseAddress;
212 }
213 
215  _In_ PVOID Entry
216  )
217 {
218  PVOID baseAddress = (*(PPH_MODULE_ITEM *)Entry)->BaseAddress;
219 
220  return PhHashIntPtr((ULONG_PTR)baseAddress);
221 }
222 
224  _In_ PPH_MODULE_PROVIDER ModuleProvider,
225  _In_ PVOID BaseAddress
226  )
227 {
228  PH_MODULE_ITEM lookupModuleItem;
229  PPH_MODULE_ITEM lookupModuleItemPtr = &lookupModuleItem;
230  PPH_MODULE_ITEM *moduleItemPtr;
231  PPH_MODULE_ITEM moduleItem;
232 
233  lookupModuleItem.BaseAddress = BaseAddress;
234 
235  PhAcquireFastLockShared(&ModuleProvider->ModuleHashtableLock);
236 
237  moduleItemPtr = (PPH_MODULE_ITEM *)PhFindEntryHashtable(
238  ModuleProvider->ModuleHashtable,
239  &lookupModuleItemPtr
240  );
241 
242  if (moduleItemPtr)
243  {
244  moduleItem = *moduleItemPtr;
245  PhReferenceObject(moduleItem);
246  }
247  else
248  {
249  moduleItem = NULL;
250  }
251 
252  PhReleaseFastLockShared(&ModuleProvider->ModuleHashtableLock);
253 
254  return moduleItem;
255 }
256 
258  _In_ PPH_MODULE_PROVIDER ModuleProvider
259  )
260 {
261  ULONG enumerationKey = 0;
262  PPH_MODULE_ITEM *moduleItem;
263 
264  PhAcquireFastLockExclusive(&ModuleProvider->ModuleHashtableLock);
265 
266  while (PhEnumHashtable(ModuleProvider->ModuleHashtable, (PVOID *)&moduleItem, &enumerationKey))
267  {
268  PhDereferenceObject(*moduleItem);
269  }
270 
271  PhReleaseFastLockExclusive(&ModuleProvider->ModuleHashtableLock);
272 }
273 
275  _In_ PPH_MODULE_PROVIDER ModuleProvider,
276  _In_ PPH_MODULE_ITEM ModuleItem
277  )
278 {
279  PhRemoveEntryHashtable(ModuleProvider->ModuleHashtable, &ModuleItem);
280  PhDereferenceObject(ModuleItem);
281 }
282 
284  _In_ PVOID Parameter
285  )
286 {
288 
289  data->VerifyResult = PhVerifyFileCached(
290  data->ModuleItem->FileName,
291  PhGetString(data->ModuleProvider->PackageFullName),
292  &data->VerifySignerName,
293  FALSE
294  );
295 
296  RtlInterlockedPushEntrySList(&data->ModuleProvider->QueryListHead, &data->ListEntry);
297 
298  PhDereferenceObject(data->ModuleProvider);
299 
300  return STATUS_SUCCESS;
301 }
302 
304  _In_ PPH_MODULE_PROVIDER ModuleProvider,
305  _In_ PPH_MODULE_ITEM ModuleItem
306  )
307 {
309 
311  return;
312 
313  data = PhAllocate(sizeof(PH_MODULE_QUERY_DATA));
314  memset(data, 0, sizeof(PH_MODULE_QUERY_DATA));
315  data->ModuleProvider = ModuleProvider;
316  data->ModuleItem = ModuleItem;
317 
318  PhReferenceObject(ModuleProvider);
319  PhReferenceObject(ModuleItem);
321 }
322 
323 static BOOLEAN NTAPI EnumModulesCallback(
324  _In_ PPH_MODULE_INFO Module,
325  _In_opt_ PVOID Context
326  )
327 {
328  PPH_MODULE_INFO copy;
329 
330  copy = PhAllocateCopy(Module, sizeof(PH_MODULE_INFO));
331  PhReferenceObject(copy->Name);
333 
334  PhAddItemList((PPH_LIST)Context, copy);
335 
336  return TRUE;
337 }
338 
340  _In_ PVOID Object
341  )
342 {
343  PPH_MODULE_PROVIDER moduleProvider = (PPH_MODULE_PROVIDER)Object;
344  PPH_LIST modules;
345  ULONG i;
346 
347  // If we didn't get a handle when we created the provider,
348  // abort (unless this is the System process - in that case
349  // we don't need a handle).
350  if (!moduleProvider->ProcessHandle && moduleProvider->ProcessId != SYSTEM_PROCESS_ID)
351  goto UpdateExit;
352 
353  modules = PhCreateList(20);
354 
355  moduleProvider->RunStatus = PhEnumGenericModules(
356  moduleProvider->ProcessId,
357  moduleProvider->ProcessHandle,
359  EnumModulesCallback,
360  modules
361  );
362 
363  // Look for removed modules.
364  {
365  PPH_LIST modulesToRemove = NULL;
366  ULONG enumerationKey = 0;
367  PPH_MODULE_ITEM *moduleItem;
368 
369  while (PhEnumHashtable(moduleProvider->ModuleHashtable, (PVOID *)&moduleItem, &enumerationKey))
370  {
371  BOOLEAN found = FALSE;
372 
373  // Check if the module still exists.
374  for (i = 0; i < modules->Count; i++)
375  {
376  PPH_MODULE_INFO module = modules->Items[i];
377 
378  if ((*moduleItem)->BaseAddress == module->BaseAddress && PhEqualString((*moduleItem)->FileName, module->FileName, TRUE))
379  {
380  found = TRUE;
381  break;
382  }
383  }
384 
385  if (!found)
386  {
387  // Raise the module removed event.
388  PhInvokeCallback(&moduleProvider->ModuleRemovedEvent, *moduleItem);
389 
390  if (!modulesToRemove)
391  modulesToRemove = PhCreateList(2);
392 
393  PhAddItemList(modulesToRemove, *moduleItem);
394  }
395  }
396 
397  if (modulesToRemove)
398  {
400 
401  for (i = 0; i < modulesToRemove->Count; i++)
402  {
404  moduleProvider,
405  (PPH_MODULE_ITEM)modulesToRemove->Items[i]
406  );
407  }
408 
410  PhDereferenceObject(modulesToRemove);
411  }
412  }
413 
414  // Go through the queued thread query data.
415  {
416  PSLIST_ENTRY entry;
418 
419  entry = RtlInterlockedFlushSList(&moduleProvider->QueryListHead);
420 
421  while (entry)
422  {
423  data = CONTAINING_RECORD(entry, PH_MODULE_QUERY_DATA, ListEntry);
424  entry = entry->Next;
425 
426  data->ModuleItem->VerifyResult = data->VerifyResult;
427  data->ModuleItem->VerifySignerName = data->VerifySignerName;
428  data->ModuleItem->JustProcessed = TRUE;
429 
430  PhDereferenceObject(data->ModuleItem);
431  PhFree(data);
432  }
433  }
434 
435  // Look for new modules.
436  for (i = 0; i < modules->Count; i++)
437  {
438  PPH_MODULE_INFO module = modules->Items[i];
439  PPH_MODULE_ITEM moduleItem;
440 
441  moduleItem = PhReferenceModuleItem(moduleProvider, module->BaseAddress);
442 
443  if (!moduleItem)
444  {
445  moduleItem = PhCreateModuleItem();
446 
447  moduleItem->BaseAddress = module->BaseAddress;
448  PhPrintPointer(moduleItem->BaseAddressString, moduleItem->BaseAddress);
449  moduleItem->Size = module->Size;
450  moduleItem->Flags = module->Flags;
451  moduleItem->Type = module->Type;
452  moduleItem->LoadReason = module->LoadReason;
453  moduleItem->LoadCount = module->LoadCount;
454  moduleItem->LoadTime = module->LoadTime;
455 
456  moduleItem->Name = module->Name;
457  PhReferenceObject(moduleItem->Name);
458  moduleItem->FileName = module->FileName;
459  PhReferenceObject(moduleItem->FileName);
460 
462  &moduleItem->VersionInfo,
463  PhGetString(moduleItem->FileName)
464  );
465 
466  moduleItem->IsFirst = i == 0;
467 
468  // Fix up the load count. If this is not an ordinary DLL or kernel module, set the load count to 0.
469  if (moduleItem->Type != PH_MODULE_TYPE_MODULE &&
470  moduleItem->Type != PH_MODULE_TYPE_WOW64_MODULE &&
471  moduleItem->Type != PH_MODULE_TYPE_KERNEL_MODULE)
472  {
473  moduleItem->LoadCount = 0;
474  }
475 
476  if (moduleItem->Type == PH_MODULE_TYPE_MODULE ||
477  moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE ||
478  moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE)
479  {
480  PH_REMOTE_MAPPED_IMAGE remoteMappedImage;
481 
482  // Note:
483  // On Windows 7 the LDRP_IMAGE_NOT_AT_BASE flag doesn't appear to be used
484  // anymore. Instead we'll check ImageBase in the image headers. We read this in
485  // from the process' memory because:
486  //
487  // 1. It (should be) faster than opening the file and mapping it in, and
488  // 2. It contains the correct original image base relocated by ASLR, if present.
489 
490  moduleItem->Flags &= ~LDRP_IMAGE_NOT_AT_BASE;
491 
492  if (NT_SUCCESS(PhLoadRemoteMappedImage(moduleProvider->ProcessHandle, moduleItem->BaseAddress, &remoteMappedImage)))
493  {
494  moduleItem->ImageTimeDateStamp = remoteMappedImage.NtHeaders->FileHeader.TimeDateStamp;
495  moduleItem->ImageCharacteristics = remoteMappedImage.NtHeaders->FileHeader.Characteristics;
496 
497  if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
498  {
499  if ((ULONG_PTR)((PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader)->ImageBase != (ULONG_PTR)moduleItem->BaseAddress)
500  moduleItem->Flags |= LDRP_IMAGE_NOT_AT_BASE;
501 
502  moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER32)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics;
503  }
504  else if (remoteMappedImage.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
505  {
506  if ((ULONG_PTR)((PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader)->ImageBase != (ULONG_PTR)moduleItem->BaseAddress)
507  moduleItem->Flags |= LDRP_IMAGE_NOT_AT_BASE;
508 
509  moduleItem->ImageDllCharacteristics = ((PIMAGE_OPTIONAL_HEADER64)&remoteMappedImage.NtHeaders->OptionalHeader)->DllCharacteristics;
510  }
511 
512  PhUnloadRemoteMappedImage(&remoteMappedImage);
513  }
514  }
515 
516  if (moduleItem->Type == PH_MODULE_TYPE_MODULE || moduleItem->Type == PH_MODULE_TYPE_KERNEL_MODULE ||
517  moduleItem->Type == PH_MODULE_TYPE_WOW64_MODULE || moduleItem->Type == PH_MODULE_TYPE_MAPPED_IMAGE)
518  {
519  // See if the file has already been verified; if not, queue for verification.
520 
521  moduleItem->VerifyResult = PhVerifyFileCached(moduleItem->FileName, NULL, &moduleItem->VerifySignerName, TRUE);
522 
523  if (moduleItem->VerifyResult == VrUnknown)
524  PhpQueueModuleQuery(moduleProvider, moduleItem);
525  }
526 
527  // Add the module item to the hashtable.
529  PhAddEntryHashtable(moduleProvider->ModuleHashtable, &moduleItem);
531 
532  // Raise the module added event.
533  PhInvokeCallback(&moduleProvider->ModuleAddedEvent, moduleItem);
534  }
535  else
536  {
537  BOOLEAN modified = FALSE;
538 
539  if (moduleItem->JustProcessed)
540  modified = TRUE;
541 
542  moduleItem->JustProcessed = FALSE;
543 
544  if (modified)
545  PhInvokeCallback(&moduleProvider->ModuleModifiedEvent, moduleItem);
546 
547  PhDereferenceObject(moduleItem);
548  }
549  }
550 
551  // Free the modules list.
552 
553  for (i = 0; i < modules->Count; i++)
554  {
555  PPH_MODULE_INFO module = modules->Items[i];
556 
557  PhDereferenceObject(module->Name);
558  PhDereferenceObject(module->FileName);
559  PhFree(module);
560  }
561 
562  PhDereferenceObject(modules);
563 
564 UpdateExit:
565  PhInvokeCallback(&moduleProvider->UpdatedEvent, NULL);
566 }