Process Hacker
mdump.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * minidump writer
4  *
5  * Copyright (C) 2010-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 <dbghelp.h>
25 #include <symprv.h>
26 #include <settings.h>
27 #include <phsvccl.h>
28 
29 #define WM_PH_MINIDUMP_STATUS_UPDATE (WM_APP + 301)
30 
31 #define PH_MINIDUMP_STATUS_UPDATE 1
32 #define PH_MINIDUMP_COMPLETED 2
33 #define PH_MINIDUMP_ERROR 3
34 
35 typedef struct _PROCESS_MINIDUMP_CONTEXT
36 {
37  HANDLE ProcessId;
38  PWSTR FileName;
39  MINIDUMP_TYPE DumpType;
40  BOOLEAN IsWow64;
41 
42  HANDLE ProcessHandle;
43  HANDLE FileHandle;
44 
45  HWND WindowHandle;
46  HANDLE ThreadHandle;
47  BOOLEAN Stop;
48  BOOLEAN Succeeded;
49 
50  ULONG LastTickCount;
52 
54  _In_ HWND hWnd,
55  _In_ HANDLE ProcessId,
56  _In_ PWSTR FileName,
57  _In_ MINIDUMP_TYPE DumpType
58  );
59 
60 INT_PTR CALLBACK PhpProcessMiniDumpDlgProc(
61  _In_ HWND hwndDlg,
62  _In_ UINT uMsg,
63  _In_ WPARAM wParam,
64  _In_ LPARAM lParam
65  );
66 
68  _In_ HWND hWnd,
69  _In_ PPH_PROCESS_ITEM Process
70  )
71 {
72  static PH_FILETYPE_FILTER filters[] =
73  {
74  { L"Dump files (*.dmp)", L"*.dmp" },
75  { L"All files (*.*)", L"*.*" }
76  };
77  PVOID fileDialog;
78  PPH_STRING fileName;
79 
80  fileDialog = PhCreateSaveFileDialog();
81  PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
82  PhSetFileDialogFileName(fileDialog, PhaConcatStrings2(Process->ProcessName->Buffer, L".dmp")->Buffer);
83 
84  if (!PhShowFileDialog(hWnd, fileDialog))
85  {
86  PhFreeFileDialog(fileDialog);
87  return FALSE;
88  }
89 
90  fileName = PhAutoDereferenceObject(PhGetFileDialogFileName(fileDialog));
91  PhFreeFileDialog(fileDialog);
92 
94  hWnd,
95  Process->ProcessId,
96  fileName->Buffer,
97  // task manager uses these flags
98  MiniDumpWithFullMemory |
99  MiniDumpWithHandleData |
100  MiniDumpWithUnloadedModules |
101  MiniDumpWithFullMemoryInfo |
102  MiniDumpWithThreadInfo
103  );
104 }
105 
107  _In_ HWND hWnd,
108  _In_ HANDLE ProcessId,
109  _In_ PWSTR FileName,
110  _In_ MINIDUMP_TYPE DumpType
111  )
112 {
113  NTSTATUS status;
114  PROCESS_MINIDUMP_CONTEXT context;
115 
116  memset(&context, 0, sizeof(PROCESS_MINIDUMP_CONTEXT));
117  context.ProcessId = ProcessId;
118  context.FileName = FileName;
119  context.DumpType = DumpType;
120 
121  if (!NT_SUCCESS(status = PhOpenProcess(
122  &context.ProcessHandle,
124  ProcessId
125  )))
126  {
127  PhShowStatus(hWnd, L"Unable to open the process", status, 0);
128  return FALSE;
129  }
130 
131 #ifdef _WIN64
132  PhGetProcessIsWow64(context.ProcessHandle, &context.IsWow64);
133 #endif
134 
135  status = PhCreateFileWin32(
136  &context.FileHandle,
137  FileName,
138  FILE_GENERIC_WRITE | DELETE,
139  0,
140  0,
143  );
144 
145  if (!NT_SUCCESS(status))
146  {
147  PhShowStatus(hWnd, L"Unable to access the dump file", status, 0);
148  NtClose(context.ProcessHandle);
149  return FALSE;
150  }
151 
152  DialogBoxParam(
154  MAKEINTRESOURCE(IDD_PROGRESS),
155  hWnd,
157  (LPARAM)&context
158  );
159 
160  NtClose(context.FileHandle);
161  NtClose(context.ProcessHandle);
162 
163  return context.Succeeded;
164 }
165 
166 static BOOL CALLBACK PhpProcessMiniDumpCallback(
167  _In_ PVOID CallbackParam,
168  _In_ const PMINIDUMP_CALLBACK_INPUT CallbackInput,
169  _Inout_ PMINIDUMP_CALLBACK_OUTPUT CallbackOutput
170  )
171 {
172  PPROCESS_MINIDUMP_CONTEXT context = CallbackParam;
173  PPH_STRING message = NULL;
174 
175  // Don't try to send status updates if we're creating a dump of the current process.
176  if (context->ProcessId == NtCurrentProcessId())
177  return TRUE;
178 
179  // MiniDumpWriteDump seems to get bored of calling the callback
180  // after it begins dumping the process handles. The code is
181  // still here in case they fix this problem in the future.
182 
183  switch (CallbackInput->CallbackType)
184  {
185  case CancelCallback:
186  {
187  if (context->Stop)
188  CallbackOutput->Cancel = TRUE;
189  }
190  break;
191  case ModuleCallback:
192  {
193  message = PhFormatString(L"Processing module %s...", CallbackInput->Module.FullPath);
194  }
195  break;
196  case ThreadCallback:
197  {
198  message = PhFormatString(L"Processing thread %u...", CallbackInput->Thread.ThreadId);
199  }
200  break;
201  }
202 
203  if (message)
204  {
205  SendMessage(
206  context->WindowHandle,
209  (LPARAM)message->Buffer
210  );
211  PhDereferenceObject(message);
212  }
213 
214  return TRUE;
215 }
216 
218  _In_ PVOID Parameter
219  )
220 {
221  PPROCESS_MINIDUMP_CONTEXT context = Parameter;
222  MINIDUMP_CALLBACK_INFORMATION callbackInfo;
223 
224  callbackInfo.CallbackRoutine = PhpProcessMiniDumpCallback;
225  callbackInfo.CallbackParam = context;
226 
227 #ifdef _WIN64
228  if (context->IsWow64)
229  {
231  {
232  NTSTATUS status;
233  PPH_STRING dbgHelpPath;
234 
235  dbgHelpPath = PhGetStringSetting(L"DbgHelpPath");
236  PhSvcCallLoadDbgHelp(dbgHelpPath->Buffer);
237  PhDereferenceObject(dbgHelpPath);
238 
240  context->ProcessHandle,
241  context->ProcessId,
242  context->FileHandle,
243  context->DumpType
244  )))
245  {
246  context->Succeeded = TRUE;
247  }
248  else
249  {
250  // We may have an old version of dbghelp - in that case, try using minimal dump flags.
251  if (status == STATUS_INVALID_PARAMETER && NT_SUCCESS(status = PhSvcCallWriteMiniDumpProcess(
252  context->ProcessHandle,
253  context->ProcessId,
254  context->FileHandle,
255  MiniDumpWithFullMemory | MiniDumpWithHandleData
256  )))
257  {
258  context->Succeeded = TRUE;
259  }
260  else
261  {
262  SendMessage(
263  context->WindowHandle,
266  (LPARAM)PhNtStatusToDosError(status)
267  );
268  }
269  }
270 
272 
273  goto Completed;
274  }
275  else
276  {
277  if (PhShowMessage(
278  context->WindowHandle,
279  MB_YESNO | MB_ICONWARNING,
280  L"The process is 32-bit, but the 32-bit version of Process Hacker could not be located. "
281  L"A 64-bit dump will be created instead. Do you want to continue?"
282  ) == IDNO)
283  {
284  FILE_DISPOSITION_INFORMATION dispositionInfo;
285  IO_STATUS_BLOCK isb;
286 
287  dispositionInfo.DeleteFile = TRUE;
289  context->FileHandle,
290  &isb,
291  &dispositionInfo,
294  );
295 
296  goto Completed;
297  }
298  }
299  }
300 #endif
301 
303  context->ProcessHandle,
304  context->ProcessId,
305  context->FileHandle,
306  context->DumpType,
307  NULL,
308  NULL,
309  &callbackInfo
310  ))
311  {
312  context->Succeeded = TRUE;
313  }
314  else
315  {
316  // We may have an old version of dbghelp - in that case, try using minimal dump flags.
317  if (GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) && PhWriteMiniDumpProcess(
318  context->ProcessHandle,
319  context->ProcessId,
320  context->FileHandle,
321  MiniDumpWithFullMemory | MiniDumpWithHandleData,
322  NULL,
323  NULL,
324  &callbackInfo
325  ))
326  {
327  context->Succeeded = TRUE;
328  }
329  else
330  {
331  SendMessage(
332  context->WindowHandle,
335  (LPARAM)GetLastError()
336  );
337  }
338  }
339 
340 #ifdef _WIN64
341 Completed:
342 #endif
343  SendMessage(
344  context->WindowHandle,
347  0
348  );
349 
350  return STATUS_SUCCESS;
351 }
352 
353 INT_PTR CALLBACK PhpProcessMiniDumpDlgProc(
354  _In_ HWND hwndDlg,
355  _In_ UINT uMsg,
356  _In_ WPARAM wParam,
357  _In_ LPARAM lParam
358  )
359 {
360  switch (uMsg)
361  {
362  case WM_INITDIALOG:
363  {
365 
366  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
367  SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context);
368 
369  SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, L"Creating the dump file...");
370 
371  PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE);
372  SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75);
373 
374  context->WindowHandle = hwndDlg;
375  context->ThreadHandle = PhCreateThread(0, PhpProcessMiniDumpThreadStart, context);
376 
377  if (!context->ThreadHandle)
378  {
379  PhShowStatus(hwndDlg, L"Unable to create the minidump thread", 0, GetLastError());
380  EndDialog(hwndDlg, IDCANCEL);
381  }
382 
383  SetTimer(hwndDlg, 1, 500, NULL);
384  }
385  break;
386  case WM_DESTROY:
387  {
389 
390  context = (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
391 
392  NtClose(context->ThreadHandle);
393 
394  RemoveProp(hwndDlg, PhMakeContextAtom());
395  }
396  break;
397  case WM_COMMAND:
398  {
399  switch (LOWORD(wParam))
400  {
401  case IDCANCEL:
402  {
403  PPROCESS_MINIDUMP_CONTEXT context =
404  (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
405 
406  EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE);
407  context->Stop = TRUE;
408  }
409  break;
410  }
411  }
412  break;
413  case WM_TIMER:
414  {
415  if (wParam == 1)
416  {
417  PPROCESS_MINIDUMP_CONTEXT context =
418  (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
419  ULONG currentTickCount;
420 
421  currentTickCount = GetTickCount();
422 
423  if (currentTickCount - context->LastTickCount >= 2000)
424  {
425  // No status message update for 2 seconds.
426 
427  SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT,
428  (PWSTR)L"Creating the dump file...");
429  InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE);
430 
431  context->LastTickCount = currentTickCount;
432  }
433  }
434  }
435  break;
437  {
439 
440  context = (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
441 
442  switch (wParam)
443  {
445  SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, (PWSTR)lParam);
446  InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE);
447  context->LastTickCount = GetTickCount();
448  break;
449  case PH_MINIDUMP_ERROR:
450  PhShowStatus(hwndDlg, L"Unable to create the minidump", 0, (ULONG)lParam);
451  break;
453  EndDialog(hwndDlg, IDOK);
454  break;
455  }
456  }
457  break;
458  }
459 
460  return FALSE;
461 }