Process Hacker
unldll.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extended Tools -
3  * unloaded DLLs display
4  *
5  * Copyright (C) 2010-2011 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 "exttools.h"
24 #include "resource.h"
25 
26 typedef struct _UNLOADED_DLLS_CONTEXT
27 {
28  PPH_PROCESS_ITEM ProcessItem;
29  HWND ListViewHandle;
30  PVOID CapturedEventTrace;
32 
33 INT_PTR CALLBACK EtpUnloadedDllsDlgProc(
34  _In_ HWND hwndDlg,
35  _In_ UINT uMsg,
36  _In_ WPARAM wParam,
37  _In_ LPARAM lParam
38  );
39 
41  _In_ HWND ParentWindowHandle,
42  _In_ PPH_PROCESS_ITEM ProcessItem
43  )
44 {
45  UNLOADED_DLLS_CONTEXT context;
46 
47  context.ProcessItem = ProcessItem;
48  context.CapturedEventTrace = NULL;
49 
50  DialogBoxParam(
52  MAKEINTRESOURCE(IDD_UNLOADEDDLLS),
53  ParentWindowHandle,
55  (LPARAM)&context
56  );
57 
58  if (context.CapturedEventTrace)
59  PhFree(context.CapturedEventTrace);
60 }
61 
63  _In_ HWND hwndDlg,
64  _In_ PUNLOADED_DLLS_CONTEXT Context
65  )
66 {
67  NTSTATUS status;
68  PULONG elementSize;
69  PULONG elementCount;
70  PVOID eventTrace;
71  HANDLE processHandle = NULL;
72  ULONG eventTraceSize;
73  ULONG capturedElementSize;
74  ULONG capturedElementCount;
75  PVOID capturedEventTracePointer;
76  PVOID capturedEventTrace = NULL;
77  ULONG i;
78  PVOID currentEvent;
79  HWND lvHandle;
80 
81  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
82  ListView_DeleteAllItems(lvHandle);
83 
84  RtlGetUnloadEventTraceEx(&elementSize, &elementCount, &eventTrace);
85 
86  if (!NT_SUCCESS(status = PhOpenProcess(&processHandle, PROCESS_VM_READ, Context->ProcessItem->ProcessId)))
87  goto CleanupExit;
88 
89  // We have the pointers for the unload event trace information.
90  // Since ntdll is loaded at the same base address across all processes,
91  // we can read the information in.
92 
93  if (!NT_SUCCESS(status = PhReadVirtualMemory(
94  processHandle,
95  elementSize,
96  &capturedElementSize,
97  sizeof(ULONG),
98  NULL
99  )))
100  goto CleanupExit;
101 
102  if (!NT_SUCCESS(status = PhReadVirtualMemory(
103  processHandle,
104  elementCount,
105  &capturedElementCount,
106  sizeof(ULONG),
107  NULL
108  )))
109  goto CleanupExit;
110 
111  if (!NT_SUCCESS(status = PhReadVirtualMemory(
112  processHandle,
113  eventTrace,
114  &capturedEventTracePointer,
115  sizeof(PVOID),
116  NULL
117  )))
118  goto CleanupExit;
119 
120  if (!capturedEventTracePointer)
121  goto CleanupExit; // no events
122 
123  if (capturedElementCount > 0x4000)
124  capturedElementCount = 0x4000;
125 
126  eventTraceSize = capturedElementSize * capturedElementCount;
127 
128  capturedEventTrace = PhAllocateSafe(eventTraceSize);
129 
130  if (!capturedEventTrace)
131  {
132  status = STATUS_NO_MEMORY;
133  goto CleanupExit;
134  }
135 
136  if (!NT_SUCCESS(status = PhReadVirtualMemory(
137  processHandle,
138  capturedEventTracePointer,
139  capturedEventTrace,
140  eventTraceSize,
141  NULL
142  )))
143  goto CleanupExit;
144 
145  currentEvent = capturedEventTrace;
146 
148 
149  for (i = 0; i < capturedElementCount; i++)
150  {
151  PRTL_UNLOAD_EVENT_TRACE rtlEvent = currentEvent;
152  INT lvItemIndex;
153  WCHAR buffer[128];
154  PPH_STRING string;
155  LARGE_INTEGER time;
156  SYSTEMTIME systemTime;
157 
158  if (!rtlEvent->BaseAddress)
159  break;
160 
161  PhPrintUInt32(buffer, rtlEvent->Sequence);
162  lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, buffer, rtlEvent);
163 
164  // Name
165  if (PhCopyStringZ(rtlEvent->ImageName, sizeof(rtlEvent->ImageName) / sizeof(WCHAR),
166  buffer, sizeof(buffer) / sizeof(WCHAR), NULL))
167  {
168  PhSetListViewSubItem(lvHandle, lvItemIndex, 1, buffer);
169  }
170 
171  // Base Address
172  PhPrintPointer(buffer, rtlEvent->BaseAddress);
173  PhSetListViewSubItem(lvHandle, lvItemIndex, 2, buffer);
174 
175  // Size
176  string = PhFormatSize(rtlEvent->SizeOfImage, -1);
177  PhSetListViewSubItem(lvHandle, lvItemIndex, 3, string->Buffer);
178  PhDereferenceObject(string);
179 
180  // Time Stamp
181  RtlSecondsSince1970ToTime(rtlEvent->TimeDateStamp, &time);
182  PhLargeIntegerToLocalSystemTime(&systemTime, &time);
183  string = PhFormatDateTime(&systemTime);
184  PhSetListViewSubItem(lvHandle, lvItemIndex, 4, string->Buffer);
185  PhDereferenceObject(string);
186 
187  // Checksum
188  PhPrintPointer(buffer, UlongToPtr(rtlEvent->CheckSum));
189  PhSetListViewSubItem(lvHandle, lvItemIndex, 5, buffer);
190 
191  currentEvent = PTR_ADD_OFFSET(currentEvent, capturedElementSize);
192  }
193 
194  ExtendedListView_SortItems(lvHandle);
195  ExtendedListView_SetRedraw(lvHandle, TRUE);
196 
197  if (Context->CapturedEventTrace)
198  PhFree(Context->CapturedEventTrace);
199 
200  Context->CapturedEventTrace = capturedEventTrace;
201 
202 CleanupExit:
203 
204  if (processHandle)
205  NtClose(processHandle);
206 
207  if (NT_SUCCESS(status))
208  {
209  return TRUE;
210  }
211  else
212  {
213  PhShowStatus(hwndDlg, L"Unable to retrieve unload event trace information", status, 0);
214  return FALSE;
215  }
216 }
217 
218 static INT NTAPI EtpNumberCompareFunction(
219  _In_ PVOID Item1,
220  _In_ PVOID Item2,
221  _In_opt_ PVOID Context
222  )
223 {
224  PRTL_UNLOAD_EVENT_TRACE item1 = Item1;
225  PRTL_UNLOAD_EVENT_TRACE item2 = Item2;
226 
227  return uintcmp(item1->Sequence, item2->Sequence);
228 }
229 
230 static INT NTAPI EtpBaseAddressCompareFunction(
231  _In_ PVOID Item1,
232  _In_ PVOID Item2,
233  _In_opt_ PVOID Context
234  )
235 {
236  PRTL_UNLOAD_EVENT_TRACE item1 = Item1;
237  PRTL_UNLOAD_EVENT_TRACE item2 = Item2;
238 
239  return uintptrcmp((ULONG_PTR)item1->BaseAddress, (ULONG_PTR)item2->BaseAddress);
240 }
241 
242 static INT NTAPI EtpSizeCompareFunction(
243  _In_ PVOID Item1,
244  _In_ PVOID Item2,
245  _In_opt_ PVOID Context
246  )
247 {
248  PRTL_UNLOAD_EVENT_TRACE item1 = Item1;
249  PRTL_UNLOAD_EVENT_TRACE item2 = Item2;
250 
251  return uintptrcmp(item1->SizeOfImage, item2->SizeOfImage);
252 }
253 
254 static INT NTAPI EtpTimeStampCompareFunction(
255  _In_ PVOID Item1,
256  _In_ PVOID Item2,
257  _In_opt_ PVOID Context
258  )
259 {
260  PRTL_UNLOAD_EVENT_TRACE item1 = Item1;
261  PRTL_UNLOAD_EVENT_TRACE item2 = Item2;
262 
263  return uintcmp(item1->TimeDateStamp, item2->TimeDateStamp);
264 }
265 
266 static INT NTAPI EtpCheckSumCompareFunction(
267  _In_ PVOID Item1,
268  _In_ PVOID Item2,
269  _In_opt_ PVOID Context
270  )
271 {
272  PRTL_UNLOAD_EVENT_TRACE item1 = Item1;
273  PRTL_UNLOAD_EVENT_TRACE item2 = Item2;
274 
275  return uintcmp(item1->CheckSum, item2->CheckSum);
276 }
277 
278 INT_PTR CALLBACK EtpUnloadedDllsDlgProc(
279  _In_ HWND hwndDlg,
280  _In_ UINT uMsg,
281  _In_ WPARAM wParam,
282  _In_ LPARAM lParam
283  )
284 {
285  PUNLOADED_DLLS_CONTEXT context;
286 
287  if (uMsg == WM_INITDIALOG)
288  {
289  context = (PUNLOADED_DLLS_CONTEXT)lParam;
290  SetProp(hwndDlg, L"Context", (HANDLE)context);
291  }
292  else
293  {
294  context = (PUNLOADED_DLLS_CONTEXT)GetProp(hwndDlg, L"Context");
295 
296  if (uMsg == WM_DESTROY)
297  RemoveProp(hwndDlg, L"Context");
298  }
299 
300  if (!context)
301  return FALSE;
302 
303  switch (uMsg)
304  {
305  case WM_INITDIALOG:
306  {
307  HWND lvHandle;
308 
309  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
310 
311  context->ListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
312 
313  PhSetListViewStyle(lvHandle, FALSE, TRUE);
314  PhSetControlTheme(lvHandle, L"explorer");
315  PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 40, L"No.");
316  PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 120, L"Name");
317  PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 80, L"Base Address");
318  PhAddListViewColumn(lvHandle, 3, 3, 3, LVCFMT_LEFT, 60, L"Size");
319  PhAddListViewColumn(lvHandle, 4, 4, 4, LVCFMT_LEFT, 100, L"Time Stamp");
320  PhAddListViewColumn(lvHandle, 5, 5, 5, LVCFMT_LEFT, 60, L"Checksum");
321 
322  PhSetExtendedListView(lvHandle);
323  ExtendedListView_SetCompareFunction(lvHandle, 0, EtpNumberCompareFunction);
324  ExtendedListView_SetCompareFunction(lvHandle, 2, EtpBaseAddressCompareFunction);
325  ExtendedListView_SetCompareFunction(lvHandle, 3, EtpSizeCompareFunction);
326  ExtendedListView_SetCompareFunction(lvHandle, 4, EtpTimeStampCompareFunction);
327  ExtendedListView_SetCompareFunction(lvHandle, 5, EtpCheckSumCompareFunction);
328 
329  if (!EtpRefreshUnloadedDlls(hwndDlg, context))
330  {
331  EndDialog(hwndDlg, IDCANCEL);
332  return FALSE;
333  }
334  }
335  break;
336  case WM_COMMAND:
337  {
338  switch (LOWORD(wParam))
339  {
340  case IDCANCEL:
341  case IDOK:
342  EndDialog(hwndDlg, IDOK);
343  break;
344  case IDC_REFRESH:
345  EtpRefreshUnloadedDlls(hwndDlg, context);
346  break;
347  }
348  }
349  break;
350  case WM_NOTIFY:
351  {
352  PhHandleListViewNotifyForCopy(lParam, context->ListViewHandle);
353  }
354  break;
355  }
356 
357  return FALSE;
358 }