1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/kasko/minidump.h"
16 :
17 : #include <Windows.h> // NOLINT
18 : #include <DbgHelp.h>
19 : #include <Psapi.h>
20 : #include <winternl.h>
21 :
22 : #include "base/files/file.h"
23 : #include "base/process/process_handle.h"
24 : #include "base/win/pe_image.h"
25 : #include "base/win/scoped_handle.h"
26 :
27 : #include "syzygy/common/com_utils.h"
28 : #include "syzygy/core/address_range.h"
29 : #include "syzygy/kasko/loader_lock.h"
30 :
31 : namespace kasko {
32 :
33 : namespace {
34 :
35 : // Minidump with stacks, PEB, TEB, and unloaded module list.
36 : const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
37 : MiniDumpWithProcessThreadData | // Get PEB and TEB.
38 : MiniDumpWithUnloadedModules); // Get unloaded modules when available.
39 :
40 : // Minidump with all of the above, plus memory referenced from stack.
41 : const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
42 : MiniDumpWithProcessThreadData | // Get PEB and TEB.
43 : MiniDumpWithUnloadedModules | // Get unloaded modules when available.
44 : MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
45 :
46 : // Large dump with all process memory.
47 : const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
48 : MiniDumpWithFullMemory | // Full memory from process.
49 : MiniDumpWithProcessThreadData | // Get PEB and TEB.
50 : MiniDumpWithHandleData | // Get all handle information.
51 : MiniDumpWithUnloadedModules); // Get unloaded modules when available.
52 :
53 : class MinidumpCallbackHandler {
54 : public:
55 : explicit MinidumpCallbackHandler(
56 : const std::vector<MinidumpRequest::MemoryRange>* memory_ranges);
57 :
58 E : const MINIDUMP_CALLBACK_INFORMATION* GetMINIDUMP_CALLBACK_INFORMATION() {
59 E : return &minidump_callback_information_;
60 E : }
61 :
62 : private:
63 : BOOL MemoryCallback(ULONG64* memory_base, ULONG* memory_size);
64 :
65 : static BOOL CALLBACK
66 : CallbackRoutine(PVOID context,
67 : const PMINIDUMP_CALLBACK_INPUT callback_input,
68 : PMINIDUMP_CALLBACK_OUTPUT callback_output);
69 :
70 : const std::vector<MinidumpRequest::MemoryRange>* memory_ranges_;
71 : size_t next_memory_range_index_;
72 : MINIDUMP_CALLBACK_INFORMATION minidump_callback_information_;
73 :
74 : DISALLOW_COPY_AND_ASSIGN(MinidumpCallbackHandler);
75 : };
76 :
77 : MinidumpCallbackHandler::MinidumpCallbackHandler(
78 : const std::vector<MinidumpRequest::MemoryRange>* memory_ranges)
79 : : memory_ranges_(memory_ranges),
80 : next_memory_range_index_(0),
81 E : minidump_callback_information_() {
82 : minidump_callback_information_.CallbackRoutine =
83 E : &MinidumpCallbackHandler::CallbackRoutine;
84 E : minidump_callback_information_.CallbackParam = reinterpret_cast<void*>(this);
85 E : }
86 :
87 : BOOL MinidumpCallbackHandler::MemoryCallback(ULONG64* memory_base,
88 E : ULONG* memory_size) {
89 E : for (; next_memory_range_index_ < memory_ranges_->size();
90 E : ++next_memory_range_index_) {
91 : // A zero-length range will terminate memory callbacks. If there is one in
92 : // our input vector, skip it.
93 E : if ((*memory_ranges_)[next_memory_range_index_].size() == 0)
94 i : continue;
95 :
96 : // Include the specified memory region.
97 E : *memory_base = (*memory_ranges_)[next_memory_range_index_].start();
98 E : *memory_size = (*memory_ranges_)[next_memory_range_index_].size();
99 E : ++next_memory_range_index_;
100 E : return TRUE;
101 i : }
102 E : return FALSE;
103 E : }
104 :
105 : // static
106 : BOOL CALLBACK MinidumpCallbackHandler::CallbackRoutine(
107 : PVOID context,
108 : const PMINIDUMP_CALLBACK_INPUT callback_input,
109 E : PMINIDUMP_CALLBACK_OUTPUT callback_output) {
110 : MinidumpCallbackHandler* self =
111 E : reinterpret_cast<MinidumpCallbackHandler*>(context);
112 E : switch (callback_input->CallbackType) {
113 : case ::MemoryCallback:
114 : return self->MemoryCallback(&callback_output->MemoryBase,
115 E : &callback_output->MemorySize);
116 :
117 : // Include all modules.
118 : case IncludeModuleCallback:
119 : case ModuleCallback:
120 E : return TRUE;
121 :
122 : // Include all threads.
123 : case IncludeThreadCallback:
124 : case ThreadCallback:
125 E : return TRUE;
126 :
127 : // Stop receiving cancel callbacks.
128 : case CancelCallback:
129 E : callback_output->CheckCancel = FALSE;
130 E : callback_output->Cancel = FALSE;
131 E : return TRUE;
132 : }
133 : // Ignore other callback types.
134 E : return FALSE;
135 E : }
136 :
137 : // Checks that the range lives in a readable section of the module.
138 : bool VerifyRangeInModule(HMODULE module,
139 E : const kasko::MinidumpRequest::MemoryRange& range) {
140 E : base::win::PEImage module_image(module);
141 : IMAGE_SECTION_HEADER* section = module_image.GetImageSectionFromAddr(
142 E : reinterpret_cast<void*>(range.start()));
143 :
144 : // If no section was returned, then the range doesn't reside in the module.
145 E : if (!section)
146 i : return false;
147 :
148 : // Make sure the range is in a readable section.
149 E : if ((section->Characteristics & IMAGE_SCN_MEM_READ) != IMAGE_SCN_MEM_READ)
150 i : return false;
151 :
152 : kasko::MinidumpRequest::MemoryRange section_range(
153 : reinterpret_cast<uint32_t>(
154 : module_image.RVAToAddr(section->VirtualAddress)),
155 E : section->SizeOfRawData);
156 E : return section_range.Contains(range);
157 E : }
158 :
159 : void AppendLoaderLockMemoryRanges(
160 E : std::vector<kasko::MinidumpRequest::MemoryRange>* memory_ranges) {
161 E : DCHECK(memory_ranges);
162 :
163 E : CRITICAL_SECTION* loader_lock = GetLoaderLock();
164 :
165 : // Add the range for the loader lock. This works because ntdll is loaded at
166 : // the same address in all processes.
167 : kasko::MinidumpRequest::MemoryRange loader_lock_memory_range(
168 E : reinterpret_cast<uint32_t>(loader_lock), sizeof(CRITICAL_SECTION));
169 E : memory_ranges->push_back(loader_lock_memory_range);
170 :
171 : // Add range for loader lock debuginfo. Dereferencing the loader lock is
172 : // required so a basic check is performed first. The loader lock should always
173 : // be living in ntdll globals and in a readable section.
174 E : HMODULE ntdll_module = ::GetModuleHandle(L"ntdll.dll");
175 E : if (VerifyRangeInModule(ntdll_module, loader_lock_memory_range)) {
176 : kasko::MinidumpRequest::MemoryRange debug_info_memory_range(
177 : reinterpret_cast<uint32_t>(loader_lock->DebugInfo),
178 E : sizeof(CRITICAL_SECTION_DEBUG));
179 E : memory_ranges->push_back(debug_info_memory_range);
180 E : DCHECK(VerifyRangeInModule(ntdll_module, debug_info_memory_range));
181 : }
182 E : }
183 :
184 : std::vector<kasko::MinidumpRequest::MemoryRange> AugmentMemoryRanges(
185 E : const std::vector<kasko::MinidumpRequest::MemoryRange>* memory_ranges) {
186 : std::vector<kasko::MinidumpRequest::MemoryRange> augmented_memory_ranges(
187 E : *memory_ranges);
188 :
189 E : AppendLoaderLockMemoryRanges(&augmented_memory_ranges);
190 :
191 E : return augmented_memory_ranges;
192 E : }
193 :
194 E : DWORD GetRequiredAccessForMinidumpTypeImpl(bool is_full_type) {
195 E : DWORD required_access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
196 :
197 E : if (is_full_type) {
198 : // A full dump includes handle data (MiniDumpWithHandleData).
199 i : required_access |= PROCESS_DUP_HANDLE;
200 : }
201 :
202 E : return required_access;
203 E : }
204 :
205 : } // namespace
206 :
207 E : DWORD GetRequiredAccessForMinidumpType(MinidumpRequest::Type type) {
208 : return GetRequiredAccessForMinidumpTypeImpl(type ==
209 E : MinidumpRequest::FULL_DUMP_TYPE);
210 E : }
211 :
212 E : DWORD GetRequiredAccessForMinidumpType(api::MinidumpType type) {
213 E : return GetRequiredAccessForMinidumpTypeImpl(type == api::FULL_DUMP_TYPE);
214 E : }
215 :
216 : bool GenerateMinidump(const base::FilePath& destination,
217 : base::ProcessHandle target_process,
218 : base::PlatformThreadId thread_id,
219 E : const MinidumpRequest& request) {
220 E : MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = nullptr;
221 : MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
222 :
223 E : if (request.exception_info_address) {
224 E : dump_exception_info.ThreadId = thread_id;
225 : dump_exception_info.ExceptionPointers =
226 E : reinterpret_cast<PEXCEPTION_POINTERS>(request.exception_info_address);
227 E : dump_exception_info.ClientPointers = request.client_exception_pointers;
228 :
229 E : dump_exception_pointers = &dump_exception_info;
230 : }
231 :
232 : base::File destination_file(destination, static_cast<base::File::Flags>(
233 : base::File::FLAG_CREATE_ALWAYS |
234 E : base::File::FLAG_WRITE));
235 E : if (!destination_file.IsValid()) {
236 E : LOG(ERROR) << "Failed to create destination file: " << destination.value();
237 E : return false;
238 : }
239 :
240 E : MINIDUMP_TYPE platform_minidump_type = kSmallDumpType;
241 :
242 E : switch (request.type) {
243 : case MinidumpRequest::SMALL_DUMP_TYPE:
244 E : platform_minidump_type = kSmallDumpType;
245 E : break;
246 : case MinidumpRequest::LARGER_DUMP_TYPE:
247 E : platform_minidump_type = kLargerDumpType;
248 E : break;
249 : case MinidumpRequest::FULL_DUMP_TYPE:
250 E : platform_minidump_type = kFullDumpType;
251 E : break;
252 : default:
253 i : NOTREACHED();
254 : break;
255 : }
256 :
257 E : std::vector<MINIDUMP_USER_STREAM> user_streams;
258 E : for (const auto& custom_stream : request.custom_streams) {
259 : MINIDUMP_USER_STREAM user_stream = {custom_stream.type,
260 : custom_stream.length,
261 E : const_cast<void*>(custom_stream.data)};
262 E : user_streams.push_back(user_stream);
263 E : }
264 :
265 : MINIDUMP_USER_STREAM_INFORMATION
266 E : user_stream_information = {user_streams.size(), user_streams.data()};
267 :
268 : // Add loader lock to the memory_ranges.
269 : std::vector<kasko::MinidumpRequest::MemoryRange> augmented_memory_ranges =
270 E : AugmentMemoryRanges(&request.user_selected_memory_ranges);
271 :
272 E : MinidumpCallbackHandler callback_handler(&augmented_memory_ranges);
273 :
274 : if (::MiniDumpWriteDump(
275 : target_process, base::GetProcId(target_process),
276 : destination_file.GetPlatformFile(), platform_minidump_type,
277 : dump_exception_pointers, &user_stream_information,
278 : const_cast<MINIDUMP_CALLBACK_INFORMATION*>(
279 E : callback_handler.GetMINIDUMP_CALLBACK_INFORMATION())) == FALSE) {
280 i : LOG(ERROR) << "MiniDumpWriteDump failed: " << ::common::LogWe() << ".";
281 i : return false;
282 : }
283 :
284 E : return true;
285 E : }
286 :
287 : } // namespace kasko
|