Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |