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_