Chromium Code Reviews| OLD | NEW | 
|---|---|
| (Empty) | |
| 1 // Copyright 2017 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(), | |
| 
 
Will Harris
2017/01/11 21:25:38
there's some stuff in the MSDN about PSAPI_VERSION
 
chrisha
2017/01/11 21:46:22
grt brought this up too. We are currently setting
 
 | |
| 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 |