chromium/chrome/test/base/process_inspector_win.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/base/process_inspector_win.h"

#include <winternl.h>

#include "base/logging.h"
#include "base/process/process.h"
#include "base/win/windows_version.h"

namespace {

// Certain Windows types that depend on the word size of the OS (rather than the
// size of the current process) are defined here.

// PROCESS_BASIC_INFORMATION.
template <class Traits>
struct ProcessInformation {
  using RemotePointer = typename Traits::RemotePointer;

  DWORD exit_status() const { return static_cast<DWORD>(exit_status_); }
  RemotePointer peb_base_address() const { return peb_base_address_; }
  RemotePointer affinity_mask() const { return affinity_mask_; }
  int base_priority() const { return static_cast<int>(base_priority_); }
  DWORD unique_process_id() const { return static_cast<DWORD>(unique_pid_); }
  DWORD inherited_from_unique_process_id() const {
    return static_cast<DWORD>(inherited_from_unique_process_id_);
  }

 private:
  RemotePointer exit_status_;
  RemotePointer peb_base_address_;
  RemotePointer affinity_mask_;
  RemotePointer base_priority_;
  RemotePointer unique_pid_;
  RemotePointer inherited_from_unique_process_id_;
};

// A subset of a process's environment block.
template <class Traits>
struct ProcessExecutionBlock {
  using RemotePointer = typename Traits::RemotePointer;

  uint8_t InheritedAddressSpace;
  uint8_t ReadImageFileExecOptions;
  uint8_t BeingDebugged;
  uint8_t ProcessFlags;
  uint8_t Padding[4];
  RemotePointer Mutant;
  RemotePointer ImageBaseAddress;
  RemotePointer Ldr;
  RemotePointer ProcessParameters;  // RtlUserProcessParameters
};

// UNICODE_STRING.
template <class Traits>
struct UnicodeString {
  using RemotePointer = typename Traits::RemotePointer;

  uint16_t Length;
  uint16_t MaximumLength;
  RemotePointer Buffer;
};

// CURDIR.
template <class Traits>
struct CurDir {
  using RemotePointer = typename Traits::RemotePointer;

  UnicodeString<Traits> DosPath;
  RemotePointer Handle;
};

// RTL_USER_PROCESS_PARAMETERS.
template <class Traits>
struct RtlUserProcessParameters {
  using RemotePointer = typename Traits::RemotePointer;

  uint32_t MaximumLength;
  uint32_t Length;
  uint32_t Flags;
  uint32_t DebugFlags;
  RemotePointer ConsoleHandle;
  uint32_t ConsoleFlags;
  RemotePointer StandardInput;
  RemotePointer StandardOutput;
  RemotePointer StandardError;
  CurDir<Traits> CurrentDirectory;
  UnicodeString<Traits> DllPath;
  UnicodeString<Traits> ImagePathName;
  UnicodeString<Traits> CommandLine;
};

// A concrete ProcessInspector that can read from another process based on the
// architecture. |Traits| specifies traits based on the OS architecture.
template <class Traits>
class Inspector : public ProcessInspector {
 public:
  Inspector();
  Inspector(const Inspector&) = delete;
  Inspector& operator=(const Inspector&) = delete;

  // ProcessInspector:
  DWORD GetParentPid() const override;
  const std::wstring& command_line() const override;

 private:
  // ProcessInspector:
  bool Inspect(const base::Process& process) override;

  ProcessInformation<Traits> process_basic_information_;
  ProcessExecutionBlock<Traits> peb_;
  RtlUserProcessParameters<Traits> process_parameters_;
  std::wstring command_line_;
};

#if !defined(_WIN64)
// Traits for a 32-bit process running in WoW.
struct Wow64Traits {
  // The name of the ntdll function to query process information.
  static const char kQueryProcessInformationFunctionName[];

  // The type of a pointer to the read process memory function.
  using ReadMemoryFn =
      NTSTATUS(NTAPI*)(HANDLE, uint64_t, void*, uint64_t, uint64_t*);

  // An unsigned integer type matching the size of a pointer in the remote
  // process.
  using RemotePointer = uint64_t;

  // Returns the function to read memory from a remote process.
  static ReadMemoryFn GetReadMemoryFn() {
    return reinterpret_cast<ReadMemoryFn>(::GetProcAddress(
        ::GetModuleHandle(L"ntdll.dll"), "NtWow64ReadVirtualMemory64"));
  }

