// 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. #ifndef COMPONENTS_ZUCCHINI_REL32_FINDER_H_ #define COMPONENTS_ZUCCHINI_REL32_FINDER_H_ #include #include #include "components/zucchini/address_translator.h" #include "components/zucchini/arm_utils.h" #include "components/zucchini/buffer_view.h" #include "components/zucchini/image_utils.h" namespace zucchini { // See README.md for definitions on abs32 and rel32 references. The following // are assumed: // * Abs32 reference bodies have fixed widths. // * Rel32 locations can be identified by heuristically disassembling machine // code, and errors are tolerated. // * The collection all abs32 and rel32 reference bodies do not overlap. // A class to visit non-empty contiguous gaps in |region| that lie outside of // |abs32_locations| elements, each with a body that spans |abs32_width_| bytes. // For example, given: // region = [base_ + 4, base_ + 26), // abs32_locations = {2, 6, 15, 20, 27}, // abs32_width_ = 4, // the following is obtained: // 111111111122222222223 -> offsets // 0123456789012345678901234567890 // ....**********************..... -> region = * // ^ ^ ^ ^ ^ -> abs32 locations // aaaaaaaa aaaa aaaa aaaa -> abs32 bodies // ....------*****----*----**..... -> regions excluding abs32 -> 3 gaps // The resulting gaps (non-empty, so [6, 6) is excluded) are: // [10, 15), [19, 20), [24, 26). // These gaps can then be passed to Rel32Finder (below) to find rel32 references // with bodies that are guaranteed to not overlap with any abs32 bodies. class Abs32GapFinder { public: // |abs32_locations| is a sorted list of non-overlapping abs32 locations in // |image|, each spanning |abs32_width| bytes. Gaps are searched in |region|, // which must be part of |image|. Abs32GapFinder(ConstBufferView image, ConstBufferView region, const std::deque& abs32_locations, size_t abs32_width); Abs32GapFinder(const Abs32GapFinder&) = delete; const Abs32GapFinder& operator=(const Abs32GapFinder&) = delete; ~Abs32GapFinder(); // Searches for the next available gap, and returns successfulness. bool FindNext(); // Returns the cached result from the last successful FindNext(). ConstBufferView GetGap() const { return gap_; } private: const ConstBufferView::const_iterator base_; const ConstBufferView::const_iterator region_end_; ConstBufferView::const_iterator cur_lo_; const std::deque::const_iterator abs32_end_; std::deque::const_iterator abs32_cur_; const size_t abs32_width_; ConstBufferView gap_; }; // A class to scan regions within an image to find successive rel32 references. // Architecture-specific parsing and result extraction are delegated to // inherited classes (say, Rel32Finder_Impl). Sample extraction loop, combined // with Abs32GapFinder usage: // // Abs32GapFinder gap_finder(...); // Rel32Finder_Impl finder(...); // while (gap_finder.FindNext()) { // rel_finder.SetRegion(gap_finder.GetGap()); // while (rel_finder.FindNext()) { // auto rel32 = rel_finder.GetRel32(); // In Rel32Finder_Impl. // if (architecture_specific_validation(rel32)) { // rel_finder.Accept(); // // Store rel32. // } // } // } class Rel32Finder { public: Rel32Finder(ConstBufferView image, const AddressTranslator& translator); Rel32Finder(const Rel32Finder&) = delete; const Rel32Finder& operator=(const Rel32Finder&) = delete; virtual ~Rel32Finder(); // Assigns the scan |region| for rel32 references to enable FindNext() use. void SetRegion(ConstBufferView region); // Scans for the next rel32 reference, and returns whether any is found, so a // "while" loop can be used for iterative rel32 extraction. The results are // cached in Rel32Finder_Impl and obtained by Rel32Finder_Impl::GetRel32(). bool FindNext(); // When a rel32 reference is found, the caller needs to decide whether to keep // the result (perhaps following more validation). If it decides to keep the // result, then it must call Accept(), so the next call to FindNext() can skip // the accepted rel32 reference. void Accept(); // Accessors for unittest. ConstBufferView::const_iterator accept_it() const { return accept_it_; } ConstBufferView region() const { return region_; } protected: // Alternatives for where to continue the next scan when a rel32 reference is // found. nulls indicate that no rel32 references remain. struct NextIterators { // The next iterator if the caller does not call Accept(). ConstBufferView::const_iterator reject; // The next iterator if the caller calls Accept(). ConstBufferView::const_iterator accept; }; // Detects and extracts architecture-specific rel32 reference. For each one // found, the implementation should cache the necessary data to be retrieved // via accessors. Returns a NextIterators that stores alternatives for where // to continue the scan. If no rel32 reference is found then the returned // NextIterators are nulls. virtual NextIterators Scan(ConstBufferView region) = 0; const ConstBufferView image_; AddressTranslator::OffsetToRvaCache offset_to_rva_; private: ConstBufferView region_; ConstBufferView::const_iterator accept_it_ = nullptr; }; // Parsing for X86 or X64: we perform naive scan for opcodes that have rel32 as // an argument, and disregard instruction alignment. class Rel32FinderIntel : public Rel32Finder { public: Rel32FinderIntel(const Rel32FinderIntel&) = delete; const Rel32FinderIntel& operator=(const Rel32FinderIntel&) = delete; // Struct to store GetRel32() results. struct Result { offset_t location; rva_t target_rva; // Some references must have their target in the same section as location, // which we use this to heuristically reject rel32 reference candidates. // When true, this constraint is relaxed. bool can_point_outside_section; }; using Rel32Finder::Rel32Finder; // Returns the cached result from the last successful FindNext(). const Result& GetRel32() { return rel32_; } protected: // Helper for Scan() that also assigns |rel32_|. Rel32Finder::NextIterators SetResult(ConstBufferView::const_iterator cursor, uint32_t code_size, bool can_point_outside_section); // Cached results. Result rel32_; // Rel32Finder: NextIterators Scan(ConstBufferView region) override = 0; }; // X86 instructions. class Rel32FinderX86 : public Rel32FinderIntel { public: using Rel32FinderIntel::Rel32FinderIntel; Rel32FinderX86(const Rel32FinderX86&) = delete; const Rel32FinderX86& operator=(const Rel32FinderX86&) = delete; private: // Rel32Finder: NextIterators Scan(ConstBufferView region) override; }; // X64 instructions. class Rel32FinderX64 : public Rel32FinderIntel { public: using Rel32FinderIntel::Rel32FinderIntel; Rel32FinderX64(const Rel32FinderX64&) = delete; const Rel32FinderX64& operator=(const Rel32FinderX64&) = delete; private: // Rel32Finder: NextIterators Scan(ConstBufferView region) override; }; // Base class for ARM (AArch32 and AArch64) instructions. template class Rel32FinderArm : public Rel32Finder { public: struct Result { offset_t location; rva_t target_rva; ADDR_TYPE type; // For testing. bool operator==(const Result& other) const { return location == other.location && target_rva == other.target_rva && type == other.type; } }; Rel32FinderArm(ConstBufferView image, const AddressTranslator& translator); Rel32FinderArm(const Rel32FinderArm&) = delete; const Rel32FinderArm& operator=(const Rel32FinderArm&) = delete; ~Rel32FinderArm() override; // Helper for Scan*() that also assigns |rel32_|. NextIterators SetResult(Result&& result, ConstBufferView::const_iterator cursor, int instr_size); // SetResult() for end of scan. NextIterators SetEmptyResult(); protected: // Cached results. Result rel32_; }; // AArch32 instructions. class Rel32FinderAArch32 : public Rel32FinderArm { public: Rel32FinderAArch32(ConstBufferView image, const AddressTranslator& translator, bool is_thumb2); Rel32FinderAArch32(const Rel32FinderAArch32&) = delete; const Rel32FinderAArch32& operator=(const Rel32FinderAArch32&) = delete; ~Rel32FinderAArch32() override; const Result& GetRel32() const { return rel32_; } private: // Rel32 extraction, assuming segment is in ARM mode. NextIterators ScanA32(ConstBufferView region); // Rel32 extraction, assuming segment is in THUMB2 mode. NextIterators ScanT32(ConstBufferView region); // Rel32Finder: NextIterators Scan(ConstBufferView region) override; // Indicates whether segment is in THUMB2 or ARM mod. In general this can // change throughout a section. However, currently we assume that this is // constant for an entire section. const bool is_thumb2_; }; // AArch64 instructions. class Rel32FinderAArch64 : public Rel32FinderArm { public: Rel32FinderAArch64(ConstBufferView image, const AddressTranslator& translator); Rel32FinderAArch64(const Rel32FinderAArch64&) = delete; const Rel32FinderAArch64& operator=(const Rel32FinderAArch64&) = delete; ~Rel32FinderAArch64() override; const Result& GetRel32() const { return rel32_; } private: // Rel32Finder: NextIterators Scan(ConstBufferView region) override; }; } // namespace zucchini #endif // COMPONENTS_ZUCCHINI_REL32_FINDER_H_