| 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..f9472830f0cfc531ee3e686ef8cd8a1c6f852f69
|
| --- /dev/null
|
| +++ b/chrome/browser/conflicts/module_event_sink_impl_win.cc
|
| @@ -0,0 +1,210 @@
|
| +// Copyright 2016 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(),
|
| + 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));
|
| +}
|
|
|