summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-25 23:26:42 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-25 23:26:42 +0000
commit03e3925b8017a2bc5cbef29e109ef2ba4e634329 (patch)
treead745d79bddbf069a9307e0fb368cdd970108110
parentef613d38eb0e0070393a245feec64c802e41fbbe (diff)
downloadchromium_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.h6
-rw-r--r--chrome/browser/safe_browsing/binary_feature_extractor_posix.cc4
-rw-r--r--chrome/browser/safe_browsing/binary_feature_extractor_win.cc56
-rw-r--r--chrome/browser/safe_browsing/binary_feature_extractor_win_unittest.cc75
-rw-r--r--chrome/browser/safe_browsing/download_protection_service.cc7
-rw-r--r--chrome/browser/safe_browsing/download_protection_service_unittest.cc39
-rw-r--r--chrome/browser/safe_browsing/pe_image_reader_win.cc327
-rw-r--r--chrome/browser/safe_browsing/pe_image_reader_win.h133
-rw-r--r--chrome/browser/safe_browsing/pe_image_reader_win_unittest.cc157
-rw-r--r--chrome/chrome.gyp2
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/safe_browsing/csd.proto33
-rw-r--r--chrome/test/data/safe_browsing/module_with_exports.cc41
-rw-r--r--tools/metrics/histograms/histograms.xml8
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,
+ &section_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(&section_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>