Index: chrome/browser/safe_browsing/module_integrity_verifier.cc |
diff --git a/chrome/browser/safe_browsing/module_integrity_verifier.cc b/chrome/browser/safe_browsing/module_integrity_verifier.cc |
index 82b9aca1c0dc193117db9641c95224b45415d15f..906e97a9e3805d094906cfc8e77a645fc6e11882 100644 |
--- a/chrome/browser/safe_browsing/module_integrity_verifier.cc |
+++ b/chrome/browser/safe_browsing/module_integrity_verifier.cc |
@@ -4,6 +4,8 @@ |
#include "chrome/browser/safe_browsing/module_integrity_verifier.h" |
+#include <set> |
+ |
#include "base/files/file_path.h" |
#include "base/files/memory_mapped_file.h" |
#include "base/scoped_native_library.h" |
@@ -13,41 +15,118 @@ namespace safe_browsing { |
namespace { |
-struct RelocEnumerationState { |
- explicit RelocEnumerationState(HMODULE hModule); |
- ~RelocEnumerationState(); |
+struct Export { |
+ Export(void* addr, std::string name); |
+ ~Export(); |
+ |
+ bool operator<(const Export& other) const; |
+ |
+ void* addr; |
+ std::string name; |
+}; |
+ |
+Export::Export(void* addr, std::string name) : addr(addr), name(name) { |
+} |
+ |
+Export::~Export() { |
+} |
+ |
+bool Export::operator<(const Export& other) const { |
+ return addr < other.addr; |
+} |
+ |
+struct ModuleVerificationState { |
+ explicit ModuleVerificationState(HMODULE hModule); |
+ ~ModuleVerificationState(); |
base::win::PEImageAsData disk_peimage; |
- // The number of bytes made equivalent between memory and disk due to |
- // relocations. |
- int bytes_corrected_by_reloc; |
+ // The module's preferred base address minus the base address it actually |
+ // loaded at. |
+ uintptr_t image_base_delta; |
+ |
+ // The location of the disk_peimage module's code section minus that of the |
+ // mem_peimage module's code section. |
+ uintptr_t code_section_delta; |
+ |
+ // The bytes corrected by relocs. |
+ base::hash_set<uintptr_t> reloc_addr; |
// Set true if the relocation table contains a reloc of type that we don't |
// currently handle. |
bool unknown_reloc_type; |
- DISALLOW_COPY_AND_ASSIGN(RelocEnumerationState); |
+ DISALLOW_COPY_AND_ASSIGN(ModuleVerificationState); |
}; |
-RelocEnumerationState::RelocEnumerationState(HMODULE hModule) |
+ModuleVerificationState::ModuleVerificationState(HMODULE hModule) |
: disk_peimage(hModule), |
- bytes_corrected_by_reloc(0), |
+ image_base_delta(0), |
+ code_section_delta(0), |
+ reloc_addr(), |
unknown_reloc_type(false) { |
} |
-RelocEnumerationState::~RelocEnumerationState() { |
+ModuleVerificationState::~ModuleVerificationState() { |
+} |
+ |
+bool ByteAccountedForByReloc(uint8_t* byte_addr, |
+ ModuleVerificationState* state) { |
+ return ((state->reloc_addr.count(reinterpret_cast<uintptr_t>(byte_addr))) > |
+ 0); |
+} |
+ |
+// Checks each byte in the module's code section again the corresponding byte on |
+// disk. Returns a list of the functions who may have been modified. |
+int ExamineBytesDiffInMemory(uint8_t* disk_code_start, |
+ uint8_t* mem_code_start, |
+ uint32_t code_size, |
+ std::vector<Export> exports, |
+ ModuleVerificationState* state, |
+ std::set<std::string>* modified_exports) { |
+ int bytes_different = 0; |
+ std::vector<Export>::iterator export_it = exports.begin(); |
+ |
+ for (uint32_t i = 0; i < code_size; ++i) { |
+ if (*(disk_code_start + i) != *(mem_code_start + i) && |
+ !ByteAccountedForByReloc(mem_code_start + i, state)) { |
+ // We get the largest export address still smaller than |addr|. It is |
+ // possible that |addr| belongs to some nonexported function located |
+ // between this export and the following one. |
+ Export addr = |
+ Export(reinterpret_cast<void*>(mem_code_start + i), std::string()); |
+ std::vector<Export>::iterator modified_export_it = |
+ std::upper_bound(export_it, exports.end(), addr); |
+ |
+ if (modified_export_it != exports.begin()) |
+ modified_exports->insert((modified_export_it - 1)->name); |
+ ++bytes_different; |
+ |
+ // No later byte can belong to an earlier export. |
+ export_it = modified_export_it; |
+ } |
+ } |
+ return bytes_different; |
} |
-int CountBytesDiffInMemory(uint8_t* disk_code_start, |
- uint8_t* memory_code_start, |
- uint32_t code_size) { |
- int counter = 0; |
- for (int i = 0; i < static_cast<int>(code_size); ++i) { |
- if (*(disk_code_start + i) != *(memory_code_start + i)) |
- ++counter; |
+// Adds to |reloc_addr| the bytes of the pointer at |address| that are corrected |
+// by adding |image_base_delta|. |
+void AddBytesCorrectedByReloc(ModuleVerificationState* state, |
+ uintptr_t address) { |
+ uintptr_t orig_value = *reinterpret_cast<uintptr_t*>(address); |
+ uintptr_t fixed_mem_value = orig_value + state->image_base_delta; |
+ uintptr_t disk_value = |
+ *reinterpret_cast<uintptr_t*>(address + state->code_section_delta); |
+ |
+ for (int i = 0; i < sizeof(address); ++i) { |
+ if ((orig_value & 0xFF) != (disk_value & 0xFF) && |
+ (fixed_mem_value & 0xFF) == (disk_value & 0xFF)) { |
+ state->reloc_addr.insert(address + i); |
+ } |
+ orig_value >>= 8; |
+ fixed_mem_value >>= 8; |
+ disk_value >>= 8; |
} |
- return counter; |
} |
bool AddrIsInCodeSection(void* address, |
@@ -60,15 +139,14 @@ bool EnumRelocsCallback(const base::win::PEImage& mem_peimage, |
WORD type, |
void* address, |
void* cookie) { |
- RelocEnumerationState* reloc_enum_state = |
- reinterpret_cast<RelocEnumerationState*>(cookie); |
- const base::win::PEImageAsData* disk_peimage_ptr = |
- &reloc_enum_state->disk_peimage; |
+ ModuleVerificationState* state = |
+ reinterpret_cast<ModuleVerificationState*>(cookie); |
+ |
uint8_t* mem_code_addr = NULL; |
uint8_t* disk_code_addr = NULL; |
uint32_t code_size = 0; |
if (!GetCodeAddrsAndSize(mem_peimage, |
- *disk_peimage_ptr, |
+ state->disk_peimage, |
&mem_code_addr, |
&disk_code_addr, |
&code_size)) |
@@ -80,21 +158,7 @@ bool EnumRelocsCallback(const base::win::PEImage& mem_peimage, |
switch (type) { |
case IMAGE_REL_BASED_HIGHLOW: { |
- uint8_t* preferred_image_base = reinterpret_cast<uint8_t*>( |
- disk_peimage_ptr->GetNTHeaders()->OptionalHeader.ImageBase); |
- uintptr_t delta = preferred_image_base - |
- reinterpret_cast<uint8_t*>(mem_peimage.module()); |
- uint8_t* new_value = (*reinterpret_cast<uint8_t**>(address)) + delta; |
- int bytes_corrected = |
- CountBytesDiffInPtr(reinterpret_cast<uintptr_t>(new_value), |
- *reinterpret_cast<uintptr_t*>(address)); |
- |
- // Check that the adding delta corrected the value to agree with disk. |
- uint8_t** disk_address = reinterpret_cast<uint8_t**>( |
- reinterpret_cast<uint8_t*>(address) - mem_code_addr + disk_code_addr); |
- if (new_value == *disk_address) { |
- reloc_enum_state->bytes_corrected_by_reloc += bytes_corrected; |
- } |
+ AddBytesCorrectedByReloc(state, reinterpret_cast<uintptr_t>(address)); |
break; |
} |
case IMAGE_REL_BASED_ABSOLUTE: |
@@ -104,13 +168,27 @@ bool EnumRelocsCallback(const base::win::PEImage& mem_peimage, |
default: { |
// TODO(krstnmnlsn): Find a reliable description of the behaviour of the |
// remaining types of relocation and handle them. |
- reloc_enum_state->unknown_reloc_type = true; |
+ state->unknown_reloc_type = true; |
break; |
} |
} |
return true; |
} |
+bool EnumExportsCallback(const base::win::PEImage& mem_peimage, |
+ DWORD ordinal, |
+ DWORD hint, |
+ LPCSTR name, |
+ PVOID function_addr, |
+ LPCSTR forward, |
+ PVOID cookie) { |
+ std::vector<Export>* exports = reinterpret_cast<std::vector<Export>*>(cookie); |
+ if (name) { |
+ exports->push_back(Export(function_addr, std::string(name))); |
+ } |
+ return true; |
+} |
+ |
} // namespace |
bool GetCodeAddrsAndSize(const base::win::PEImage& mem_peimage, |
@@ -140,18 +218,9 @@ bool GetCodeAddrsAndSize(const base::win::PEImage& mem_peimage, |
return true; |
} |
-int CountBytesDiffInPtr(uintptr_t num_a, uintptr_t num_b) { |
- int num_bytes = 0; |
- for (int i = 0; i < sizeof(num_a); ++i) { |
- if ((num_a & 0xFF) != (num_b & 0xFF)) |
- ++num_bytes; |
- num_a >>= 8; |
- num_b >>= 8; |
- } |
- return num_bytes; |
-} |
- |
-ModuleState VerifyModule(const wchar_t* module_name) { |
+ModuleState VerifyModule(const wchar_t* module_name, |
+ std::set<std::string>* modified_exports) { |
+ // Get module handle, load a copy from disk as data and create PEImages. |
HMODULE module_handle = NULL; |
if (!GetModuleHandleEx(0, module_name, &module_handle)) |
return MODULE_STATE_UNKNOWN; |
@@ -166,31 +235,52 @@ ModuleState VerifyModule(const wchar_t* module_name) { |
base::MemoryMappedFile mapped_module; |
if (!mapped_module.Initialize(base::FilePath(module_path))) |
return MODULE_STATE_UNKNOWN; |
- RelocEnumerationState reloc_enum_state( |
+ ModuleVerificationState state( |
reinterpret_cast<HMODULE>(const_cast<uint8*>(mapped_module.data()))); |
base::win::PEImage mem_peimage(module_handle); |
- if (!mem_peimage.VerifyMagic() || |
- !reloc_enum_state.disk_peimage.VerifyMagic()) |
+ if (!mem_peimage.VerifyMagic() || !state.disk_peimage.VerifyMagic()) |
return MODULE_STATE_UNKNOWN; |
+ // Get the list of exports. |
+ std::vector<Export> exports; |
+ mem_peimage.EnumExports(EnumExportsCallback, &exports); |
+ std::sort(exports.begin(), exports.end()); |
+ |
+ // Get the addresses of the code sections then calculate |code_section_delta| |
+ // and |image_base_delta|. |
uint8_t* mem_code_addr = NULL; |
uint8_t* disk_code_addr = NULL; |
uint32_t code_size = 0; |
if (!GetCodeAddrsAndSize(mem_peimage, |
- reloc_enum_state.disk_peimage, |
+ state.disk_peimage, |
&mem_code_addr, |
&disk_code_addr, |
&code_size)) |
return MODULE_STATE_UNKNOWN; |
- mem_peimage.EnumRelocs(EnumRelocsCallback, &reloc_enum_state); |
- if (reloc_enum_state.unknown_reloc_type) |
+ state.code_section_delta = disk_code_addr - mem_code_addr; |
+ |
+ uint8_t* preferred_image_base = reinterpret_cast<uint8_t*>( |
+ state.disk_peimage.GetNTHeaders()->OptionalHeader.ImageBase); |
+ state.image_base_delta = |
+ preferred_image_base - reinterpret_cast<uint8_t*>(mem_peimage.module()); |
+ |
+ // Get the relocations. |
+ mem_peimage.EnumRelocs(EnumRelocsCallback, &state); |
+ if (state.unknown_reloc_type) |
return MODULE_STATE_UNKNOWN; |
- int num_bytes_different = |
- CountBytesDiffInMemory(disk_code_addr, mem_code_addr, code_size); |
- if (num_bytes_different == reloc_enum_state.bytes_corrected_by_reloc) |
+ // Count the modified bytes (after accounting for relocs) and get the set of |
+ // modified functions. |
+ int num_bytes_different = ExamineBytesDiffInMemory(disk_code_addr, |
+ mem_code_addr, |
+ code_size, |
+ exports, |
+ &state, |
+ modified_exports); |
+ |
+ if (num_bytes_different == 0) |
return MODULE_STATE_UNMODIFIED; |
return MODULE_STATE_MODIFIED; |
} |