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/memory_analyzer.h"
16 :
17 : #include <dbghelp.h>
18 : #include <string>
19 :
20 : #include "base/memory/scoped_ptr.h"
21 : #include "syzygy/core/address_space.h"
22 : #include "syzygy/refinery/process_state/process_state_util.h"
23 : #include "syzygy/refinery/process_state/refinery.pb.h"
24 :
25 : namespace refinery {
26 :
27 : namespace {
28 :
29 : using MemoryAddressSpace = core::AddressSpace<Address, Size, std::string>;
30 :
31 : bool RecordMemoryContents(AddressRange new_range,
32 : std::string bytes,
33 E : MemoryAddressSpace* address_space) {
34 E : DCHECK(address_space);
35 E : DCHECK_EQ(new_range.size(), bytes.size());
36 :
37 : MemoryAddressSpace::iterator it =
38 E : address_space->FindFirstIntersection(new_range);
39 E : for (; it != address_space->end() && it->first.Intersects(new_range); ++it) {
40 E : const auto& range = it->first;
41 E : const std::string& data = it->second;
42 : // If this range is fully subsumed by the new range there's nothing
43 : // to do. Otherwise we need to slice the data and prepend and/or append
44 : // it to the new range and data.
45 E : if (range.start() < new_range.start()) {
46 E : size_t prepend = new_range.start() - range.start();
47 E : bytes.insert(0, data, 0, prepend);
48 E : new_range = AddressRange(range.start(), new_range.size() + prepend);
49 : }
50 E : if (range.end() > new_range.end()) {
51 E : size_t append = range.end() - new_range.end();
52 E : bytes.append(data, data.size() - append, append);
53 E : new_range = AddressRange(new_range.start(), new_range.size() + append);
54 : }
55 E : }
56 E : DCHECK_EQ(new_range.size(), bytes.size());
57 :
58 E : if (!address_space->SubsumeInsert(new_range, bytes)) {
59 i : NOTREACHED() << "SubsumeInsert failed!";
60 i : return false;
61 : }
62 :
63 E : return true;
64 E : }
65 :
66 : } // namespace
67 :
68 : // static
69 : const char MemoryAnalyzer::kMemoryAnalyzerName[] = "MemoryAnalyzer";
70 :
71 : Analyzer::AnalysisResult MemoryAnalyzer::Analyze(
72 : const minidump::Minidump& minidump,
73 E : ProcessState* process_state) {
74 E : DCHECK(process_state != nullptr);
75 :
76 : minidump::Minidump::Stream memory_list =
77 E : minidump.FindNextStream(nullptr, MemoryListStream);
78 E : if (!memory_list.IsValid())
79 i : return ANALYSIS_ERROR;
80 : // Ensure MemoryListStream is unique.
81 : minidump::Minidump::Stream offending_list =
82 E : minidump.FindNextStream(&memory_list, MemoryListStream);
83 E : if (offending_list.IsValid())
84 i : return ANALYSIS_ERROR;
85 :
86 E : ULONG32 num_ranges = 0;
87 E : if (!memory_list.ReadElement(&num_ranges))
88 i : return ANALYSIS_ERROR;
89 :
90 E : BytesLayerPtr bytes_layer;
91 E : process_state->FindOrCreateLayer(&bytes_layer);
92 :
93 : // It seems minidumps sometimes contain overlapping memory ranges. It's
94 : // difficult to reason on why this is, and it's difficult to know which byte
95 : // value of two or more alternates is "the one". To consolidate this
96 : // consistently into the byte layer we choose the byte values from the last
97 : // range that supplies a given byte.
98 : using MemoryAddressSpace = core::AddressSpace<Address, Size, std::string>;
99 E : MemoryAddressSpace memory_temp;
100 E : for (size_t i = 0; i < num_ranges; ++i) {
101 E : MINIDUMP_MEMORY_DESCRIPTOR descriptor = {};
102 E : if (!memory_list.ReadElement(&descriptor))
103 i : return ANALYSIS_ERROR;
104 :
105 E : Address range_addr = descriptor.StartOfMemoryRange;
106 E : Size range_size = descriptor.Memory.DataSize;
107 : minidump::Minidump::Stream bytes_stream =
108 E : minidump.GetStreamFor(descriptor.Memory);
109 :
110 E : std::string bytes;
111 E : if (!bytes_stream.ReadBytes(range_size, &bytes))
112 i : return ANALYSIS_ERROR;
113 :
114 E : AddressRange new_range(range_addr, range_size);
115 E : if (!new_range.IsValid())
116 i : return ANALYSIS_ERROR;
117 :
118 : // Record the new range and consolidate it with any overlaps.
119 E : if (!RecordMemoryContents(new_range, bytes, &memory_temp))
120 i : return ANALYSIS_ERROR;
121 E : }
122 :
123 : // Now transfer the temp address space to the bytes layer.
124 E : for (const auto& entry : memory_temp) {
125 : // Create the memory record.
126 E : AddressRange new_range(entry.first.start(), entry.first.size());
127 E : BytesRecordPtr bytes_record;
128 E : bytes_layer->CreateRecord(new_range, &bytes_record);
129 E : Bytes* bytes_proto = bytes_record->mutable_data();
130 E : bytes_proto->mutable_data()->assign(entry.second);
131 E : }
132 :
133 E : return ANALYSIS_COMPLETE;
134 E : }
135 :
136 : } // namespace refinery
|