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