// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include "base/files/file_path.h" #include "base/files/memory_mapped_file.h" #include "base/memory/raw_ptr.h" #include "base/path_service.h" #include "base/win/pe_image_reader.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using ::testing::Gt; using ::testing::NotNull; using ::testing::Return; using ::testing::StrictMock; namespace base { namespace win { struct TestData { const char* filename; PeImageReader::WordSize word_size; WORD machine_identifier; WORD optional_header_size; size_t number_of_sections; size_t number_of_debug_entries; }; // A test fixture parameterized on test data containing the name of a PE image // to parse and the expected values to be read from it. The file is read from // the src/base/test/data/pe_image_reader directory. class PeImageReaderTest : public testing::TestWithParam { protected: PeImageReaderTest() : expected_data_(GetParam()) {} void SetUp() override { ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_file_path_)); data_file_path_ = data_file_path_.AppendASCII("pe_image_reader"); data_file_path_ = data_file_path_.AppendASCII(expected_data_->filename); ASSERT_TRUE(data_file_.Initialize(data_file_path_)); ASSERT_TRUE( image_reader_.Initialize(data_file_.data(), data_file_.length())); } raw_ptr expected_data_; FilePath data_file_path_; MemoryMappedFile data_file_; PeImageReader image_reader_; }; TEST_P(PeImageReaderTest, GetWordSize) { EXPECT_EQ(expected_data_->word_size, image_reader_.GetWordSize()); } TEST_P(PeImageReaderTest, GetDosHeader) { const IMAGE_DOS_HEADER* dos_header = image_reader_.GetDosHeader(); ASSERT_NE(nullptr, dos_header); EXPECT_EQ(IMAGE_DOS_SIGNATURE, dos_header->e_magic); } TEST_P(PeImageReaderTest, GetCoffFileHeader) { const IMAGE_FILE_HEADER* file_header = image_reader_.GetCoffFileHeader(); ASSERT_NE(nullptr, file_header); EXPECT_EQ(expected_data_->machine_identifier, file_header->Machine); EXPECT_EQ(expected_data_->optional_header_size, file_header->SizeOfOptionalHeader); } TEST_P(PeImageReaderTest, GetOptionalHeaderData) { size_t optional_header_size = 0; const uint8_t* optional_header_data = image_reader_.GetOptionalHeaderData(&optional_header_size); ASSERT_NE(nullptr, optional_header_data); EXPECT_EQ(expected_data_->optional_header_size, optional_header_size); } TEST_P(PeImageReaderTest, GetNumberOfSections) { EXPECT_EQ(expected_data_->number_of_sections, image_reader_.GetNumberOfSections()); } TEST_P(PeImageReaderTest, GetSectionHeaderAt) { size_t number_of_sections = image_reader_.GetNumberOfSections(); for (size_t i = 0; i < number_of_sections; ++i) { const IMAGE_SECTION_HEADER* section_header = image_reader_.GetSectionHeaderAt(i); ASSERT_NE(nullptr, section_header); } } TEST_P(PeImageReaderTest, InitializeFailTruncatedFile) { // Compute the size of all headers through the section headers. const IMAGE_SECTION_HEADER* last_section_header = image_reader_.GetSectionHeaderAt(image_reader_.GetNumberOfSections() - 1); const uint8_t* headers_end = reinterpret_cast(last_section_header) + sizeof(*last_section_header); size_t header_size = headers_end - data_file_.data(); PeImageReader short_reader; // Initialize should succeed when all headers are present. EXPECT_TRUE(short_reader.Initialize(data_file_.data(), header_size)); // But fail if anything is missing. for (size_t i = 0; i < header_size; ++i) { EXPECT_FALSE(short_reader.Initialize(data_file_.data(), i)); } } TEST_P(PeImageReaderTest, GetExportSection) { size_t section_size = 0; const uint8_t* export_section = image_reader_.GetExportSection(§ion_size); ASSERT_NE(nullptr, export_section); EXPECT_NE(0U, section_size); } TEST_P(PeImageReaderTest, GetNumberOfDebugEntries) { EXPECT_EQ(expected_data_->number_of_debug_entries, image_reader_.GetNumberOfDebugEntries()); } TEST_P(PeImageReaderTest, GetDebugEntry) { size_t number_of_debug_entries = image_reader_.GetNumberOfDebugEntries(); for (size_t i = 0; i < number_of_debug_entries; ++i) { const uint8_t* raw_data = nullptr; size_t raw_data_size = 0; const IMAGE_DEBUG_DIRECTORY* entry = image_reader_.GetDebugEntry(i, &raw_data, &raw_data_size); EXPECT_NE(nullptr, entry); EXPECT_NE(nullptr, raw_data); EXPECT_NE(0U, raw_data_size); } } namespace { const TestData kTestData[] = { { "module_with_exports_x86.dll", PeImageReader::WORD_SIZE_32, IMAGE_FILE_MACHINE_I386, sizeof(IMAGE_OPTIONAL_HEADER32), 4, 1, }, { "module_with_exports_x64.dll", PeImageReader::WORD_SIZE_64, IMAGE_FILE_MACHINE_AMD64, sizeof(IMAGE_OPTIONAL_HEADER64), 5, 1, }, }; } // namespace INSTANTIATE_TEST_SUITE_P(WordSize32, PeImageReaderTest, testing::Values(&kTestData[0])); INSTANTIATE_TEST_SUITE_P(WordSize64, PeImageReaderTest, testing::Values(&kTestData[1])); // An object exposing a PeImageReader::EnumCertificatesCallback that invokes a // virtual OnCertificate() method. This method is suitable for mocking in tests. class CertificateReceiver { public: void* AsContext() { return this; } static bool OnCertificateCallback(uint16_t revision, uint16_t certificate_type, const uint8_t* certificate_data, size_t certificate_data_size, void* context) { return reinterpret_cast(context)->OnCertificate( revision, certificate_type, certificate_data, certificate_data_size); } protected: CertificateReceiver() = default; virtual ~CertificateReceiver() = default; virtual bool OnCertificate(uint16_t revision, uint16_t certificate_type, const uint8_t* certificate_data, size_t certificate_data_size) = 0; }; class MockCertificateReceiver : public CertificateReceiver { public: MockCertificateReceiver() = default; MockCertificateReceiver(const MockCertificateReceiver&) = delete; MockCertificateReceiver& operator=(const MockCertificateReceiver&) = delete; MOCK_METHOD4(OnCertificate, bool(uint16_t, uint16_t, const uint8_t*, size_t)); }; struct CertificateTestData { const char* filename; int num_signers; }; // A test fixture parameterized on test data containing the name of a PE image // to parse and the expected values to be read from it. The file is read from // the src/chrome/test/data/safe_browsing/download_protection directory. class PeImageReaderCertificateTest : public testing::TestWithParam { protected: PeImageReaderCertificateTest() : expected_data_(GetParam()) {} void SetUp() override { ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_file_path_)); data_file_path_ = data_file_path_.AppendASCII("pe_image_reader"); data_file_path_ = data_file_path_.AppendASCII(expected_data_->filename); ASSERT_TRUE(data_file_.Initialize(data_file_path_)); ASSERT_TRUE( image_reader_.Initialize(data_file_.data(), data_file_.length())); } raw_ptr expected_data_; FilePath data_file_path_; MemoryMappedFile data_file_; PeImageReader image_reader_; }; TEST_P(PeImageReaderCertificateTest, EnumCertificates) { StrictMock receiver; if (expected_data_->num_signers) { EXPECT_CALL(receiver, OnCertificate(WIN_CERT_REVISION_2_0, WIN_CERT_TYPE_PKCS_SIGNED_DATA, NotNull(), Gt(0U))) .Times(expected_data_->num_signers) .WillRepeatedly(Return(true)); } EXPECT_TRUE(image_reader_.EnumCertificates( &CertificateReceiver::OnCertificateCallback, receiver.AsContext())); } TEST_P(PeImageReaderCertificateTest, AbortEnum) { StrictMock receiver; if (expected_data_->num_signers) { // Return false for the first cert, thereby stopping the enumeration. EXPECT_CALL(receiver, OnCertificate(_, _, _, _)).WillOnce(Return(false)); EXPECT_FALSE(image_reader_.EnumCertificates( &CertificateReceiver::OnCertificateCallback, receiver.AsContext())); } else { // An unsigned file always reports true with no invocations of the callback. EXPECT_TRUE(image_reader_.EnumCertificates( &CertificateReceiver::OnCertificateCallback, receiver.AsContext())); } } namespace { const CertificateTestData kCertificateTestData[] = { { "signed.exe", 1, }, { "unsigned.exe", 0, }, { "disable_outdated_build_detector.exe", 1, }, { "signed_twice.exe", 2, }, }; } // namespace INSTANTIATE_TEST_SUITE_P(SignedExe, PeImageReaderCertificateTest, testing::Values(&kCertificateTestData[0])); INSTANTIATE_TEST_SUITE_P(UnsignedExe, PeImageReaderCertificateTest, testing::Values(&kCertificateTestData[1])); INSTANTIATE_TEST_SUITE_P(DisableOutdatedBuildDetectorExe, PeImageReaderCertificateTest, testing::Values(&kCertificateTestData[2])); INSTANTIATE_TEST_SUITE_P(SignedTwiceExe, PeImageReaderCertificateTest, testing::Values(&kCertificateTestData[3])); } // namespace win } // namespace base