blob: fa5adf406ae7ec598898bc461eda00d9d7a8cc0e [file] [log] [blame]
// Copyright 2017 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 "components/zucchini/element_detection.h"
#include <utility>
#include "base/logging.h"
#include "components/zucchini/buildflags.h"
#include "components/zucchini/disassembler.h"
#include "components/zucchini/disassembler_no_op.h"
#if BUILDFLAG(ENABLE_DEX)
#include "components/zucchini/disassembler_dex.h"
#endif // BUILDFLAG(ENABLE_DEX)
#if BUILDFLAG(ENABLE_WIN)
#include "components/zucchini/disassembler_win32.h"
#endif // BUILDFLAG(ENABLE_WIN)
#if BUILDFLAG(ENABLE_ELF)
#include "components/zucchini/disassembler_elf.h"
#endif // BUILDFLAG(ENABLE_ELF)
#if BUILDFLAG(ENABLE_ZTF)
#include "components/zucchini/disassembler_ztf.h"
#endif // BUILDFLAG(ENABLE_ZTF)
namespace zucchini {
namespace {
// Impose a minimal program size to eliminate pathological cases.
enum : size_t { kMinProgramSize = 16 };
} // namespace
/******** Utility Functions ********/
std::unique_ptr<Disassembler> MakeDisassemblerWithoutFallback(
ConstBufferView image) {
#if BUILDFLAG(ENABLE_WIN)
if (DisassemblerWin32X86::QuickDetect(image)) {
auto disasm = Disassembler::Make<DisassemblerWin32X86>(image);
if (disasm && disasm->size() >= kMinProgramSize)
return disasm;
}
if (DisassemblerWin32X64::QuickDetect(image)) {
auto disasm = Disassembler::Make<DisassemblerWin32X64>(image);
if (disasm && disasm->size() >= kMinProgramSize)
return disasm;
}
#endif // BUILDFLAG(ENABLE_WIN)
#if BUILDFLAG(ENABLE_ELF)
if (DisassemblerElfX86::QuickDetect(image)) {
auto disasm = Disassembler::Make<DisassemblerElfX86>(image);
if (disasm && disasm->size() >= kMinProgramSize)
return disasm;
}
if (DisassemblerElfX64::QuickDetect(image)) {
auto disasm = Disassembler::Make<DisassemblerElfX64>(image);
if (disasm && disasm->size() >= kMinProgramSize)
return disasm;
}
#endif // BUILDFLAG(ENABLE_ELF)
#if BUILDFLAG(ENABLE_DEX)
if (DisassemblerDex::QuickDetect(image)) {
auto disasm = Disassembler::Make<DisassemblerDex>(image);
if (disasm && disasm->size() >= kMinProgramSize)
return disasm;
}
#endif // BUILDFLAG(ENABLE_DEX)
#if BUILDFLAG(ENABLE_ZTF)
if (DisassemblerZtf::QuickDetect(image)) {
// This disallows very short examples like "ZTxtxtZ\n" in ensemble patching.
auto disasm = Disassembler::Make<DisassemblerZtf>(image);
if (disasm && disasm->size() >= kMinProgramSize)
return disasm;
}
#endif // BUILDFLAG(ENABLE_ZTF)
return nullptr;
}
std::unique_ptr<Disassembler> MakeDisassemblerOfType(ConstBufferView image,
ExecutableType exe_type) {
switch (exe_type) {
#if BUILDFLAG(ENABLE_WIN)
case kExeTypeWin32X86:
return Disassembler::Make<DisassemblerWin32X86>(image);
case kExeTypeWin32X64:
return Disassembler::Make<DisassemblerWin32X64>(image);
#endif // BUILDFLAG(ENABLE_WIN)
#if BUILDFLAG(ENABLE_ELF)
case kExeTypeElfX86:
return Disassembler::Make<DisassemblerElfX86>(image);
case kExeTypeElfX64:
return Disassembler::Make<DisassemblerElfX64>(image);
#endif // BUILDFLAG(ENABLE_ELF)
#if BUILDFLAG(ENABLE_DEX)
case kExeTypeDex:
return Disassembler::Make<DisassemblerDex>(image);
#endif // BUILDFLAG(ENABLE_DEX)
#if BUILDFLAG(ENABLE_ZTF)
case kExeTypeZtf:
return Disassembler::Make<DisassemblerZtf>(image);
#endif // BUILDFLAG(ENABLE_ZTF)
case kExeTypeNoOp:
return Disassembler::Make<DisassemblerNoOp>(image);
default:
// If an architecture is disabled then null is handled gracefully.
return nullptr;
}
}
base::Optional<Element> DetectElementFromDisassembler(ConstBufferView image) {
std::unique_ptr<Disassembler> disasm = MakeDisassemblerWithoutFallback(image);
if (disasm)
return Element({0, disasm->size()}, disasm->GetExeType());
return base::nullopt;
}
/******** ProgramScanner ********/
ElementFinder::ElementFinder(ConstBufferView image, ElementDetector&& detector)
: image_(image), detector_(std::move(detector)) {}
ElementFinder::~ElementFinder() = default;
base::Optional<Element> ElementFinder::GetNext() {
for (; pos_ < image_.size(); ++pos_) {
ConstBufferView test_image =
ConstBufferView::FromRange(image_.begin() + pos_, image_.end());
base::Optional<Element> element = detector_.Run(test_image);
if (element) {
element->offset += pos_;
pos_ = element->EndOffset();
return element;
}
}
return base::nullopt;
}
} // namespace zucchini