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 <algorithm> |
| 18 |
| 19 #include "base/logging.h" |
| 20 #include "base/memory/scoped_ptr.h" |
| 21 |
| 22 namespace { |
| 23 |
| 24 void AddLanguageAndNeutralSublanguage(std::vector<uint16_t>* languages, |
| 25 uint16_t language) { |
| 26 languages->push_back(language); |
| 27 if (SUBLANGID(language) != SUBLANG_NEUTRAL) { |
| 28 languages->push_back(MAKELANGID(PRIMARYLANGID(language), SUBLANG_NEUTRAL)); |
| 29 } |
| 30 } |
| 31 |
| 32 } // namespace |
| 33 |
| 34 namespace crashpad { |
| 35 |
| 36 PEImageResourceReader::PEImageResourceReader() |
| 37 : resources_subrange_reader_(), |
| 38 module_base_(0), |
| 39 initialized_() { |
| 40 } |
| 41 |
| 42 PEImageResourceReader::~PEImageResourceReader() {} |
| 43 |
| 44 bool PEImageResourceReader::Initialize( |
| 45 const ProcessSubrangeReader& module_subrange_reader, |
| 46 const IMAGE_DATA_DIRECTORY& resources_directory_entry) { |
| 47 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| 48 |
| 49 module_base_ = module_subrange_reader.Base(); |
| 50 |
| 51 if (!resources_subrange_reader_.InitializeSubrange( |
| 52 module_subrange_reader, |
| 53 module_base_ + resources_directory_entry.VirtualAddress, |
| 54 resources_directory_entry.Size, |
| 55 "resources")) { |
| 56 return false; |
| 57 } |
| 58 |
| 59 INITIALIZATION_STATE_SET_VALID(initialized_); |
| 60 return true; |
| 61 } |
| 62 |
| 63 bool PEImageResourceReader::FindResourceByID(uint16_t type, |
| 64 uint16_t name, |
| 65 uint16_t language, |
| 66 WinVMAddress* address, |
| 67 WinVMSize* size, |
| 68 uint32_t* code_page) const { |
| 69 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 70 |
| 71 // The root resource directory is at the beginning of the resources area |
| 72 // within the module. |
| 73 const uint32_t name_directory_offset = |
| 74 GetEntryFromResourceDirectoryByID(0, type, true); |
| 75 if (!name_directory_offset) { |
| 76 return false; |
| 77 } |
| 78 |
| 79 const uint32_t language_directory_offset = |
| 80 GetEntryFromResourceDirectoryByID(name_directory_offset, name, true); |
| 81 if (!language_directory_offset) { |
| 82 return false; |
| 83 } |
| 84 |
| 85 // The definition of IMAGE_RESOURCE_DIRECTORY_ENTRY in <winnt.h> has a comment |
| 86 // saying that its offsets are relative to “the resource directory of the data |
| 87 // associated with this directory entry”. That could be interpreted to mean |
| 88 // that language_directory_offset is relative to name_directory_offset, since |
| 89 // the language directory entry is found within the name directory. This is |
| 90 // not correct. All resource offsets are relative to the resources area within |
| 91 // the module. |
| 92 const uint32_t data_offset = GetEntryFromResourceDirectoryByLanguage( |
| 93 language_directory_offset, language); |
| 94 if (!data_offset) { |
| 95 return false; |
| 96 } |
| 97 |
| 98 IMAGE_RESOURCE_DATA_ENTRY data_entry; |
| 99 if (!resources_subrange_reader_.ReadMemory( |
| 100 resources_subrange_reader_.Base() + data_offset, |
| 101 sizeof(data_entry), |
| 102 &data_entry)) { |
| 103 LOG(WARNING) << "could not read resource data entry from " |
| 104 << resources_subrange_reader_.name(); |
| 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 const auto entry_it = |
| 133 std::find_if(entries_by_id.begin(), |
| 134 entries_by_id.end(), |
| 135 [id](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) { |
| 136 return !entry.NameIsString && entry.Id == id; |
| 137 }); |
| 138 if (entry_it != entries_by_id.end()) { |
| 139 if ((entry_it->DataIsDirectory != 0) != want_subdirectory) { |
| 140 LOG(WARNING) << "expected " << (want_subdirectory ? "" : "non-") |
| 141 << "directory for entry id " << id << " in " |
| 142 << resources_subrange_reader_.name(); |
| 143 return 0; |
| 144 } |
| 145 |
| 146 return entry_it->DataIsDirectory ? entry_it->OffsetToDirectory |
| 147 : entry_it->OffsetToData; |
| 148 } |
| 149 |
| 150 return 0; |
| 151 } |
| 152 |
| 153 uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByLanguage( |
| 154 uint32_t resource_directory_offset, |
| 155 uint16_t language) const { |
| 156 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 157 |
| 158 std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_language; |
| 159 if (!ReadResourceDirectory( |
| 160 resource_directory_offset, nullptr, nullptr, &entries_by_language)) { |
| 161 return 0; |
| 162 } |
| 163 |
| 164 if (entries_by_language.empty()) { |
| 165 return 0; |
| 166 } |
| 167 |
| 168 // https://msdn.microsoft.com/en-us/library/cc194810.aspx |
| 169 // |
| 170 // TODO(mark): It seems like FindResourceEx() might do something more complex. |
| 171 // It would be best to mimic its behavior. |
| 172 std::vector<uint16_t> try_languages; |
| 173 if (PRIMARYLANGID(language) != LANG_NEUTRAL) { |
| 174 AddLanguageAndNeutralSublanguage(&try_languages, language); |
| 175 } else { |
| 176 if (SUBLANGID(language) != SUBLANG_SYS_DEFAULT) { |
| 177 AddLanguageAndNeutralSublanguage(&try_languages, |
| 178 LANGIDFROMLCID(GetThreadLocale())); |
| 179 AddLanguageAndNeutralSublanguage(&try_languages, |
| 180 LANGIDFROMLCID(GetUserDefaultLCID())); |
| 181 } |
| 182 if (SUBLANGID(language) != SUBLANG_DEFAULT) { |
| 183 AddLanguageAndNeutralSublanguage(&try_languages, |
| 184 LANGIDFROMLCID(GetSystemDefaultLCID())); |
| 185 } |
| 186 } |
| 187 |
| 188 try_languages.push_back(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); |
| 189 try_languages.push_back(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT)); |
| 190 |
| 191 for (const auto try_language : try_languages) { |
| 192 const auto entry_it = std::find_if( |
| 193 entries_by_language.begin(), |
| 194 entries_by_language.end(), |
| 195 [try_language](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) { |
| 196 return !entry.NameIsString && entry.Id == try_language; |
| 197 }); |
| 198 if (entry_it != entries_by_language.end()) { |
| 199 if (entry_it->DataIsDirectory) { |
| 200 LOG(WARNING) << "expected non-directory for entry language " |
| 201 << try_language << " in " |
| 202 << resources_subrange_reader_.name(); |
| 203 return 0; |
| 204 } |
| 205 |
| 206 return entry_it->OffsetToData; |
| 207 } |
| 208 } |
| 209 |
| 210 // Fall back to the first entry in the list. |
| 211 const auto& entry = entries_by_language.front(); |
| 212 if (entry.DataIsDirectory) { |
| 213 LOG(WARNING) << "expected non-directory for entry in " |
| 214 << resources_subrange_reader_.name(); |
| 215 return 0; |
| 216 } |
| 217 |
| 218 return entry.OffsetToData; |
| 219 } |
| 220 |
| 221 bool PEImageResourceReader::ReadResourceDirectory( |
| 222 uint32_t resource_directory_offset, |
| 223 IMAGE_RESOURCE_DIRECTORY* resource_directory, |
| 224 std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* named_entries, |
| 225 std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* id_entries) const { |
| 226 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 227 |
| 228 // resource_directory is optional, but it’s still needed locally even if the |
| 229 // caller isn’t interested in it. |
| 230 scoped_ptr<IMAGE_RESOURCE_DIRECTORY> local_resource_directory; |
| 231 if (!resource_directory) { |
| 232 local_resource_directory.reset(new IMAGE_RESOURCE_DIRECTORY); |
| 233 resource_directory = local_resource_directory.get(); |
| 234 } |
| 235 |
| 236 const WinVMAddress address = |
| 237 resources_subrange_reader_.Base() + resource_directory_offset; |
| 238 |
| 239 if (!resources_subrange_reader_.ReadMemory( |
| 240 address, sizeof(*resource_directory), resource_directory)) { |
| 241 LOG(WARNING) << "could not read resource directory from " |
| 242 << resources_subrange_reader_.name(); |
| 243 return false; |
| 244 } |
| 245 |
| 246 if (named_entries) { |
| 247 named_entries->clear(); |
| 248 named_entries->resize(resource_directory->NumberOfNamedEntries); |
| 249 if (!named_entries->empty() && |
| 250 !resources_subrange_reader_.ReadMemory( |
| 251 address + sizeof(*resource_directory), |
| 252 named_entries->size() * sizeof((*named_entries)[0]), |
| 253 &(*named_entries)[0])) { |
| 254 LOG(WARNING) << "could not read resource directory named entries from " |
| 255 << resources_subrange_reader_.name(); |
| 256 return false; |
| 257 } |
| 258 } |
| 259 |
| 260 if (id_entries) { |
| 261 id_entries->clear(); |
| 262 id_entries->resize(resource_directory->NumberOfIdEntries); |
| 263 if (!id_entries->empty() && |
| 264 !resources_subrange_reader_.ReadMemory( |
| 265 address + sizeof(*resource_directory) + |
| 266 resource_directory->NumberOfNamedEntries * |
| 267 sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY), |
| 268 id_entries->size() * sizeof((*id_entries)[0]), |
| 269 &(*id_entries)[0])) { |
| 270 LOG(WARNING) << "could not read resource directory ID entries from " |
| 271 << resources_subrange_reader_.name(); |
| 272 return false; |
| 273 } |
| 274 } |
| 275 |
| 276 return true; |
| 277 } |
| 278 |
| 279 } // namespace crashpad |
OLD | NEW |