| // 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/test/base/process_inspector_win.h" |
| |
| #include <winternl.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(); |
| |
| // ProcessInspector: |
| DWORD GetParentPid() const override; |
| const base::string16& 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_; |
| base::string16 command_line_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Inspector); |
| }; |
| |
| #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 base::string16& 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()->wow64_status() == OSInfo::WOW64_ENABLED) |
| inspector = std::make_unique<Inspector<Wow64Traits>>(); |
| #endif |
| if (!inspector) |
| inspector = std::make_unique<Inspector<NormalTraits>>(); |
| if (!inspector->Inspect(process)) |
| inspector.reset(); |
| return inspector; |
| } |