mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +00:00
Get module versions and types from in-memory images
Don't call GetFileVersionInfo(), which calls LoadLibrary() to be able to access the module's resources. Loading modules from the crashy process into the handler process can cause trouble. The Crashpad handler definitely doesn't want to run arbitrary modules' module initializer code. Since the VS_FIXEDFILEINFO needed is already in memory in the remote process' address space, just access it from there. BUG=crashpad:78 R=scottmg@chromium.org Review URL: https://codereview.chromium.org/1475023004 .
This commit is contained in:
parent
d81d83661d
commit
5be8ce4ea0
@ -103,10 +103,14 @@
|
|||||||
'win/pe_image_annotations_reader.h',
|
'win/pe_image_annotations_reader.h',
|
||||||
'win/pe_image_reader.cc',
|
'win/pe_image_reader.cc',
|
||||||
'win/pe_image_reader.h',
|
'win/pe_image_reader.h',
|
||||||
|
'win/pe_image_resource_reader.cc',
|
||||||
|
'win/pe_image_resource_reader.h',
|
||||||
'win/process_reader_win.cc',
|
'win/process_reader_win.cc',
|
||||||
'win/process_reader_win.h',
|
'win/process_reader_win.h',
|
||||||
'win/process_snapshot_win.cc',
|
'win/process_snapshot_win.cc',
|
||||||
'win/process_snapshot_win.h',
|
'win/process_snapshot_win.h',
|
||||||
|
'win/process_subrange_reader.cc',
|
||||||
|
'win/process_subrange_reader.h',
|
||||||
'win/system_snapshot_win.cc',
|
'win/system_snapshot_win.cc',
|
||||||
'win/system_snapshot_win.h',
|
'win/system_snapshot_win.h',
|
||||||
'win/thread_snapshot_win.cc',
|
'win/thread_snapshot_win.cc',
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
#include "snapshot/win/pe_image_reader.h"
|
#include "snapshot/win/pe_image_reader.h"
|
||||||
#include "util/misc/tri_state.h"
|
#include "util/misc/tri_state.h"
|
||||||
#include "util/misc/uuid.h"
|
#include "util/misc/uuid.h"
|
||||||
#include "util/win/module_version.h"
|
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
@ -210,7 +209,7 @@ const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const {
|
|||||||
|
|
||||||
if (initialized_vs_fixed_file_info_.is_uninitialized()) {
|
if (initialized_vs_fixed_file_info_.is_uninitialized()) {
|
||||||
initialized_vs_fixed_file_info_.set_invalid();
|
initialized_vs_fixed_file_info_.set_invalid();
|
||||||
if (GetModuleVersionAndType(base::FilePath(name_), &vs_fixed_file_info_)) {
|
if (pe_image_reader_->VSFixedFileInfo(&vs_fixed_file_info_)) {
|
||||||
initialized_vs_fixed_file_info_.set_valid();
|
initialized_vs_fixed_file_info_.set_valid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,8 @@
|
|||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/memory/scoped_ptr.h"
|
#include "base/memory/scoped_ptr.h"
|
||||||
#include "base/strings/stringprintf.h"
|
|
||||||
#include "client/crashpad_info.h"
|
#include "client/crashpad_info.h"
|
||||||
#include "snapshot/win/process_reader_win.h"
|
#include "snapshot/win/pe_image_resource_reader.h"
|
||||||
#include "util/misc/pdb_structures.h"
|
#include "util/misc/pdb_structures.h"
|
||||||
#include "util/win/process_structs.h"
|
#include "util/win/process_structs.h"
|
||||||
|
|
||||||
@ -28,13 +27,6 @@ namespace crashpad {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string RangeToString(const CheckedWinAddressRange& range) {
|
|
||||||
return base::StringPrintf("[0x%llx + 0x%llx (%s)]",
|
|
||||||
range.Base(),
|
|
||||||
range.Size(),
|
|
||||||
range.Is64Bit() ? "64" : "32");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map from Traits to an IMAGE_NT_HEADERSxx.
|
// Map from Traits to an IMAGE_NT_HEADERSxx.
|
||||||
template <class Traits>
|
template <class Traits>
|
||||||
struct NtHeadersForTraits;
|
struct NtHeadersForTraits;
|
||||||
@ -52,9 +44,7 @@ struct NtHeadersForTraits<process_types::internal::Traits64> {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PEImageReader::PEImageReader()
|
PEImageReader::PEImageReader()
|
||||||
: process_reader_(nullptr),
|
: module_subrange_reader_(),
|
||||||
module_range_(),
|
|
||||||
module_name_(),
|
|
||||||
initialized_() {
|
initialized_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,14 +57,10 @@ bool PEImageReader::Initialize(ProcessReaderWin* process_reader,
|
|||||||
const std::string& module_name) {
|
const std::string& module_name) {
|
||||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
process_reader_ = process_reader;
|
if (!module_subrange_reader_.Initialize(
|
||||||
module_range_.SetRange(process_reader_->Is64Bit(), address, size);
|
process_reader, address, size, module_name)) {
|
||||||
if (!module_range_.IsValid()) {
|
|
||||||
LOG(WARNING) << "invalid module range for " << module_name << ": "
|
|
||||||
<< RangeToString(module_range_);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
module_name_ = module_name;
|
|
||||||
|
|
||||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
return true;
|
return true;
|
||||||
@ -93,36 +79,34 @@ bool PEImageReader::GetCrashpadInfo(
|
|||||||
|
|
||||||
if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo<Traits>)) {
|
if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo<Traits>)) {
|
||||||
LOG(WARNING) << "small crashpad info section size "
|
LOG(WARNING) << "small crashpad info section size "
|
||||||
<< section.Misc.VirtualSize << ", " << module_name_;
|
<< section.Misc.VirtualSize << ", "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WinVMAddress crashpad_info_address = Address() + section.VirtualAddress;
|
ProcessSubrangeReader crashpad_info_subrange_reader;
|
||||||
CheckedWinAddressRange crashpad_info_range(process_reader_->Is64Bit(),
|
const WinVMAddress crashpad_info_address = Address() + section.VirtualAddress;
|
||||||
crashpad_info_address,
|
if (!crashpad_info_subrange_reader.InitializeSubrange(
|
||||||
section.Misc.VirtualSize);
|
module_subrange_reader_,
|
||||||
if (!crashpad_info_range.IsValid()) {
|
crashpad_info_address,
|
||||||
LOG(WARNING) << "invalid range for crashpad info: "
|
section.Misc.VirtualSize,
|
||||||
<< RangeToString(crashpad_info_range);
|
"crashpad_info")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!module_range_.ContainsRange(crashpad_info_range)) {
|
if (!crashpad_info_subrange_reader.ReadMemory(
|
||||||
LOG(WARNING) << "crashpad info does not fall inside module "
|
crashpad_info_address,
|
||||||
<< module_name_;
|
sizeof(process_types::CrashpadInfo<Traits>),
|
||||||
return false;
|
crashpad_info)) {
|
||||||
}
|
LOG(WARNING) << "could not read crashpad info from "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
if (!process_reader_->ReadMemory(crashpad_info_address,
|
|
||||||
sizeof(process_types::CrashpadInfo<Traits>),
|
|
||||||
crashpad_info)) {
|
|
||||||
LOG(WARNING) << "could not read crashpad info " << module_name_;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crashpad_info->signature != CrashpadInfo::kSignature ||
|
if (crashpad_info->signature != CrashpadInfo::kSignature ||
|
||||||
crashpad_info->version < 1) {
|
crashpad_info->version < 1) {
|
||||||
LOG(WARNING) << "unexpected crashpad info data " << module_name_;
|
LOG(WARNING) << "unexpected crashpad info data in "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,46 +116,23 @@ bool PEImageReader::GetCrashpadInfo(
|
|||||||
bool PEImageReader::DebugDirectoryInformation(UUID* uuid,
|
bool PEImageReader::DebugDirectoryInformation(UUID* uuid,
|
||||||
DWORD* age,
|
DWORD* age,
|
||||||
std::string* pdbname) const {
|
std::string* pdbname) const {
|
||||||
if (process_reader_->Is64Bit()) {
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return ReadDebugDirectoryInformation<IMAGE_NT_HEADERS64>(
|
|
||||||
uuid, age, pdbname);
|
|
||||||
} else {
|
|
||||||
return ReadDebugDirectoryInformation<IMAGE_NT_HEADERS32>(
|
|
||||||
uuid, age, pdbname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class NtHeadersType>
|
IMAGE_DATA_DIRECTORY data_directory;
|
||||||
bool PEImageReader::ReadDebugDirectoryInformation(UUID* uuid,
|
if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG, &data_directory))
|
||||||
DWORD* age,
|
|
||||||
std::string* pdbname) const {
|
|
||||||
NtHeadersType nt_headers;
|
|
||||||
if (!ReadNtHeaders(&nt_headers, nullptr))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (nt_headers.FileHeader.SizeOfOptionalHeader <
|
|
||||||
offsetof(decltype(nt_headers.OptionalHeader),
|
|
||||||
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]) +
|
|
||||||
sizeof(nt_headers.OptionalHeader
|
|
||||||
.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]) ||
|
|
||||||
nt_headers.OptionalHeader.NumberOfRvaAndSizes <=
|
|
||||||
IMAGE_DIRECTORY_ENTRY_DEBUG) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const IMAGE_DATA_DIRECTORY& data_directory =
|
|
||||||
nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
|
|
||||||
if (data_directory.VirtualAddress == 0 || data_directory.Size == 0)
|
|
||||||
return false;
|
|
||||||
IMAGE_DEBUG_DIRECTORY debug_directory;
|
IMAGE_DEBUG_DIRECTORY debug_directory;
|
||||||
if (data_directory.Size % sizeof(debug_directory) != 0)
|
if (data_directory.Size % sizeof(debug_directory) != 0)
|
||||||
return false;
|
return false;
|
||||||
for (size_t offset = 0; offset < data_directory.Size;
|
for (size_t offset = 0; offset < data_directory.Size;
|
||||||
offset += sizeof(debug_directory)) {
|
offset += sizeof(debug_directory)) {
|
||||||
if (!CheckedReadMemory(Address() + data_directory.VirtualAddress + offset,
|
if (!module_subrange_reader_.ReadMemory(
|
||||||
sizeof(debug_directory),
|
Address() + data_directory.VirtualAddress + offset,
|
||||||
&debug_directory)) {
|
sizeof(debug_directory),
|
||||||
LOG(WARNING) << "could not read data directory";
|
&debug_directory)) {
|
||||||
|
LOG(WARNING) << "could not read data directory from "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,21 +141,25 @@ bool PEImageReader::ReadDebugDirectoryInformation(UUID* uuid,
|
|||||||
|
|
||||||
if (debug_directory.AddressOfRawData) {
|
if (debug_directory.AddressOfRawData) {
|
||||||
if (debug_directory.SizeOfData < sizeof(CodeViewRecordPDB70)) {
|
if (debug_directory.SizeOfData < sizeof(CodeViewRecordPDB70)) {
|
||||||
LOG(WARNING) << "CodeView debug entry of unexpected size";
|
LOG(WARNING) << "CodeView debug entry of unexpected size in "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_ptr<char[]> data(new char[debug_directory.SizeOfData]);
|
scoped_ptr<char[]> data(new char[debug_directory.SizeOfData]);
|
||||||
if (!CheckedReadMemory(Address() + debug_directory.AddressOfRawData,
|
if (!module_subrange_reader_.ReadMemory(
|
||||||
debug_directory.SizeOfData,
|
Address() + debug_directory.AddressOfRawData,
|
||||||
data.get())) {
|
debug_directory.SizeOfData,
|
||||||
LOG(WARNING) << "could not read debug directory";
|
data.get())) {
|
||||||
|
LOG(WARNING) << "could not read debug directory from "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*reinterpret_cast<DWORD*>(data.get()) !=
|
if (*reinterpret_cast<DWORD*>(data.get()) !=
|
||||||
CodeViewRecordPDB70::kSignature) {
|
CodeViewRecordPDB70::kSignature) {
|
||||||
LOG(WARNING) << "encountered non-7.0 CodeView debug record";
|
LOG(WARNING) << "encountered non-7.0 CodeView debug record in "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,29 +183,120 @@ bool PEImageReader::ReadDebugDirectoryInformation(UUID* uuid,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PEImageReader::VSFixedFileInfo(
|
||||||
|
VS_FIXEDFILEINFO* vs_fixed_file_info) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
IMAGE_DATA_DIRECTORY data_directory;
|
||||||
|
if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE,
|
||||||
|
&data_directory)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PEImageResourceReader resource_reader;
|
||||||
|
if (!resource_reader.Initialize(module_subrange_reader_, data_directory)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WinVMAddress address;
|
||||||
|
WinVMSize size;
|
||||||
|
if (!resource_reader.FindResourceByID(
|
||||||
|
reinterpret_cast<uint16_t>(VS_FILE_INFO), // RT_VERSION
|
||||||
|
VS_VERSION_INFO,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
|
||||||
|
&address,
|
||||||
|
&size,
|
||||||
|
nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This structure is not declared anywhere in the SDK, but is documented at
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647001.aspx.
|
||||||
|
struct VS_VERSIONINFO {
|
||||||
|
WORD wLength;
|
||||||
|
WORD wValueLength;
|
||||||
|
WORD wType;
|
||||||
|
|
||||||
|
// The structure documentation on MSDN doesn’t show the [16], but it does
|
||||||
|
// say that it’s supposed to be L"VS_VERSION_INFO", which is is in fact a
|
||||||
|
// 16-character string (including its NUL terminator).
|
||||||
|
WCHAR szKey[16];
|
||||||
|
|
||||||
|
WORD Padding1;
|
||||||
|
VS_FIXEDFILEINFO Value;
|
||||||
|
|
||||||
|
// Don’t include Children or the Padding2 that precedes it, because they may
|
||||||
|
// not be present.
|
||||||
|
// WORD Padding2;
|
||||||
|
// WORD Children;
|
||||||
|
};
|
||||||
|
VS_VERSIONINFO version_info;
|
||||||
|
|
||||||
|
if (size < sizeof(version_info)) {
|
||||||
|
LOG(WARNING) << "version info size " << size
|
||||||
|
<< " too small for structure of size " << sizeof(version_info)
|
||||||
|
<< " in " << module_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!module_subrange_reader_.ReadMemory(
|
||||||
|
address, sizeof(version_info), &version_info)) {
|
||||||
|
LOG(WARNING) << "could not read version info from "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version_info.wLength < sizeof(version_info) ||
|
||||||
|
version_info.wValueLength != sizeof(version_info.Value) ||
|
||||||
|
version_info.wType != 0 ||
|
||||||
|
wcsncmp(version_info.szKey,
|
||||||
|
L"VS_VERSION_INFO",
|
||||||
|
arraysize(version_info.szKey)) != 0) {
|
||||||
|
LOG(WARNING) << "unexpected VS_VERSIONINFO in "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version_info.Value.dwSignature != VS_FFI_SIGNATURE ||
|
||||||
|
version_info.Value.dwStrucVersion != VS_FFI_STRUCVERSION) {
|
||||||
|
LOG(WARNING) << "unexpected VS_FIXEDFILEINFO in "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*vs_fixed_file_info = version_info.Value;
|
||||||
|
vs_fixed_file_info->dwFileFlags &= vs_fixed_file_info->dwFileFlagsMask;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <class NtHeadersType>
|
template <class NtHeadersType>
|
||||||
bool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers,
|
bool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers,
|
||||||
WinVMAddress* nt_headers_address) const {
|
WinVMAddress* nt_headers_address) const {
|
||||||
IMAGE_DOS_HEADER dos_header;
|
IMAGE_DOS_HEADER dos_header;
|
||||||
if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
|
if (!module_subrange_reader_.ReadMemory(
|
||||||
LOG(WARNING) << "could not read dos header of " << module_name_;
|
Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
|
||||||
|
LOG(WARNING) << "could not read dos header from "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) {
|
if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) {
|
||||||
LOG(WARNING) << "invalid e_magic in dos header of " << module_name_;
|
LOG(WARNING) << "invalid e_magic in dos header of "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WinVMAddress local_nt_headers_address = Address() + dos_header.e_lfanew;
|
WinVMAddress local_nt_headers_address = Address() + dos_header.e_lfanew;
|
||||||
if (!CheckedReadMemory(
|
if (!module_subrange_reader_.ReadMemory(
|
||||||
local_nt_headers_address, sizeof(NtHeadersType), nt_headers)) {
|
local_nt_headers_address, sizeof(NtHeadersType), nt_headers)) {
|
||||||
LOG(WARNING) << "could not read nt headers of " << module_name_;
|
LOG(WARNING) << "could not read nt headers from "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
|
if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
|
||||||
LOG(WARNING) << "invalid signature in nt headers of " << module_name_;
|
LOG(WARNING) << "invalid signature in nt headers of "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,9 +325,10 @@ bool PEImageReader::GetSectionByName(const std::string& name,
|
|||||||
for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {
|
for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {
|
||||||
WinVMAddress section_address =
|
WinVMAddress section_address =
|
||||||
first_section_address + sizeof(IMAGE_SECTION_HEADER) * i;
|
first_section_address + sizeof(IMAGE_SECTION_HEADER) * i;
|
||||||
if (!CheckedReadMemory(
|
if (!module_subrange_reader_.ReadMemory(
|
||||||
section_address, sizeof(IMAGE_SECTION_HEADER), section)) {
|
section_address, sizeof(IMAGE_SECTION_HEADER), section)) {
|
||||||
LOG(WARNING) << "could not read section " << i << " of " << module_name_;
|
LOG(WARNING) << "could not read section " << i << " from "
|
||||||
|
<< module_subrange_reader_.name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (strncmp(reinterpret_cast<const char*>(section->Name),
|
if (strncmp(reinterpret_cast<const char*>(section->Name),
|
||||||
@ -284,20 +341,36 @@ bool PEImageReader::GetSectionByName(const std::string& name,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PEImageReader::CheckedReadMemory(WinVMAddress address,
|
bool PEImageReader::ImageDataDirectoryEntry(size_t index,
|
||||||
WinVMSize size,
|
IMAGE_DATA_DIRECTORY* entry) const {
|
||||||
void* into) const {
|
bool rv;
|
||||||
CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size);
|
if (module_subrange_reader_.Is64Bit()) {
|
||||||
if (!read_range.IsValid()) {
|
rv = ImageDataDirectoryEntryT<IMAGE_NT_HEADERS64>(index, entry);
|
||||||
LOG(WARNING) << "invalid read range: " << RangeToString(read_range);
|
} else {
|
||||||
|
rv = ImageDataDirectoryEntryT<IMAGE_NT_HEADERS32>(index, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv && entry->VirtualAddress != 0 && entry->Size != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class NtHeadersType>
|
||||||
|
bool PEImageReader::ImageDataDirectoryEntryT(
|
||||||
|
size_t index,
|
||||||
|
IMAGE_DATA_DIRECTORY* entry) const {
|
||||||
|
NtHeadersType nt_headers;
|
||||||
|
if (!ReadNtHeaders(&nt_headers, nullptr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!module_range_.ContainsRange(read_range)) {
|
|
||||||
LOG(WARNING) << "attempt to read outside of module " << module_name_
|
if (nt_headers.FileHeader.SizeOfOptionalHeader <
|
||||||
<< " at range: " << RangeToString(read_range);
|
offsetof(decltype(nt_headers.OptionalHeader), DataDirectory[index]) +
|
||||||
|
sizeof(nt_headers.OptionalHeader.DataDirectory[index]) ||
|
||||||
|
nt_headers.OptionalHeader.NumberOfRvaAndSizes <= index) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return process_reader_->ReadMemory(address, size, into);
|
|
||||||
|
*entry = nt_headers.OptionalHeader.DataDirectory[index];
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit instantiations with the only 2 valid template arguments to avoid
|
// Explicit instantiations with the only 2 valid template arguments to avoid
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/basictypes.h"
|
#include "base/basictypes.h"
|
||||||
|
#include "snapshot/win/process_subrange_reader.h"
|
||||||
#include "util/misc/initialization_state_dcheck.h"
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
#include "util/misc/uuid.h"
|
#include "util/misc/uuid.h"
|
||||||
#include "util/win/address_types.h"
|
#include "util/win/address_types.h"
|
||||||
#include "util/win/checked_win_address_range.h"
|
|
||||||
#include "util/win/process_structs.h"
|
#include "util/win/process_structs.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -51,6 +51,7 @@ struct CrashpadInfo {
|
|||||||
//! bitness of the remote process.
|
//! bitness of the remote process.
|
||||||
//!
|
//!
|
||||||
//! \sa PEImageAnnotationsReader
|
//! \sa PEImageAnnotationsReader
|
||||||
|
//! \sa PEImageResourceReader
|
||||||
class PEImageReader {
|
class PEImageReader {
|
||||||
public:
|
public:
|
||||||
PEImageReader();
|
PEImageReader();
|
||||||
@ -65,8 +66,8 @@ class PEImageReader {
|
|||||||
//! \param[in] address The address, in the remote process' address space,
|
//! \param[in] address The address, in the remote process' address space,
|
||||||
//! where the `IMAGE_DOS_HEADER` is located.
|
//! where the `IMAGE_DOS_HEADER` is located.
|
||||||
//! \param[in] size The size of the image.
|
//! \param[in] size The size of the image.
|
||||||
//! \param[in] name The module's name, a string to be used in logged messages.
|
//! \param[in] module_name The module's name, a string to be used in logged
|
||||||
//! This string is for diagnostic purposes.
|
//! messages. This string is for diagnostic purposes.
|
||||||
//!
|
//!
|
||||||
//! \return `true` if the image was read successfully, `false` otherwise, with
|
//! \return `true` if the image was read successfully, `false` otherwise, with
|
||||||
//! an appropriate message logged.
|
//! an appropriate message logged.
|
||||||
@ -78,12 +79,12 @@ class PEImageReader {
|
|||||||
//! \brief Returns the image's load address.
|
//! \brief Returns the image's load address.
|
||||||
//!
|
//!
|
||||||
//! This is the value passed as \a address to Initialize().
|
//! This is the value passed as \a address to Initialize().
|
||||||
WinVMAddress Address() const { return module_range_.Base(); }
|
WinVMAddress Address() const { return module_subrange_reader_.Base(); }
|
||||||
|
|
||||||
//! \brief Returns the image's size.
|
//! \brief Returns the image's size.
|
||||||
//!
|
//!
|
||||||
//! This is the value passed as \a size to Initialize().
|
//! This is the value passed as \a size to Initialize().
|
||||||
WinVMSize Size() const { return module_range_.Size(); }
|
WinVMSize Size() const { return module_subrange_reader_.Size(); }
|
||||||
|
|
||||||
//! \brief Obtains the module's CrashpadInfo structure.
|
//! \brief Obtains the module's CrashpadInfo structure.
|
||||||
//!
|
//!
|
||||||
@ -100,25 +101,34 @@ class PEImageReader {
|
|||||||
//! \param[out] age The age field for the pdb (the number of times it's been
|
//! \param[out] age The age field for the pdb (the number of times it's been
|
||||||
//! relinked).
|
//! relinked).
|
||||||
//! \param[out] pdbname Name of the pdb file.
|
//! \param[out] pdbname Name of the pdb file.
|
||||||
//! \return `true` on success, or `false` if the module has no debug directory
|
|
||||||
//! entry.
|
|
||||||
bool DebugDirectoryInformation(UUID* uuid,
|
|
||||||
DWORD* age,
|
|
||||||
std::string* pdbname) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
//! \brief Implementation helper for DebugDirectoryInformation() templated by
|
|
||||||
//! `IMAGE_NT_HEADERS` type for different bitnesses.
|
|
||||||
//!
|
//!
|
||||||
//! \return `true` on success, with the parameters set appropriately. `false`
|
//! \return `true` on success, with the parameters set appropriately. `false`
|
||||||
//! on failure. This method may return `false` without logging anything in
|
//! on failure. This method may return `false` without logging anything in
|
||||||
//! the case of a module that does not contain relevant debugging
|
//! the case of a module that does not contain relevant debugging
|
||||||
//! information but is otherwise properly structured.
|
//! information but is otherwise properly structured.
|
||||||
template <class NtHeadersType>
|
bool DebugDirectoryInformation(UUID* uuid,
|
||||||
bool ReadDebugDirectoryInformation(UUID* uuid,
|
DWORD* age,
|
||||||
DWORD* age,
|
std::string* pdbname) const;
|
||||||
std::string* pdbname) const;
|
|
||||||
|
|
||||||
|
//! \brief Obtains the module’s `VS_FIXEDFILEINFO`, containing its version and
|
||||||
|
//! type information.
|
||||||
|
//!
|
||||||
|
//! The data obtained from this method should be equivalent to what could be
|
||||||
|
//! obtained by calling GetModuleVersionAndType(). Avoiding that function
|
||||||
|
//! ensures that the data in the module loaded into the remote process will be
|
||||||
|
//! used as-is, without the risks associated with loading the module into the
|
||||||
|
//! reading process.
|
||||||
|
//!
|
||||||
|
//! \param[out] vs_fixed_file_info The VS_FIXEDFILEINFO on success.
|
||||||
|
//! VS_FIXEDFILEINFO::dwFileFlags will have been masked with
|
||||||
|
//! VS_FIXEDFILEINFO::dwFileFlagsMask already.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success. `false` if the module does not contain this
|
||||||
|
//! information, without logging any messages. `false` on failure, with
|
||||||
|
//! a message logged.
|
||||||
|
bool VSFixedFileInfo(VS_FIXEDFILEINFO* vs_fixed_file_info) const;
|
||||||
|
|
||||||
|
private:
|
||||||
//! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image.
|
//! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image.
|
||||||
//!
|
//!
|
||||||
//! \param[out] nt_headers The contents of the templated NtHeadersType
|
//! \param[out] nt_headers The contents of the templated NtHeadersType
|
||||||
@ -139,18 +149,25 @@ class PEImageReader {
|
|||||||
bool GetSectionByName(const std::string& name,
|
bool GetSectionByName(const std::string& name,
|
||||||
IMAGE_SECTION_HEADER* section) const;
|
IMAGE_SECTION_HEADER* section) const;
|
||||||
|
|
||||||
//! \brief Reads memory from target process, first checking whether the range
|
//! \brief Finds the `IMAGE_DATA_DIRECTORY` in
|
||||||
//! requested falls inside module_range_.
|
//! `IMAGE_OPTIONAL_HEADER::DataDirectory` at the specified \a index.
|
||||||
//!
|
//!
|
||||||
//! \return `true` on success, with \a into filled out, otherwise `false` and
|
//! \param[in] index An `IMAGE_DIRECTORY_ENTRY_*` constant specifying the
|
||||||
//! a message will be logged.
|
//! data to be returned.
|
||||||
bool CheckedReadMemory(WinVMAddress address,
|
//! \param[out] entry The `IMAGE_DATA_DIRECTORY` found within the module.
|
||||||
WinVMSize size,
|
//!
|
||||||
void* into) const;
|
//! \return `true` on success, with \a entry set appropriately. `false` if the
|
||||||
|
//! module does not contain the specified information, without logging a
|
||||||
|
//! message. `false` on failure, with a message logged.
|
||||||
|
bool ImageDataDirectoryEntry(size_t index, IMAGE_DATA_DIRECTORY* entry) const;
|
||||||
|
|
||||||
ProcessReaderWin* process_reader_; // weak
|
//! \brief A templatized helper for ImageDataDirectoryEntry() to account for
|
||||||
CheckedWinAddressRange module_range_;
|
//! differences in \a NtHeadersType.
|
||||||
std::string module_name_;
|
template <class NtHeadersType>
|
||||||
|
bool ImageDataDirectoryEntryT(size_t index,
|
||||||
|
IMAGE_DATA_DIRECTORY* entry) const;
|
||||||
|
|
||||||
|
ProcessSubrangeReader module_subrange_reader_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(PEImageReader);
|
DISALLOW_COPY_AND_ASSIGN(PEImageReader);
|
||||||
|
@ -17,9 +17,13 @@
|
|||||||
#define PSAPI_VERSION 1
|
#define PSAPI_VERSION 1
|
||||||
#include <psapi.h>
|
#include <psapi.h>
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "snapshot/win/process_reader_win.h"
|
#include "snapshot/win/process_reader_win.h"
|
||||||
|
#include "test/errors.h"
|
||||||
#include "util/win/get_function.h"
|
#include "util/win/get_function.h"
|
||||||
|
#include "util/win/module_version.h"
|
||||||
|
|
||||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||||
|
|
||||||
@ -28,9 +32,9 @@ namespace test {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
BOOL CrashpadGetModuleInformation(HANDLE process,
|
BOOL CrashpadGetModuleInformation(HANDLE process,
|
||||||
HMODULE module,
|
HMODULE module,
|
||||||
MODULEINFO* module_info,
|
MODULEINFO* module_info,
|
||||||
DWORD cb) {
|
DWORD cb) {
|
||||||
static const auto get_module_information =
|
static const auto get_module_information =
|
||||||
GET_FUNCTION_REQUIRED(L"psapi.dll", ::GetModuleInformation);
|
GET_FUNCTION_REQUIRED(L"psapi.dll", ::GetModuleInformation);
|
||||||
return get_module_information(process, module, module_info, cb);
|
return get_module_information(process, module, module_info, cb);
|
||||||
@ -44,9 +48,10 @@ TEST(PEImageReader, DebugDirectory) {
|
|||||||
HMODULE self = reinterpret_cast<HMODULE>(&__ImageBase);
|
HMODULE self = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||||
MODULEINFO module_info;
|
MODULEINFO module_info;
|
||||||
ASSERT_TRUE(CrashpadGetModuleInformation(
|
ASSERT_TRUE(CrashpadGetModuleInformation(
|
||||||
GetCurrentProcess(), self, &module_info, sizeof(module_info)));
|
GetCurrentProcess(), self, &module_info, sizeof(module_info)))
|
||||||
|
<< ErrorMessage("GetModuleInformation");
|
||||||
EXPECT_EQ(self, module_info.lpBaseOfDll);
|
EXPECT_EQ(self, module_info.lpBaseOfDll);
|
||||||
EXPECT_TRUE(pe_image_reader.Initialize(&process_reader,
|
ASSERT_TRUE(pe_image_reader.Initialize(&process_reader,
|
||||||
reinterpret_cast<WinVMAddress>(self),
|
reinterpret_cast<WinVMAddress>(self),
|
||||||
module_info.SizeOfImage,
|
module_info.SizeOfImage,
|
||||||
"self"));
|
"self"));
|
||||||
@ -61,6 +66,56 @@ TEST(PEImageReader, DebugDirectory) {
|
|||||||
pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix));
|
pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PEImageReader, VSFixedFileInfo) {
|
||||||
|
ProcessReaderWin process_reader;
|
||||||
|
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
|
|
||||||
|
const wchar_t kModuleName[] = L"kernel32.dll";
|
||||||
|
|
||||||
|
HMODULE module_handle = GetModuleHandle(kModuleName);
|
||||||
|
ASSERT_TRUE(module_handle) << ErrorMessage("GetModuleHandle");
|
||||||
|
|
||||||
|
MODULEINFO module_info;
|
||||||
|
ASSERT_TRUE(CrashpadGetModuleInformation(
|
||||||
|
GetCurrentProcess(), module_handle, &module_info, sizeof(module_info)))
|
||||||
|
<< ErrorMessage("GetModuleInformation");
|
||||||
|
EXPECT_EQ(module_handle, module_info.lpBaseOfDll);
|
||||||
|
|
||||||
|
PEImageReader pe_image_reader;
|
||||||
|
ASSERT_TRUE(
|
||||||
|
pe_image_reader.Initialize(&process_reader,
|
||||||
|
reinterpret_cast<WinVMAddress>(module_handle),
|
||||||
|
module_info.SizeOfImage,
|
||||||
|
base::UTF16ToUTF8(kModuleName)));
|
||||||
|
|
||||||
|
VS_FIXEDFILEINFO observed;
|
||||||
|
ASSERT_TRUE(pe_image_reader.VSFixedFileInfo(&observed));
|
||||||
|
|
||||||
|
EXPECT_EQ(VS_FFI_SIGNATURE, observed.dwSignature);
|
||||||
|
EXPECT_EQ(VS_FFI_STRUCVERSION, observed.dwStrucVersion);
|
||||||
|
EXPECT_EQ(0, observed.dwFileFlags & ~observed.dwFileFlagsMask);
|
||||||
|
EXPECT_EQ(VOS_NT_WINDOWS32, observed.dwFileOS);
|
||||||
|
EXPECT_EQ(VFT_DLL, observed.dwFileType);
|
||||||
|
|
||||||
|
VS_FIXEDFILEINFO expected;
|
||||||
|
ASSERT_TRUE(GetModuleVersionAndType(base::FilePath(kModuleName), &expected));
|
||||||
|
|
||||||
|
EXPECT_EQ(expected.dwSignature, observed.dwSignature);
|
||||||
|
EXPECT_EQ(expected.dwStrucVersion, observed.dwStrucVersion);
|
||||||
|
EXPECT_EQ(expected.dwFileVersionMS, observed.dwFileVersionMS);
|
||||||
|
EXPECT_EQ(expected.dwFileVersionLS, observed.dwFileVersionLS);
|
||||||
|
EXPECT_EQ(expected.dwProductVersionMS, observed.dwProductVersionMS);
|
||||||
|
EXPECT_EQ(expected.dwProductVersionLS, observed.dwProductVersionLS);
|
||||||
|
EXPECT_EQ(expected.dwFileFlagsMask, observed.dwFileFlagsMask);
|
||||||
|
EXPECT_EQ(expected.dwFileFlags, observed.dwFileFlags);
|
||||||
|
EXPECT_EQ(expected.dwFileOS, observed.dwFileOS);
|
||||||
|
EXPECT_EQ(expected.dwFileType, observed.dwFileType);
|
||||||
|
EXPECT_EQ(expected.dwFileSubtype, observed.dwFileSubtype);
|
||||||
|
EXPECT_EQ(expected.dwFileDateMS, observed.dwFileDateMS);
|
||||||
|
EXPECT_EQ(expected.dwFileDateLS, observed.dwFileDateLS);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
279
snapshot/win/pe_image_resource_reader.cc
Normal file
279
snapshot/win/pe_image_resource_reader.cc
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "snapshot/win/pe_image_resource_reader.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/memory/scoped_ptr.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void AddLanguageAndNeutralSublanguage(std::vector<uint16_t>* languages,
|
||||||
|
uint16_t language) {
|
||||||
|
languages->push_back(language);
|
||||||
|
if (SUBLANGID(language) != SUBLANG_NEUTRAL) {
|
||||||
|
languages->push_back(MAKELANGID(PRIMARYLANGID(language), SUBLANG_NEUTRAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
PEImageResourceReader::PEImageResourceReader()
|
||||||
|
: resources_subrange_reader_(),
|
||||||
|
module_base_(0),
|
||||||
|
initialized_() {
|
||||||
|
}
|
||||||
|
|
||||||
|
PEImageResourceReader::~PEImageResourceReader() {}
|
||||||
|
|
||||||
|
bool PEImageResourceReader::Initialize(
|
||||||
|
const ProcessSubrangeReader& module_subrange_reader,
|
||||||
|
const IMAGE_DATA_DIRECTORY& resources_directory_entry) {
|
||||||
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
|
module_base_ = module_subrange_reader.Base();
|
||||||
|
|
||||||
|
if (!resources_subrange_reader_.InitializeSubrange(
|
||||||
|
module_subrange_reader,
|
||||||
|
module_base_ + resources_directory_entry.VirtualAddress,
|
||||||
|
resources_directory_entry.Size,
|
||||||
|
"resources")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PEImageResourceReader::FindResourceByID(uint16_t type,
|
||||||
|
uint16_t name,
|
||||||
|
uint16_t language,
|
||||||
|
WinVMAddress* address,
|
||||||
|
WinVMSize* size,
|
||||||
|
uint32_t* code_page) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
// The root resource directory is at the beginning of the resources area
|
||||||
|
// within the module.
|
||||||
|
const uint32_t name_directory_offset =
|
||||||
|
GetEntryFromResourceDirectoryByID(0, type, true);
|
||||||
|
if (!name_directory_offset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t language_directory_offset =
|
||||||
|
GetEntryFromResourceDirectoryByID(name_directory_offset, name, true);
|
||||||
|
if (!language_directory_offset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The definition of IMAGE_RESOURCE_DIRECTORY_ENTRY in <winnt.h> has a comment
|
||||||
|
// saying that its offsets are relative to “the resource directory of the data
|
||||||
|
// associated with this directory entry”. That could be interpreted to mean
|
||||||
|
// that language_directory_offset is relative to name_directory_offset, since
|
||||||
|
// the language directory entry is found within the name directory. This is
|
||||||
|
// not correct. All resource offsets are relative to the resources area within
|
||||||
|
// the module.
|
||||||
|
const uint32_t data_offset = GetEntryFromResourceDirectoryByLanguage(
|
||||||
|
language_directory_offset, language);
|
||||||
|
if (!data_offset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_RESOURCE_DATA_ENTRY data_entry;
|
||||||
|
if (!resources_subrange_reader_.ReadMemory(
|
||||||
|
resources_subrange_reader_.Base() + data_offset,
|
||||||
|
sizeof(data_entry),
|
||||||
|
&data_entry)) {
|
||||||
|
LOG(WARNING) << "could not read resource data entry from "
|
||||||
|
<< resources_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The definition of IMAGE_RESOURCE_DATA_ENTRY in <winnt.h> has a comment
|
||||||
|
// saying that OffsetToData is relative to the beginning of the resource data.
|
||||||
|
// This is not correct. It’s module-relative.
|
||||||
|
*address = module_base_ + data_entry.OffsetToData;
|
||||||
|
*size = data_entry.Size;
|
||||||
|
if (code_page) {
|
||||||
|
*code_page = data_entry.CodePage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByID(
|
||||||
|
uint32_t language_directory_offset,
|
||||||
|
uint16_t id,
|
||||||
|
bool want_subdirectory) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_id;
|
||||||
|
if (!ReadResourceDirectory(
|
||||||
|
language_directory_offset, nullptr, nullptr, &entries_by_id)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto entry_it =
|
||||||
|
std::find_if(entries_by_id.begin(),
|
||||||
|
entries_by_id.end(),
|
||||||
|
[id](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) {
|
||||||
|
return !entry.NameIsString && entry.Id == id;
|
||||||
|
});
|
||||||
|
if (entry_it != entries_by_id.end()) {
|
||||||
|
if ((entry_it->DataIsDirectory != 0) != want_subdirectory) {
|
||||||
|
LOG(WARNING) << "expected " << (want_subdirectory ? "" : "non-")
|
||||||
|
<< "directory for entry id " << id << " in "
|
||||||
|
<< resources_subrange_reader_.name();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry_it->DataIsDirectory ? entry_it->OffsetToDirectory
|
||||||
|
: entry_it->OffsetToData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByLanguage(
|
||||||
|
uint32_t resource_directory_offset,
|
||||||
|
uint16_t language) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_language;
|
||||||
|
if (!ReadResourceDirectory(
|
||||||
|
resource_directory_offset, nullptr, nullptr, &entries_by_language)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries_by_language.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://msdn.microsoft.com/en-us/library/cc194810.aspx
|
||||||
|
//
|
||||||
|
// TODO(mark): It seems like FindResourceEx() might do something more complex.
|
||||||
|
// It would be best to mimic its behavior.
|
||||||
|
std::vector<uint16_t> try_languages;
|
||||||
|
if (PRIMARYLANGID(language) != LANG_NEUTRAL) {
|
||||||
|
AddLanguageAndNeutralSublanguage(&try_languages, language);
|
||||||
|
} else {
|
||||||
|
if (SUBLANGID(language) != SUBLANG_SYS_DEFAULT) {
|
||||||
|
AddLanguageAndNeutralSublanguage(&try_languages,
|
||||||
|
LANGIDFROMLCID(GetThreadLocale()));
|
||||||
|
AddLanguageAndNeutralSublanguage(&try_languages,
|
||||||
|
LANGIDFROMLCID(GetUserDefaultLCID()));
|
||||||
|
}
|
||||||
|
if (SUBLANGID(language) != SUBLANG_DEFAULT) {
|
||||||
|
AddLanguageAndNeutralSublanguage(&try_languages,
|
||||||
|
LANGIDFROMLCID(GetSystemDefaultLCID()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try_languages.push_back(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
|
||||||
|
try_languages.push_back(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT));
|
||||||
|
|
||||||
|
for (const auto try_language : try_languages) {
|
||||||
|
const auto entry_it = std::find_if(
|
||||||
|
entries_by_language.begin(),
|
||||||
|
entries_by_language.end(),
|
||||||
|
[try_language](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) {
|
||||||
|
return !entry.NameIsString && entry.Id == try_language;
|
||||||
|
});
|
||||||
|
if (entry_it != entries_by_language.end()) {
|
||||||
|
if (entry_it->DataIsDirectory) {
|
||||||
|
LOG(WARNING) << "expected non-directory for entry language "
|
||||||
|
<< try_language << " in "
|
||||||
|
<< resources_subrange_reader_.name();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry_it->OffsetToData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the first entry in the list.
|
||||||
|
const auto& entry = entries_by_language.front();
|
||||||
|
if (entry.DataIsDirectory) {
|
||||||
|
LOG(WARNING) << "expected non-directory for entry in "
|
||||||
|
<< resources_subrange_reader_.name();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.OffsetToData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PEImageResourceReader::ReadResourceDirectory(
|
||||||
|
uint32_t resource_directory_offset,
|
||||||
|
IMAGE_RESOURCE_DIRECTORY* resource_directory,
|
||||||
|
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* named_entries,
|
||||||
|
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* id_entries) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
// resource_directory is optional, but it’s still needed locally even if the
|
||||||
|
// caller isn’t interested in it.
|
||||||
|
scoped_ptr<IMAGE_RESOURCE_DIRECTORY> local_resource_directory;
|
||||||
|
if (!resource_directory) {
|
||||||
|
local_resource_directory.reset(new IMAGE_RESOURCE_DIRECTORY);
|
||||||
|
resource_directory = local_resource_directory.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const WinVMAddress address =
|
||||||
|
resources_subrange_reader_.Base() + resource_directory_offset;
|
||||||
|
|
||||||
|
if (!resources_subrange_reader_.ReadMemory(
|
||||||
|
address, sizeof(*resource_directory), resource_directory)) {
|
||||||
|
LOG(WARNING) << "could not read resource directory from "
|
||||||
|
<< resources_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (named_entries) {
|
||||||
|
named_entries->clear();
|
||||||
|
named_entries->resize(resource_directory->NumberOfNamedEntries);
|
||||||
|
if (!named_entries->empty() &&
|
||||||
|
!resources_subrange_reader_.ReadMemory(
|
||||||
|
address + sizeof(*resource_directory),
|
||||||
|
named_entries->size() * sizeof((*named_entries)[0]),
|
||||||
|
&(*named_entries)[0])) {
|
||||||
|
LOG(WARNING) << "could not read resource directory named entries from "
|
||||||
|
<< resources_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id_entries) {
|
||||||
|
id_entries->clear();
|
||||||
|
id_entries->resize(resource_directory->NumberOfIdEntries);
|
||||||
|
if (!id_entries->empty() &&
|
||||||
|
!resources_subrange_reader_.ReadMemory(
|
||||||
|
address + sizeof(*resource_directory) +
|
||||||
|
resource_directory->NumberOfNamedEntries *
|
||||||
|
sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY),
|
||||||
|
id_entries->size() * sizeof((*id_entries)[0]),
|
||||||
|
&(*id_entries)[0])) {
|
||||||
|
LOG(WARNING) << "could not read resource directory ID entries from "
|
||||||
|
<< resources_subrange_reader_.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
179
snapshot/win/pe_image_resource_reader.h
Normal file
179
snapshot/win/pe_image_resource_reader.h
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
// 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_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_
|
||||||
|
#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/basictypes.h"
|
||||||
|
#include "snapshot/win/process_subrange_reader.h"
|
||||||
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
|
#include "util/win/address_types.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief A reader for resources stored in PE images mapped into another
|
||||||
|
//! process.
|
||||||
|
//!
|
||||||
|
//! \sa PEImageReader
|
||||||
|
class PEImageResourceReader {
|
||||||
|
public:
|
||||||
|
PEImageResourceReader();
|
||||||
|
~PEImageResourceReader();
|
||||||
|
|
||||||
|
//! \brief Initializes the resource reader.
|
||||||
|
//!
|
||||||
|
//! \param[in] module_subrange_reader The reader for the module.
|
||||||
|
//! \param[in] resources_directory_entry The module’s `IMAGE_DATA_DIRECTORY`
|
||||||
|
//! for its resources area. This is taken from the module’s
|
||||||
|
//! `IMAGE_OPTIONAL_HEADER::DataDirectory` at index
|
||||||
|
//! `IMAGE_DIRECTORY_ENTRY_RESOURCE`.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, `false` on failure with a message logged.
|
||||||
|
bool Initialize(const ProcessSubrangeReader& module_subrange_reader,
|
||||||
|
const IMAGE_DATA_DIRECTORY& resources_directory_entry);
|
||||||
|
|
||||||
|
//! \brief Locates a resource in a module by its ID.
|
||||||
|
//!
|
||||||
|
//! This method is similar to `FindResourceEx()`, but it operates on modules
|
||||||
|
//! loaded in a remote process’ address space. It is not necessary to
|
||||||
|
//! `LoadLibrary()` a module into a process in order to use this method.
|
||||||
|
//!
|
||||||
|
//! No support is provided at present for locating resources by \a type or \a
|
||||||
|
//! name using strings as opposed to integer identifiers.
|
||||||
|
//!
|
||||||
|
//! Languages are scanned in the order determined by
|
||||||
|
//! GetEntryFromResourceDirectoryByLanguage().
|
||||||
|
//!
|
||||||
|
//! \param[in] type The integer identifier of the resource type, as in the
|
||||||
|
//! `lpType` parameter of `FindResourceEx()`.
|
||||||
|
//! \param[in] name The integer identifier of the resource, as in the `lpName`
|
||||||
|
//! parameter of `FindResourceEx()`.
|
||||||
|
//! \param[in] language The language of the resource, as in the `wLanguage`
|
||||||
|
//! parameter of `FindResourceEx()`.
|
||||||
|
//! \param[out] address The address, in the remote process’ address space, of
|
||||||
|
//! the resource data.
|
||||||
|
//! \param[out] size The size of the resource data.
|
||||||
|
//! \param[out] code_page The code page used to encode textual resource data.
|
||||||
|
//! This parameter is optional.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, with the out parameters set appropriately.
|
||||||
|
//! `false` if the resource was not found, without logging any messages.
|
||||||
|
//! `false` on failure, with a message logged.
|
||||||
|
bool FindResourceByID(uint16_t type,
|
||||||
|
uint16_t name,
|
||||||
|
uint16_t language,
|
||||||
|
WinVMAddress* address,
|
||||||
|
WinVMSize* size,
|
||||||
|
uint32_t* code_page) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! \brief Locates a resource directory entry within a resource directory by
|
||||||
|
//! integer ID.
|
||||||
|
//!
|
||||||
|
//! \param[in] resource_directory_offset The offset, in the module’s resources
|
||||||
|
//! area, of the resource directory to search.
|
||||||
|
//! \param[in] id The integer identifier of the resource to search for.
|
||||||
|
//! \param[in] want_subdirectory `true` if the resource directory entry is
|
||||||
|
//! expected to be a resource directory itself, `false` otherwise.
|
||||||
|
//!
|
||||||
|
//! \return The offset, in the module’s resources area, of the entry that was
|
||||||
|
//! found. On failure, `0`. `0` is technically a valid offset, but it
|
||||||
|
//! corresponds to the root resource directory, which should never be the
|
||||||
|
//! offset of another resource directory entry. If \a id was not found,
|
||||||
|
//! `0` will be returned without logging anything. For other failures, a
|
||||||
|
//! message will be logged.
|
||||||
|
uint32_t GetEntryFromResourceDirectoryByID(uint32_t resource_directory_offset,
|
||||||
|
uint16_t id,
|
||||||
|
bool want_subdirectory) const;
|
||||||
|
|
||||||
|
//! \brief Locates a resource directory entry within a resource directory by
|
||||||
|
//! language.
|
||||||
|
//!
|
||||||
|
//! This method is similar to GetEntryFromResourceDirectoryByID() with \a
|
||||||
|
//! want_subdirectory set to `false`. Attempts are made to locate the resource
|
||||||
|
//! by using these languages:
|
||||||
|
//! <ul>
|
||||||
|
//! <li>If \a language is `LANG_NEUTRAL`:</li>
|
||||||
|
//! <ul>
|
||||||
|
//! <li>Unless `SUBLANG_SYS_DEFAULT` is specified, the language of the
|
||||||
|
//! thread’s locale, with its normal sublanguage and with
|
||||||
|
//! `SUBLANG_NEUTRAL`.</li>
|
||||||
|
//! <li>Unless `SUBLANG_SYS_DEFAULT` is specified, the language of the
|
||||||
|
//! user’s default locale, with its normal sublanguage and with
|
||||||
|
//! `SUBLANG_NEUTRAL`.</li>
|
||||||
|
//! <li>Unless `SUBLANG_DEFAULT` is specified, the language of the
|
||||||
|
//! system’s default locale, with its normal sublanguage and with
|
||||||
|
//! `SUBLANG_NEUTRAL`.</li>
|
||||||
|
//! </ul>
|
||||||
|
//! <li>If \a language is not `LANG_NEUTRAL`:</li>
|
||||||
|
//! <ul>
|
||||||
|
//! <li>\a language</li>
|
||||||
|
//! <li>\a language, with `SUBLANG_NEUTRAL`</li>
|
||||||
|
//! </ul>
|
||||||
|
//! <li>`LANG_NEUTRAL` with `SUBLANG_NEUTRAL`</li>
|
||||||
|
//! <li>`LANG_ENGLISH` with `SUBLANG_DEFAULT`</li>
|
||||||
|
//! <li>If none of the above match, the first language found</li>
|
||||||
|
//! </ul>
|
||||||
|
//!
|
||||||
|
//! If only a specific language is desired without any fallbacks, call
|
||||||
|
//! GetEntryFromResourceDirectoryByID() with the language directory’s offset
|
||||||
|
//! instead, passing the desired language in the \a id parameter, and `false`
|
||||||
|
//! for \a want_subdirectory.
|
||||||
|
//!
|
||||||
|
//! \param[in] language_directory_offset The offset, in the module’s resources
|
||||||
|
//! area, of the resource directory to search.
|
||||||
|
//! \param[in] language The language of the resource to search for.
|
||||||
|
//!
|
||||||
|
//! \return The return value is as in GetEntryFromResourceDirectoryByID().
|
||||||
|
uint32_t GetEntryFromResourceDirectoryByLanguage(
|
||||||
|
uint32_t language_directory_offset,
|
||||||
|
uint16_t language) const;
|
||||||
|
|
||||||
|
//! \brief Reads a resource directory.
|
||||||
|
//!
|
||||||
|
//! \param[in] resource_directory_offset The offset, in the module’s resources
|
||||||
|
//! area, of the resource directory to read.
|
||||||
|
//! \param[out] resource_directory The `IMAGE_RESOURCE_DIRECTORY` structure.
|
||||||
|
//! This parameter is optional.
|
||||||
|
//! \param[out] named_entries A vector of \a
|
||||||
|
//! resource_directory->NumberOfNamedEntries
|
||||||
|
//! `IMAGE_RESOURCE_DIRECTORY_ENTRY` items that follow the resource
|
||||||
|
//! directory. This parameter is optional.
|
||||||
|
//! \param[out] id_entries A vector of \a
|
||||||
|
//! resource_directory->NumberOfIdEntries `IMAGE_RESOURCE_DIRECTORY_ENTRY`
|
||||||
|
//! items that follow the named entries. This parameter is optional.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, with the out parameters set appropriately.
|
||||||
|
//! `false` on failure with a message logged.
|
||||||
|
bool ReadResourceDirectory(
|
||||||
|
uint32_t resource_directory_offset,
|
||||||
|
IMAGE_RESOURCE_DIRECTORY* resource_directory,
|
||||||
|
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* named_entries,
|
||||||
|
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* id_entries) const;
|
||||||
|
|
||||||
|
ProcessSubrangeReader resources_subrange_reader_;
|
||||||
|
WinVMAddress module_base_;
|
||||||
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(PEImageResourceReader);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_
|
104
snapshot/win/process_subrange_reader.cc
Normal file
104
snapshot/win/process_subrange_reader.cc
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "snapshot/win/process_subrange_reader.h"
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "snapshot/win/process_reader_win.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
ProcessSubrangeReader::ProcessSubrangeReader()
|
||||||
|
: name_(),
|
||||||
|
range_(),
|
||||||
|
process_reader_(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessSubrangeReader::~ProcessSubrangeReader() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessSubrangeReader::Initialize(ProcessReaderWin* process_reader,
|
||||||
|
WinVMAddress base,
|
||||||
|
WinVMSize size,
|
||||||
|
const std::string& name) {
|
||||||
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
|
if (!InitializeInternal(process_reader, base, size, name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessSubrangeReader::InitializeSubrange(
|
||||||
|
const ProcessSubrangeReader& that,
|
||||||
|
WinVMAddress base,
|
||||||
|
WinVMSize size,
|
||||||
|
const std::string& sub_name) {
|
||||||
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(that.initialized_);
|
||||||
|
|
||||||
|
if (!InitializeInternal(
|
||||||
|
that.process_reader_, base, size, that.name_ + " " + sub_name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!that.range_.ContainsRange(range_)) {
|
||||||
|
LOG(WARNING) << "range " << range_.AsString() << " outside of range "
|
||||||
|
<< that.range_.AsString() << " for " << name_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessSubrangeReader::ReadMemory(WinVMAddress address,
|
||||||
|
WinVMSize size,
|
||||||
|
void* into) const {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size);
|
||||||
|
if (!read_range.IsValid()) {
|
||||||
|
LOG(WARNING) << "invalid read range " << read_range.AsString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!range_.ContainsRange(read_range)) {
|
||||||
|
LOG(WARNING) << "attempt to read outside of " << name_ << " range "
|
||||||
|
<< range_.AsString() << " at range " << read_range.AsString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return process_reader_->ReadMemory(address, size, into);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessSubrangeReader::InitializeInternal(ProcessReaderWin* process_reader,
|
||||||
|
WinVMAddress base,
|
||||||
|
WinVMSize size,
|
||||||
|
const std::string& name) {
|
||||||
|
range_.SetRange(process_reader->Is64Bit(), base, size);
|
||||||
|
if (!range_.IsValid()) {
|
||||||
|
LOG(WARNING) << "invalid range " << range_.AsString() << " for " << name;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
name_ = name;
|
||||||
|
process_reader_ = process_reader;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
114
snapshot/win/process_subrange_reader.h
Normal file
114
snapshot/win/process_subrange_reader.h
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// 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_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_
|
||||||
|
#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/basictypes.h"
|
||||||
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
|
#include "util/win/address_types.h"
|
||||||
|
#include "util/win/checked_win_address_range.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
class ProcessReaderWin;
|
||||||
|
|
||||||
|
//! \brief A wrapper for ProcessReaderWin that only allows a specific subrange
|
||||||
|
//! to be read from.
|
||||||
|
//!
|
||||||
|
//! This class is useful to restrict reads to a specific address range, such as
|
||||||
|
//! the address range occupied by a loaded module, or a specific section within
|
||||||
|
//! a module.
|
||||||
|
class ProcessSubrangeReader {
|
||||||
|
public:
|
||||||
|
ProcessSubrangeReader();
|
||||||
|
~ProcessSubrangeReader();
|
||||||
|
|
||||||
|
//! \brief Initializes the object.
|
||||||
|
//!
|
||||||
|
//! \param[in] process_reader A reader for a remote process.
|
||||||
|
//! \param[in] base The base address for the range that reads should be
|
||||||
|
//! restricted to.
|
||||||
|
//! \param[in] size The size of the range that reads should be restricted to.
|
||||||
|
//! \param[in] name The range’s name, a string to be used in logged messages.
|
||||||
|
//! This string is for diagnostic purposes.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, `false` on failure with a message logged. The
|
||||||
|
//! other methods in this class must not be called unless this method or
|
||||||
|
//! InitializeSubrange() has returned true.
|
||||||
|
bool Initialize(ProcessReaderWin* process_reader,
|
||||||
|
WinVMAddress base,
|
||||||
|
WinVMSize size,
|
||||||
|
const std::string& name);
|
||||||
|
|
||||||
|
//! \brief Initializes the object to a subrange of an existing
|
||||||
|
//! ProcessSubrangeReader.
|
||||||
|
//!
|
||||||
|
//! The subrange identified by \a base and \a size must be contained within
|
||||||
|
//! the subrange in \a that.
|
||||||
|
//!
|
||||||
|
//! \param[in] that The existing ProcessSubrangeReader to base the new object
|
||||||
|
//! on.
|
||||||
|
//! \param[in] base The base address for the range that reads should be
|
||||||
|
//! restricted to.
|
||||||
|
//! \param[in] size The size of the range that reads should be restricted to.
|
||||||
|
//! \param[in] sub_name A description of the subrange, which will be appended
|
||||||
|
//! to the \a name in \a that and used in logged messages. This string is
|
||||||
|
//! for diagnostic purposes.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, `false` on failure with a message logged. The
|
||||||
|
//! other methods in this class must not be called unless this method or
|
||||||
|
//! Initialize() has returned true.
|
||||||
|
bool InitializeSubrange(const ProcessSubrangeReader& that,
|
||||||
|
WinVMAddress base,
|
||||||
|
WinVMSize size,
|
||||||
|
const std::string& sub_name);
|
||||||
|
|
||||||
|
bool Is64Bit() const { return range_.Is64Bit(); }
|
||||||
|
WinVMAddress Base() const { return range_.Base(); }
|
||||||
|
WinVMAddress Size() const { return range_.Size(); }
|
||||||
|
const std::string& name() const { return name_; }
|
||||||
|
|
||||||
|
//! \brief Reads memory from the remote process.
|
||||||
|
//!
|
||||||
|
//! The range specified by \a address and \a size must be contained within
|
||||||
|
//! the range that this object is permitted to read.
|
||||||
|
//!
|
||||||
|
//! \param[in] address The address to read from.
|
||||||
|
//! \param[in] size The size of data to read, in bytes.
|
||||||
|
//! \param[out] into The buffer to read data into.
|
||||||
|
//!
|
||||||
|
//! \return `true` on success, `false` on failure with a message logged.
|
||||||
|
bool ReadMemory(WinVMAddress address, WinVMSize size, void* into) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Common helper for Initialize() and InitializeSubrange().
|
||||||
|
bool InitializeInternal(ProcessReaderWin* process_reader,
|
||||||
|
WinVMAddress base,
|
||||||
|
WinVMSize size,
|
||||||
|
const std::string& name);
|
||||||
|
|
||||||
|
std::string name_;
|
||||||
|
CheckedWinAddressRange range_;
|
||||||
|
ProcessReaderWin* process_reader_; // weak
|
||||||
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ProcessSubrangeReader);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_
|
@ -86,35 +86,37 @@ void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) {
|
|||||||
|
|
||||||
process_reader_ = process_reader;
|
process_reader_ = process_reader;
|
||||||
|
|
||||||
// We use both GetVersionEx and VerQueryValue. GetVersionEx is not trustworthy
|
// We use both GetVersionEx() and GetModuleVersionAndType() (which uses
|
||||||
// after Windows 8 (depending on the application manifest) so its data is used
|
// VerQueryValue() internally). GetVersionEx() is not trustworthy after
|
||||||
// only to fill the os_server_ field, and the rest comes from the version
|
// Windows 8 (depending on the application manifest) so its data is used only
|
||||||
|
// to fill the os_server_ field, and the rest comes from the version
|
||||||
// information stamped on kernel32.dll.
|
// information stamped on kernel32.dll.
|
||||||
OSVERSIONINFOEX version_info = {sizeof(version_info)};
|
OSVERSIONINFOEX version_info = {sizeof(version_info)};
|
||||||
if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info))) {
|
if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info))) {
|
||||||
PLOG(WARNING) << "GetVersionEx";
|
PLOG(WARNING) << "GetVersionEx";
|
||||||
} else {
|
} else {
|
||||||
const wchar_t kSystemDll[] = L"kernel32.dll";
|
os_server_ = version_info.wProductType != VER_NT_WORKSTATION;
|
||||||
VS_FIXEDFILEINFO ffi;
|
}
|
||||||
if (GetModuleVersionAndType(base::FilePath(kSystemDll), &ffi)) {
|
|
||||||
std::string flags_string = GetStringForFileFlags(ffi.dwFileFlags);
|
const wchar_t kSystemDll[] = L"kernel32.dll";
|
||||||
os_server_ = version_info.wProductType != VER_NT_WORKSTATION;
|
VS_FIXEDFILEINFO ffi;
|
||||||
std::string os_name = GetStringForFileOS(ffi.dwFileOS);
|
if (GetModuleVersionAndType(base::FilePath(kSystemDll), &ffi)) {
|
||||||
os_version_major_ = ffi.dwFileVersionMS >> 16;
|
std::string flags_string = GetStringForFileFlags(ffi.dwFileFlags);
|
||||||
os_version_minor_ = ffi.dwFileVersionMS & 0xffff;
|
std::string os_name = GetStringForFileOS(ffi.dwFileOS);
|
||||||
os_version_bugfix_ = ffi.dwFileVersionLS >> 16;
|
os_version_major_ = ffi.dwFileVersionMS >> 16;
|
||||||
os_version_build_ =
|
os_version_minor_ = ffi.dwFileVersionMS & 0xffff;
|
||||||
base::StringPrintf("%d", ffi.dwFileVersionLS & 0xffff);
|
os_version_bugfix_ = ffi.dwFileVersionLS >> 16;
|
||||||
os_version_full_ = base::StringPrintf(
|
os_version_build_ =
|
||||||
"%s %d.%d.%d.%s%s",
|
base::StringPrintf("%d", ffi.dwFileVersionLS & 0xffff);
|
||||||
os_name.c_str(),
|
os_version_full_ = base::StringPrintf(
|
||||||
os_version_major_,
|
"%s %d.%d.%d.%s%s",
|
||||||
os_version_minor_,
|
os_name.c_str(),
|
||||||
os_version_bugfix_,
|
os_version_major_,
|
||||||
os_version_build_.c_str(),
|
os_version_minor_,
|
||||||
flags_string.empty() ? "" : (std::string(" (") + flags_string + ")")
|
os_version_bugfix_,
|
||||||
.c_str());
|
os_version_build_.c_str(),
|
||||||
}
|
flags_string.empty() ? "" : (std::string(" (") + flags_string + ")")
|
||||||
|
.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "util/numeric/checked_address_range.h"
|
#include "util/numeric/checked_address_range.h"
|
||||||
|
|
||||||
|
#include "base/strings/stringprintf.h"
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
@ -109,6 +111,12 @@ bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsRange(
|
|||||||
: range_32_.ContainsRange(that.range_32_);
|
: range_32_.ContainsRange(that.range_32_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class ValueType, class SizeType>
|
||||||
|
std::string CheckedAddressRangeGeneric<ValueType, SizeType>::AsString() const {
|
||||||
|
return base::StringPrintf(
|
||||||
|
"0x%llx + 0x%llx (%s)", Base(), Size(), Is64Bit() ? "64" : "32");
|
||||||
|
}
|
||||||
|
|
||||||
// Explicit instantiations for the cases we use.
|
// Explicit instantiations for the cases we use.
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
template class CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;
|
template class CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "util/numeric/checked_range.h"
|
#include "util/numeric/checked_range.h"
|
||||||
|
|
||||||
@ -108,6 +110,12 @@ class CheckedAddressRangeGeneric {
|
|||||||
//! CheckedAddressRangeGeneric objects involved.
|
//! CheckedAddressRangeGeneric objects involved.
|
||||||
bool ContainsRange(const CheckedAddressRangeGeneric& that) const;
|
bool ContainsRange(const CheckedAddressRangeGeneric& that) const;
|
||||||
|
|
||||||
|
//! \brief Returns a string describing the address range.
|
||||||
|
//!
|
||||||
|
//! The string will be formatted as `"0x123 + 0x45 (64)"`, where the
|
||||||
|
//! individual components are the address, size, and bitness.
|
||||||
|
std::string AsString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if defined(COMPILER_MSVC)
|
#if defined(COMPILER_MSVC)
|
||||||
// MSVC cannot handle a union containing CheckedRange (with constructor, etc.)
|
// MSVC cannot handle a union containing CheckedRange (with constructor, etc.)
|
||||||
|
@ -24,9 +24,15 @@ namespace crashpad {
|
|||||||
//! \brief Retrieve the type and version information from a given module (exe,
|
//! \brief Retrieve the type and version information from a given module (exe,
|
||||||
//! dll, etc.)
|
//! dll, etc.)
|
||||||
//!
|
//!
|
||||||
|
//! This function calls `GetFileVersionInfo()`, which can implicitly call
|
||||||
|
//! `LoadLibrary()` to load \a path into the calling process. Do not call this
|
||||||
|
//! function on an untrusted module, because there is a risk of executing the
|
||||||
|
//! module’s code.
|
||||||
|
//!
|
||||||
//! \param[in] path The path to the module to be inspected.
|
//! \param[in] path The path to the module to be inspected.
|
||||||
//! \param[out] vs_fixedfileinfo The `VS_FIXEDFILEINFO` on success.
|
//! \param[out] vs_fixedfileinfo The VS_FIXEDFILEINFO on success.
|
||||||
//! `dwFileFlags` will have been masked with `dwFileFlagsMask` already.
|
//! VS_FIXEDFILEINFO::dwFileFlags will have been masked with
|
||||||
|
//! VS_FIXEDFILEINFO::dwFileFlagsMask already.
|
||||||
//!
|
//!
|
||||||
//! \return `true` on success, or `false` on failure with a message logged. If
|
//! \return `true` on success, or `false` on failure with a message logged. If
|
||||||
//! the module has no `VERSIONINFO` resource, `false` will be returned
|
//! the module has no `VERSIONINFO` resource, `false` will be returned
|
||||||
|
Loading…
x
Reference in New Issue
Block a user