Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(118)

Side by Side Diff: snapshot/win/pe_image_resource_reader.cc

Issue 1475023004: Get module versions and types from in-memory images (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Crashpad Authors. 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 "snapshot/win/pe_image_resource_reader.h"
16
17 #include "base/logging.h"
18 #include "base/memory/scoped_ptr.h"
19
20 namespace crashpad {
21
22 PEImageResourceReader::PEImageResourceReader()
23 : process_reader_(nullptr),
24 resources_range_(),
25 module_base_(0),
26 module_name_(),
27 initialized_() {
28 }
29
30 PEImageResourceReader::~PEImageResourceReader() {
31 }
32
33 bool PEImageResourceReader::Initialize(
34 ProcessReaderWin* process_reader,
35 const IMAGE_DATA_DIRECTORY& resources_directory_entry,
36 const CheckedWinAddressRange& module_range,
37 std::string module_name) {
38 INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
39
40 process_reader_ = process_reader;
41
42 resources_range_.SetRange(
43 process_reader_->Is64Bit(),
44 module_range.Base() + resources_directory_entry.VirtualAddress,
45 resources_directory_entry.Size);
46 if (!resources_range_.IsValid()) {
47 LOG(WARNING) << "invalid resources range " << resources_range_.AsString()
48 << " for " << module_name;
49 return false;
50 }
51
52 if (!module_range.ContainsRange(resources_range_)) {
53 LOG(WARNING) << "resources range " << resources_range_.AsString()
54 << " outside of module range " << module_range.AsString()
55 << " for " << module_name;
56 return false;
57 }
58
59 module_base_ = module_range.Base();
60 module_name_ = module_name;
61
62 INITIALIZATION_STATE_SET_VALID(initialized_);
63 return true;
64 }
65
66 bool PEImageResourceReader::FindResourceByID(uint16_t type,
67 uint16_t name,
68 uint16_t language,
69 WinVMAddress* address,
70 WinVMSize* size,
71 uint32_t* code_page) const {
72 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
73
74 // The root resource directory is at the beginning of the resources area
75 // within the module.
76 const uint32_t name_directory_offset =
77 GetEntryFromResourceDirectoryByID(0, type, true);
78 if (!name_directory_offset) {
79 return false;
80 }
81
82 const uint32_t language_directory_offset =
83 GetEntryFromResourceDirectoryByID(name_directory_offset, name, true);
84 if (!language_directory_offset) {
85 return false;
86 }
87
88 // The definition of IMAGE_RESOURCE_DIRECTORY_ENTRY in <winnt.h> has a comment
89 // saying that its offsets are relative to “the resource directory of the data
90 // associated with this directory entry”. That could be interpreted to mean
91 // that language_directory_offset is relative to name_directory_offset, since
92 // the language directory entry is found within the name directory. This is
93 // not correct. All resource offsets are relative to the resources area within
94 // the module.
95 const uint32_t data_offset = GetEntryFromResourceDirectoryByLanguage(
96 language_directory_offset, language);
97 if (!data_offset) {
98 return false;
99 }
100
101 IMAGE_RESOURCE_DATA_ENTRY data_entry;
102 if (!CheckedReadMemory(resources_range_.Base() + data_offset,
103 sizeof(data_entry),
104 &data_entry)) {
105 return false;
106 }
107
108 // The definition of IMAGE_RESOURCE_DATA_ENTRY in <winnt.h> has a comment
109 // saying that OffsetToData is relative to the beginning of the resource data.
110 // This is not correct. It’s module-relative.
111 *address = module_base_ + data_entry.OffsetToData;
112 *size = data_entry.Size;
113 if (code_page) {
114 *code_page = data_entry.CodePage;
115 }
116
117 return true;
118 }
119
120 uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByID(
121 uint32_t language_directory_offset,
122 uint16_t id,
123 bool want_subdirectory) const {
124 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
125
126 std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_id;
127 if (!ReadResourceDirectory(
128 language_directory_offset, nullptr, nullptr, &entries_by_id)) {
129 return 0;
130 }
131
132 for (const auto& entry : entries_by_id) {
133 if (!entry.NameIsString && entry.Id == id) {
134 if (static_cast<bool>(entry.DataIsDirectory) != want_subdirectory) {
135 LOG(WARNING) << "expected " << (want_subdirectory ? "" : "non-")
136 << "directory for entry id " << id << " in "
137 << module_name_;
138 return 0;
139 }
140
141 return entry.DataIsDirectory ? entry.OffsetToDirectory
scottmg 2015/11/26 21:30:00 The != above warns but this one doesn't? Silly.
Mark Mentovai 2015/11/27 17:56:28 scottmg wrote:
scottmg 2015/11/27 18:06:58 Sorry, I just meant that there wasn't a need to st
Mark Mentovai 2015/11/27 21:40:37 scottmg wrote:
142 : entry.OffsetToData;
143 }
144 }
145
146 return 0;
147 }
148
149 uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByLanguage(
150 uint32_t resource_directory_offset,
151 uint16_t language) const {
152 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
153
154 std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_language;
155 if (!ReadResourceDirectory(
156 resource_directory_offset, nullptr, nullptr, &entries_by_language)) {
157 return 0;
158 }
159
160 if (entries_by_language.empty()) {
161 return 0;
162 }
163
164 // https://msdn.microsoft.com/en-us/library/cc194810.aspx
165 //
166 // TODO(mark): It seems like the FindResourceEx() might do something more
scottmg 2015/11/26 21:30:00 Remove "the ".
Mark Mentovai 2015/12/01 18:48:01 scottmg wrote:
167 // complex. It would be best to mimic its behavior.
168 std::vector<uint16_t> try_languages;
169 try_languages.push_back(language);
170 try_languages.push_back(MAKELANGID(PRIMARYLANGID(language), SUBLANG_NEUTRAL));
171 try_languages.push_back(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
172 try_languages.push_back(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT));
scottmg 2015/11/26 21:30:00 Presumably this should be the UI language rather t
Mark Mentovai 2015/12/01 18:48:01 scottmg wrote:
173
174 for (const auto try_language : try_languages) {
175 for (const auto& entry : entries_by_language) {
176 if (!entry.NameIsString && entry.Id == try_language) {
177 if (entry.DataIsDirectory) {
178 LOG(WARNING) << "expected non-directory for entry language "
179 << try_language << " in " << module_name_;
180 return 0;
181 }
182
183 return entry.OffsetToData;
184 }
185 }
186 }
187
188 // Fall back to the first entry in the list.
189 const auto& entry = entries_by_language.front();
190 if (entry.DataIsDirectory) {
191 LOG(WARNING) << "expected non-directory for entry in " << module_name_;
192 return 0;
193 }
194
195 return entry.OffsetToData;
196 }
197
198 bool PEImageResourceReader::ReadResourceDirectory(
199 uint32_t resource_directory_offset,
200 IMAGE_RESOURCE_DIRECTORY* resource_directory,
201 std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* named_entries,
202 std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* id_entries) const {
203 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
204
205 // resource_directory is optional, but it’s still needed locally even if the
206 // caller isn’t interested in it.
207 scoped_ptr<IMAGE_RESOURCE_DIRECTORY> local_resource_directory;
208 if (!resource_directory) {
209 local_resource_directory.reset(new IMAGE_RESOURCE_DIRECTORY);
210 resource_directory = local_resource_directory.get();
211 }
212
213 const WinVMAddress address =
214 resources_range_.Base() + resource_directory_offset;
215
216 if (!CheckedReadMemory(
217 address, sizeof(*resource_directory), resource_directory)) {
218 LOG(WARNING) << "could not read resource directory";
219 return false;
220 }
221
222 if (named_entries) {
223 named_entries->clear();
224 named_entries->resize(resource_directory->NumberOfNamedEntries);
225 if (!named_entries->empty() &&
226 !CheckedReadMemory(address + sizeof(*resource_directory),
227 named_entries->size() * sizeof((*named_entries)[0]),
228 &(*named_entries)[0])) {
229 LOG(WARNING) << "could not read resource directory named entries";
230 return false;
231 }
232 }
233
234 if (id_entries) {
235 id_entries->clear();
236 id_entries->resize(resource_directory->NumberOfIdEntries);
237 if (!id_entries->empty() &&
238 !CheckedReadMemory(address + sizeof(*resource_directory) +
239 resource_directory->NumberOfNamedEntries *
240 sizeof((*named_entries)[0]),
scottmg 2015/11/26 21:30:00 Just confirming that this is OK when named_entries
Mark Mentovai 2015/12/01 18:48:01 scottmg wrote:
241 id_entries->size() * sizeof((*id_entries)[0]),
242 &(*id_entries)[0])) {
243 LOG(WARNING) << "could not read resource directory ID entries";
244 return false;
245 }
246 }
247
248 return true;
249 }
250
251 bool PEImageResourceReader::CheckedReadMemory(WinVMAddress address,
scottmg 2015/11/26 21:30:00 Can we avoid duplicating this here and in PEImageR
Mark Mentovai 2015/12/01 18:48:01 scottmg wrote:
252 WinVMSize size,
253 void* into) const {
254 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
255
256 CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size);
257 if (!read_range.IsValid()) {
258 LOG(WARNING) << "invalid read range: " << read_range.AsString();
259 return false;
260 }
261 if (!resources_range_.ContainsRange(read_range)) {
262 LOG(WARNING) << "attempt to read outside of module " << module_name_
263 << " resources at range: " << read_range.AsString();
264 return false;
265 }
266 return process_reader_->ReadMemory(address, size, into);
267 }
268
269 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698