| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verif
ier_win.h" | 5 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verif
ier_win.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> |
| 8 #include <vector> | 9 #include <vector> |
| 9 | 10 |
| 10 #include "base/containers/hash_tables.h" | |
| 11 #include "base/files/file_path.h" | 11 #include "base/files/file_path.h" |
| 12 #include "base/files/memory_mapped_file.h" | 12 #include "base/files/memory_mapped_file.h" |
| 13 #include "base/metrics/sparse_histogram.h" | 13 #include "base/metrics/sparse_histogram.h" |
| 14 #include "base/scoped_native_library.h" | 14 #include "base/scoped_native_library.h" |
| 15 #include "base/strings/utf_string_conversions.h" |
| 15 #include "base/win/pe_image.h" | 16 #include "base/win/pe_image.h" |
| 16 #include "build/build_config.h" | 17 #include "chrome/common/safe_browsing/csd.pb.h" |
| 17 | 18 |
| 18 namespace safe_browsing { | 19 namespace safe_browsing { |
| 19 | 20 |
| 20 namespace { | 21 namespace { |
| 21 | 22 |
| 22 // The maximum amount of bytes that can be reported as modified by | 23 // The maximum amount of bytes that can be reported as modified by VerifyModule. |
| 23 // NewVerifyModule. | |
| 24 const int kMaxModuleModificationBytes = 256; | 24 const int kMaxModuleModificationBytes = 256; |
| 25 | 25 |
| 26 struct Export { | 26 struct Export { |
| 27 Export(void* addr, const std::string& name); | 27 Export(void* addr, const std::string& name); |
| 28 ~Export(); | 28 ~Export(); |
| 29 | 29 |
| 30 bool operator<(const Export& other) const { | 30 bool operator<(const Export& other) const { |
| 31 return addr < other.addr; | 31 return addr < other.addr; |
| 32 } | 32 } |
| 33 | 33 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 48 base::win::PEImageAsData disk_peimage; | 48 base::win::PEImageAsData disk_peimage; |
| 49 | 49 |
| 50 // The module's preferred base address minus the base address it actually | 50 // The module's preferred base address minus the base address it actually |
| 51 // loaded at. | 51 // loaded at. |
| 52 intptr_t image_base_delta; | 52 intptr_t image_base_delta; |
| 53 | 53 |
| 54 // The location of the disk_peimage module's code section minus that of the | 54 // The location of the disk_peimage module's code section minus that of the |
| 55 // mem_peimage module's code section. | 55 // mem_peimage module's code section. |
| 56 intptr_t code_section_delta; | 56 intptr_t code_section_delta; |
| 57 | 57 |
| 58 // The bytes corrected by relocs. | |
| 59 base::hash_set<uintptr_t> reloc_addr; | |
| 60 | |
| 61 // Set true if the relocation table contains a reloc of type that we don't | 58 // Set true if the relocation table contains a reloc of type that we don't |
| 62 // currently handle. | 59 // currently handle. |
| 63 bool unknown_reloc_type; | 60 bool unknown_reloc_type; |
| 64 | 61 |
| 65 // The start of the code section of the in-memory binary. | 62 // The start of the code section of the in-memory binary. |
| 66 uint8_t* mem_code_addr; | 63 uint8_t* mem_code_addr; |
| 67 | 64 |
| 68 // The start of the code section of the on-disk binary. | 65 // The start of the code section of the on-disk binary. |
| 69 uint8_t* disk_code_addr; | 66 uint8_t* disk_code_addr; |
| 70 | 67 |
| 71 // The size of the binary's code section. | 68 // The size of the binary's code section. |
| 72 uint32_t code_size; | 69 uint32_t code_size; |
| 73 | 70 |
| 74 // The exports of the DLL, sorted by address in ascending order. | 71 // The exports of the DLL, sorted by address in ascending order. |
| 75 std::vector<Export> exports; | 72 std::vector<Export> exports; |
| 76 | 73 |
| 77 // The location in the in-memory binary of the latest reloc encountered by | 74 // The location in the in-memory binary of the latest reloc encountered by |
| 78 // |NewEnumRelocsCallback|. | 75 // |EnumRelocsCallback|. |
| 79 uint8_t* last_mem_reloc_position; | 76 uint8_t* last_mem_reloc_position; |
| 80 | 77 |
| 81 // The location in the on-disk binary of the latest reloc encountered by | 78 // The location in the on-disk binary of the latest reloc encountered by |
| 82 // |NewEnumRelocsCallback|. | 79 // |EnumRelocsCallback|. |
| 83 uint8_t* last_disk_reloc_position; | 80 uint8_t* last_disk_reloc_position; |
| 84 | 81 |
| 85 // The number of bytes with a different value on disk and in memory, as | 82 // The number of bytes with a different value on disk and in memory, as |
| 86 // computed by |NewVerifyModule|. | 83 // computed by |VerifyModule|. |
| 87 int bytes_different; | 84 int bytes_different; |
| 88 | 85 |
| 89 // The module state protobuf object that |NewVerifyModule| will populate. | 86 // The module state protobuf object that |VerifyModule| will populate. |
| 90 ClientIncidentReport_EnvironmentData_Process_ModuleState* module_state; | 87 ClientIncidentReport_EnvironmentData_Process_ModuleState* module_state; |
| 91 | 88 |
| 92 private: | 89 private: |
| 93 DISALLOW_COPY_AND_ASSIGN(ModuleVerificationState); | 90 DISALLOW_COPY_AND_ASSIGN(ModuleVerificationState); |
| 94 }; | 91 }; |
| 95 | 92 |
| 96 ModuleVerificationState::ModuleVerificationState(HMODULE hModule) | 93 ModuleVerificationState::ModuleVerificationState(HMODULE hModule) |
| 97 : disk_peimage(hModule), | 94 : disk_peimage(hModule), |
| 98 image_base_delta(0), | 95 image_base_delta(0), |
| 99 code_section_delta(0), | 96 code_section_delta(0), |
| 100 reloc_addr(), | |
| 101 unknown_reloc_type(false), | 97 unknown_reloc_type(false), |
| 102 mem_code_addr(nullptr), | 98 mem_code_addr(nullptr), |
| 103 disk_code_addr(nullptr), | 99 disk_code_addr(nullptr), |
| 104 code_size(0), | 100 code_size(0), |
| 105 last_mem_reloc_position(nullptr), | 101 last_mem_reloc_position(nullptr), |
| 106 last_disk_reloc_position(nullptr), | 102 last_disk_reloc_position(nullptr), |
| 107 bytes_different(0), | 103 bytes_different(0), |
| 108 module_state(nullptr) { | 104 module_state(nullptr) { |
| 109 } | 105 } |
| 110 | 106 |
| 111 ModuleVerificationState::~ModuleVerificationState() { | 107 ModuleVerificationState::~ModuleVerificationState() { |
| 112 } | 108 } |
| 113 | 109 |
| 114 bool ByteAccountedForByReloc(uint8_t* byte_addr, | |
| 115 const ModuleVerificationState& state) { | |
| 116 return ((state.reloc_addr.count(reinterpret_cast<uintptr_t>(byte_addr))) > 0); | |
| 117 } | |
| 118 | |
| 119 // Find which export a modification at address |mem_address| is in. Looks for | 110 // Find which export a modification at address |mem_address| is in. Looks for |
| 120 // the largest export address still smaller than |mem_address|. |start| and | 111 // the largest export address still smaller than |mem_address|. |start| and |
| 121 // |end| must come from a sorted collection. | 112 // |end| must come from a sorted collection. |
| 122 std::vector<Export>::const_iterator FindModifiedExport( | 113 std::vector<Export>::const_iterator FindModifiedExport( |
| 123 uint8_t* mem_address, | 114 uint8_t* mem_address, |
| 124 std::vector<Export>::const_iterator start, | 115 std::vector<Export>::const_iterator start, |
| 125 std::vector<Export>::const_iterator end) { | 116 std::vector<Export>::const_iterator end) { |
| 126 // We get the largest export address still smaller than |addr|. It is | 117 // We get the largest export address still smaller than |addr|. It is |
| 127 // possible that |addr| belongs to some nonexported function located | 118 // possible that |addr| belongs to some nonexported function located |
| 128 // between this export and the following one. | 119 // between this export and the following one. |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 int bytes_in_modification = mem_start - range_start; | 158 int bytes_in_modification = mem_start - range_start; |
| 168 bytes_different += bytes_in_modification; | 159 bytes_different += bytes_in_modification; |
| 169 modification->set_byte_count(bytes_in_modification); | 160 modification->set_byte_count(bytes_in_modification); |
| 170 modification->set_modified_bytes( | 161 modification->set_modified_bytes( |
| 171 range_start, | 162 range_start, |
| 172 std::min(bytes_in_modification, kMaxModuleModificationBytes)); | 163 std::min(bytes_in_modification, kMaxModuleModificationBytes)); |
| 173 } | 164 } |
| 174 return bytes_different; | 165 return bytes_different; |
| 175 } | 166 } |
| 176 | 167 |
| 177 // Checks each byte in the module's code section again the corresponding byte on | |
| 178 // disk, returning the number of bytes differing between the two. Also adds the | |
| 179 // names of any modfied functions exported by name to |modified_exports|. | |
| 180 // |state.exports| must be sorted. | |
| 181 int ExamineBytesDiffInMemory(uint8_t* disk_code_start, | |
| 182 uint8_t* mem_code_start, | |
| 183 uint32_t code_size, | |
| 184 const ModuleVerificationState& state, | |
| 185 std::set<std::string>* modified_exports) { | |
| 186 int bytes_different = 0; | |
| 187 std::vector<Export>::const_iterator export_it = state.exports.begin(); | |
| 188 | |
| 189 for (uint8_t* end = mem_code_start + code_size; mem_code_start != end; | |
| 190 ++mem_code_start) { | |
| 191 if ((*disk_code_start++ != *mem_code_start) && | |
| 192 !ByteAccountedForByReloc(mem_code_start, state)) { | |
| 193 std::vector<Export>::const_iterator modified_export_it = | |
| 194 FindModifiedExport(mem_code_start, export_it, state.exports.end()); | |
| 195 | |
| 196 if (modified_export_it != state.exports.begin()) | |
| 197 modified_exports->insert((modified_export_it - 1)->name); | |
| 198 ++bytes_different; | |
| 199 | |
| 200 // No later byte can belong to an earlier export. | |
| 201 export_it = modified_export_it; | |
| 202 } | |
| 203 } | |
| 204 return bytes_different; | |
| 205 } | |
| 206 | |
| 207 // Adds to |state->reloc_addr| the bytes of the pointer at |address| that are | |
| 208 // corrected by adding |image_base_delta|. | |
| 209 void AddBytesCorrectedByReloc(uintptr_t address, | |
| 210 ModuleVerificationState* state) { | |
| 211 #if defined(ARCH_CPU_LITTLE_ENDIAN) | |
| 212 # define OFFSET(i) i | |
| 213 #else | |
| 214 # define OFFSET(i) (sizeof(uintptr_t) - i) | |
| 215 #endif | |
| 216 | |
| 217 uintptr_t orig_mem_value = *reinterpret_cast<uintptr_t*>(address); | |
| 218 uintptr_t fixed_mem_value = orig_mem_value + state->image_base_delta; | |
| 219 uintptr_t disk_value = | |
| 220 *reinterpret_cast<uintptr_t*>(address + state->code_section_delta); | |
| 221 | |
| 222 uintptr_t diff_before = orig_mem_value ^ disk_value; | |
| 223 uintptr_t shared_after = ~(fixed_mem_value ^ disk_value); | |
| 224 int i = 0; | |
| 225 for (uintptr_t fixed = diff_before & shared_after; fixed; fixed >>= 8, ++i) { | |
| 226 if (fixed & 0xFF) | |
| 227 state->reloc_addr.insert(address + OFFSET(i)); | |
| 228 } | |
| 229 #undef OFFSET | |
| 230 } | |
| 231 | |
| 232 bool AddrIsInCodeSection(void* address, | 168 bool AddrIsInCodeSection(void* address, |
| 233 uint8_t* code_addr, | 169 uint8_t* code_addr, |
| 234 uint32_t code_size) { | 170 uint32_t code_size) { |
| 235 return (code_addr <= address && address < code_addr + code_size); | 171 return (code_addr <= address && address < code_addr + code_size); |
| 236 } | 172 } |
| 237 | 173 |
| 238 bool NewEnumRelocsCallback(const base::win::PEImage& mem_peimage, | 174 bool EnumRelocsCallback(const base::win::PEImage& mem_peimage, |
| 239 WORD type, | 175 WORD type, |
| 240 void* address, | 176 void* address, |
| 241 void* cookie) { | 177 void* cookie) { |
| 242 ModuleVerificationState* state = | 178 ModuleVerificationState* state = |
| 243 reinterpret_cast<ModuleVerificationState*>(cookie); | 179 reinterpret_cast<ModuleVerificationState*>(cookie); |
| 244 | 180 |
| 245 // If not in the code section return true to continue to the next reloc. | 181 // If not in the code section return true to continue to the next reloc. |
| 246 if (!AddrIsInCodeSection(address, state->mem_code_addr, state->code_size)) | 182 if (!AddrIsInCodeSection(address, state->mem_code_addr, state->code_size)) |
| 247 return true; | 183 return true; |
| 248 | 184 |
| 249 switch (type) { | 185 switch (type) { |
| 250 case IMAGE_REL_BASED_ABSOLUTE: // 0 | 186 case IMAGE_REL_BASED_ABSOLUTE: // 0 |
| 251 break; | 187 break; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 default: | 248 default: |
| 313 // TODO(robertshield): Find a reliable description of the behaviour of the | 249 // TODO(robertshield): Find a reliable description of the behaviour of the |
| 314 // remaining types of relocation and handle them. | 250 // remaining types of relocation and handle them. |
| 315 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.ModuleBaseRelocation", type); | 251 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.ModuleBaseRelocation", type); |
| 316 state->unknown_reloc_type = true; | 252 state->unknown_reloc_type = true; |
| 317 break; | 253 break; |
| 318 } | 254 } |
| 319 return true; | 255 return true; |
| 320 } | 256 } |
| 321 | 257 |
| 322 bool EnumRelocsCallback(const base::win::PEImage& mem_peimage, | |
| 323 WORD type, | |
| 324 void* address, | |
| 325 void* cookie) { | |
| 326 ModuleVerificationState* state = | |
| 327 reinterpret_cast<ModuleVerificationState*>(cookie); | |
| 328 | |
| 329 uint8_t* mem_code_addr = NULL; | |
| 330 uint8_t* disk_code_addr = NULL; | |
| 331 uint32_t code_size = 0; | |
| 332 if (!GetCodeAddrsAndSize(mem_peimage, | |
| 333 state->disk_peimage, | |
| 334 &mem_code_addr, | |
| 335 &disk_code_addr, | |
| 336 &code_size)) | |
| 337 return false; | |
| 338 | |
| 339 // If not in the code section return true to continue to the next reloc. | |
| 340 if (!AddrIsInCodeSection(address, mem_code_addr, code_size)) | |
| 341 return true; | |
| 342 | |
| 343 switch (type) { | |
| 344 case IMAGE_REL_BASED_ABSOLUTE: // 0 | |
| 345 // Absolute type relocations are a noop, sometimes used to pad a section | |
| 346 // of relocations. | |
| 347 break; | |
| 348 case IMAGE_REL_BASED_HIGHLOW: // 3 | |
| 349 // The base relocation applies all 32 bits of the difference to the 32-bit | |
| 350 // field at offset. | |
| 351 AddBytesCorrectedByReloc(reinterpret_cast<uintptr_t>(address), state); | |
| 352 break; | |
| 353 case IMAGE_REL_BASED_DIR64: // 10 | |
| 354 // The base relocation applies the difference to the 64-bit field at | |
| 355 // offset. | |
| 356 // TODO(robertshield): Handle this type of reloc. | |
| 357 break; | |
| 358 default: | |
| 359 // TODO(robertshield): Find a reliable description of the behaviour of the | |
| 360 // remaining types of relocation and handle them. | |
| 361 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.ModuleBaseRelocation", type); | |
| 362 state->unknown_reloc_type = true; | |
| 363 break; | |
| 364 } | |
| 365 return true; | |
| 366 } | |
| 367 | |
| 368 bool EnumExportsCallback(const base::win::PEImage& mem_peimage, | 258 bool EnumExportsCallback(const base::win::PEImage& mem_peimage, |
| 369 DWORD ordinal, | 259 DWORD ordinal, |
| 370 DWORD hint, | 260 DWORD hint, |
| 371 LPCSTR name, | 261 LPCSTR name, |
| 372 PVOID function_addr, | 262 PVOID function_addr, |
| 373 LPCSTR forward, | 263 LPCSTR forward, |
| 374 PVOID cookie) { | 264 PVOID cookie) { |
| 375 std::vector<Export>* exports = reinterpret_cast<std::vector<Export>*>(cookie); | 265 std::vector<Export>* exports = reinterpret_cast<std::vector<Export>*>(cookie); |
| 376 if (name) | 266 if (name) |
| 377 exports->push_back(Export(function_addr, std::string(name))); | 267 exports->push_back(Export(function_addr, std::string(name))); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 403 // Get the address of the code section in the module mapped as data from disk. | 293 // Get the address of the code section in the module mapped as data from disk. |
| 404 DWORD disk_code_offset = 0; | 294 DWORD disk_code_offset = 0; |
| 405 if (!mem_peimage.ImageAddrToOnDiskOffset( | 295 if (!mem_peimage.ImageAddrToOnDiskOffset( |
| 406 reinterpret_cast<void*>(*mem_code_addr), &disk_code_offset)) | 296 reinterpret_cast<void*>(*mem_code_addr), &disk_code_offset)) |
| 407 return false; | 297 return false; |
| 408 *disk_code_addr = | 298 *disk_code_addr = |
| 409 reinterpret_cast<uint8_t*>(disk_peimage.module()) + disk_code_offset; | 299 reinterpret_cast<uint8_t*>(disk_peimage.module()) + disk_code_offset; |
| 410 return true; | 300 return true; |
| 411 } | 301 } |
| 412 | 302 |
| 413 VerificationResult NewVerifyModule( | 303 bool VerifyModule( |
| 414 const wchar_t* module_name, | 304 const wchar_t* module_name, |
| 415 ClientIncidentReport_EnvironmentData_Process_ModuleState* module_state) { | 305 ClientIncidentReport_EnvironmentData_Process_ModuleState* module_state, |
| 416 VerificationResult result = { MODULE_STATE_UNKNOWN, 0, false, }; | 306 int* num_bytes_different) { |
| 307 using ModuleState = ClientIncidentReport_EnvironmentData_Process_ModuleState; |
| 308 *num_bytes_different = 0; |
| 309 module_state->set_name(base::WideToUTF8(module_name)); |
| 310 module_state->set_modified_state(ModuleState::MODULE_STATE_UNKNOWN); |
| 417 | 311 |
| 418 // Get module handle, load a copy from disk as data and create PEImages. | 312 // Get module handle, load a copy from disk as data and create PEImages. |
| 419 HMODULE module_handle = NULL; | 313 HMODULE module_handle = NULL; |
| 420 if (!GetModuleHandleEx(0, module_name, &module_handle)) | 314 if (!GetModuleHandleEx(0, module_name, &module_handle)) |
| 421 return result; | 315 return false; |
| 422 base::ScopedNativeLibrary native_library(module_handle); | 316 base::ScopedNativeLibrary native_library(module_handle); |
| 423 | 317 |
| 424 WCHAR module_path[MAX_PATH] = {}; | 318 WCHAR module_path[MAX_PATH] = {}; |
| 425 DWORD length = | 319 DWORD length = |
| 426 GetModuleFileName(module_handle, module_path, arraysize(module_path)); | 320 GetModuleFileName(module_handle, module_path, arraysize(module_path)); |
| 427 if (!length || length == arraysize(module_path)) | 321 if (!length || length == arraysize(module_path)) |
| 428 return result; | 322 return false; |
| 429 | 323 |
| 430 base::MemoryMappedFile mapped_module; | 324 base::MemoryMappedFile mapped_module; |
| 431 if (!mapped_module.Initialize(base::FilePath(module_path))) | 325 if (!mapped_module.Initialize(base::FilePath(module_path))) |
| 432 return result; | 326 return false; |
| 433 ModuleVerificationState state( | 327 ModuleVerificationState state( |
| 434 reinterpret_cast<HMODULE>(const_cast<uint8*>(mapped_module.data()))); | 328 reinterpret_cast<HMODULE>(const_cast<uint8*>(mapped_module.data()))); |
| 435 | 329 |
| 436 base::win::PEImage mem_peimage(module_handle); | 330 base::win::PEImage mem_peimage(module_handle); |
| 437 if (!mem_peimage.VerifyMagic() || !state.disk_peimage.VerifyMagic()) | 331 if (!mem_peimage.VerifyMagic() || !state.disk_peimage.VerifyMagic()) |
| 438 return result; | 332 return false; |
| 439 | 333 |
| 440 // Get the list of exports and sort them by address for efficient lookups. | 334 // Get the list of exports and sort them by address for efficient lookups. |
| 441 mem_peimage.EnumExports(EnumExportsCallback, &state.exports); | 335 mem_peimage.EnumExports(EnumExportsCallback, &state.exports); |
| 442 std::sort(state.exports.begin(), state.exports.end()); | 336 std::sort(state.exports.begin(), state.exports.end()); |
| 443 | 337 |
| 444 // Get the addresses of the code sections then calculate |code_section_delta| | 338 // Get the addresses of the code sections then calculate |code_section_delta| |
| 445 // and |image_base_delta|. | 339 // and |image_base_delta|. |
| 446 if (!GetCodeAddrsAndSize(mem_peimage, | 340 if (!GetCodeAddrsAndSize(mem_peimage, |
| 447 state.disk_peimage, | 341 state.disk_peimage, |
| 448 &state.mem_code_addr, | 342 &state.mem_code_addr, |
| 449 &state.disk_code_addr, | 343 &state.disk_code_addr, |
| 450 &state.code_size)) | 344 &state.code_size)) |
| 451 return result; | 345 return false; |
| 452 | 346 |
| 453 state.module_state = module_state; | 347 state.module_state = module_state; |
| 454 state.last_mem_reloc_position = state.mem_code_addr; | 348 state.last_mem_reloc_position = state.mem_code_addr; |
| 455 state.last_disk_reloc_position = state.disk_code_addr; | 349 state.last_disk_reloc_position = state.disk_code_addr; |
| 456 state.code_section_delta = state.disk_code_addr - state.mem_code_addr; | 350 state.code_section_delta = state.disk_code_addr - state.mem_code_addr; |
| 457 | 351 |
| 458 uint8_t* preferred_image_base = reinterpret_cast<uint8_t*>( | 352 uint8_t* preferred_image_base = reinterpret_cast<uint8_t*>( |
| 459 state.disk_peimage.GetNTHeaders()->OptionalHeader.ImageBase); | 353 state.disk_peimage.GetNTHeaders()->OptionalHeader.ImageBase); |
| 460 state.image_base_delta = | 354 state.image_base_delta = |
| 461 preferred_image_base - reinterpret_cast<uint8_t*>(mem_peimage.module()); | 355 preferred_image_base - reinterpret_cast<uint8_t*>(mem_peimage.module()); |
| 462 | 356 |
| 463 state.last_mem_reloc_position = state.mem_code_addr; | 357 state.last_mem_reloc_position = state.mem_code_addr; |
| 464 state.last_disk_reloc_position = state.disk_code_addr; | 358 state.last_disk_reloc_position = state.disk_code_addr; |
| 465 | 359 |
| 466 // Enumerate relocations and verify the bytes between them. | 360 // Enumerate relocations and verify the bytes between them. |
| 467 result.verification_completed = | 361 bool scan_complete = mem_peimage.EnumRelocs(EnumRelocsCallback, &state); |
| 468 mem_peimage.EnumRelocs(NewEnumRelocsCallback, &state); | |
| 469 | 362 |
| 470 if (result.verification_completed) { | 363 if (scan_complete) { |
| 471 size_t range_size = | 364 size_t range_size = |
| 472 state.code_size - (state.last_mem_reloc_position - state.mem_code_addr); | 365 state.code_size - (state.last_mem_reloc_position - state.mem_code_addr); |
| 473 // Inspect the last chunk spanning from the furthest relocation to the end | 366 // Inspect the last chunk spanning from the furthest relocation to the end |
| 474 // of the code section. | 367 // of the code section. |
| 475 state.bytes_different += ExamineByteRangeDiff( | 368 state.bytes_different += ExamineByteRangeDiff( |
| 476 state.last_disk_reloc_position, | 369 state.last_disk_reloc_position, |
| 477 state.last_mem_reloc_position, | 370 state.last_mem_reloc_position, |
| 478 range_size, | 371 range_size, |
| 479 &state); | 372 &state); |
| 480 } | 373 } |
| 481 result.num_bytes_different = state.bytes_different; | 374 *num_bytes_different = state.bytes_different; |
| 482 | 375 |
| 483 // Report STATE_MODIFIED if any difference was found, regardless of whether or | 376 // Report STATE_MODIFIED if any difference was found, regardless of whether or |
| 484 // not the entire module was scanned. Report STATE_UNMODIFIED only if the | 377 // not the entire module was scanned. Report STATE_UNMODIFIED only if the |
| 485 // entire module was scanned and understood. | 378 // entire module was scanned and understood. |
| 486 if (state.bytes_different) | 379 if (state.bytes_different) |
| 487 result.state = MODULE_STATE_MODIFIED; | 380 module_state->set_modified_state(ModuleState::MODULE_STATE_MODIFIED); |
| 488 else if (!state.unknown_reloc_type && result.verification_completed) | 381 else if (!state.unknown_reloc_type && scan_complete) |
| 489 result.state = MODULE_STATE_UNMODIFIED; | 382 module_state->set_modified_state(ModuleState::MODULE_STATE_UNMODIFIED); |
| 490 | 383 |
| 491 return result; | 384 return scan_complete; |
| 492 } | |
| 493 | |
| 494 ModuleState VerifyModule(const wchar_t* module_name, | |
| 495 std::set<std::string>* modified_exports, | |
| 496 int* num_bytes_different) { | |
| 497 // Get module handle, load a copy from disk as data and create PEImages. | |
| 498 HMODULE module_handle = NULL; | |
| 499 if (!GetModuleHandleEx(0, module_name, &module_handle)) | |
| 500 return MODULE_STATE_UNKNOWN; | |
| 501 base::ScopedNativeLibrary native_library(module_handle); | |
| 502 | |
| 503 WCHAR module_path[MAX_PATH] = {}; | |
| 504 DWORD length = | |
| 505 GetModuleFileName(module_handle, module_path, arraysize(module_path)); | |
| 506 if (!length || length == arraysize(module_path)) | |
| 507 return MODULE_STATE_UNKNOWN; | |
| 508 | |
| 509 base::MemoryMappedFile mapped_module; | |
| 510 if (!mapped_module.Initialize(base::FilePath(module_path))) | |
| 511 return MODULE_STATE_UNKNOWN; | |
| 512 ModuleVerificationState state( | |
| 513 reinterpret_cast<HMODULE>(const_cast<uint8*>(mapped_module.data()))); | |
| 514 | |
| 515 base::win::PEImage mem_peimage(module_handle); | |
| 516 if (!mem_peimage.VerifyMagic() || !state.disk_peimage.VerifyMagic()) | |
| 517 return MODULE_STATE_UNKNOWN; | |
| 518 | |
| 519 // Get the list of exports. | |
| 520 mem_peimage.EnumExports(EnumExportsCallback, &state.exports); | |
| 521 std::sort(state.exports.begin(), state.exports.end()); | |
| 522 | |
| 523 // Get the addresses of the code sections then calculate |code_section_delta| | |
| 524 // and |image_base_delta|. | |
| 525 uint8_t* mem_code_addr = NULL; | |
| 526 uint8_t* disk_code_addr = NULL; | |
| 527 uint32_t code_size = 0; | |
| 528 if (!GetCodeAddrsAndSize(mem_peimage, | |
| 529 state.disk_peimage, | |
| 530 &mem_code_addr, | |
| 531 &disk_code_addr, | |
| 532 &code_size)) | |
| 533 return MODULE_STATE_UNKNOWN; | |
| 534 | |
| 535 state.code_section_delta = disk_code_addr - mem_code_addr; | |
| 536 | |
| 537 uint8_t* preferred_image_base = reinterpret_cast<uint8_t*>( | |
| 538 state.disk_peimage.GetNTHeaders()->OptionalHeader.ImageBase); | |
| 539 state.image_base_delta = | |
| 540 preferred_image_base - reinterpret_cast<uint8_t*>(mem_peimage.module()); | |
| 541 | |
| 542 // Get the relocations. | |
| 543 mem_peimage.EnumRelocs(EnumRelocsCallback, &state); | |
| 544 if (state.unknown_reloc_type) | |
| 545 return MODULE_STATE_UNKNOWN; | |
| 546 | |
| 547 // Count the modified bytes (after accounting for relocs) and get the set of | |
| 548 // modified functions. | |
| 549 *num_bytes_different = ExamineBytesDiffInMemory(disk_code_addr, | |
| 550 mem_code_addr, | |
| 551 code_size, | |
| 552 state, | |
| 553 modified_exports); | |
| 554 | |
| 555 return *num_bytes_different ? MODULE_STATE_MODIFIED : MODULE_STATE_UNMODIFIED; | |
| 556 } | 385 } |
| 557 | 386 |
| 558 } // namespace safe_browsing | 387 } // namespace safe_browsing |
| OLD | NEW |