diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-25 23:26:42 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-25 23:26:42 +0000 |
commit | 03e3925b8017a2bc5cbef29e109ef2ba4e634329 (patch) | |
tree | ad745d79bddbf069a9307e0fb368cdd970108110 | |
parent | ef613d38eb0e0070393a245feec64c802e41fbbe (diff) | |
download | chromium_src-03e3925b8017a2bc5cbef29e109ef2ba4e634329.zip chromium_src-03e3925b8017a2bc5cbef29e109ef2ba4e634329.tar.gz chromium_src-03e3925b8017a2bc5cbef29e109ef2ba4e634329.tar.bz2 |
Include PE image headers in client download pings.
BUG=none
TBR=sky@chromium.org
Review URL: https://codereview.chromium.org/205523002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@259392 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/safe_browsing/binary_feature_extractor.h | 6 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/binary_feature_extractor_posix.cc | 4 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/binary_feature_extractor_win.cc | 56 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/binary_feature_extractor_win_unittest.cc | 75 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/download_protection_service.cc | 7 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/download_protection_service_unittest.cc | 39 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/pe_image_reader_win.cc | 327 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/pe_image_reader_win.h | 133 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/pe_image_reader_win_unittest.cc | 157 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 1 | ||||
-rw-r--r-- | chrome/common/safe_browsing/csd.proto | 33 | ||||
-rw-r--r-- | chrome/test/data/safe_browsing/module_with_exports.cc | 41 | ||||
-rw-r--r-- | tools/metrics/histograms/histograms.xml | 8 |
15 files changed, 869 insertions, 22 deletions
diff --git a/chrome/browser/safe_browsing/binary_feature_extractor.h b/chrome/browser/safe_browsing/binary_feature_extractor.h index bbf9b0d..a06a242 100644 --- a/chrome/browser/safe_browsing/binary_feature_extractor.h +++ b/chrome/browser/safe_browsing/binary_feature_extractor.h @@ -16,6 +16,7 @@ class FilePath; } namespace safe_browsing { +class ClientDownloadRequest_ImageHeaders; class ClientDownloadRequest_SignatureInfo; class BinaryFeatureExtractor @@ -29,6 +30,11 @@ class BinaryFeatureExtractor const base::FilePath& file_path, ClientDownloadRequest_SignatureInfo* signature_info); + // Populates |image_headers| with the PE image headers of |file_path|. + virtual void ExtractImageHeaders( + const base::FilePath& file_path, + ClientDownloadRequest_ImageHeaders* image_headers); + protected: friend class base::RefCountedThreadSafe<BinaryFeatureExtractor>; virtual ~BinaryFeatureExtractor(); diff --git a/chrome/browser/safe_browsing/binary_feature_extractor_posix.cc b/chrome/browser/safe_browsing/binary_feature_extractor_posix.cc index a1dafc5..a7da722 100644 --- a/chrome/browser/safe_browsing/binary_feature_extractor_posix.cc +++ b/chrome/browser/safe_browsing/binary_feature_extractor_posix.cc @@ -17,4 +17,8 @@ void BinaryFeatureExtractor::CheckSignature( const base::FilePath& file_path, ClientDownloadRequest_SignatureInfo* signature_info) {} +void BinaryFeatureExtractor::ExtractImageHeaders( + const base::FilePath& file_path, + ClientDownloadRequest_ImageHeaders* image_headers) {} + } // namespace safe_browsing diff --git a/chrome/browser/safe_browsing/binary_feature_extractor_win.cc b/chrome/browser/safe_browsing/binary_feature_extractor_win.cc index 243b497..74fcc44 100644 --- a/chrome/browser/safe_browsing/binary_feature_extractor_win.cc +++ b/chrome/browser/safe_browsing/binary_feature_extractor_win.cc @@ -9,7 +9,9 @@ #include <wintrust.h> #include "base/files/file_path.h" +#include "base/files/memory_mapped_file.h" #include "base/logging.h" +#include "chrome/browser/safe_browsing/pe_image_reader_win.h" #include "chrome/common/safe_browsing/csd.pb.h" #pragma comment(lib, "wintrust.lib") @@ -88,4 +90,58 @@ void BinaryFeatureExtractor::CheckSignature( } } +void BinaryFeatureExtractor::ExtractImageHeaders( + const base::FilePath& file_path, + ClientDownloadRequest_ImageHeaders* image_headers) { + // Map the file into memory. + base::MemoryMappedFile file; + if (!file.Initialize(file_path)) + return; + + PeImageReader pe_image; + if (!pe_image.Initialize(file.data(), file.length())) + return; + + // Copy the headers. + ClientDownloadRequest_PEImageHeaders* pe_headers = + image_headers->mutable_pe_headers(); + pe_headers->set_dos_header(pe_image.GetDosHeader(), sizeof(IMAGE_DOS_HEADER)); + pe_headers->set_file_header(pe_image.GetCoffFileHeader(), + sizeof(IMAGE_FILE_HEADER)); + size_t optional_header_size = 0; + const uint8_t* optional_header_data = + pe_image.GetOptionalHeaderData(&optional_header_size); + if (pe_image.GetWordSize() == PeImageReader::WORD_SIZE_32) { + pe_headers->set_optional_headers32(optional_header_data, + optional_header_size); + } else { + pe_headers->set_optional_headers64(optional_header_data, + optional_header_size); + } + const size_t number_of_sections = pe_image.GetNumberOfSections(); + for (size_t i = 0; i != number_of_sections; ++i) { + pe_headers->add_section_header(pe_image.GetSectionHeaderAt(i), + sizeof(IMAGE_SECTION_HEADER)); + } + size_t export_size = 0; + const uint8_t* export_section = pe_image.GetExportSection(&export_size); + if (export_section) + pe_headers->set_export_section_data(export_section, export_size); + size_t number_of_debug_entries = pe_image.GetNumberOfDebugEntries(); + for (size_t i = 0; i != number_of_debug_entries; ++i) { + const uint8_t* raw_data = NULL; + size_t raw_data_size = 0; + const IMAGE_DEBUG_DIRECTORY* directory_entry = + pe_image.GetDebugEntry(i, &raw_data, &raw_data_size); + if (directory_entry) { + ClientDownloadRequest_PEImageHeaders_DebugData* debug_data = + pe_headers->mutable_debug_data(i); + debug_data->set_directory_entry(directory_entry, + sizeof(*directory_entry)); + if (raw_data) + debug_data->set_raw_data(raw_data, raw_data_size); + } + } +} + } // namespace safe_browsing diff --git a/chrome/browser/safe_browsing/binary_feature_extractor_win_unittest.cc b/chrome/browser/safe_browsing/binary_feature_extractor_win_unittest.cc index 31a4b41..1d63bf2 100644 --- a/chrome/browser/safe_browsing/binary_feature_extractor_win_unittest.cc +++ b/chrome/browser/safe_browsing/binary_feature_extractor_win_unittest.cc @@ -11,6 +11,7 @@ #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/path_service.h" +#include "chrome/common/chrome_paths.h" #include "chrome/common/safe_browsing/csd.pb.h" #include "net/cert/x509_cert_types.h" #include "net/cert/x509_certificate.h" @@ -20,15 +21,14 @@ namespace safe_browsing { class BinaryFeatureExtractorWinTest : public testing::Test { protected: - virtual void SetUp() { + virtual void SetUp() OVERRIDE { base::FilePath source_path; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &source_path)); + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_path)); testdata_path_ = source_path - .AppendASCII("chrome") - .AppendASCII("test") - .AppendASCII("data") .AppendASCII("safe_browsing") .AppendASCII("download_protection"); + + binary_feature_extractor_ = new BinaryFeatureExtractor(); } // Given a certificate chain protobuf, parse it into X509Certificates. @@ -44,15 +44,15 @@ class BinaryFeatureExtractorWinTest : public testing::Test { } base::FilePath testdata_path_; + scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_; }; TEST_F(BinaryFeatureExtractorWinTest, UntrustedSignedBinary) { // signed.exe is signed by an untrusted root CA. - scoped_refptr<BinaryFeatureExtractor> signature_util( - new BinaryFeatureExtractor()); ClientDownloadRequest_SignatureInfo signature_info; - signature_util->CheckSignature(testdata_path_.Append(L"signed.exe"), - &signature_info); + binary_feature_extractor_->CheckSignature( + testdata_path_.Append(L"signed.exe"), + &signature_info); ASSERT_EQ(1, signature_info.certificate_chain_size()); std::vector<scoped_refptr<net::X509Certificate> > certs; ParseCertificateChain(signature_info.certificate_chain(0), &certs); @@ -66,11 +66,10 @@ TEST_F(BinaryFeatureExtractorWinTest, UntrustedSignedBinary) { TEST_F(BinaryFeatureExtractorWinTest, TrustedBinary) { // wow_helper.exe is signed using Google's signing certifiacte. - scoped_refptr<BinaryFeatureExtractor> signature_util( - new BinaryFeatureExtractor()); ClientDownloadRequest_SignatureInfo signature_info; - signature_util->CheckSignature(testdata_path_.Append(L"wow_helper.exe"), - &signature_info); + binary_feature_extractor_->CheckSignature( + testdata_path_.Append(L"wow_helper.exe"), + &signature_info); ASSERT_EQ(1, signature_info.certificate_chain_size()); std::vector<scoped_refptr<net::X509Certificate> > certs; ParseCertificateChain(signature_info.certificate_chain(0), &certs); @@ -87,24 +86,58 @@ TEST_F(BinaryFeatureExtractorWinTest, TrustedBinary) { TEST_F(BinaryFeatureExtractorWinTest, UnsignedBinary) { // unsigned.exe has no signature information. - scoped_refptr<BinaryFeatureExtractor> signature_util( - new BinaryFeatureExtractor()); ClientDownloadRequest_SignatureInfo signature_info; - signature_util->CheckSignature(testdata_path_.Append(L"unsigned.exe"), - &signature_info); + binary_feature_extractor_->CheckSignature( + testdata_path_.Append(L"unsigned.exe"), + &signature_info); EXPECT_EQ(0, signature_info.certificate_chain_size()); EXPECT_FALSE(signature_info.has_trusted()); } TEST_F(BinaryFeatureExtractorWinTest, NonExistentBinary) { // Test a file that doesn't exist. - scoped_refptr<BinaryFeatureExtractor> signature_util( - new BinaryFeatureExtractor()); ClientDownloadRequest_SignatureInfo signature_info; - signature_util->CheckSignature(testdata_path_.Append(L"doesnotexist.exe"), - &signature_info); + binary_feature_extractor_->CheckSignature( + testdata_path_.Append(L"doesnotexist.exe"), + &signature_info); EXPECT_EQ(0, signature_info.certificate_chain_size()); EXPECT_FALSE(signature_info.has_trusted()); } +TEST_F(BinaryFeatureExtractorWinTest, ExtractImageHeadersNoFile) { + // Test extracting headers from a file that doesn't exist. + ClientDownloadRequest_ImageHeaders image_headers; + binary_feature_extractor_->ExtractImageHeaders( + testdata_path_.AppendASCII("this_file_does_not_exist"), + &image_headers); + EXPECT_FALSE(image_headers.has_pe_headers()); +} + +TEST_F(BinaryFeatureExtractorWinTest, ExtractImageHeadersNonImage) { + // Test extracting headers from something that is not a PE image. + ClientDownloadRequest_ImageHeaders image_headers; + binary_feature_extractor_->ExtractImageHeaders( + testdata_path_.AppendASCII("simple_exe.cc"), + &image_headers); + EXPECT_FALSE(image_headers.has_pe_headers()); +} + +TEST_F(BinaryFeatureExtractorWinTest, ExtractImageHeaders) { + // Test extracting headers from something that is a PE image. + ClientDownloadRequest_ImageHeaders image_headers; + binary_feature_extractor_->ExtractImageHeaders( + testdata_path_.AppendASCII("unsigned.exe"), + &image_headers); + EXPECT_TRUE(image_headers.has_pe_headers()); + const ClientDownloadRequest_PEImageHeaders& pe_headers = + image_headers.pe_headers(); + EXPECT_TRUE(pe_headers.has_dos_header()); + EXPECT_TRUE(pe_headers.has_file_header()); + EXPECT_TRUE(pe_headers.has_optional_headers32()); + EXPECT_FALSE(pe_headers.has_optional_headers64()); + EXPECT_NE(0, pe_headers.section_header_size()); + EXPECT_FALSE(pe_headers.has_export_section_data()); + EXPECT_EQ(0, pe_headers.debug_data_size()); +} + } // namespace safe_browsing diff --git a/chrome/browser/safe_browsing/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection_service.cc index 3147598..abdcb0f 100644 --- a/chrome/browser/safe_browsing/download_protection_service.cc +++ b/chrome/browser/safe_browsing/download_protection_service.cc @@ -526,6 +526,11 @@ class DownloadProtectionService::CheckClientDownloadRequest UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime", base::TimeTicks::Now() - start_time); + start_time = base::TimeTicks::Now(); + binary_feature_extractor_->ExtractImageHeaders(file_path, &image_headers_); + UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime", + base::TimeTicks::Now() - start_time); + OnFileFeatureExtractionDone(); } @@ -707,6 +712,7 @@ class DownloadProtectionService::CheckClientDownloadRequest item_->GetTargetFilePath().BaseName().AsUTF8Unsafe()); request.set_download_type(type_); request.mutable_signature()->CopyFrom(signature_info_); + request.mutable_image_headers()->CopyFrom(image_headers_); if (!request.SerializeToString(&client_download_request_data_)) { FinishRequest(SAFE, REASON_INVALID_REQUEST_PROTO); return; @@ -821,6 +827,7 @@ class DownloadProtectionService::CheckClientDownloadRequest bool zipped_executable_; ClientDownloadRequest_SignatureInfo signature_info_; + ClientDownloadRequest_ImageHeaders image_headers_; CheckDownloadCallback callback_; // Will be NULL if the request has been canceled. DownloadProtectionService* service_; diff --git a/chrome/browser/safe_browsing/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection_service_unittest.cc index df63d32..5692a10 100644 --- a/chrome/browser/safe_browsing/download_protection_service_unittest.cc +++ b/chrome/browser/safe_browsing/download_protection_service_unittest.cc @@ -103,6 +103,8 @@ class MockBinaryFeatureExtractor : public BinaryFeatureExtractor { MockBinaryFeatureExtractor() {} MOCK_METHOD2(CheckSignature, void (const base::FilePath&, ClientDownloadRequest_SignatureInfo*)); + MOCK_METHOD2(ExtractImageHeaders, void (const base::FilePath&, + ClientDownloadRequest_ImageHeaders*)); protected: virtual ~MockBinaryFeatureExtractor() {} @@ -145,6 +147,10 @@ ACTION_P(SetCertificateContents, contents) { arg1->add_certificate_chain()->add_element()->set_certificate(contents); } +ACTION_P(SetDosHeaderContents, contents) { + arg1->mutable_pe_headers()->set_dos_header(contents); +} + ACTION_P(TrustSignature, certificate_file) { arg1->set_trusted(true); // Add a certificate chain. Note that we add the certificate twice so that @@ -426,6 +432,8 @@ TEST_F(DownloadProtectionServiceTest, CheckClientDownloadWhitelistedUrl) { EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(4); + EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageHeaders(a_tmp, _)) + .Times(4); // We should not get whilelist checks for other URLs than specified below. EXPECT_CALL(*sb_service_->mock_database_manager(), @@ -519,6 +527,7 @@ TEST_F(DownloadProtectionServiceTest, CheckClientDownloadFetchFailed) { MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)); + EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageHeaders(a_tmp, _)); download_service_->CheckClientDownload( &item, @@ -565,6 +574,8 @@ TEST_F(DownloadProtectionServiceTest, CheckClientDownloadSuccess) { .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(6); + EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageHeaders(a_tmp, _)) + .Times(6); download_service_->CheckClientDownload( &item, @@ -713,6 +724,8 @@ TEST_F(DownloadProtectionServiceTest, CheckClientDownloadHTTPS) { .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(1); + EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageHeaders(a_tmp, _)) + .Times(1); download_service_->CheckClientDownload( &item, @@ -899,6 +912,8 @@ TEST_F(DownloadProtectionServiceTest, CheckClientCrxDownloadSuccess) { .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(1); + EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageHeaders(a_tmp, _)) + .Times(1); EXPECT_FALSE(download_service_->IsSupportedDownload(item, a_crx)); download_service_->CheckClientDownload( @@ -941,6 +956,9 @@ TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequest) { .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)) .WillOnce(SetCertificateContents("dummy cert data")); + EXPECT_CALL(*binary_feature_extractor_.get(), + ExtractImageHeaders(tmp_path, _)) + .WillOnce(SetDosHeaderContents("dummy dos header")); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, @@ -977,6 +995,12 @@ TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequest) { request.signature().certificate_chain(0); ASSERT_EQ(1, chain.element_size()); EXPECT_EQ("dummy cert data", chain.element(0).certificate()); + EXPECT_TRUE(request.has_image_headers()); + const ClientDownloadRequest_ImageHeaders& headers = + request.image_headers(); + EXPECT_TRUE(headers.has_pe_headers()); + EXPECT_TRUE(headers.pe_headers().has_dos_header()); + EXPECT_EQ("dummy dos header", headers.pe_headers().dos_header()); // Simulate the request finishing. base::MessageLoop::current()->PostTask( @@ -1020,6 +1044,8 @@ TEST_F(DownloadProtectionServiceTest, MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)); + EXPECT_CALL(*binary_feature_extractor_.get(), + ExtractImageHeaders(tmp_path, _)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, @@ -1103,6 +1129,9 @@ TEST_F(DownloadProtectionServiceTest, .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)) .WillRepeatedly(SetCertificateContents("dummy cert data")); + EXPECT_CALL(*binary_feature_extractor_.get(), + ExtractImageHeaders(tmp_path, _)) + .WillRepeatedly(SetDosHeaderContents("dummy dos header")); // First test with no history match for the tab URL. { @@ -1148,6 +1177,12 @@ TEST_F(DownloadProtectionServiceTest, request.signature().certificate_chain(0); ASSERT_EQ(1, chain.element_size()); EXPECT_EQ("dummy cert data", chain.element(0).certificate()); + EXPECT_TRUE(request.has_image_headers()); + const ClientDownloadRequest_ImageHeaders& headers = + request.image_headers(); + EXPECT_TRUE(headers.has_pe_headers()); + EXPECT_TRUE(headers.pe_headers().has_dos_header()); + EXPECT_EQ("dummy dos header", headers.pe_headers().dos_header()); // Simulate the request finishing. base::MessageLoop::current()->PostTask( @@ -1331,6 +1366,8 @@ TEST_F(DownloadProtectionServiceTest, TestDownloadRequestTimeout) { MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)); + EXPECT_CALL(*binary_feature_extractor_.get(), + ExtractImageHeaders(tmp_path, _)); download_service_->download_request_timeout_ms_ = 10; download_service_->CheckClientDownload( @@ -1375,6 +1412,8 @@ TEST_F(DownloadProtectionServiceTest, TestDownloadItemDestroyed) { MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)); + EXPECT_CALL(*binary_feature_extractor_.get(), + ExtractImageHeaders(tmp_path, _)); download_service_->CheckClientDownload( &item, diff --git a/chrome/browser/safe_browsing/pe_image_reader_win.cc b/chrome/browser/safe_browsing/pe_image_reader_win.cc new file mode 100644 index 0000000..70734fdb --- /dev/null +++ b/chrome/browser/safe_browsing/pe_image_reader_win.cc @@ -0,0 +1,327 @@ +// 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 "chrome/browser/safe_browsing/pe_image_reader_win.h" + +#include "base/logging.h" + +namespace safe_browsing { + +// A class template of traits pertaining to IMAGE_OPTIONAL_HEADER{32,64}. +template<class HEADER_TYPE> +struct OptionalHeaderTraits { +}; + +template<> +struct OptionalHeaderTraits<IMAGE_OPTIONAL_HEADER32> { + static const PeImageReader::WordSize word_size = PeImageReader::WORD_SIZE_32; +}; + +template<> +struct OptionalHeaderTraits<IMAGE_OPTIONAL_HEADER64> { + static const PeImageReader::WordSize word_size = PeImageReader::WORD_SIZE_64; +}; + +// A template for type-specific optional header implementations. This, in +// conjunction with the OptionalHeader interface, effectively erases the +// underlying structure type from the point of view of the PeImageReader. +template<class OPTIONAL_HEADER_TYPE> +class PeImageReader::OptionalHeaderImpl : public PeImageReader::OptionalHeader { + public: + typedef OptionalHeaderTraits<OPTIONAL_HEADER_TYPE> TraitsType; + + explicit OptionalHeaderImpl(const uint8_t* optional_header_start) + : optional_header_(reinterpret_cast<const OPTIONAL_HEADER_TYPE*>( + optional_header_start)) {} + + virtual WordSize GetWordSize() OVERRIDE { + return TraitsType::word_size; + } + + virtual size_t GetDataDirectoryOffset() OVERRIDE { + return offsetof(OPTIONAL_HEADER_TYPE, DataDirectory); + } + + virtual DWORD GetDataDirectorySize() OVERRIDE { + return optional_header_->NumberOfRvaAndSizes; + } + + virtual const IMAGE_DATA_DIRECTORY* GetDataDirectoryEntries() OVERRIDE { + return &optional_header_->DataDirectory[0]; + } + + private: + const OPTIONAL_HEADER_TYPE* optional_header_; + DISALLOW_COPY_AND_ASSIGN(OptionalHeaderImpl); +}; + +PeImageReader::PeImageReader() + : image_data_(), + image_size_(), + validation_state_() {} + +PeImageReader::~PeImageReader() { + Clear(); +} + +bool PeImageReader::Initialize(const uint8_t* image_data, size_t image_size) { + image_data_ = image_data; + image_size_ = image_size; + + if (!ValidateDosHeader() || + !ValidatePeSignature() || + !ValidateCoffFileHeader() || + !ValidateOptionalHeader() || + !ValidateSectionHeaders()) { + Clear(); + return false; + } + + return true; +} + +PeImageReader::WordSize PeImageReader::GetWordSize() { + return optional_header_->GetWordSize(); +} + +const IMAGE_DOS_HEADER* PeImageReader::GetDosHeader() { + DCHECK_NE((validation_state_ & VALID_DOS_HEADER), 0U); + return reinterpret_cast<const IMAGE_DOS_HEADER*>(image_data_); +} + +const IMAGE_FILE_HEADER* PeImageReader::GetCoffFileHeader() { + DCHECK_NE((validation_state_ & VALID_COFF_FILE_HEADER), 0U); + return reinterpret_cast<const IMAGE_FILE_HEADER*>( + image_data_ + GetDosHeader()->e_lfanew + sizeof(DWORD)); +} + +const uint8_t* PeImageReader::GetOptionalHeaderData( + size_t* optional_header_size) { + *optional_header_size = GetOptionalHeaderSize(); + return GetOptionalHeaderStart(); +} + +size_t PeImageReader::GetNumberOfSections() { + return GetCoffFileHeader()->NumberOfSections; +} + +const IMAGE_SECTION_HEADER* PeImageReader::GetSectionHeaderAt(size_t index) { + DCHECK_NE((validation_state_ & VALID_SECTION_HEADERS), 0U); + DCHECK_LT(index, GetNumberOfSections()); + return reinterpret_cast<const IMAGE_SECTION_HEADER*>( + GetOptionalHeaderStart() + + GetOptionalHeaderSize() + + (sizeof(IMAGE_SECTION_HEADER) * index)); +} + +const uint8_t* PeImageReader::GetExportSection(size_t* section_size) { + size_t data_size = 0; + const uint8_t* data = GetImageData(IMAGE_DIRECTORY_ENTRY_EXPORT, &data_size); + + // The export section data must be big enough for the export directory. + if (!data || data_size < sizeof(IMAGE_EXPORT_DIRECTORY)) + return NULL; + + *section_size = data_size; + return data; +} + +size_t PeImageReader::GetNumberOfDebugEntries() { + size_t data_size = 0; + const uint8_t* data = GetImageData(IMAGE_DIRECTORY_ENTRY_DEBUG, &data_size); + return data ? (data_size / sizeof(IMAGE_DEBUG_DIRECTORY)) : 0; +} + +const IMAGE_DEBUG_DIRECTORY* PeImageReader::GetDebugEntry( + size_t index, + const uint8_t** raw_data, + size_t* raw_data_size) { + DCHECK_LT(index, GetNumberOfDebugEntries()); + + // Get the debug directory. + size_t debug_directory_size = 0; + const IMAGE_DEBUG_DIRECTORY* entries = + reinterpret_cast<const IMAGE_DEBUG_DIRECTORY*>( + GetImageData(IMAGE_DIRECTORY_ENTRY_DEBUG, &debug_directory_size)); + if (!entries) + return NULL; + + const IMAGE_DEBUG_DIRECTORY& entry = entries[index]; + const uint8_t* debug_data = NULL; + if (GetStructureAt(entry.PointerToRawData, entry.SizeOfData, &debug_data)) { + *raw_data = debug_data; + *raw_data_size = entry.SizeOfData; + } + return &entry; +} + +void PeImageReader::Clear() { + image_data_ = NULL; + image_size_ = 0; + validation_state_ = 0; + optional_header_.reset(); +} + +bool PeImageReader::ValidateDosHeader() { + const IMAGE_DOS_HEADER* dos_header = NULL; + if (!GetStructureAt(0, &dos_header) || + dos_header->e_magic != IMAGE_DOS_SIGNATURE || + dos_header->e_lfanew < 0) { + return false; + } + + validation_state_ |= VALID_DOS_HEADER; + return true; +} + +bool PeImageReader::ValidatePeSignature() { + const DWORD* signature = NULL; + if (!GetStructureAt(GetDosHeader()->e_lfanew, &signature) || + *signature != IMAGE_NT_SIGNATURE) { + return false; + } + + validation_state_ |= VALID_PE_SIGNATURE; + return true; +} + +bool PeImageReader::ValidateCoffFileHeader() { + DCHECK_NE((validation_state_ & VALID_PE_SIGNATURE), 0U); + const IMAGE_FILE_HEADER* file_header = NULL; + if (!GetStructureAt(GetDosHeader()->e_lfanew + + offsetof(IMAGE_NT_HEADERS32, FileHeader), + &file_header)) { + return false; + } + + validation_state_ |= VALID_COFF_FILE_HEADER; + return true; +} + +bool PeImageReader::ValidateOptionalHeader() { + const IMAGE_FILE_HEADER* file_header = GetCoffFileHeader(); + const size_t optional_header_offset = + GetDosHeader()->e_lfanew + offsetof(IMAGE_NT_HEADERS32, OptionalHeader); + const size_t optional_header_size = file_header->SizeOfOptionalHeader; + const WORD* optional_header_magic = NULL; + + if (optional_header_size < sizeof(*optional_header_magic) || + !GetStructureAt(optional_header_offset, &optional_header_magic)) { + return false; + } + + scoped_ptr<OptionalHeader> optional_header; + if (*optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + optional_header.reset(new OptionalHeaderImpl<IMAGE_OPTIONAL_HEADER32>( + image_data_ + optional_header_offset)); + } else if (*optional_header_magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + optional_header.reset(new OptionalHeaderImpl<IMAGE_OPTIONAL_HEADER64>( + image_data_ + optional_header_offset)); + } else { + return false; + } + + // Does all of the claimed optional header fit in the image? + if (optional_header_size > image_size_ - optional_header_offset) + return false; + + // Is the claimed optional header big enough for everything but the dir? + if (optional_header->GetDataDirectoryOffset() > optional_header_size) + return false; + + // Is there enough room for all of the claimed directory entries? + if (optional_header->GetDataDirectorySize() > + ((optional_header_size - optional_header->GetDataDirectoryOffset()) / + sizeof(IMAGE_DATA_DIRECTORY))) { + return false; + } + + optional_header_.swap(optional_header); + validation_state_ |= VALID_OPTIONAL_HEADER; + return true; +} + +bool PeImageReader::ValidateSectionHeaders() { + const uint8_t* first_section_header = + GetOptionalHeaderStart() + GetOptionalHeaderSize(); + const size_t number_of_sections = GetNumberOfSections(); + + // Do all section headers fit in the image? + if (!GetStructureAt(first_section_header - image_data_, + number_of_sections * sizeof(IMAGE_SECTION_HEADER), + &first_section_header)) { + return false; + } + + validation_state_ |= VALID_SECTION_HEADERS; + return true; +} + +const uint8_t* PeImageReader::GetOptionalHeaderStart() { + DCHECK_NE((validation_state_ & VALID_OPTIONAL_HEADER), 0U); + return (image_data_ + + GetDosHeader()->e_lfanew + + offsetof(IMAGE_NT_HEADERS32, OptionalHeader)); +} + +size_t PeImageReader::GetOptionalHeaderSize() { + return GetCoffFileHeader()->SizeOfOptionalHeader; +} + +const IMAGE_DATA_DIRECTORY* PeImageReader::GetDataDirectoryEntryAt( + size_t index) { + DCHECK_NE((validation_state_ & VALID_OPTIONAL_HEADER), 0U); + if (index >= optional_header_->GetDataDirectorySize()) + return NULL; + return &optional_header_->GetDataDirectoryEntries()[index]; +} + +const IMAGE_SECTION_HEADER* PeImageReader::FindSectionFromRva( + uint32_t relative_address) { + const size_t number_of_sections = GetNumberOfSections(); + for (size_t i = 0; i < number_of_sections; ++i) { + const IMAGE_SECTION_HEADER* section_header = GetSectionHeaderAt(i); + // Is the raw data present in the image? If no, optimistically keep looking. + const uint8_t* section_data = NULL; + if (!GetStructureAt(section_header->PointerToRawData, + section_header->SizeOfRawData, + §ion_data)) { + continue; + } + // Does the RVA lie on or after this section's start when mapped? If no, + // bail. + if (section_header->VirtualAddress > relative_address) + break; + // Does the RVA lie within the section when mapped? If no, keep looking. + size_t address_offset = relative_address - section_header->VirtualAddress; + if (address_offset > section_header->Misc.VirtualSize) + continue; + // We have a winner. + return section_header; + } + return NULL; +} + +const uint8_t* PeImageReader::GetImageData(size_t index, size_t* data_length) { + // Get the directory entry for the debug data. + const IMAGE_DATA_DIRECTORY* entry = GetDataDirectoryEntryAt(index); + if (!entry) + return NULL; + + // Find the section containing the data. + const IMAGE_SECTION_HEADER* header = + FindSectionFromRva(entry->VirtualAddress); + if (!header) + return NULL; + + // Does the data fit within the section when mapped? + size_t data_offset = entry->VirtualAddress - header->VirtualAddress; + if (entry->Size > (header->Misc.VirtualSize - data_offset)) + return NULL; + + *data_length = entry->Size; + return image_data_ + header->PointerToRawData + data_offset; +} + +} // namespace safe_browsing diff --git a/chrome/browser/safe_browsing/pe_image_reader_win.h b/chrome/browser/safe_browsing/pe_image_reader_win.h new file mode 100644 index 0000000..6fa5349 --- /dev/null +++ b/chrome/browser/safe_browsing/pe_image_reader_win.h @@ -0,0 +1,133 @@ +// 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. + +#ifndef CHROME_BROWSER_SAFE_BROWSING_PE_IMAGE_READER_WIN_H_ +#define CHROME_BROWSER_SAFE_BROWSING_PE_IMAGE_READER_WIN_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace safe_browsing { + +// Parses headers and various data from a PE image. This parser is safe for use +// on untrusted data. +class PeImageReader { + public: + enum WordSize { + WORD_SIZE_32, + WORD_SIZE_64, + }; + + PeImageReader(); + ~PeImageReader(); + + // Returns false if the given data does not appear to be a valid PE image. + bool Initialize(const uint8_t* image_data, size_t image_size); + + // Returns the machine word size for the image. + WordSize GetWordSize(); + + const IMAGE_DOS_HEADER* GetDosHeader(); + const IMAGE_FILE_HEADER* GetCoffFileHeader(); + + // Returns a pointer to the optional header and its size. + const uint8_t* GetOptionalHeaderData(size_t* optional_data_size); + size_t GetNumberOfSections(); + const IMAGE_SECTION_HEADER* GetSectionHeaderAt(size_t index); + + // Returns a pointer to the image's export data (.edata) section and its size, + // or NULL if the section is not present. + const uint8_t* GetExportSection(size_t* section_size); + + size_t GetNumberOfDebugEntries(); + const IMAGE_DEBUG_DIRECTORY* GetDebugEntry(size_t index, + const uint8_t** raw_data, + size_t* raw_data_size); + + private: + // Bits indicating what portions of the image have been validated. + enum ValidationStages { + VALID_DOS_HEADER = 1 << 0, + VALID_PE_SIGNATURE = 1 << 1, + VALID_COFF_FILE_HEADER = 1 << 2, + VALID_OPTIONAL_HEADER = 1 << 3, + VALID_SECTION_HEADERS = 1 << 4, + }; + + // An interface to an image's optional header. + class OptionalHeader { + public: + virtual WordSize GetWordSize() = 0; + + // Returns the offset of the DataDirectory member relative to the start of + // the optional header. + virtual size_t GetDataDirectoryOffset() = 0; + + // Returns the number of entries in the data directory. + virtual DWORD GetDataDirectorySize() = 0; + + // Returns a pointer to the first data directory entry. + virtual const IMAGE_DATA_DIRECTORY* GetDataDirectoryEntries() = 0; + }; + + template<class OPTIONAL_HEADER_TYPE> + class OptionalHeaderImpl; + + void Clear(); + bool ValidateDosHeader(); + bool ValidatePeSignature(); + bool ValidateCoffFileHeader(); + bool ValidateOptionalHeader(); + bool ValidateSectionHeaders(); + + // Return a pointer to the first byte of the image's optional header. + const uint8_t* GetOptionalHeaderStart(); + size_t GetOptionalHeaderSize(); + + // Returns the desired directory entry, or NULL if |index| is out of bounds. + const IMAGE_DATA_DIRECTORY* GetDataDirectoryEntryAt(size_t index); + + // Returns the header for the section that contains the given address, or NULL + // if the address is out of bounds or the image does not contain the section. + const IMAGE_SECTION_HEADER* FindSectionFromRva(uint32_t relative_address); + + // Returns a pointer to the |data_length| bytes referenced by the |index|'th + // data directory entry. + const uint8_t* GetImageData(size_t index, size_t* data_length); + + // Populates |structure| with a pointer to a desired structure of type T at + // the given offset if the image is sufficiently large to contain it. Returns + // false if the structure does not fully fit within the image at the given + // offset. + template<typename T> bool GetStructureAt(size_t offset, const T** structure) { + return GetStructureAt(offset, sizeof(**structure), structure); + } + + // Populates |structure| with a pointer to a desired structure of type T at + // the given offset if the image is sufficiently large to contain + // |structure_size| bytes. Returns false if the structure does not fully fit + // within the image at the given offset. + template<typename T> bool GetStructureAt(size_t offset, + size_t structure_size, + const T** structure) { + if (offset > image_size_) + return false; + if (structure_size > image_size_ - offset) + return false; + *structure = reinterpret_cast<const T*>(image_data_ + offset); + return true; + } + + const uint8_t* image_data_; + size_t image_size_; + uint32_t validation_state_; + scoped_ptr<OptionalHeader> optional_header_; + DISALLOW_COPY_AND_ASSIGN(PeImageReader); +}; + +} // namespace safe_browsing + +#endif // CHROME_BROWSER_SAFE_BROWSING_PE_IMAGE_READER_WIN_H_ diff --git a/chrome/browser/safe_browsing/pe_image_reader_win_unittest.cc b/chrome/browser/safe_browsing/pe_image_reader_win_unittest.cc new file mode 100644 index 0000000..63e6826 --- /dev/null +++ b/chrome/browser/safe_browsing/pe_image_reader_win_unittest.cc @@ -0,0 +1,157 @@ +// 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 "base/files/file_path.h" +#include "base/files/memory_mapped_file.h" +#include "base/path_service.h" +#include "chrome/browser/safe_browsing/pe_image_reader_win.h" +#include "chrome/common/chrome_paths.h" +#include "testing/gtest/include/gtest/gtest.h" + +struct TestData { + const char* filename; + safe_browsing::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/chrome/test/data/safe_browsing directory. +class PeImageReaderTest : public testing::TestWithParam<const TestData*> { + protected: + PeImageReaderTest() : expected_data_(GetParam()) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_file_path_)); + data_file_path_ = data_file_path_.AppendASCII("safe_browsing"); + 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())); + } + + const TestData* expected_data_; + base::FilePath data_file_path_; + base::MemoryMappedFile data_file_; + safe_browsing::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(reinterpret_cast<const IMAGE_DOS_HEADER*>(NULL), 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(reinterpret_cast<const IMAGE_FILE_HEADER*>(NULL), 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(reinterpret_cast<const uint8_t*>(NULL), 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(reinterpret_cast<const IMAGE_SECTION_HEADER*>(NULL), + 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<const uint8_t*>(last_section_header) + + sizeof(*last_section_header); + size_t header_size = headers_end - data_file_.data(); + safe_browsing::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(reinterpret_cast<const uint8_t*>(NULL), 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 = NULL; + size_t raw_data_size = 0; + const IMAGE_DEBUG_DIRECTORY* entry = + image_reader_.GetDebugEntry(i, &raw_data, &raw_data_size); + EXPECT_NE(reinterpret_cast<const IMAGE_DEBUG_DIRECTORY*>(NULL), entry); + EXPECT_NE(reinterpret_cast<const uint8_t*>(NULL), raw_data); + EXPECT_NE(0U, raw_data_size); + } +} + +namespace { + +const TestData kTestData[] = { + { + "module_with_exports_x86.dll", + safe_browsing::PeImageReader::WORD_SIZE_32, + IMAGE_FILE_MACHINE_I386, + sizeof(IMAGE_OPTIONAL_HEADER32), + 4, + 1, + }, { + "module_with_exports_x64.dll", + safe_browsing::PeImageReader::WORD_SIZE_64, + IMAGE_FILE_MACHINE_AMD64, + sizeof(IMAGE_OPTIONAL_HEADER64), + 5, + 1, + }, +}; + +} // namespace + +INSTANTIATE_TEST_CASE_P(WordSize32, + PeImageReaderTest, + testing::Values(&kTestData[0])); +INSTANTIATE_TEST_CASE_P(WordSize64, + PeImageReaderTest, + testing::Values(&kTestData[1])); diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 321977c..c73bc7d 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -915,6 +915,8 @@ 'sources': [ 'browser/safe_browsing/binary_feature_extractor.h', 'browser/safe_browsing/binary_feature_extractor_win.cc', + 'browser/safe_browsing/pe_image_reader_win.cc', + 'browser/safe_browsing/pe_image_reader_win.h', 'tools/safe_browsing/sb_sigutil.cc', ], }, diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index af17cb0..6fa104a 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1976,6 +1976,8 @@ 'browser/safe_browsing/malware_details_cache.h', 'browser/safe_browsing/malware_details_history.cc', 'browser/safe_browsing/malware_details_history.h', + 'browser/safe_browsing/pe_image_reader_win.cc', + 'browser/safe_browsing/pe_image_reader_win.h', 'browser/safe_browsing/ping_manager.cc', 'browser/safe_browsing/ping_manager.h', 'browser/safe_browsing/prefix_set.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 537cafe..4200b68 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1224,6 +1224,7 @@ 'browser/safe_browsing/download_protection_service_unittest.cc', 'browser/safe_browsing/local_two_phase_testserver.cc', 'browser/safe_browsing/malware_details_unittest.cc', + 'browser/safe_browsing/pe_image_reader_win_unittest.cc', 'browser/safe_browsing/ping_manager_unittest.cc', 'browser/safe_browsing/prefix_set_unittest.cc', 'browser/safe_browsing/protocol_manager_unittest.cc', diff --git a/chrome/common/safe_browsing/csd.proto b/chrome/common/safe_browsing/csd.proto index a7ef9d3..6b5f6c6 100644 --- a/chrome/common/safe_browsing/csd.proto +++ b/chrome/common/safe_browsing/csd.proto @@ -220,7 +220,38 @@ message ClientDownloadRequest { // Locale of the device, eg en, en_US. optional string locale = 11; - // Field 12 is only used on the server. + message PEImageHeaders { + // IMAGE_DOS_HEADER. + optional bytes dos_header = 1; + // IMAGE_FILE_HEADER. + optional bytes file_header = 2; + // IMAGE_OPTIONAL_HEADER32. Present only for 32-bit PE images. + optional bytes optional_headers32 = 3; + // IMAGE_OPTIONAL_HEADER64. Present only for 64-bit PE images. + optional bytes optional_headers64 = 4; + // IMAGE_SECTION_HEADER. + repeated bytes section_header = 5; + // Contents of the .edata section. + optional bytes export_section_data = 6; + + message DebugData { + // IMAGE_DEBUG_DIRECTORY. + optional bytes directory_entry = 1; + optional bytes raw_data = 2; + } + + repeated DebugData debug_data = 7; + } + + message ImageHeaders { + // Windows Portable Executable image headers. + optional PEImageHeaders pe_headers = 1; + }; + + // Fields 12-17 are reserved for server-side use and are never sent by the + // client. + + optional ImageHeaders image_headers = 18; } message ClientDownloadResponse { diff --git a/chrome/test/data/safe_browsing/module_with_exports.cc b/chrome/test/data/safe_browsing/module_with_exports.cc new file mode 100644 index 0000000..f1895ec --- /dev/null +++ b/chrome/test/data/safe_browsing/module_with_exports.cc @@ -0,0 +1,41 @@ +// 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. + +// A dummy source file that exports a single symbol. Built using compiler and +// linker flags very similar to those used for official builds as of March 2014. +// +// x86: +// cl /nologo -D_WIN32_WINNT=0x0602 -DWINVER=0x0602 -DWIN32 -D_WINDOWS +// -DNOMINMAX -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -D__STD_C +// -D_CRT_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_DEPRECATE +// -DNTDDI_VERSION=0x06020000 -D_USING_V110_SDK71_ -D__STDC_CONSTANT_MACROS +// -D__STDC_FORMAT_MACROS -DNDEBUG -D_UNICODE -DUNICODE /O1 /Ob2 /GF /GT /Oy- +// /Oi /Os /W4 /WX /Zi /GR- /Gy /GS /MT /we4389 /Oy- /FS /TP /FC +// module_with_exports.cc /link /nologo /DLL /OUT:module_with_exports_x86.dll +// /DEBUG /MACHINE:X86 /safeseh /largeaddressaware /SUBSYSTEM:CONSOLE,5.01 +// /INCREMENTAL:NO /FIXED:NO /OPT:REF /OPT:ICF /LTCG /PROFILE /DYNAMICBASE +// /NXCOMPAT /MANIFEST /MANIFESTUAC:NO +// +// x64: +// cl /nologo -D_WIN32_WINNT=0x0602 -DWINVER=0x0602 -DWIN32 -D_WINDOWS +// -DNOMINMAX -DWIN32_LEAN_AND_MEAN -D_HAS_EXCEPTIONS=0 -D__STD_C +// -D_CRT_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_DEPRECATE +// -DNTDDI_VERSION=0x06020000 -D_USING_V110_SDK71_ -D__STDC_CONSTANT_MACROS +// -D__STDC_FORMAT_MACROS -DNDEBUG -D_UNICODE -DUNICODE /wd4351 /wd4355 /wd4396 +// /wd4503 /wd4819 /wd4100 /wd4121 /wd4125 /wd4127 /wd4130 /wd4131 /wd4189 +// /wd4201 /wd4238 /wd4244 /wd4245 /wd4310 /wd4428 /wd4481 /wd4505 /wd4510 +// /wd4512 /wd4530 /wd4610 /wd4611 /wd4701 /wd4702 /wd4706 /O1 /Ob2 /GF /GT /Oy- +// /Oi /Os /W4 /WX /Zi /GR- /Gy /GS /MT /we4389 /Oy- /FS /TP /FC +// module_with_exports.cc /link /nologo /DLL /OUT:module_with_exports_x64.dll +// /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /FIXED:NO /OPT:REF +// /OPT:ICF /LTCG /PROFILE /DYNAMICBASE /NXCOMPAT /MANIFEST /MANIFESTUAC:NO + +#include <windows.h> + +extern "C" __declspec(dllexport) void AnExport(void) { +} + +extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD, void*) { + return TRUE; +} diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index e8757fa..29062b7 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -21632,6 +21632,14 @@ other types of suffix sets. </summary> </histogram> +<histogram name="SBClientDownload.ExtractImageHeadersTime" units="milliseconds"> + <owner>grt@chromium.org</owner> + <summary> + Records the time it takes for the SafeBrowsing download service to extract + image headers from a downloaded binary. + </summary> +</histogram> + <histogram name="SBClientDownload.ExtractSignatureFeaturesTime" units="milliseconds"> <owner>mattm@chromium.org</owner> |