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