blob: dbe2c4750f80c3163bed568894b5f1b3c56be03a [file] [log] [blame]
// Copyright 2015 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/utility/safe_browsing/mac/dmg_analyzer.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "chrome/common/safe_browsing/binary_feature_extractor.h"
#include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
#include "chrome/utility/safe_browsing/mac/dmg_iterator.h"
#include "chrome/utility/safe_browsing/mac/read_stream.h"
#include "components/safe_browsing/proto/csd.pb.h"
#include "crypto/secure_hash.h"
#include "crypto/sha2.h"
namespace safe_browsing {
namespace dmg {
namespace {
// MachOFeatureExtractor examines files to determine if they are Mach-O, and,
// if so, it uses the BinaryFeatureExtractor to obtain information about the
// image. In addition, this class will compute the SHA256 hash of the file.
class MachOFeatureExtractor {
public:
MachOFeatureExtractor();
~MachOFeatureExtractor();
// Tests if the stream references a Mach-O image by examinig its magic
// number.
bool IsMachO(ReadStream* stream);
// Computes the hash of the data in |stream| and extracts the Mach-O
// features from the data. Returns true if successful, or false on error or
// if the file was not Mach-O.
bool ExtractFeatures(ReadStream* stream,
ClientDownloadRequest_ArchivedBinary* result);
private:
// Reads the entire stream and updates the hash.
bool HashAndCopyStream(ReadStream* stream,
uint8_t digest[crypto::kSHA256Length]);
scoped_refptr<BinaryFeatureExtractor> bfe_;
std::vector<uint8_t> buffer_; // Buffer that contains read stream data.
DISALLOW_COPY_AND_ASSIGN(MachOFeatureExtractor);
};
MachOFeatureExtractor::MachOFeatureExtractor()
: bfe_(new BinaryFeatureExtractor()),
buffer_() {
buffer_.reserve(1024 * 1024);
}
MachOFeatureExtractor::~MachOFeatureExtractor() {}
bool MachOFeatureExtractor::IsMachO(ReadStream* stream) {
uint32_t magic = 0;
return stream->ReadType<uint32_t>(&magic) &&
MachOImageReader::IsMachOMagicValue(magic);
}
bool MachOFeatureExtractor::ExtractFeatures(
ReadStream* stream,
ClientDownloadRequest_ArchivedBinary* result) {
uint8_t digest[crypto::kSHA256Length];
if (!HashAndCopyStream(stream, digest))
return false;
if (!bfe_->ExtractImageFeaturesFromData(
&buffer_[0], buffer_.size(), 0,
result->mutable_image_headers(),
result->mutable_signature()->mutable_signed_data())) {
return false;
}
result->set_length(buffer_.size());
result->mutable_digests()->set_sha256(digest, sizeof(digest));
return true;
}
bool MachOFeatureExtractor::HashAndCopyStream(
ReadStream* stream, uint8_t digest[crypto::kSHA256Length]) {
if (stream->Seek(0, SEEK_SET) != 0)
return false;
buffer_.clear();
std::unique_ptr<crypto::SecureHash> sha256(
crypto::SecureHash::Create(crypto::SecureHash::SHA256));
size_t bytes_read;
const size_t kBufferSize = 2048;
do {
size_t buffer_offset = buffer_.size();
buffer_.resize(buffer_.size() + kBufferSize);
if (!stream->Read(&buffer_[buffer_offset], kBufferSize, &bytes_read))
return false;
buffer_.resize(buffer_offset + bytes_read);
if (bytes_read)
sha256->Update(&buffer_[buffer_offset], bytes_read);
} while (bytes_read > 0);
sha256->Finish(digest, crypto::kSHA256Length);
return true;
}
// The first few bytes of a DER-encoded pkcs7-signedData object.
constexpr uint8_t kDERPKCS7SignedData[] = {0x30, 0x80, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x07, 0x02, 0xa0};
} // namespace
void AnalyzeDMGFile(base::File dmg_file, ArchiveAnalyzerResults* results) {
FileReadStream read_stream(dmg_file.GetPlatformFile());
DMGIterator iterator(&read_stream);
AnalyzeDMGFile(&iterator, results);
}
void AnalyzeDMGFile(DMGIterator* iterator, ArchiveAnalyzerResults* results) {
results->success = false;
if (!iterator->Open())
return;
MachOFeatureExtractor feature_extractor;
results->signature_blob = iterator->GetCodeSignature();
while (iterator->Next()) {
std::unique_ptr<ReadStream> stream = iterator->GetReadStream();
if (!stream)
continue;
std::string path = base::UTF16ToUTF8(iterator->GetPath());
bool is_detached_code_signature_file = base::EndsWith(
path, "_CodeSignature/CodeSignature", base::CompareCase::SENSITIVE);
if (is_detached_code_signature_file) {
results->has_executable = true;
std::vector<uint8_t> signature_contents;
if (!ReadEntireStream(stream.get(), &signature_contents))
continue;
if (signature_contents.size() < base::size(kDERPKCS7SignedData))
continue;
if (memcmp(kDERPKCS7SignedData, signature_contents.data(),
base::size(kDERPKCS7SignedData)) != 0) {
continue;
}
ClientDownloadRequest_DetachedCodeSignature* detached_signature =
results->detached_code_signatures.Add();
detached_signature->set_file_name(path);
detached_signature->set_contents(signature_contents.data(),
signature_contents.size());
} else if (feature_extractor.IsMachO(stream.get())) {
ClientDownloadRequest_ArchivedBinary* binary =
results->archived_binary.Add();
binary->set_file_basename(path);
if (feature_extractor.ExtractFeatures(stream.get(), binary)) {
binary->set_download_type(
ClientDownloadRequest_DownloadType_MAC_EXECUTABLE);
results->has_executable = true;
} else {
results->archived_binary.RemoveLast();
}
}
}
results->success = true;
}
} // namespace dmg
} // namespace safe_browsing