  // Reads |buffer_size| bytes from |handle|'s process at |address| into
  // |buffer| using |fn|. Returns true on success.
  static bool ReadMemory(ReadMemoryFn fn,
                         HANDLE handle,
                         RemotePointer address,
                         void* buffer,
                         RemotePointer buffer_size) {
    NTSTATUS status = fn(handle, address, buffer, buffer_size, nullptr);
    if (NT_SUCCESS(status))
      return true;
    LOG(ERROR) << "Failed to read process memory with status " << std::hex
               << status << ".";
    return false;
  }
};

// static
constexpr char Wow64Traits::kQueryProcessInformationFunctionName[] =
    "NtWow64QueryInformationProcess64";

#endif

// Traits for a 32-bit process running on 32-bit Windows, or a 64-bit process
// running on 64-bit Windows.
struct NormalTraits {
  // The name of the ntdll function to query process information.
  static const char kQueryProcessInformationFunctionName[];

  // The type of a pointer to the read process memory function.
  using ReadMemoryFn = decltype(&::ReadProcessMemory);

  // An unsigned integer type matching the size of a pointer in the remote
  // process.
  using RemotePointer = uintptr_t;

  // Returns the function to read memory from a remote process.
  static ReadMemoryFn GetReadMemoryFn() { return &::ReadProcessMemory; }

  // Reads |buffer_size| bytes from |handle|'s process at |address| into
  // |buffer| using |fn|. Returns true on success.
  static bool ReadMemory(ReadMemoryFn fn,
                         HANDLE handle,
                         RemotePointer address,
                         void* buffer,
                         RemotePointer buffer_size) {
    BOOL result = fn(handle, reinterpret_cast<const void*>(address), buffer,
                     buffer_size, nullptr);
    if (result)
      return true;
    PLOG(ERROR) << "Failed to read process memory";
    return false;
  }
};

// static
constexpr char NormalTraits::kQueryProcessInformationFunctionName[] =
    "NtQueryInformationProcess";

template <class Traits>
Inspector<Traits>::Inspector() = default;

template <class Traits>
DWORD Inspector<Traits>::GetParentPid() const {
  return process_basic_information_.inherited_from_unique_process_id();
}

template <class Traits>
const std::wstring& Inspector<Traits>::command_line() const {
  return command_line_;
}

template <class Traits>
bool Inspector<Traits>::Inspect(const base::Process& process) {
  auto query_information_process_fn =
      reinterpret_cast<decltype(&::NtQueryInformationProcess)>(
          ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"),
                           Traits::kQueryProcessInformationFunctionName));
  typename Traits::ReadMemoryFn read_memory_fn = Traits::GetReadMemoryFn();

  if (!query_information_process_fn)
    return false;
  ULONG in_len = sizeof(process_basic_information_);
  ULONG out_len = 0;
  NTSTATUS status = query_information_process_fn(
      process.Handle(), ProcessBasicInformation, &process_basic_information_,
      in_len, &out_len);
  if (NT_ERROR(status) || out_len != in_len)
    return false;

  if (!Traits::ReadMemory(read_memory_fn, process.Handle(),
                          process_basic_information_.peb_base_address(), &peb_,
                          sizeof(peb_))) {
    return false;
  }
  if (!Traits::ReadMemory(read_memory_fn, process.Handle(),
                          peb_.ProcessParameters, &process_parameters_,
                          sizeof(process_parameters_))) {
    return false;
  }
  if (process_parameters_.CommandLine.Length) {
    command_line_.resize(process_parameters_.CommandLine.Length /
                         sizeof(wchar_t));
    if (!Traits::ReadMemory(read_memory_fn, process.Handle(),
                            process_parameters_.CommandLine.Buffer,
                            &command_line_[0],
                            process_parameters_.CommandLine.Length)) {
      command_line_.clear();
      return false;
    }
  }
  return true;
}

}  // namespace

// static
std::unique_ptr<ProcessInspector> ProcessInspector::Create(
    const base::Process& process) {
  std::unique_ptr<ProcessInspector> inspector;
#if !defined(_WIN64)
  using base::win::OSInfo;
  if (OSInfo::GetInstance()->IsWowX86OnAMD64())
    inspector = std::make_unique<Inspector<Wow64Traits>>();
#endif
  if (!inspector)
    inspector = std::make_unique<Inspector<NormalTraits>>();
  if (!inspector->Inspect(process))
    inspector.reset();
  return inspector;
}