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
This commit is contained in:
Scott Graham 2015-03-09 16:37:43 -07:00
parent 1a635e3a79
commit ab6cdb6bc1
5 changed files with 479 additions and 136 deletions

View File

@ -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.
},
},
},

View File

@ -14,7 +14,11 @@
#include "util/win/process_info.h"
#include <winternl.h>
#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 <class T>
bool ReadUnicodeString(HANDLE process,
const UNICODE_STRING& us,
const process_types::UNICODE_STRING<T>& 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<const void*>(us.Buffer),
&result->operator[](0),
us.Length,
&bytes_read)) {
PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
return false;
}
@ -91,35 +99,89 @@ template <class T> 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 <class Traits>
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<Traits> peb;
if (!ReadStruct(process, peb_address, &peb))
return false;
process_types::RTL_USER_PROCESS_PARAMETERS<Traits> 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<Traits> peb_ldr_data;
if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
return false;
std::wstring module;
process_types::LDR_DATA_TABLE_ENTRY<Traits> 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<uintptr_t>(
reinterpret_cast<const char*>(
peb_ldr_data.InMemoryOrderModuleList.Flink) -
offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
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<uintptr_t>(
reinterpret_cast<const char*>(cur) -
offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
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_types::internal::Traits32>
process_basic_information;
#else
process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits64>
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<uintptr_t>(
process_basic_information_.PebBaseAddress),
&peb)) {
return false;
}
RTL_USER_PROCESS_PARAMETERS process_parameters;
if (!ReadStruct(process,
reinterpret_cast<uintptr_t>(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<uintptr_t>(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<uintptr_t>(
reinterpret_cast<const char*>(
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<uintptr_t>(reinterpret_cast<const char*>(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_types::internal::Traits64>(
process, peb_address, this)
: ReadProcessData<process_types::internal::Traits32>(
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 {

View File

@ -15,10 +15,8 @@
#ifndef CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
#define CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
#include <basetsd.h>
#include <sys/types.h>
#include <windows.h>
#include <winternl.h>
#include <string>
#include <vector>
@ -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<std::wstring>* modules) const;
private:
internal::FULL_PROCESS_BASIC_INFORMATION process_basic_information_;
template <class T>
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<std::wstring> modules_;
bool is_64_bit_;

View File

@ -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<std::wstring> 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

291
util/win/process_structs.h Normal file
View File

@ -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 <windows.h>
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 <class Traits>
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 <class Traits>
struct LIST_ENTRY {
typename Traits::Pointer Flink;
typename Traits::Pointer Blink;
};
template <class Traits>
struct UNICODE_STRING {
union {
struct {
USHORT Length;
USHORT MaximumLength;
};
typename Traits::Pad padding_for_x64;
};
typename Traits::Pointer Buffer;
};
template <class Traits>
struct PEB_LDR_DATA {
ULONG Length;
DWORD Initialized;
typename Traits::Pointer SsHandle;
LIST_ENTRY<Traits> InLoadOrderModuleList;
LIST_ENTRY<Traits> InMemoryOrderModuleList;
LIST_ENTRY<Traits> InInitializationOrderModuleList;
};
template <class Traits>
struct LDR_DATA_TABLE_ENTRY {
LIST_ENTRY<Traits> InLoadOrderLinks;
LIST_ENTRY<Traits> InMemoryOrderLinks;
LIST_ENTRY<Traits> InInitializationOrderLinks;
typename Traits::Pointer DllBase;
typename Traits::Pointer EntryPoint;
union {
ULONG SizeOfImage;
typename Traits::Pad padding_for_x64;
};
UNICODE_STRING<Traits> FullDllName;
UNICODE_STRING<Traits> BaseDllName;
ULONG Flags;
USHORT ObsoleteLoadCount;
USHORT TlsIndex;
LIST_ENTRY<Traits> HashLinks;
ULONG TimeDateStamp;
};
template <class Traits>
struct CURDIR {
UNICODE_STRING<Traits> DosPath;
typename Traits::Pointer Handle;
};
template <class Traits>
struct STRING {
union {
struct {
DWORD Length;
DWORD MaximumLength;
};
typename Traits::Pad padding_for_x64;
};
typename Traits::Pointer Buffer;
};
template <class Traits>
struct RTL_DRIVE_LETTER_CURDIR {
WORD Flags;
WORD Length;
DWORD TimeStamp;
STRING<Traits> DosPath;
};
template <class Traits>
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<Traits> CurrentDirectory;
UNICODE_STRING<Traits> DllPath;
UNICODE_STRING<Traits> ImagePathName;
UNICODE_STRING<Traits> 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<Traits> WindowTitle;
UNICODE_STRING<Traits> DesktopInfo;
UNICODE_STRING<Traits> ShellInfo;
UNICODE_STRING<Traits> RuntimeData;
RTL_DRIVE_LETTER_CURDIR<Traits> CurrentDirectores[32]; // sic.
};
template <class T>
struct GdiHandleBufferCountForBitness;
template <>
struct GdiHandleBufferCountForBitness<internal::Traits32> {
enum { value = 34 };
};
template <>
struct GdiHandleBufferCountForBitness<internal::Traits64> {
enum { value = 60 };
};
template <class Traits>
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<Traits>::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<Traits> 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<Traits> 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_