| // 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_reader.h" |
| |
| #ifndef PSAPI_VERSION |
| #define PSAPI_VERSION 2 |
| #endif |
| #include <psapi.h> |
| |
| #include "base/files/file_path.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "gtest/gtest.h" |
| #include "snapshot/win/process_reader_win.h" |
| #include "test/errors.h" |
| #include "test/scoped_module_handle.h" |
| #include "test/test_paths.h" |
| #include "util/misc/from_pointer_cast.h" |
| #include "util/win/get_module_information.h" |
| #include "util/win/module_version.h" |
| #include "util/win/process_info.h" |
| |
| namespace crashpad { |
| namespace test { |
| namespace { |
| |
| TEST(PEImageReader, DebugDirectory) { |
| base::FilePath module_path = |
| TestPaths::BuildArtifact(L"snapshot", |
| L"image_reader_module", |
| TestPaths::FileType::kLoadableModule); |
| ScopedModuleHandle module_handle(LoadLibrary(module_path.value().c_str())); |
| ASSERT_TRUE(module_handle.valid()) << ErrorMessage("LoadLibrary"); |
| |
| PEImageReader pe_image_reader; |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), |
| ProcessSuspensionState::kRunning)); |
| |
| MODULEINFO module_info; |
| ASSERT_TRUE(CrashpadGetModuleInformation(GetCurrentProcess(), |
| module_handle.get(), |
| &module_info, |
| sizeof(module_info))) |
| << ErrorMessage("GetModuleInformation"); |
| EXPECT_EQ(module_info.lpBaseOfDll, module_handle.get()); |
| |
| base::FilePath module_basename = module_path.BaseName(); |
| ASSERT_TRUE(pe_image_reader.Initialize( |
| &process_reader, |
| FromPointerCast<WinVMAddress>(module_handle.get()), |
| module_info.SizeOfImage, |
| base::UTF16ToUTF8(module_basename.value()))); |
| |
| UUID uuid; |
| DWORD age; |
| std::string pdbname; |
| ASSERT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname)); |
| std::string module_name = |
| base::UTF16ToUTF8(module_basename.RemoveFinalExtension().value()); |
| EXPECT_NE(pdbname.find(module_name), std::string::npos); |
| const std::string suffix(".pdb"); |
| EXPECT_EQ( |
| pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix), |
| 0); |
| } |
| |
| void TestVSFixedFileInfo(ProcessReaderWin* process_reader, |
| const ProcessInfo::Module& module, |
| bool known_dll) { |
| PEImageReader pe_image_reader; |
| ASSERT_TRUE(pe_image_reader.Initialize(process_reader, |
| module.dll_base, |
| module.size, |
| base::UTF16ToUTF8(module.name))); |
| |
| VS_FIXEDFILEINFO observed; |
| const bool observed_rv = pe_image_reader.VSFixedFileInfo(&observed); |
| ASSERT_TRUE(observed_rv || !known_dll); |
| |
| if (observed_rv) { |
| EXPECT_EQ(observed.dwSignature, static_cast<DWORD>(VS_FFI_SIGNATURE)); |
| EXPECT_EQ(observed.dwStrucVersion, static_cast<DWORD>(VS_FFI_STRUCVERSION)); |
| EXPECT_EQ(observed.dwFileFlags & ~observed.dwFileFlagsMask, 0u); |
| if (known_dll) { |
| EXPECT_EQ(observed.dwFileOS, static_cast<DWORD>(VOS_NT_WINDOWS32)); |
| EXPECT_EQ(observed.dwFileType, static_cast<DWORD>(VFT_DLL)); |
| } else { |
| EXPECT_NE(observed.dwFileOS & VOS_NT_WINDOWS32, 0u); |
| |
| // VFT_DRV/VFT2_DRV_NETWORK is for nsi.dll, “network service interface.” |
| // It’s not normally loaded, but has been observed to be loaded in some |
| // cases. |
| EXPECT_TRUE(observed.dwFileType == VFT_APP || |
| observed.dwFileType == VFT_DLL || |
| (observed.dwFileType == VFT_DRV && |
| observed.dwFileSubtype == VFT2_DRV_NETWORK)) |
| << base::StringPrintf("type 0x%lx, subtype 0x%lx", |
| observed.dwFileType, |
| observed.dwFileSubtype); |
| } |
| } |
| |
| base::FilePath module_path(module.name); |
| |
| const DWORD version = GetVersion(); |
| const int major_version = LOBYTE(LOWORD(version)); |
| const int minor_version = HIBYTE(LOWORD(version)); |
| if (major_version > 6 || (major_version == 6 && minor_version >= 2)) { |
| // Windows 8 or later. |
| // |
| // Use BaseName() to ensure that GetModuleVersionAndType() finds the |
| // already-loaded module with the specified name. Otherwise, dwFileVersionMS |
| // may not match. This appears to be related to the changes made in Windows |
| // 8.1 to GetVersion() and GetVersionEx() for non-manifested applications |
| module_path = module_path.BaseName(); |
| } |
| |
| VS_FIXEDFILEINFO expected; |
| const bool expected_rv = GetModuleVersionAndType(module_path, &expected); |
| ASSERT_TRUE(expected_rv || !known_dll); |
| |
| EXPECT_EQ(observed_rv, expected_rv); |
| |
| if (observed_rv && expected_rv) { |
| EXPECT_EQ(observed.dwSignature, expected.dwSignature); |
| EXPECT_EQ(observed.dwStrucVersion, expected.dwStrucVersion); |
| EXPECT_EQ(observed.dwFileVersionMS, expected.dwFileVersionMS); |
| EXPECT_EQ(observed.dwFileVersionLS, expected.dwFileVersionLS); |
| EXPECT_EQ(observed.dwProductVersionMS, expected.dwProductVersionMS); |
| EXPECT_EQ(observed.dwProductVersionLS, expected.dwProductVersionLS); |
| EXPECT_EQ(observed.dwFileFlagsMask, expected.dwFileFlagsMask); |
| EXPECT_EQ(observed.dwFileFlags, expected.dwFileFlags); |
| EXPECT_EQ(observed.dwFileOS, expected.dwFileOS); |
| EXPECT_EQ(observed.dwFileType, expected.dwFileType); |
| EXPECT_EQ(observed.dwFileSubtype, expected.dwFileSubtype); |
| EXPECT_EQ(observed.dwFileDateMS, expected.dwFileDateMS); |
| EXPECT_EQ(observed.dwFileDateLS, expected.dwFileDateLS); |
| } |
| } |
| |
| TEST(PEImageReader, VSFixedFileInfo_OneModule) { |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), |
| ProcessSuspensionState::kRunning)); |
| |
| static constexpr wchar_t kModuleName[] = L"kernel32.dll"; |
| const 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_info.lpBaseOfDll, module_handle); |
| |
| ProcessInfo::Module module; |
| module.name = kModuleName; |
| module.dll_base = FromPointerCast<WinVMAddress>(module_info.lpBaseOfDll); |
| module.size = module_info.SizeOfImage; |
| |
| TestVSFixedFileInfo(&process_reader, module, true); |
| } |
| |
| TEST(PEImageReader, VSFixedFileInfo_AllModules) { |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), |
| ProcessSuspensionState::kRunning)); |
| |
| const std::vector<ProcessInfo::Module>& modules = process_reader.Modules(); |
| EXPECT_GT(modules.size(), 2u); |
| |
| for (const auto& module : modules) { |
| SCOPED_TRACE(base::UTF16ToUTF8(module.name)); |
| TestVSFixedFileInfo(&process_reader, module, false); |
| } |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |