| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/conflicts/module_event_sink_impl_win.h" |
| 6 |
| 7 #include <windows.h> |
| 8 #include <psapi.h> |
| 9 |
| 10 #include <utility> |
| 11 |
| 12 #include "base/bind.h" |
| 13 #include "base/callback.h" |
| 14 #include "base/files/file_path.h" |
| 15 #include "base/macros.h" |
| 16 #include "base/memory/ptr_util.h" |
| 17 #include "base/strings/string_piece.h" |
| 18 #include "chrome/browser/conflicts/module_database_win.h" |
| 19 #include "chrome/common/conflicts/module_watcher_win.h" |
| 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "mojo/public/cpp/bindings/strong_binding.h" |
| 22 |
| 23 namespace { |
| 24 |
| 25 // Gets the process creation time associated with the given process. |
| 26 bool GetProcessCreationTime(base::ProcessHandle process, |
| 27 uint64_t* creation_time) { |
| 28 FILETIME creation_ft = {}; |
| 29 FILETIME exit_ft = {}; |
| 30 FILETIME kernel_ft = {}; |
| 31 FILETIME user_ft = {}; |
| 32 if (!::GetProcessTimes(process, &creation_ft, &exit_ft, &kernel_ft, |
| 33 &user_ft)) { |
| 34 return false; |
| 35 } |
| 36 *creation_time = (static_cast<uint64_t>(creation_ft.dwHighDateTime) << 32) | |
| 37 static_cast<uint64_t>(creation_ft.dwLowDateTime); |
| 38 return true; |
| 39 } |
| 40 |
| 41 // Gets the path of the module in the provided remote process. Returns true on |
| 42 // success, false otherwise. |
| 43 bool GetModulePath(base::ProcessHandle process, |
| 44 HMODULE module, |
| 45 base::FilePath* path) { |
| 46 std::vector<wchar_t> temp_path(MAX_PATH); |
| 47 size_t length = 0; |
| 48 while (true) { |
| 49 length = ::GetModuleFileNameEx(process, module, temp_path.data(), |
| 50 temp_path.size()); |
| 51 if (length == 0) |
| 52 return false; |
| 53 if (length < temp_path.size()) |
| 54 break; |
| 55 // The entire buffer was consumed, so grow it to ensure the result wasn't |
| 56 // actually truncated. |
| 57 temp_path.resize(2 * temp_path.size()); |
| 58 } |
| 59 |
| 60 *path = base::FilePath(base::StringPiece16(temp_path.data(), length)); |
| 61 return true; |
| 62 } |
| 63 |
| 64 // Gets the size of a module in a remote process. Returns true on success, false |
| 65 // otherwise. |
| 66 bool GetModuleSize(base::ProcessHandle process, |
| 67 HMODULE module, |
| 68 uint32_t* size) { |
| 69 MODULEINFO info = {}; |
| 70 if (!::GetModuleInformation(process, module, &info, sizeof(info))) |
| 71 return false; |
| 72 *size = info.SizeOfImage; |
| 73 return true; |
| 74 } |
| 75 |
| 76 // Reads the typed data from a remote process. Returns true on success, false |
| 77 // otherwise. |
| 78 template <typename T> |
| 79 bool ReadRemoteData(base::ProcessHandle process, uint64_t address, T* data) { |
| 80 const void* typed_address = |
| 81 reinterpret_cast<const void*>(static_cast<uintptr_t>(address)); |
| 82 SIZE_T bytes_read = 0; |
| 83 if (!::ReadProcessMemory(process, typed_address, &data, sizeof(data), |
| 84 &bytes_read)) { |
| 85 return false; |
| 86 } |
| 87 if (bytes_read != sizeof(data)) |
| 88 return false; |
| 89 return true; |
| 90 } |
| 91 |
| 92 // Reads the time date stamp from the module loaded in the provided remote |
| 93 // |process| at the provided remote |load_address|. |
| 94 bool GetModuleTimeDateStamp(base::ProcessHandle process, |
| 95 uint64_t load_address, |
| 96 uint32_t* time_date_stamp) { |
| 97 uint64_t address = load_address + offsetof(IMAGE_DOS_HEADER, e_lfanew); |
| 98 LONG e_lfanew = 0; |
| 99 if (!ReadRemoteData(process, address, &e_lfanew)) |
| 100 return false; |
| 101 |
| 102 address = load_address + e_lfanew + offsetof(IMAGE_NT_HEADERS, FileHeader) + |
| 103 offsetof(IMAGE_FILE_HEADER, TimeDateStamp); |
| 104 DWORD temp = 0; |
| 105 if (!ReadRemoteData(process, address, &temp)) |
| 106 return false; |
| 107 |
| 108 *time_date_stamp = temp; |
| 109 return true; |
| 110 } |
| 111 |
| 112 } // namespace |
| 113 |
| 114 ModuleEventSinkImpl::ModuleEventSinkImpl(base::ProcessHandle process, |
| 115 content::ProcessType process_type, |
| 116 ModuleDatabase* module_database) |
| 117 : process_(process), |
| 118 module_database_(module_database), |
| 119 process_id_(0), |
| 120 creation_time_(0), |
| 121 in_error_(false) { |
| 122 // Failing to get basic process information means this channel should not |
| 123 // continue to forward data, thus it is marked as being in error. |
| 124 process_id_ = ::GetProcessId(process_); |
| 125 if (!GetProcessCreationTime(process_, &creation_time_)) { |
| 126 in_error_ = true; |
| 127 return; |
| 128 } |
| 129 module_database->OnProcessStarted(process_id_, creation_time_, process_type); |
| 130 } |
| 131 |
| 132 ModuleEventSinkImpl::~ModuleEventSinkImpl() = default; |
| 133 |
| 134 // static |
| 135 void ModuleEventSinkImpl::Create(base::ProcessHandle process, |
| 136 content::ProcessType process_type, |
| 137 ModuleDatabase* module_database, |
| 138 mojom::ModuleEventSinkRequest request) { |
| 139 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 140 auto module_event_sink_impl = base::MakeUnique<ModuleEventSinkImpl>( |
| 141 process, process_type, module_database); |
| 142 base::Closure error_handler = base::Bind( |
| 143 &ModuleDatabase::OnProcessEnded, base::Unretained(module_database), |
| 144 module_event_sink_impl->process_id_, |
| 145 module_event_sink_impl->creation_time_); |
| 146 auto binding = mojo::MakeStrongBinding(std::move(module_event_sink_impl), |
| 147 std::move(request)); |
| 148 binding->set_connection_error_handler(error_handler); |
| 149 } |
| 150 |
| 151 void ModuleEventSinkImpl::OnModuleEvent(mojom::ModuleEventType event_type, |
| 152 uint64_t load_address) { |
| 153 if (in_error_) |
| 154 return; |
| 155 |
| 156 // Mojo takes care of validating |event_type|, so only |load_address| needs to |
| 157 // be checked. Load addresses must be aligned with the allocation granularity |
| 158 // which is at least 64KB on any supported Windows OS. |
| 159 if (load_address == 0 || load_address % (64 * 1024) != 0) |
| 160 return; |
| 161 |
| 162 switch (event_type) { |
| 163 case mojom::ModuleEventType::MODULE_ALREADY_LOADED: |
| 164 case mojom::ModuleEventType::MODULE_LOADED: |
| 165 return OnModuleLoad(load_address); |
| 166 |
| 167 case mojom::ModuleEventType::MODULE_UNLOADED: |
| 168 return OnModuleUnload(load_address); |
| 169 } |
| 170 } |
| 171 |
| 172 void ModuleEventSinkImpl::OnModuleLoad(uint64_t load_address) { |
| 173 if (in_error_) |
| 174 return; |
| 175 |
| 176 // The |load_address| is a unique key to a module in a remote process. If |
| 177 // there is a valid module there then the following queries should all pass. |
| 178 // If any of them fail then the load event is silently swallowed. The entire |
| 179 // channel is not marked as being in an error mode, as later events may be |
| 180 // well formed. |
| 181 |
| 182 // Convert the |load_address| to a module handle. |
| 183 HMODULE module = |
| 184 reinterpret_cast<HMODULE>(static_cast<uintptr_t>(load_address)); |
| 185 |
| 186 // Look up the various pieces of module metadata in the remote process. |
| 187 |
| 188 base::FilePath module_path; |
| 189 if (!GetModulePath(process_, module, &module_path)) |
| 190 return; |
| 191 |
| 192 uint32_t module_size = 0; |
| 193 if (!GetModuleSize(process_, module, &module_size)) |
| 194 return; |
| 195 |
| 196 uint32_t module_time_date_stamp = 0; |
| 197 if (!GetModuleTimeDateStamp(process_, load_address, &module_time_date_stamp)) |
| 198 return; |
| 199 |
| 200 // Forward this to the module database. |
| 201 module_database_->OnModuleLoad(process_id_, creation_time_, module_path, |
| 202 module_size, module_time_date_stamp, |
| 203 load_address); |
| 204 } |
| 205 |
| 206 void ModuleEventSinkImpl::OnModuleUnload(uint64_t load_address) { |
| 207 // Forward this directly to the module database. |
| 208 module_database_->OnModuleUnload(process_id_, creation_time_, |
| 209 static_cast<uintptr_t>(load_address)); |
| 210 } |
| OLD | NEW |