1 : // Copyright 2015 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/refinery/analyzers/stack_analyzer_impl.h"
16 :
17 : #include <algorithm>
18 : #include <string>
19 : #include <vector>
20 :
21 : #include "base/logging.h"
22 : #include "base/memory/scoped_ptr.h"
23 : #include "base/numerics/safe_conversions.h"
24 : #include "base/strings/string16.h"
25 : #include "base/strings/string_util.h"
26 : #include "base/strings/stringprintf.h"
27 : #include "base/strings/utf_string_conversions.h"
28 : #include "base/win/scoped_bstr.h"
29 : #include "base/win/scoped_comptr.h"
30 : #include "syzygy/common/com_utils.h"
31 : #include "syzygy/pe/dia_util.h"
32 : #include "syzygy/refinery/core/addressed_data.h"
33 : #include "syzygy/refinery/process_state/process_state.h"
34 : #include "syzygy/refinery/process_state/process_state_util.h"
35 : #include "syzygy/refinery/process_state/refinery.pb.h"
36 :
37 m : namespace refinery {
38 :
39 m : namespace {
40 :
41 m : bool GetDiaSession(ULONGLONG va,
42 m : ProcessState* process_state,
43 m : scoped_refptr<DiaSymbolProvider> symbol_provider,
44 m : base::win::ScopedComPtr<IDiaSession>* session) {
45 m : DCHECK(process_state); DCHECK(session);
46 :
47 : // Get the module's signature.
48 m : ModuleLayerAccessor accessor(process_state);
49 m : pe::PEFile::Signature signature;
50 m : if (!accessor.GetModuleSignature(va, &signature))
51 m : return false;
52 :
53 : // Retrieve the session.
54 m : base::win::ScopedComPtr<IDiaSession> session_tmp;
55 m : if (!symbol_provider->FindOrCreateDiaSession(signature, &session_tmp))
56 m : return false;
57 :
58 : // Set the load address (the same module might be loaded at multiple VAs).
59 m : HRESULT hr = session_tmp->put_loadAddress(signature.base_address.value());
60 m : if (FAILED(hr)) {
61 m : LOG(ERROR) << "Unable to set session's load address: " << common::LogHr(hr);
62 m : return false;
63 m : }
64 m : *session = session_tmp;
65 m : return true;
66 m : }
67 :
68 m : } // namespace
69 :
70 m : StackWalkHelper::StackWalkHelper(
71 m : scoped_refptr<DiaSymbolProvider> symbol_provider)
72 m : : symbol_provider_(symbol_provider) {
73 m : DCHECK(symbol_provider.get() != nullptr);
74 m : }
75 :
76 m : StackWalkHelper::~StackWalkHelper() {
77 m : }
78 :
79 m : void StackWalkHelper::SetState(StackRecordPtr stack_record,
80 m : ProcessState* process_state) {
81 : // Set up the context. For the excepting thread, we use the exception's
82 : // context.
83 : // TODO(manzagop): consider walking stack from all available contexts and
84 : // merging the walks.
85 m : registers_.clear();
86 m : DCHECK(stack_record->data().has_thread_info());
87 m : const ThreadInformation& thread_info = stack_record->data().thread_info();
88 m : const RegisterInformation* context;
89 m : if (thread_info.has_exception()) {
90 m : context = &thread_info.exception().register_info();
91 m : } else {
92 m : context = &thread_info.register_info();
93 m : }
94 :
95 : // Set registers that are handled.
96 : // TODO(manzagop): should the the allreg registers also be set?
97 m : put_registerValue(CV_REG_GS, context->seg_gs());
98 m : put_registerValue(CV_REG_FS, context->seg_fs());
99 m : put_registerValue(CV_REG_ES, context->seg_es());
100 m : put_registerValue(CV_REG_DS, context->seg_ds());
101 m : put_registerValue(CV_REG_EDI, context->edi());
102 m : put_registerValue(CV_REG_ESI, context->esi());
103 m : put_registerValue(CV_REG_EBX, context->ebx());
104 m : put_registerValue(CV_REG_EDX, context->edx());
105 m : put_registerValue(CV_REG_ECX, context->ecx());
106 m : put_registerValue(CV_REG_EAX, context->eax());
107 m : put_registerValue(CV_REG_EBP, context->ebp());
108 m : put_registerValue(CV_REG_EIP, context->eip());
109 m : put_registerValue(CV_REG_CS, context->seg_cs());
110 m : put_registerValue(CV_REG_EFLAGS, context->eflags());
111 m : put_registerValue(CV_REG_ESP, context->esp());
112 m : put_registerValue(CV_REG_SS, context->seg_ss());
113 :
114 : // Set the process state.
115 m : process_state_ = process_state;
116 m : }
117 :
118 m : STDMETHODIMP_(ULONG) StackWalkHelper::AddRef() {
119 m : return base::win::IUnknownImpl::AddRef();
120 m : }
121 :
122 m : STDMETHODIMP_(ULONG) StackWalkHelper::Release() {
123 m : return base::win::IUnknownImpl::Release();
124 m : }
125 :
126 m : STDMETHODIMP StackWalkHelper::QueryInterface(REFIID riid, PVOID* ptr_void) {
127 m : DCHECK(ptr_void);
128 :
129 m : if (riid == __uuidof(IDiaStackWalkHelper)) {
130 m : *ptr_void = static_cast<IDiaStackWalkHelper*>(this);
131 m : AddRef();
132 m : return S_OK;
133 m : } else {
134 : // Know it if another interface is requested.
135 m : const int kGUIDSize = 39;
136 m : std::wstring riid_str;
137 m : int result =
138 m : StringFromGUID2(riid, base::WriteInto(&riid_str, kGUIDSize), kGUIDSize);
139 m : DCHECK_GT(result, 0);
140 m : LOG(ERROR) << base::StringPrintf(L"StackWalkHelper::QueryInterface for %ls",
141 m : riid_str.c_str());
142 m : DCHECK(false);
143 m : return base::win::IUnknownImpl::QueryInterface(riid, ptr_void);
144 m : }
145 m : }
146 :
147 m : STDMETHODIMP StackWalkHelper::get_registerValue(DWORD index,
148 m : ULONGLONG* pRetVal) {
149 m : if (!pRetVal) {
150 m : return E_INVALIDARG;
151 m : }
152 :
153 : // Only support retrieval of registers that were previously set.
154 m : const auto it = registers_.find(static_cast<CV_HREG_e>(index));
155 m : if (it != registers_.end()) {
156 m : *pRetVal = it->second;
157 m : return S_OK;
158 m : }
159 :
160 : // Even though this isn't in the function's contract, this ensures we'll pick
161 : // up on unexpected register retrieval attempts.
162 : // TODO(manzagop): add symbolic names for the registers.
163 m : LOG(ERROR) << base::StringPrintf("Failed to get register value (%u).", index);
164 m : DCHECK(false);
165 m : return E_FAIL;
166 m : }
167 :
168 m : STDMETHODIMP StackWalkHelper::put_registerValue(DWORD index, ULONGLONG NewVal) {
169 m : registers_[static_cast<CV_HREG_e>(index)] = NewVal;
170 m : return S_OK;
171 m : }
172 :
173 m : STDMETHODIMP StackWalkHelper::readMemory(MemoryTypeEnum unused_type,
174 m : ULONGLONG va,
175 m : DWORD cbData,
176 m : DWORD* pcbData,
177 m : BYTE* pbData) {
178 m : DCHECK(pcbData != NULL);
179 :
180 : // Handle the 0 size case.
181 m : if (cbData == 0) {
182 m : *pcbData = 0;
183 m : return S_OK;
184 m : }
185 :
186 : // Ensure range validity.
187 m : AddressRange range(va, cbData);
188 m : if (!range.IsValid()) {
189 m : LOG(ERROR) << "Invalid memory range.";
190 m : return E_FAIL;
191 m : }
192 :
193 : // Read from the backing process state.
194 m : size_t bytes_read = 0U;
195 m : if (process_state_->GetFrom(range, &bytes_read, pbData)) {
196 : // Note: this may only be a partial read.
197 m : *pcbData = bytes_read;
198 m : return S_OK;
199 m : }
200 :
201 : // If the memory comes from a module's range, attempt to service from the
202 : // module.
203 : // TODO(manzagop): success should depend on whether the module's memory
204 : // matches the requested memory type.
205 m : bytes_read = 0U;
206 m : if (ReadFromModule(range, &bytes_read, pbData)) {
207 m : VLOG(1) << "Servicing read from module. May not reflect actual memory.";
208 m : *pcbData = bytes_read;
209 m : return S_OK;
210 m : }
211 :
212 : // TODO(manzagop): introduce a function for logging VA to avoid crashing on
213 : // unexercised code when the error case occurs.
214 m : LOG(ERROR) << base::StringPrintf("\n Read failed (va: %08llx, size: %04x)",
215 m : va, cbData);
216 m : return E_FAIL;
217 m : }
218 :
219 m : STDMETHODIMP StackWalkHelper::searchForReturnAddress(IDiaFrameData* frame,
220 m : ULONGLONG* returnAddress) {
221 m : return E_NOTIMPL; // Use DIA's default search.
222 m : }
223 :
224 m : STDMETHODIMP StackWalkHelper::searchForReturnAddressStart(
225 m : IDiaFrameData* frame,
226 m : ULONGLONG startAddress,
227 m : ULONGLONG* returnAddress) {
228 m : return E_NOTIMPL; // Use DIA's default search.
229 m : }
230 :
231 m : STDMETHODIMP StackWalkHelper::frameForVA(ULONGLONG va,
232 m : IDiaFrameData** frame_data) {
233 m : base::win::ScopedComPtr<IDiaSession> session;
234 m : if (!GetDiaSession(va, process_state_, symbol_provider_, &session)) {
235 m : LOG(ERROR) << "Failed to get dia session.";
236 m : return E_FAIL;
237 m : }
238 :
239 : // Get the table that is a frame data enumerator.
240 m : base::win::ScopedComPtr<IDiaEnumFrameData> frame_enumerator;
241 m : pe::SearchResult result =
242 m : pe::FindDiaTable(__uuidof(IDiaEnumFrameData), session.get(),
243 m : frame_enumerator.ReceiveVoid());
244 m : if (result != pe::kSearchSucceeded) {
245 m : LOG(ERROR) << "Failed to obtain frame data from the pdb.";
246 m : return E_FAIL;
247 m : }
248 :
249 : // Get the frame data.
250 m : HRESULT hr = frame_enumerator->frameByVA(va, frame_data);
251 m : if (hr != S_OK) {
252 m : if (hr == S_FALSE) {
253 m : LOG(ERROR) << "No frame data matches specified address.";
254 m : } else {
255 m : LOG(ERROR) << "Failed to get frame data.";
256 m : }
257 m : }
258 :
259 m : return hr;
260 m : }
261 :
262 m : STDMETHODIMP StackWalkHelper::symbolForVA(ULONGLONG va, IDiaSymbol** ppSymbol) {
263 m : base::win::ScopedComPtr<IDiaSession> session;
264 m : if (!GetDiaSession(va, process_state_, symbol_provider_, &session)) {
265 m : LOG(ERROR) << "Failed to get dia session.";
266 m : return E_FAIL;
267 m : }
268 :
269 : // Search for a function.
270 m : base::win::ScopedComPtr<IDiaSymbol> function;
271 m : HRESULT hr = session->findSymbolByVA(va, SymTagFunction, function.Receive());
272 m : if (hr == S_OK) {
273 : // Get the associated function type.
274 m : base::win::ScopedComPtr<IDiaSymbol> function_type;
275 m : if (function->get_type(function_type.Receive()) != S_OK) {
276 m : LOG(ERROR) << "Failed to get function's type.";
277 m : return E_FAIL;
278 m : }
279 m : DWORD symtag = 0U;
280 m : DCHECK_EQ(S_OK, function_type->get_symTag(&symtag));
281 m : if (symtag == SymTagBaseType) {
282 : // We've observed a case of a function type that was a SymTagBaseType with
283 : // a base type of btNoType. Fail in this case.
284 m : LOG(ERROR) << "Function's type is not SymTagFunctionType.";
285 m : return E_FAIL;
286 m : }
287 m : DCHECK(symtag == SymTagFunctionType);
288 :
289 m : *ppSymbol = function_type.Detach();
290 m : } else {
291 : // Note: not having symbols is to be expected sometimes.
292 m : LOG(INFO) << base::StringPrintf("No symbols for VA (%08llx).", va);
293 m : }
294 :
295 m : return hr;
296 m : }
297 :
298 m : STDMETHODIMP StackWalkHelper::pdataForVA(ULONGLONG va,
299 m : DWORD cbData,
300 m : DWORD* pcbData,
301 m : BYTE* pbData) {
302 : // TODO(manzagop): implement to handle 64 bit stack walking.
303 m : return E_NOTIMPL;
304 m : }
305 :
306 m : STDMETHODIMP StackWalkHelper::imageForVA(ULONGLONG vaContext,
307 m : ULONGLONG* pvaImageStart) {
308 : // Get module's base address.
309 : // TODO(manzagop): set up indexing to optimize this.
310 m : ModuleRecordPtr module_record;
311 m : if (!process_state_->FindSingleRecord(vaContext, &module_record)) {
312 m : LOG(ERROR) << "Failed to find module for VA.";
313 m : return E_FAIL;
314 m : }
315 m : *pvaImageStart = module_record->range().start();
316 m : return S_OK;
317 m : }
318 :
319 m : STDMETHODIMP StackWalkHelper::addressForVA(ULONGLONG va,
320 m : _Out_ DWORD* pISect,
321 m : _Out_ DWORD* pOffset) {
322 m : base::win::ScopedComPtr<IDiaSession> session;
323 m : if (!GetDiaSession(va, process_state_, symbol_provider_, &session)) {
324 m : LOG(ERROR) << "Failed to get dia session.";
325 m : return E_FAIL;
326 m : }
327 :
328 m : HRESULT hr = session->addressForVA(va, pISect, pOffset);
329 m : if (hr != S_OK) {
330 m : LOG(ERROR) << "Failed to get address for va.";
331 m : }
332 :
333 m : return hr;
334 m : }
335 :
336 m : bool StackWalkHelper::ReadFromModule(const AddressRange& range,
337 m : size_t* bytes_read,
338 m : void* buffer) {
339 m : DCHECK(bytes_read);
340 m : *bytes_read = 0U;
341 :
342 : // Identify the module
343 m : ModuleRecordPtr module_record;
344 m : if (!process_state_->FindSingleRecord(range.start(), &module_record))
345 m : return false;
346 :
347 : // TODO(manzagop): actually implement the read instead of successfully
348 : // reading 0 bytes.
349 :
350 m : return true;
351 m : }
352 :
353 m : } // namespace refinery
|