From ab6cdb6bc18ebb91b9523ee12d0385743f0cccad Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 9 Mar 2015 16:37:43 -0700 Subject: [PATCH] win: Support reading process info cross-bitness This only works 64->64, 32->32, and 64->32. We shouldn't have a need for 32->64. It will also currently not work if the crash service is running on Wow64 itself (that is, 32->32, but on an x64 OS). We should also be able to avoid needing that. Primarily, this change templatizes the winternl.h process structure types on word size, so the PEB can be read in a foreign bitsize process. This also happens to resolve using void* as pointer values into foreign processes, as they're now all either DWORD or DWORD64 depending on which traits class is used. R=mark@chromium.org BUG=crashpad:1 Review URL: https://codereview.chromium.org/981393003 --- util/util.gyp | 26 ++- util/win/process_info.cc | 251 ++++++++++++++++------------- util/win/process_info.h | 25 +-- util/win/process_info_test.cc | 22 ++- util/win/process_structs.h | 291 ++++++++++++++++++++++++++++++++++ 5 files changed, 479 insertions(+), 136 deletions(-) create mode 100644 util/win/process_structs.h diff --git a/util/util.gyp b/util/util.gyp index 4fe0009c..e71233da 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -134,6 +134,7 @@ 'synchronization/semaphore.h', 'win/process_info.cc', 'win/process_info.h', + 'win/process_structs.h', 'win/scoped_handle.cc', 'win/scoped_handle.h', 'win/time.cc', @@ -347,11 +348,12 @@ ['OS=="win"', { 'targets': [ { - 'target_name': 'crashpad_util_test_process_info_test_child', + 'target_name': 'crashpad_util_test_process_info_test_child_x64', 'type': 'executable', 'sources': [ 'win/process_info_test_child.cc', ], + 'msvs_configuration_platform': 'x64', # Set an unusually high load address to make sure that the main # executable still appears as the first element in # ProcessInfo::Modules(). @@ -361,6 +363,28 @@ '/BASE:0x78000000', '/FIXED', ], + 'TargetMachine': '17', # x64. + }, + }, + }, + { + # Same as above, but explicitly x86 to test 64->32 access. + 'target_name': 'crashpad_util_test_process_info_test_child_x86', + 'type': 'executable', + 'sources': [ + 'win/process_info_test_child.cc', + ], + 'msvs_configuration_platform': 'x86', + # Set an unusually high load address to make sure that the main + # executable still appears as the first element in + # ProcessInfo::Modules(). + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/BASE:0x78000000', + '/FIXED', + ], + 'TargetMachine': '1', # x86. }, }, }, diff --git a/util/win/process_info.cc b/util/win/process_info.cc index 66157aa4..cc10fbc5 100644 --- a/util/win/process_info.cc +++ b/util/win/process_info.cc @@ -14,7 +14,11 @@ #include "util/win/process_info.h" +#include + #include "base/logging.h" +#include "util/numeric/safe_assignment.h" +#include "util/win/process_structs.h" namespace crashpad { @@ -50,8 +54,9 @@ bool IsProcessWow64(HANDLE process_handle) { return is_wow64; } +template bool ReadUnicodeString(HANDLE process, - const UNICODE_STRING& us, + const process_types::UNICODE_STRING& us, std::wstring* result) { if (us.Length == 0) { result->clear(); @@ -60,8 +65,11 @@ bool ReadUnicodeString(HANDLE process, DCHECK_EQ(us.Length % sizeof(wchar_t), 0u); result->resize(us.Length / sizeof(wchar_t)); SIZE_T bytes_read; - if (!ReadProcessMemory( - process, us.Buffer, &result->operator[](0), us.Length, &bytes_read)) { + if (!ReadProcessMemory(process, + reinterpret_cast(us.Buffer), + &result->operator[](0), + us.Length, + &bytes_read)) { PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; return false; } @@ -91,35 +99,89 @@ template bool ReadStruct(HANDLE process, uintptr_t at, T* into) { return true; } -// PEB_LDR_DATA in winternl.h doesn't document the trailing -// InInitializationOrderModuleList field. See `dt ntdll!PEB_LDR_DATA`. -struct FULL_PEB_LDR_DATA : public PEB_LDR_DATA { - LIST_ENTRY InInitializationOrderModuleList; -}; - -// LDR_DATA_TABLE_ENTRY doesn't include InInitializationOrderLinks, define a -// complete version here. See `dt ntdll!_LDR_DATA_TABLE_ENTRY`. -struct FULL_LDR_DATA_TABLE_ENTRY { - LIST_ENTRY InLoadOrderLinks; - LIST_ENTRY InMemoryOrderLinks; - LIST_ENTRY InInitializationOrderLinks; - PVOID DllBase; - PVOID EntryPoint; - ULONG SizeOfImage; - UNICODE_STRING FullDllName; - UNICODE_STRING BaseDllName; - ULONG Flags; - WORD LoadCount; - WORD TlsIndex; - LIST_ENTRY HashLinks; - ULONG TimeDateStamp; - _ACTIVATION_CONTEXT* EntryPointActivationContext; -}; - } // namespace +template +bool ReadProcessData(HANDLE process, + uintptr_t peb_address_uintptr, + ProcessInfo* process_info) { + Traits::Pointer peb_address; + if (!AssignIfInRange(&peb_address, peb_address_uintptr)) { + LOG(ERROR) << "peb_address_uintptr " << peb_address_uintptr + << " out of range"; + return false; + } + + // Try to read the process environment block. + process_types::PEB peb; + if (!ReadStruct(process, peb_address, &peb)) + return false; + + process_types::RTL_USER_PROCESS_PARAMETERS process_parameters; + if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) + return false; + + if (!ReadUnicodeString(process, + process_parameters.CommandLine, + &process_info->command_line_)) { + return false; + } + + process_types::PEB_LDR_DATA peb_ldr_data; + if (!ReadStruct(process, peb.Ldr, &peb_ldr_data)) + return false; + + std::wstring module; + process_types::LDR_DATA_TABLE_ENTRY ldr_data_table_entry; + + // Include the first module in the memory order list to get our the main + // executable's name, as it's not included in initialization order below. + if (!ReadStruct(process, + reinterpret_cast( + reinterpret_cast( + peb_ldr_data.InMemoryOrderModuleList.Flink) - + offsetof(process_types::LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks)), + &ldr_data_table_entry)) { + return false; + } + if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module)) + return false; + process_info->modules_.push_back(module); + + // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded + // modules. We use this method rather than EnumProcessModules to get the + // modules in initialization order rather than memory order. + Traits::Pointer last = peb_ldr_data.InInitializationOrderModuleList.Blink; + for (Traits::Pointer cur = peb_ldr_data.InInitializationOrderModuleList.Flink; + ; + cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) { + // |cur| is the pointer to the LIST_ENTRY embedded in the + // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need + // to read from the target, and also offset back to the beginning of the + // structure. + if (!ReadStruct(process, + reinterpret_cast( + reinterpret_cast(cur) - + offsetof(process_types::LDR_DATA_TABLE_ENTRY, + InInitializationOrderLinks)), + &ldr_data_table_entry)) { + break; + } + // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too? + if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module)) + break; + process_info->modules_.push_back(module); + if (cur == last) + break; + } + + return true; +} + ProcessInfo::ProcessInfo() - : process_basic_information_(), + : process_id_(), + inherited_from_process_id_(), command_line_(), modules_(), is_64_bit_(false), @@ -147,12 +209,7 @@ bool ProcessInfo::Initialize(HANDLE process) { system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; } -#if ARCH_CPU_64_BITS - if (!is_64_bit_) { - LOG(ERROR) << "Reading different bitness not yet supported"; - return false; - } -#else +#if ARCH_CPU_32_BITS if (is_64_bit_) { LOG(ERROR) << "Reading x64 process from x86 process not supported"; return false; @@ -160,94 +217,68 @@ bool ProcessInfo::Initialize(HANDLE process) { #endif ULONG bytes_returned; + // We assume this process is not running on Wow64. The + // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64 + // bit one.) + // TODO(scottmg): Either support running as Wow64, or check/resolve this at a + // higher level. +#if ARCH_CPU_32_BITS + process_types::PROCESS_BASIC_INFORMATION + process_basic_information; +#else + process_types::PROCESS_BASIC_INFORMATION + process_basic_information; +#endif NTSTATUS status = crashpad::NtQueryInformationProcess(process, ProcessBasicInformation, - &process_basic_information_, - sizeof(process_basic_information_), + &process_basic_information, + sizeof(process_basic_information), &bytes_returned); if (status < 0) { LOG(ERROR) << "NtQueryInformationProcess: status=" << status; return false; } - if (bytes_returned != sizeof(process_basic_information_)) { + if (bytes_returned != sizeof(process_basic_information)) { LOG(ERROR) << "NtQueryInformationProcess incorrect size"; return false; } + process_id_ = process_basic_information.UniqueProcessId; + inherited_from_process_id_ = + process_basic_information.InheritedFromUniqueProcessId; - // Try to read the process environment block. - PEB peb; - if (!ReadStruct(process, - reinterpret_cast( - process_basic_information_.PebBaseAddress), - &peb)) { - return false; - } - - RTL_USER_PROCESS_PARAMETERS process_parameters; - if (!ReadStruct(process, - reinterpret_cast(peb.ProcessParameters), - &process_parameters)) { - return false; - } - - if (!ReadUnicodeString( - process, process_parameters.CommandLine, &command_line_)) { - return false; - } - - FULL_PEB_LDR_DATA peb_ldr_data; - if (!ReadStruct(process, reinterpret_cast(peb.Ldr), &peb_ldr_data)) - return false; - - // Include the first module in the memory order list to get our own name as - // it's not included in initialization order below. - std::wstring self_module; - FULL_LDR_DATA_TABLE_ENTRY self_ldr_data_table_entry; - if (!ReadStruct(process, - reinterpret_cast( - reinterpret_cast( - peb_ldr_data.InMemoryOrderModuleList.Flink) - - offsetof(FULL_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)), - &self_ldr_data_table_entry)) { - return false; - } - if (!ReadUnicodeString( - process, self_ldr_data_table_entry.FullDllName, &self_module)) { - return false; - } - modules_.push_back(self_module); - - // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded - // modules. We use this method rather than EnumProcessModules to get the - // modules in initialization order rather than memory order. - const LIST_ENTRY* last = peb_ldr_data.InInitializationOrderModuleList.Blink; - FULL_LDR_DATA_TABLE_ENTRY ldr_data_table_entry; - for (const LIST_ENTRY* cur = - peb_ldr_data.InInitializationOrderModuleList.Flink; - ; - cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) { - // |cur| is the pointer to the LIST_ENTRY embedded in the - // FULL_LDR_DATA_TABLE_ENTRY, in the target process's address space. So we - // need to read from the target, and also offset back to the beginning of - // the structure. - if (!ReadStruct( - process, - reinterpret_cast(reinterpret_cast(cur) - - offsetof(FULL_LDR_DATA_TABLE_ENTRY, - InInitializationOrderLinks)), - &ldr_data_table_entry)) { - break; + // We now want to read the PEB to gather the rest of our information. The + // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32, + // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both). + // The address of this is found by a second call to NtQueryInformationProcess. + uintptr_t peb_address = process_basic_information.PebBaseAddress; + if (is_wow64_) { + ULONG_PTR wow64_peb_address; + status = + crashpad::NtQueryInformationProcess(process, + ProcessWow64Information, + &wow64_peb_address, + sizeof(wow64_peb_address), + &bytes_returned); + if (status < 0) { + LOG(ERROR) << "NtQueryInformationProcess: status=" << status; + return false; } - // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too? - std::wstring module; - if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module)) - break; - modules_.push_back(module); - if (cur == last) - break; + if (bytes_returned != sizeof(wow64_peb_address)) { + LOG(ERROR) << "NtQueryInformationProcess incorrect size"; + return false; + } + peb_address = wow64_peb_address; } + // Read the PEB data using the correct word size. + bool result = is_64_bit_ ? ReadProcessData( + process, peb_address, this) + : ReadProcessData( + process, peb_address, this); + if (!result) + return false; + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } @@ -264,12 +295,12 @@ bool ProcessInfo::IsWow64() const { pid_t ProcessInfo::ProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return process_basic_information_.UniqueProcessId; + return process_id_; } pid_t ProcessInfo::ParentProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return process_basic_information_.InheritedFromUniqueProcessId; + return inherited_from_process_id_; } bool ProcessInfo::CommandLine(std::wstring* command_line) const { diff --git a/util/win/process_info.h b/util/win/process_info.h index 2c01668a..91ddb4ae 100644 --- a/util/win/process_info.h +++ b/util/win/process_info.h @@ -15,10 +15,8 @@ #ifndef CRASHPAD_UTIL_WIN_PROCESS_INFO_H_ #define CRASHPAD_UTIL_WIN_PROCESS_INFO_H_ -#include #include #include -#include #include #include @@ -28,21 +26,6 @@ namespace crashpad { -namespace internal { - -//! \brief This structure matches PROCESS_BASIC_INFORMATION in winternl.h but -//! gives names to the Reserved fields (matching the WDK's ntddk.h). -struct FULL_PROCESS_BASIC_INFORMATION { - NTSTATUS ExitStatus; - PPEB PebBaseAddress; - KAFFINITY AffinityMask; - PVOID BasePriority; - ULONG_PTR UniqueProcessId; - ULONG_PTR InheritedFromUniqueProcessId; -}; - -} // namespace internal - //! \brief Gathers information about a process given its `HANDLE`. This consists //! primarily of information stored in the Process Environment Block. class ProcessInfo { @@ -84,7 +67,13 @@ class ProcessInfo { bool Modules(std::vector* modules) const; private: - internal::FULL_PROCESS_BASIC_INFORMATION process_basic_information_; + template + friend bool ReadProcessData(HANDLE process, + uintptr_t peb_address_uintptr, + ProcessInfo* process_info); + + pid_t process_id_; + pid_t inherited_from_process_id_; std::wstring command_line_; std::vector modules_; bool is_64_bit_; diff --git a/util/win/process_info_test.cc b/util/win/process_info_test.cc index 5b1b7995..38fd84a8 100644 --- a/util/win/process_info_test.cc +++ b/util/win/process_info_test.cc @@ -61,7 +61,7 @@ TEST(ProcessInfo, Self) { modules[1].substr(modules[1].size() - wcslen(kNtdllName))); } -TEST(ProcessInfo, SomeOtherProcess) { +void TestOtherProcess(const std::wstring& child_name_suffix) { ProcessInfo process_info; ::UUID system_uuid; @@ -80,7 +80,7 @@ TEST(ProcessInfo, SomeOtherProcess) { base::FilePath test_executable = Paths::Executable(); std::wstring child_test_executable = test_executable.RemoveFinalExtension().value() + - L"_process_info_test_child.exe"; + L"_process_info_test_child_" + child_name_suffix + L".exe"; // TODO(scottmg): Command line escaping utility. std::wstring command_line = child_test_executable + L" " + started_uuid.ToString16() + L" " + @@ -113,11 +113,11 @@ TEST(ProcessInfo, SomeOtherProcess) { std::vector modules; EXPECT_TRUE(process_info.Modules(&modules)); ASSERT_GE(modules.size(), 3u); - const wchar_t kChildName[] = - L"\\crashpad_util_test_process_info_test_child.exe"; - ASSERT_GE(modules[0].size(), wcslen(kChildName)); - EXPECT_EQ(kChildName, - modules[0].substr(modules[0].size() - wcslen(kChildName))); + std::wstring child_name = L"\\crashpad_util_test_process_info_test_child_" + + child_name_suffix + L".exe"; + ASSERT_GE(modules[0].size(), child_name.size()); + EXPECT_EQ(child_name, + modules[0].substr(modules[0].size() - child_name.size())); ASSERT_GE(modules[1].size(), wcslen(kNtdllName)); EXPECT_EQ(kNtdllName, modules[1].substr(modules[1].size() - wcslen(kNtdllName))); @@ -130,6 +130,14 @@ TEST(ProcessInfo, SomeOtherProcess) { modules.back().substr(modules.back().size() - wcslen(kLz32dllName))); } +TEST(ProcessInfo, OtherProcessX64) { + TestOtherProcess(L"x64"); +} + +TEST(ProcessInfo, OtherProcessX86) { + TestOtherProcess(L"x86"); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/util/win/process_structs.h b/util/win/process_structs.h new file mode 100644 index 00000000..ece60896 --- /dev/null +++ b/util/win/process_structs.h @@ -0,0 +1,291 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_ +#define CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_ + +#include + +namespace crashpad { +namespace process_types { + +namespace internal { + +struct Traits32 { + using Pad = DWORD; + using UnsignedIntegral = DWORD; + using Pointer = DWORD; +}; + +struct Traits64 { + using Pad = DWORD64; + using UnsignedIntegral = DWORD64; + using Pointer = DWORD64; +}; + +} // namespace internal + +//! \{ + +//! \brief Selected structures from winternl.h, ntddk.h, and `dt ntdll!xxx`, +//! customized to have both x86 and x64 sizes available. +//! +//! The structure and field names follow the Windows names for clarity. We do, +//! however, use plain integral types rather than pointer types. This is both +//! easier to define, and avoids accidentally treating them as pointers into the +//! current address space. +//! +//! The templates below should be instantiated with either internal::Traits32 +//! for structures targeting x86, or internal::Traits64 for x64. + +// We set packing to 1 so that we can explicitly control the layout to make it +// match the OS defined structures. +#pragma pack(push, 1) + +template +struct PROCESS_BASIC_INFORMATION { + union { + DWORD ExitStatus; + typename Traits::Pad padding_for_x64_0; + }; + typename Traits::Pointer PebBaseAddress; + typename Traits::UnsignedIntegral AffinityMask; + union { + DWORD BasePriority; + typename Traits::Pad padding_for_x64_1; + }; + typename Traits::UnsignedIntegral UniqueProcessId; + typename Traits::UnsignedIntegral InheritedFromUniqueProcessId; +}; + +template +struct LIST_ENTRY { + typename Traits::Pointer Flink; + typename Traits::Pointer Blink; +}; + +template +struct UNICODE_STRING { + union { + struct { + USHORT Length; + USHORT MaximumLength; + }; + typename Traits::Pad padding_for_x64; + }; + typename Traits::Pointer Buffer; +}; + +template +struct PEB_LDR_DATA { + ULONG Length; + DWORD Initialized; + typename Traits::Pointer SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; +}; + +template +struct LDR_DATA_TABLE_ENTRY { + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + typename Traits::Pointer DllBase; + typename Traits::Pointer EntryPoint; + union { + ULONG SizeOfImage; + typename Traits::Pad padding_for_x64; + }; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + USHORT ObsoleteLoadCount; + USHORT TlsIndex; + LIST_ENTRY HashLinks; + ULONG TimeDateStamp; +}; + +template +struct CURDIR { + UNICODE_STRING DosPath; + typename Traits::Pointer Handle; +}; + +template +struct STRING { + union { + struct { + DWORD Length; + DWORD MaximumLength; + }; + typename Traits::Pad padding_for_x64; + }; + typename Traits::Pointer Buffer; +}; + +template +struct RTL_DRIVE_LETTER_CURDIR { + WORD Flags; + WORD Length; + DWORD TimeStamp; + STRING DosPath; +}; + +template +struct RTL_USER_PROCESS_PARAMETERS { + DWORD MaximumLength; + DWORD Length; + DWORD Flags; + DWORD DebugFlags; + typename Traits::Pointer ConsoleHandle; + union { + DWORD ConsoleFlags; + typename Traits::Pad padding_for_x64; + }; + typename Traits::Pointer StandardInput; + typename Traits::Pointer StandardOutput; + typename Traits::Pointer StandardError; + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + typename Traits::Pointer Environment; + DWORD StartingX; + DWORD StartingY; + DWORD CountX; + DWORD CountY; + DWORD CountCharsX; + DWORD CountCharsY; + DWORD FillAttribute; + DWORD WindowFlags; + DWORD ShowWindowFlags; + UNICODE_STRING WindowTitle; + UNICODE_STRING DesktopInfo; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeData; + RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32]; // sic. +}; + +template +struct GdiHandleBufferCountForBitness; + +template <> +struct GdiHandleBufferCountForBitness { + enum { value = 34 }; +}; +template <> +struct GdiHandleBufferCountForBitness { + enum { value = 60 }; +}; + +template +struct PEB { + union { + struct { + BYTE InheritedAddressSpace; + BYTE ReadImageFileExecOptions; + BYTE BeingDebugged; + BYTE BitField; + }; + typename Traits::Pad padding_for_x64_0; + }; + typename Traits::Pointer Mutant; + typename Traits::Pointer ImageBaseAddress; + typename Traits::Pointer Ldr; + typename Traits::Pointer ProcessParameters; + typename Traits::Pointer SubSystemData; + typename Traits::Pointer ProcessHeap; + typename Traits::Pointer FastPebLock; + typename Traits::Pointer AtlThunkSListPtr; + typename Traits::Pointer IFEOKey; + union { + DWORD CrossProcessFlags; + typename Traits::Pad padding_for_x64_1; + }; + typename Traits::Pointer KernelCallbackTable; + DWORD SystemReserved; + DWORD AtlThunkSListPtr32; + typename Traits::Pointer ApiSetMap; + union { + DWORD TlsExpansionCounter; + typename Traits::Pad padding_for_x64_2; + }; + typename Traits::Pointer TlsBitmap; + DWORD TlsBitmapBits[2]; + typename Traits::Pointer ReadOnlySharedMemoryBase; + typename Traits::Pointer SparePvoid0; + typename Traits::Pointer ReadOnlyStaticServerData; + typename Traits::Pointer AnsiCodePageData; + typename Traits::Pointer OemCodePageData; + typename Traits::Pointer UnicodeCaseTableData; + DWORD NumberOfProcessors; + DWORD NtGlobalFlag; + LARGE_INTEGER CriticalSectionTimeout; + typename Traits::UnsignedIntegral HeapSegmentReserve; + typename Traits::UnsignedIntegral HeapSegmentCommit; + typename Traits::UnsignedIntegral HeapDeCommitTotalFreeThreshold; + typename Traits::UnsignedIntegral HeapDeCommitFreeBlockThreshold; + DWORD NumberOfHeaps; + DWORD MaximumNumberOfHeaps; + typename Traits::Pointer ProcessHeaps; + typename Traits::Pointer GdiSharedHandleTable; + typename Traits::Pointer ProcessStarterHelper; + DWORD GdiDCAttributeList; + typename Traits::Pointer LoaderLock; + DWORD OSMajorVersion; + DWORD OSMinorVersion; + WORD OSBuildNumber; + WORD OSCSDVersion; + DWORD OSPlatformId; + DWORD ImageSubsystem; + DWORD ImageSubsystemMajorVersion; + union { + DWORD ImageSubsystemMinorVersion; + typename Traits::Pad padding_for_x64_3; + }; + typename Traits::UnsignedIntegral ActiveProcessAffinityMask; + DWORD GdiHandleBuffer[GdiHandleBufferCountForBitness::value]; + typename Traits::Pointer PostProcessInitRoutine; + typename Traits::Pointer TlsExpansionBitmap; + DWORD TlsExpansionBitmapBits[32]; + union { + DWORD SessionId; + typename Traits::Pad padding_for_x64_4; + }; + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + typename Traits::Pointer pShimData; + typename Traits::Pointer AppCompatInfo; + UNICODE_STRING CSDVersion; + typename Traits::Pointer ActivationContextData; + typename Traits::Pointer ProcessAssemblyStorageMap; + typename Traits::Pointer SystemDefaultActivationContextData; + typename Traits::Pointer SystemAssemblyStorageMap; + typename Traits::UnsignedIntegral MinimumStackCommit; + typename Traits::Pointer FlsCallback; + LIST_ENTRY FlsListHead; + typename Traits::Pointer FlsBitmap; + DWORD FlsBitmapBits[4]; + DWORD FlsHighIndex; +}; + +#pragma pack(pop) + +//! \} + +} // namespace process_types +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_