Process Hacker
memrslt.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * memory search results
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 <phapp.h>
24 #include <emenu.h>
25 #include <settings.h>
26 #include <memsrch.h>
27 #include "pcre/pcre.h"
28 #include <windowsx.h>
29 
30 #define FILTER_CONTAINS 1
31 #define FILTER_CONTAINS_IGNORECASE 2
32 #define FILTER_REGEX 3
33 #define FILTER_REGEX_IGNORECASE 4
34 
35 typedef struct _MEMORY_RESULTS_CONTEXT
36 {
37  HANDLE ProcessId;
38  PPH_LIST Results;
39 
40  PH_LAYOUT_MANAGER LayoutManager;
42 
43 INT_PTR CALLBACK PhpMemoryResultsDlgProc(
44  _In_ HWND hwndDlg,
45  _In_ UINT uMsg,
46  _In_ WPARAM wParam,
47  _In_ LPARAM lParam
48  );
49 
50 static RECT MinimumSize = { -1, -1, -1, -1 };
51 
53  _In_ HANDLE ProcessId,
54  _In_ PPH_LIST Results
55  )
56 {
57  HWND windowHandle;
59  ULONG i;
60 
61  context = PhAllocate(sizeof(MEMORY_RESULTS_CONTEXT));
62  context->ProcessId = ProcessId;
63  context->Results = Results;
64 
65  PhReferenceObject(Results);
66 
67  for (i = 0; i < Results->Count; i++)
68  PhReferenceMemoryResult(Results->Items[i]);
69 
70  windowHandle = CreateDialogParam(
72  MAKEINTRESOURCE(IDD_MEMRESULTS),
73  NULL,
75  (LPARAM)context
76  );
77  ShowWindow(windowHandle, SW_SHOW);
78 }
79 
80 static PPH_STRING PhpGetStringForSelectedResults(
81  _In_ HWND ListViewHandle,
82  _In_ PPH_LIST Results,
83  _In_ BOOLEAN All
84  )
85 {
86  PH_STRING_BUILDER stringBuilder;
87  ULONG i;
88 
89  PhInitializeStringBuilder(&stringBuilder, 0x100);
90 
91  for (i = 0; i < Results->Count; i++)
92  {
93  PPH_MEMORY_RESULT result;
94 
95  if (!All)
96  {
97  if (!(ListView_GetItemState(ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED))
98  continue;
99  }
100 
101  result = Results->Items[i];
102 
103  PhAppendFormatStringBuilder(&stringBuilder, L"0x%Ix (%u): %s\r\n", result->Address, result->Length,
104  result->Display.Buffer ? result->Display.Buffer : L"");
105  }
106 
107  return PhFinalStringBuilderString(&stringBuilder);
108 }
109 
110 static VOID FilterResults(
111  _In_ HWND hwndDlg,
112  _In_ PMEMORY_RESULTS_CONTEXT Context,
113  _In_ ULONG Type
114  )
115 {
116  PPH_STRING selectedChoice = NULL;
117  PPH_LIST results;
118  pcre *expression;
119  pcre_extra *expression_extra;
120 
121  results = Context->Results;
122 
123  SetCursor(LoadCursor(NULL, IDC_WAIT));
124 
125  while (PhaChoiceDialog(
126  hwndDlg,
127  L"Filter",
128  L"Enter the filter pattern:",
129  NULL,
130  0,
131  NULL,
133  &selectedChoice,
134  NULL,
135  L"MemFilterChoices"
136  ))
137  {
138  PPH_LIST newResults = NULL;
139  ULONG i;
140 
141  if (Type == FILTER_CONTAINS || Type == FILTER_CONTAINS_IGNORECASE)
142  {
143  newResults = PhCreateList(1024);
144 
145  if (Type == FILTER_CONTAINS)
146  {
147  for (i = 0; i < results->Count; i++)
148  {
149  PPH_MEMORY_RESULT result = results->Items[i];
150 
151  if (wcsstr(result->Display.Buffer, selectedChoice->Buffer))
152  {
153  PhReferenceMemoryResult(result);
154  PhAddItemList(newResults, result);
155  }
156  }
157  }
158  else
159  {
160  PPH_STRING upperChoice;
161 
162  upperChoice = PhaUpperString(selectedChoice);
163 
164  for (i = 0; i < results->Count; i++)
165  {
166  PPH_MEMORY_RESULT result = results->Items[i];
167  PWSTR upperDisplay;
168 
169  upperDisplay = PhAllocateForMemorySearch(result->Display.Length + sizeof(WCHAR));
170  // Copy the null terminator as well.
171  memcpy(upperDisplay, result->Display.Buffer, result->Display.Length + sizeof(WCHAR));
172 
173  _wcsupr(upperDisplay);
174 
175  if (wcsstr(upperDisplay, upperChoice->Buffer))
176  {
177  PhReferenceMemoryResult(result);
178  PhAddItemList(newResults, result);
179  }
180 
181  PhFreeForMemorySearch(upperDisplay);
182  }
183  }
184  }
185  else if (Type == FILTER_REGEX || Type == FILTER_REGEX_IGNORECASE)
186  {
187  PPH_BYTES patternString;
188  char *errorString;
189  int errorOffset;
190  PCHAR asciiBuffer;
191 
192  // Assume that everything is plain ASCII.
193  patternString = PhConvertUtf16ToUtf8Ex(
194  selectedChoice->Buffer,
195  selectedChoice->Length
196  );
197  PhAutoDereferenceObject(patternString);
198 
199  expression = pcre_compile2(
200  patternString->Buffer,
202  NULL,
203  &errorString,
204  &errorOffset,
205  NULL
206  );
207 
208  if (!expression)
209  {
210  PhShowError(hwndDlg, L"Unable to compile the regular expression: \"%S\" at position %d.",
211  errorString,
212  errorOffset
213  );
214  continue;
215  }
216 
217  expression_extra = pcre_study(expression, 0, &errorString);
218 
219  asciiBuffer = PhAllocatePage(PH_DISPLAY_BUFFER_COUNT + 1, NULL);
220  newResults = PhCreateList(1024);
221 
222  for (i = 0; i < results->Count; i++)
223  {
224  PPH_MEMORY_RESULT result = results->Items[i];
225  SIZE_T asciiLength;
226  int r;
227 
229  asciiBuffer,
231  &asciiLength,
232  result->Display.Buffer,
233  result->Display.Length
234  )))
235  continue;
236 
237  // Guard against stack overflows.
238  __try
239  {
240  r = pcre_exec(
241  expression,
242  expression_extra,
243  asciiBuffer,
244  (ULONG)asciiLength,
245  0,
246  0,
247  NULL,
248  0
249  );
250  }
251  __except (SIMPLE_EXCEPTION_FILTER(GetExceptionCode() == STATUS_STACK_OVERFLOW))
252  {
253  r = -1;
254 
255  if (!_resetstkoflw())
256  {
257  PhRaiseStatus(STATUS_STACK_OVERFLOW);
258  }
259  }
260 
261  if (r >= 0)
262  {
263  PhReferenceMemoryResult(result);
264  PhAddItemList(newResults, result);
265  }
266  }
267 
268  PhFreePage(asciiBuffer);
269 
270  pcre_free(expression_extra);
271  pcre_free(expression);
272  }
273 
274  if (newResults)
275  {
276  PhShowMemoryResultsDialog(Context->ProcessId, newResults);
277  PhDereferenceMemoryResults((PPH_MEMORY_RESULT *)newResults->Items, newResults->Count);
278  PhDereferenceObject(newResults);
279  break;
280  }
281  }
282 
283  SetCursor(LoadCursor(NULL, IDC_ARROW));
284 }
285 
286 INT_PTR CALLBACK PhpMemoryResultsDlgProc(
287  _In_ HWND hwndDlg,
288  _In_ UINT uMsg,
289  _In_ WPARAM wParam,
290  _In_ LPARAM lParam
291  )
292 {
293  PMEMORY_RESULTS_CONTEXT context;
294 
295  if (uMsg != WM_INITDIALOG)
296  {
297  context = GetProp(hwndDlg, PhMakeContextAtom());
298  }
299  else
300  {
301  context = (PMEMORY_RESULTS_CONTEXT)lParam;
302  SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context);
303  }
304 
305  if (!context)
306  return FALSE;
307 
308  switch (uMsg)
309  {
310  case WM_INITDIALOG:
311  {
312  HWND lvHandle;
313 
314  PhRegisterDialog(hwndDlg);
315 
316  {
317  PPH_PROCESS_ITEM processItem;
318 
319  if (processItem = PhReferenceProcessItem(context->ProcessId))
320  {
321  SetWindowText(hwndDlg, PhaFormatString(L"Results - %s (%u)",
322  processItem->ProcessName->Buffer, (ULONG)processItem->ProcessId)->Buffer);
323  PhDereferenceObject(processItem);
324  }
325  }
326 
327  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
328  PhSetListViewStyle(lvHandle, FALSE, TRUE);
329  PhSetControlTheme(lvHandle, L"explorer");
330  PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 120, L"Address");
331  PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 80, L"Length");
332  PhAddListViewColumn(lvHandle, 2, 2, 2, LVCFMT_LEFT, 200, L"Result");
333 
334  PhLoadListViewColumnsFromSetting(L"MemResultsListViewColumns", lvHandle);
335 
336  PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
337  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL,
338  PH_ANCHOR_ALL);
339  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL,
341  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL,
343  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL,
345  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_FILTER), NULL,
347 
348  if (MinimumSize.left == -1)
349  {
350  RECT rect;
351 
352  rect.left = 0;
353  rect.top = 0;
354  rect.right = 250;
355  rect.bottom = 180;
356  MapDialogRect(hwndDlg, &rect);
357  MinimumSize = rect;
358  MinimumSize.left = 0;
359  }
360 
361  ListView_SetItemCount(lvHandle, context->Results->Count);
362 
363  SetDlgItemText(hwndDlg, IDC_INTRO, PhaFormatString(L"%s results.",
364  PhaFormatUInt64(context->Results->Count, TRUE)->Buffer)->Buffer);
365 
366  {
367  PH_RECTANGLE windowRectangle;
368 
369  windowRectangle.Position = PhGetIntegerPairSetting(L"MemResultsPosition");
370  windowRectangle.Size = PhGetIntegerPairSetting(L"MemResultsSize");
371  PhAdjustRectangleToWorkingArea(hwndDlg, &windowRectangle);
372 
373  MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top,
374  windowRectangle.Width, windowRectangle.Height, FALSE);
375 
376  // Implement cascading by saving an offsetted rectangle.
377  windowRectangle.Left += 20;
378  windowRectangle.Top += 20;
379 
380  PhSetIntegerPairSetting(L"MemResultsPosition", windowRectangle.Position);
381  PhSetIntegerPairSetting(L"MemResultsSize", windowRectangle.Size);
382  }
383  }
384  break;
385  case WM_DESTROY:
386  {
387  PhSaveWindowPlacementToSetting(L"MemResultsPosition", L"MemResultsSize", hwndDlg);
388  PhSaveListViewColumnsToSetting(L"MemResultsListViewColumns", GetDlgItem(hwndDlg, IDC_LIST));
389 
390  PhDeleteLayoutManager(&context->LayoutManager);
391  PhUnregisterDialog(hwndDlg);
392  RemoveProp(hwndDlg, PhMakeContextAtom());
393 
394  PhDereferenceMemoryResults((PPH_MEMORY_RESULT *)context->Results->Items, context->Results->Count);
395  PhDereferenceObject(context->Results);
396  PhFree(context);
397  }
398  break;
399  case WM_COMMAND:
400  {
401  switch (LOWORD(wParam))
402  {
403  case IDCANCEL:
404  case IDOK:
405  DestroyWindow(hwndDlg);
406  break;
407  case IDC_COPY:
408  {
409  HWND lvHandle;
410  PPH_STRING string;
411  ULONG selectedCount;
412 
413  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
414  selectedCount = ListView_GetSelectedCount(lvHandle);
415 
416  if (selectedCount == 0)
417  {
418  // User didn't select anything, so copy all items.
419  string = PhpGetStringForSelectedResults(lvHandle, context->Results, TRUE);
420  PhSetStateAllListViewItems(lvHandle, LVIS_SELECTED, LVIS_SELECTED);
421  }
422  else
423  {
424  string = PhpGetStringForSelectedResults(lvHandle, context->Results, FALSE);
425  }
426 
427  PhSetClipboardString(hwndDlg, &string->sr);
428  PhDereferenceObject(string);
429 
430  SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)lvHandle, TRUE);
431  }
432  break;
433  case IDC_SAVE:
434  {
435  static PH_FILETYPE_FILTER filters[] =
436  {
437  { L"Text files (*.txt)", L"*.txt" },
438  { L"All files (*.*)", L"*.*" }
439  };
440  PVOID fileDialog;
441 
442  fileDialog = PhCreateSaveFileDialog();
443 
444  PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
445  PhSetFileDialogFileName(fileDialog, L"Search Results.txt");
446 
447  if (PhShowFileDialog(hwndDlg, fileDialog))
448  {
449  NTSTATUS status;
450  PPH_STRING fileName;
451  PPH_FILE_STREAM fileStream;
452  PPH_STRING string;
453 
454  fileName = PhGetFileDialogFileName(fileDialog);
455  PhAutoDereferenceObject(fileName);
456 
457  if (NT_SUCCESS(status = PhCreateFileStream(
458  &fileStream,
459  fileName->Buffer,
460  FILE_GENERIC_WRITE,
461  FILE_SHARE_READ,
463  0
464  )))
465  {
467  PhWritePhTextHeader(fileStream);
468 
469  string = PhpGetStringForSelectedResults(GetDlgItem(hwndDlg, IDC_LIST), context->Results, TRUE);
470  PhWriteStringAsUtf8FileStreamEx(fileStream, string->Buffer, string->Length);
471  PhDereferenceObject(string);
472 
473  PhDereferenceObject(fileStream);
474  }
475 
476  if (!NT_SUCCESS(status))
477  PhShowStatus(hwndDlg, L"Unable to create the file", status, 0);
478  }
479 
480  PhFreeFileDialog(fileDialog);
481  }
482  break;
483  case IDC_FILTER:
484  {
485  PPH_EMENU menu;
486  RECT buttonRect;
487  POINT point;
488  PPH_EMENU_ITEM selectedItem;
489  ULONG filterType = 0;
490 
491  menu = PhCreateEMenu();
492  PhLoadResourceEMenuItem(menu, PhInstanceHandle, MAKEINTRESOURCE(IDR_MEMFILTER), 0);
493 
494  GetClientRect(GetDlgItem(hwndDlg, IDC_FILTER), &buttonRect);
495  point.x = 0;
496  point.y = buttonRect.bottom;
497 
498  ClientToScreen(GetDlgItem(hwndDlg, IDC_FILTER), &point);
499  selectedItem = PhShowEMenu(menu, hwndDlg, PH_EMENU_SHOW_LEFTRIGHT,
500  PH_ALIGN_LEFT | PH_ALIGN_TOP, point.x, point.y);
501 
502  if (selectedItem)
503  {
504  switch (selectedItem->Id)
505  {
506  case ID_FILTER_CONTAINS:
507  filterType = FILTER_CONTAINS;
508  break;
510  filterType = FILTER_CONTAINS_IGNORECASE;
511  break;
512  case ID_FILTER_REGEX:
513  filterType = FILTER_REGEX;
514  break;
516  filterType = FILTER_REGEX_IGNORECASE;
517  break;
518  }
519  }
520 
521  if (filterType != 0)
522  FilterResults(hwndDlg, context, filterType);
523 
524  PhDestroyEMenu(menu);
525  }
526  break;
527  }
528  }
529  break;
530  case WM_NOTIFY:
531  {
532  LPNMHDR header = (LPNMHDR)lParam;
533  HWND lvHandle;
534 
535  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
536  PhHandleListViewNotifyForCopy(lParam, lvHandle);
537 
538  switch (header->code)
539  {
540  case LVN_GETDISPINFO:
541  {
542  NMLVDISPINFO *dispInfo = (NMLVDISPINFO *)header;
543 
544  if (dispInfo->item.mask & LVIF_TEXT)
545  {
546  PPH_MEMORY_RESULT result = context->Results->Items[dispInfo->item.iItem];
547 
548  switch (dispInfo->item.iSubItem)
549  {
550  case 0:
551  {
552  WCHAR addressString[PH_PTR_STR_LEN_1];
553 
554  PhPrintPointer(addressString, result->Address);
555  wcsncpy_s(
556  dispInfo->item.pszText,
557  dispInfo->item.cchTextMax,
558  addressString,
559  _TRUNCATE
560  );
561  }
562  break;
563  case 1:
564  {
565  WCHAR lengthString[PH_INT32_STR_LEN_1];
566 
567  PhPrintUInt32(lengthString, (ULONG)result->Length);
568  wcsncpy_s(
569  dispInfo->item.pszText,
570  dispInfo->item.cchTextMax,
571  lengthString,
572  _TRUNCATE
573  );
574  }
575  break;
576  case 2:
577  wcsncpy_s(
578  dispInfo->item.pszText,
579  dispInfo->item.cchTextMax,
580  result->Display.Buffer,
581  _TRUNCATE
582  );
583  break;
584  }
585  }
586  }
587  break;
588  case NM_DBLCLK:
589  {
590  if (header->hwndFrom == lvHandle)
591  {
592  INT index;
593 
594  if ((index = ListView_GetNextItem(
595  lvHandle,
596  -1,
597  LVNI_SELECTED
598  )) != -1)
599  {
600  NTSTATUS status;
601  PPH_MEMORY_RESULT result = context->Results->Items[index];
602  HANDLE processHandle;
603  MEMORY_BASIC_INFORMATION basicInfo;
604  PPH_SHOWMEMORYEDITOR showMemoryEditor;
605 
606  if (NT_SUCCESS(status = PhOpenProcess(
607  &processHandle,
609  context->ProcessId
610  )))
611  {
612  if (NT_SUCCESS(status = NtQueryVirtualMemory(
613  processHandle,
614  result->Address,
616  &basicInfo,
617  sizeof(MEMORY_BASIC_INFORMATION),
618  NULL
619  )))
620  {
621  showMemoryEditor = PhAllocate(sizeof(PH_SHOWMEMORYEDITOR));
622  memset(showMemoryEditor, 0, sizeof(PH_SHOWMEMORYEDITOR));
623  showMemoryEditor->ProcessId = context->ProcessId;
624  showMemoryEditor->BaseAddress = basicInfo.BaseAddress;
625  showMemoryEditor->RegionSize = basicInfo.RegionSize;
626  showMemoryEditor->SelectOffset = (ULONG)((ULONG_PTR)result->Address - (ULONG_PTR)basicInfo.BaseAddress);
627  showMemoryEditor->SelectLength = (ULONG)result->Length;
629  }
630 
631  NtClose(processHandle);
632  }
633 
634  if (!NT_SUCCESS(status))
635  PhShowStatus(hwndDlg, L"Unable to edit memory", status, 0);
636  }
637  }
638  }
639  break;
640  }
641  }
642  break;
643  case WM_SIZE:
644  {
645  PhLayoutManagerLayout(&context->LayoutManager);
646  }
647  break;
648  case WM_SIZING:
649  {
650  PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom);
651  }
652  break;
653  }
654 
655  return FALSE;
656 }