Chromium Code Reviews| Index: chrome/browser/conflicts/module_event_sink_impl_win.cc |
| diff --git a/chrome/browser/conflicts/module_event_sink_impl_win.cc b/chrome/browser/conflicts/module_event_sink_impl_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..428b3824e4eeda44530e9e78f0ff149ae6cfbffe |
| --- /dev/null |
| +++ b/chrome/browser/conflicts/module_event_sink_impl_win.cc |
| @@ -0,0 +1,210 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/conflicts/module_event_sink_impl_win.h" |
| + |
| +#include <windows.h> |
| +#include <psapi.h> |
| + |
| +#include <utility> |
| + |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/files/file_path.h" |
| +#include "base/macros.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/strings/string_piece.h" |
| +#include "chrome/browser/conflicts/module_database_win.h" |
| +#include "chrome/common/conflicts/module_watcher_win.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "mojo/public/cpp/bindings/strong_binding.h" |
| + |
| +namespace { |
| + |
| +// Gets the process creation time associated with the given process. |
| +bool GetProcessCreationTime(base::ProcessHandle process, |
| + uint64_t* creation_time) { |
| + FILETIME creation_ft = {}; |
| + FILETIME exit_ft = {}; |
| + FILETIME kernel_ft = {}; |
| + FILETIME user_ft = {}; |
| + if (!::GetProcessTimes(process, &creation_ft, &exit_ft, &kernel_ft, |
| + &user_ft)) { |
| + return false; |
| + } |
| + *creation_time = (static_cast<uint64_t>(creation_ft.dwHighDateTime) << 32) | |
| + static_cast<uint64_t>(creation_ft.dwLowDateTime); |
| + return true; |
| +} |
| + |
| +// Gets the path of the module in the provided remote process. Returns true on |
| +// success, false otherwise. |
| +bool GetModulePath(base::ProcessHandle process, |
| + HMODULE module, |
| + base::FilePath* path) { |
| + std::vector<wchar_t> temp_path(MAX_PATH); |
| + size_t length = 0; |
| + while (true) { |
| + 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
|
| + temp_path.size()); |
| + if (length == 0) |
| + return false; |
| + if (length < temp_path.size()) |
| + break; |
| + // The entire buffer was consumed, so grow it to ensure the result wasn't |
| + // actually truncated. |
| + temp_path.resize(2 * temp_path.size()); |
| + } |
| + |
| + *path = base::FilePath(base::StringPiece16(temp_path.data(), length)); |
| + return true; |
| +} |
| + |
| +// Gets the size of a module in a remote process. Returns true on success, false |
| +// otherwise. |
| +bool GetModuleSize(base::ProcessHandle process, |
| + HMODULE module, |
| + uint32_t* size) { |
| + MODULEINFO info = {}; |
| + if (!::GetModuleInformation(process, module, &info, sizeof(info))) |
| + return false; |
| + *size = info.SizeOfImage; |
| + return true; |
| +} |
| + |
| +// Reads the typed data from a remote process. Returns true on success, false |
| +// otherwise. |
| +template <typename T> |
| +bool ReadRemoteData(base::ProcessHandle process, uint64_t address, T* data) { |
| + const void* typed_address = |
| + reinterpret_cast<const void*>(static_cast<uintptr_t>(address)); |
| + SIZE_T bytes_read = 0; |
| + if (!::ReadProcessMemory(process, typed_address, &data, sizeof(data), |
| + &bytes_read)) { |
| + return false; |
| + } |
| + if (bytes_read != sizeof(data)) |
| + return false; |
| + return true; |
| +} |
| + |
| +// Reads the time date stamp from the module loaded in the provided remote |
| +// |process| at the provided remote |load_address|. |
| +bool GetModuleTimeDateStamp(base::ProcessHandle process, |
| + uint64_t load_address, |
| + uint32_t* time_date_stamp) { |
| + uint64_t address = load_address + offsetof(IMAGE_DOS_HEADER, e_lfanew); |
| + LONG e_lfanew = 0; |
| + if (!ReadRemoteData(process, address, &e_lfanew)) |
| + return false; |
| + |
| + address = load_address + e_lfanew + offsetof(IMAGE_NT_HEADERS, FileHeader) + |
| + offsetof(IMAGE_FILE_HEADER, TimeDateStamp); |
| + DWORD temp = 0; |
| + if (!ReadRemoteData(process, address, &temp)) |
| + return false; |
| + |
| + *time_date_stamp = temp; |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +ModuleEventSinkImpl::ModuleEventSinkImpl(base::ProcessHandle process, |
| + content::ProcessType process_type, |
| + ModuleDatabase* module_database) |
| + : process_(process), |
| + module_database_(module_database), |
| + process_id_(0), |
| + creation_time_(0), |
| + in_error_(false) { |
| + // Failing to get basic process information means this channel should not |
| + // continue to forward data, thus it is marked as being in error. |
| + process_id_ = ::GetProcessId(process_); |
| + if (!GetProcessCreationTime(process_, &creation_time_)) { |
| + in_error_ = true; |
| + return; |
| + } |
| + module_database->OnProcessStarted(process_id_, creation_time_, process_type); |
| +} |
| + |
| +ModuleEventSinkImpl::~ModuleEventSinkImpl() = default; |
| + |
| +// static |
| +void ModuleEventSinkImpl::Create(base::ProcessHandle process, |
| + content::ProcessType process_type, |
| + ModuleDatabase* module_database, |
| + mojom::ModuleEventSinkRequest request) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + auto module_event_sink_impl = base::MakeUnique<ModuleEventSinkImpl>( |
| + process, process_type, module_database); |
| + base::Closure error_handler = base::Bind( |
| + &ModuleDatabase::OnProcessEnded, base::Unretained(module_database), |
| + module_event_sink_impl->process_id_, |
| + module_event_sink_impl->creation_time_); |
| + auto binding = mojo::MakeStrongBinding(std::move(module_event_sink_impl), |
| + std::move(request)); |
| + binding->set_connection_error_handler(error_handler); |
| +} |
| + |
| +void ModuleEventSinkImpl::OnModuleEvent(mojom::ModuleEventType event_type, |
| + uint64_t load_address) { |
| + if (in_error_) |
| + return; |
| + |
| + // Mojo takes care of validating |event_type|, so only |load_address| needs to |
| + // be checked. Load addresses must be aligned with the allocation granularity |
| + // which is at least 64KB on any supported Windows OS. |
| + if (load_address == 0 || load_address % (64 * 1024) != 0) |
| + return; |
| + |
| + switch (event_type) { |
| + case mojom::ModuleEventType::MODULE_ALREADY_LOADED: |
| + case mojom::ModuleEventType::MODULE_LOADED: |
| + return OnModuleLoad(load_address); |
| + |
| + case mojom::ModuleEventType::MODULE_UNLOADED: |
| + return OnModuleUnload(load_address); |
| + } |
| +} |
| + |
| +void ModuleEventSinkImpl::OnModuleLoad(uint64_t load_address) { |
| + if (in_error_) |
| + return; |
| + |
| + // The |load_address| is a unique key to a module in a remote process. If |
| + // there is a valid module there then the following queries should all pass. |
| + // If any of them fail then the load event is silently swallowed. The entire |
| + // channel is not marked as being in an error mode, as later events may be |
| + // well formed. |
| + |
| + // Convert the |load_address| to a module handle. |
| + HMODULE module = |
| + reinterpret_cast<HMODULE>(static_cast<uintptr_t>(load_address)); |
| + |
| + // Look up the various pieces of module metadata in the remote process. |
| + |
| + base::FilePath module_path; |
| + if (!GetModulePath(process_, module, &module_path)) |
| + return; |
| + |
| + uint32_t module_size = 0; |
| + if (!GetModuleSize(process_, module, &module_size)) |
| + return; |
| + |
| + uint32_t module_time_date_stamp = 0; |
| + if (!GetModuleTimeDateStamp(process_, load_address, &module_time_date_stamp)) |
| + return; |
| + |
| + // Forward this to the module database. |
| + module_database_->OnModuleLoad(process_id_, creation_time_, module_path, |
| + module_size, module_time_date_stamp, |
| + load_address); |
| +} |
| + |
| +void ModuleEventSinkImpl::OnModuleUnload(uint64_t load_address) { |
| + // Forward this directly to the module database. |
| + module_database_->OnModuleUnload(process_id_, creation_time_, |
| + static_cast<uintptr_t>(load_address)); |
| +} |