diff options
-rw-r--r-- | BUILD.gn | 3 | ||||
-rw-r--r-- | buffer_view.h | 8 | ||||
-rw-r--r-- | buffer_view_unittest.cc | 34 | ||||
-rw-r--r-- | disassembler_dex.cc | 307 | ||||
-rw-r--r-- | disassembler_dex.h | 65 | ||||
-rw-r--r-- | element_detection.cc | 9 | ||||
-rw-r--r-- | ensemble_matcher.cc | 17 | ||||
-rw-r--r-- | type_dex.h | 261 |
8 files changed, 703 insertions, 1 deletions
@@ -24,6 +24,8 @@ static_library("zucchini_lib") { "crc32.h", "disassembler.cc", "disassembler.h", + "disassembler_dex.cc", + "disassembler_dex.h", "disassembler_no_op.cc", "disassembler_no_op.h", "disassembler_win32.cc", @@ -61,6 +63,7 @@ static_library("zucchini_lib") { "target_pool.h", "targets_affinity.cc", "targets_affinity.h", + "type_dex.h", "type_win_pe.h", "typed_value.h", "zucchini.h", diff --git a/buffer_view.h b/buffer_view.h index a7dfd17..8dbe817 100644 --- a/buffer_view.h +++ b/buffer_view.h @@ -105,6 +105,14 @@ class BufferViewBase { return region.FitsIn(size()); } + // Returns whether the buffer is large enough to cover an array starting at + // |offset| with |num| elements, each taking |elt_size| bytes. + bool covers_array(size_t offset, size_t num, size_t elt_size) { + DCHECK_GT(elt_size, 0U); + // Use subtraction and division to avoid overflow. + return offset < size() && (size() - offset) / elt_size >= num; + } + // Element access // Returns the raw value at specified location |pos|. diff --git a/buffer_view_unittest.cc b/buffer_view_unittest.cc index cfb3d9b..1d3ccb8 100644 --- a/buffer_view_unittest.cc +++ b/buffer_view_unittest.cc @@ -176,6 +176,40 @@ TEST_F(BufferViewTest, Covers) { EXPECT_FALSE(view.covers({size_t(-1), size_t(-1)})); } +TEST_F(BufferViewTest, CoversArray) { + ConstBufferView view(bytes_.data(), bytes_.size()); + + for (uint32_t i = 1; i <= bytes_.size(); ++i) { + EXPECT_TRUE(view.covers_array(0, 1, i)); + EXPECT_TRUE(view.covers_array(0, i, 1)); + EXPECT_TRUE(view.covers_array(0, i, bytes_.size() / i)); + EXPECT_TRUE(view.covers_array(0, bytes_.size() / i, i)); + if (i < bytes_.size()) { + EXPECT_TRUE(view.covers_array(i, 1, bytes_.size() - i)); + EXPECT_TRUE(view.covers_array(i, bytes_.size() - i, 1)); + } + EXPECT_TRUE(view.covers_array(bytes_.size() - (bytes_.size() / i) * i, 1, + bytes_.size() / i)); + } + + EXPECT_TRUE(view.covers_array(0, 0, bytes_.size())); + EXPECT_TRUE(view.covers_array(bytes_.size() - 1, 0, bytes_.size())); + EXPECT_FALSE(view.covers_array(bytes_.size(), 0, bytes_.size())); + EXPECT_TRUE(view.covers_array(0, 0, 0x10000)); + EXPECT_TRUE(view.covers_array(bytes_.size() - 1, 0, 0x10000)); + EXPECT_FALSE(view.covers_array(bytes_.size(), 0, 0x10000)); + + EXPECT_FALSE(view.covers_array(0, 1, bytes_.size() + 1)); + EXPECT_FALSE(view.covers_array(0, 2, bytes_.size())); + EXPECT_FALSE(view.covers_array(0, bytes_.size() + 11, 1)); + EXPECT_FALSE(view.covers_array(0, bytes_.size(), 2)); + EXPECT_FALSE(view.covers_array(1, bytes_.size(), 1)); + + EXPECT_FALSE(view.covers_array(bytes_.size(), 1, 1)); + EXPECT_FALSE(view.covers_array(bytes_.size(), 0, 1)); + EXPECT_FALSE(view.covers_array(0, 0x10000, 0x10000)); +} + TEST_F(BufferViewTest, Equals) { // Almost identical to |bytes_|, except at 2 places: v v std::vector<uint8_t> bytes2 = ParseHexString("10 32 54 76 98 AB CD FE 10 00"); diff --git a/disassembler_dex.cc b/disassembler_dex.cc new file mode 100644 index 0000000..23198cc --- /dev/null +++ b/disassembler_dex.cc @@ -0,0 +1,307 @@ +// Copyright 2018 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/disassembler_dex.h" + +#include <cmath> +#include <set> +#include <utility> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "components/zucchini/buffer_source.h" +#include "components/zucchini/buffer_view.h" + +namespace zucchini { + +namespace { + +// Size of a Dalvik instruction unit. Need to cast to signed int because +// sizeof() gives size_t, which dominates when operated on ptrdiff_t, then +// wrecks havoc for base::checked_cast<int16_t>(). +constexpr int kInstrUnitSize = static_cast<int>(sizeof(uint16_t)); + +// Buffer for ReadDexHeader() to optionally return results. +struct ReadDexHeaderResults { + BufferSource source; + const dex::HeaderItem* header; + int dex_version; +}; + +// Returns whether |image| points to a DEX file. If this is a possibility and +// |opt_results| is not null, then uses it to pass extracted data to enable +// further parsing. +bool ReadDexHeader(ConstBufferView image, ReadDexHeaderResults* opt_results) { + // This part needs to be fairly efficient since it may be called many times. + BufferSource source(image); + const dex::HeaderItem* header = source.GetPointer<dex::HeaderItem>(); + if (!header) + return false; + if (header->magic[0] != 'd' || header->magic[1] != 'e' || + header->magic[2] != 'x' || header->magic[3] != '\n' || + header->magic[7] != '\0') { + return false; + } + + // Magic matches: More detailed tests can be conducted. + int dex_version = 0; + for (int i = 4; i < 7; ++i) { + if (!isdigit(header->magic[i])) + return false; + dex_version = dex_version * 10 + (header->magic[i] - '0'); + } + if (dex_version != 35 && dex_version != 37) + return false; + + if (header->file_size > image.size() || + header->file_size < sizeof(dex::HeaderItem) || + header->map_off < sizeof(dex::HeaderItem)) { + return false; + } + + if (opt_results) + *opt_results = {source, header, dex_version}; + return true; +} + +} // namespace + +/******** CodeItemParser ********/ + +// A parser to extract successive code items from a DEX image whose header has +// been parsed. +class CodeItemParser { + public: + using size_type = BufferSource::size_type; + + explicit CodeItemParser(ConstBufferView image) : image_(image) {} + + // Initializes the parser, returns true on success and false on error. + bool Init(const dex::MapItem& code_map_item) { + // Sanity check to quickly fail if |code_map_item.offset| or + // |code_map_item.size| is too large. This is a heuristic because code item + // sizes need to be parsed (sizeof(dex::CodeItem) is a lower bound). + if (!image_.covers_array(code_map_item.offset, code_map_item.size, + sizeof(dex::CodeItem))) { + return false; + } + source_ = std::move(BufferSource(image_).Skip(code_map_item.offset)); + return true; + } + + // Extracts the header of the next code item, and skips the variable-length + // data. Returns the offset of the code item if successful. Otherwise returns + // kInvalidOffset, and thereafter the parser becomes valid. For reference, + // here's a pseudo-struct of a complete code item: + // + // struct code_item { + // // 4-byte aligned here. + // // 16-byte header defined (dex::CodeItem). + // uint16_t registers_size; + // uint16_t ins_size; + // uint16_t outs_size; + // uint16_t tries_size; + // uint32_t debug_info_off; + // uint32_t insns_size; + // + // // Variable-length data follow. + // uint16_t insns[insns_size]; // Instruction bytes. + // uint16_t padding[(tries_size > 0 && insns_size % 2 == 1) ? 1 : 0]; + // + // if (tries_size > 0) { + // // 4-byte aligned here. + // struct try_item { // dex::TryItem. + // uint32_t start_addr; + // uint16_t insn_count; + // uint16_t handler_off; + // } tries[tries_size]; + // + // struct encoded_catch_handler_list { + // uleb128 handlers_size; + // struct encoded_catch_handler { + // sleb128 encoded_catch_handler_size; + // struct encoded_type_addr_pair { + // uleb128 type_idx; + // uleb128 addr; + // } handlers[abs(encoded_catch_handler_size)]; + // if (encoded_catch_handler_size <= 0) { + // uleb128 catch_all_addr; + // } + // } handlers_list[handlers_size]; + // } handlers_group; // Confusingly called "handlers" in DEX doc. + // } + // + // // Padding to 4-bytes align next code_item *only if more exist*. + // } + offset_t GetNext() { + // Read header CodeItem. + if (!source_.AlignOn(image_, 4U)) + return kInvalidOffset; + const offset_t code_item_offset = + base::checked_cast<offset_t>(source_.begin() - image_.begin()); + const auto* code_item = source_.GetPointer<const dex::CodeItem>(); + if (!code_item) + return kInvalidOffset; + DCHECK_EQ(0U, code_item_offset % 4U); + + // Skip instruction bytes. + if (!source_.GetArray<uint16_t>(code_item->insns_size)) + return kInvalidOffset; + // Skip padding if present. + if (code_item->tries_size > 0 && !source_.AlignOn(image_, 4U)) + return kInvalidOffset; + + // Skip tries[] and handlers_group to arrive at the next code item. Parsing + // is nontrivial due to use of uleb128 / sleb128. + if (code_item->tries_size > 0) { + // Skip (try_item) tries[]. + if (!source_.GetArray<dex::TryItem>(code_item->tries_size)) + return kInvalidOffset; + + // Skip handlers_group. + uint32_t handlers_size = 0; + if (!source_.GetUleb128(&handlers_size)) + return kInvalidOffset; + // Sanity check to quickly reject excessively large |handlers_size|. + if (source_.Remaining() < static_cast<size_type>(handlers_size)) + return kInvalidOffset; + + // Skip (encoded_catch_handler) handlers_list[]. + for (uint32_t k = 0; k < handlers_size; ++k) { + int32_t encoded_catch_handler_size = 0; + if (!source_.GetSleb128(&encoded_catch_handler_size)) + return kInvalidOffset; + const size_type abs_size = std::abs(encoded_catch_handler_size); + if (source_.Remaining() < abs_size) // Sanity check. + return kInvalidOffset; + // Skip (encoded_type_addr_pair) handlers[]. + for (size_type j = 0; j < abs_size; ++j) { + if (!source_.SkipLeb128() || !source_.SkipLeb128()) + return kInvalidOffset; + } + // Skip catch_all_addr. + if (encoded_catch_handler_size <= 0) { + if (!source_.SkipLeb128()) + return kInvalidOffset; + } + } + } + // Success! |code_item->insns_size| is validated, but its content is still + // considered unsafe and requires validation. + return code_item_offset; + } + + // Given |code_item_offset| that points to the start of a valid code item in + // |image|, returns |insns| bytes as ConstBufferView. + static ConstBufferView GetCodeItemInsns(ConstBufferView image, + offset_t code_item_offset) { + BufferSource source(BufferSource(image).Skip(code_item_offset)); + const auto* code_item = source.GetPointer<const dex::CodeItem>(); + DCHECK(code_item); + BufferRegion insns{0, code_item->insns_size * kInstrUnitSize}; + DCHECK(source.covers(insns)); + return source[insns]; + } + + private: + ConstBufferView image_; + BufferSource source_; +}; + +/******** DisassemblerDex ********/ + +DisassemblerDex::DisassemblerDex() : Disassembler(4) {} + +DisassemblerDex::~DisassemblerDex() = default; + +// static. +bool DisassemblerDex::QuickDetect(ConstBufferView image) { + return ReadDexHeader(image, nullptr); +} + +ExecutableType DisassemblerDex::GetExeType() const { + return kExeTypeDex; +} + +std::string DisassemblerDex::GetExeTypeString() const { + return base::StringPrintf("DEX (version %d)", dex_version_); +} + +std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const { + return {}; +} + +bool DisassemblerDex::Parse(ConstBufferView image) { + image_ = image; + return ParseHeader(); +} + +bool DisassemblerDex::ParseHeader() { + ReadDexHeaderResults results; + if (!ReadDexHeader(image_, &results)) + return false; + + header_ = results.header; + dex_version_ = results.dex_version; + BufferSource source = results.source; + + // DEX header contains file size, so use it to resize |image_| right away. + image_.shrink(header_->file_size); + + // Read map list. This is not a fixed-size array, so instead of reading + // MapList directly, read |MapList::size| first, then visit elements in + // |MapList::list|. + static_assert( + offsetof(dex::MapList, list) == sizeof(decltype(dex::MapList::size)), + "MapList size error."); + source = std::move(BufferSource(image_).Skip(header_->map_off)); + decltype(dex::MapList::size) list_size = 0; + if (!source.GetValue(&list_size) || list_size > dex::kMaxItemListSize) + return false; + const auto* item_list = source.GetArray<const dex::MapItem>(list_size); + if (!item_list) + return false; + + // Read and validate map list, ensuring that required item types are present. + std::set<uint16_t> required_item_types = { + dex::kTypeStringIdItem, dex::kTypeTypeIdItem, dex::kTypeFieldIdItem, + dex::kTypeMethodIdItem, dex::kTypeCodeItem}; + for (offset_t i = 0; i < list_size; ++i) { + const dex::MapItem* item = &item_list[i]; + // Sanity check to reject unreasonably large |item->size|. + // TODO(huangs): Implement a more stringent check. + if (!image_.covers({item->offset, item->size})) + return false; + if (!map_item_map_.insert(std::make_pair(item->type, item)).second) + return false; // A given type must appear at most once. + required_item_types.erase(item->type); + } + if (!required_item_types.empty()) + return false; + + // Make local copies of main map items. + string_map_item_ = *map_item_map_[dex::kTypeStringIdItem]; + type_map_item_ = *map_item_map_[dex::kTypeTypeIdItem]; + field_map_item_ = *map_item_map_[dex::kTypeFieldIdItem]; + method_map_item_ = *map_item_map_[dex::kTypeMethodIdItem]; + code_map_item_ = *map_item_map_[dex::kTypeCodeItem]; + + // Iteratively extract variable-length code items blocks. Any failure would + // indicate invalid DEX. Success indicates that no structural problem is + // found. However, contained instructions still need validation on use. + CodeItemParser code_item_parser(image_); + if (!code_item_parser.Init(code_map_item_)) + return false; + code_item_offsets_.resize(code_map_item_.size); + for (size_t i = 0; i < code_map_item_.size; ++i) { + const offset_t code_item_offset = code_item_parser.GetNext(); + if (code_item_offset == kInvalidOffset) + return false; + code_item_offsets_[i] = code_item_offset; + } + return true; +} + +} // namespace zucchini diff --git a/disassembler_dex.h b/disassembler_dex.h new file mode 100644 index 0000000..7bbe5d1 --- /dev/null +++ b/disassembler_dex.h @@ -0,0 +1,65 @@ +// Copyright 2018 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_DISASSEMBLER_DEX_H_ +#define COMPONENTS_ZUCCHINI_DISASSEMBLER_DEX_H_ + +#include <stdint.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "components/zucchini/disassembler.h" +#include "components/zucchini/image_utils.h" +#include "components/zucchini/type_dex.h" + +namespace zucchini { + +// For consistency, let "canonical order" of DEX data types be the order defined +// in https://source.android.com/devices/tech/dalvik/dex-format "Type Codes" +// section. + +class DisassemblerDex : public Disassembler { + public: + DisassemblerDex(); + ~DisassemblerDex() override; + + // Applies quick checks to determine if |image| *may* point to the start of an + // executable. Returns true on success. + static bool QuickDetect(ConstBufferView image); + + // Disassembler: + ExecutableType GetExeType() const override; + std::string GetExeTypeString() const override; + std::vector<ReferenceGroup> MakeReferenceGroups() const override; + + private: + friend Disassembler; + using MapItemMap = std::map<uint16_t, const dex::MapItem*>; + + // Disassembler: + bool Parse(ConstBufferView image) override; + + bool ParseHeader(); + + const dex::HeaderItem* header_ = nullptr; + int dex_version_ = 0; + MapItemMap map_item_map_ = {}; + dex::MapItem string_map_item_ = {}; + dex::MapItem type_map_item_ = {}; + dex::MapItem field_map_item_ = {}; + dex::MapItem method_map_item_ = {}; + dex::MapItem code_map_item_ = {}; + + // Sorted list of offsets of code items in |image_|. + std::vector<offset_t> code_item_offsets_; + + DISALLOW_COPY_AND_ASSIGN(DisassemblerDex); +}; + +} // namespace zucchini + +#endif // COMPONENTS_ZUCCHINI_DISASSEMBLER_DEX_H_ diff --git a/element_detection.cc b/element_detection.cc index d6bba5f..2fa3604 100644 --- a/element_detection.cc +++ b/element_detection.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "components/zucchini/disassembler.h" +#include "components/zucchini/disassembler_dex.h" #include "components/zucchini/disassembler_no_op.h" #include "components/zucchini/disassembler_win32.h" @@ -36,6 +37,12 @@ std::unique_ptr<Disassembler> MakeDisassemblerWithoutFallback( return disasm; } + if (DisassemblerDex::QuickDetect(image)) { + auto disasm = Disassembler::Make<DisassemblerDex>(image); + if (disasm && disasm->size() >= kMinProgramSize) + return disasm; + } + return nullptr; } @@ -46,6 +53,8 @@ std::unique_ptr<Disassembler> MakeDisassemblerOfType(ConstBufferView image, return Disassembler::Make<DisassemblerWin32X86>(image); case kExeTypeWin32X64: return Disassembler::Make<DisassemblerWin32X64>(image); + case kExeTypeDex: + return Disassembler::Make<DisassemblerDex>(image); case kExeTypeNoOp: return Disassembler::Make<DisassemblerNoOp>(image); default: diff --git a/ensemble_matcher.cc b/ensemble_matcher.cc index eebbae9..37a7af9 100644 --- a/ensemble_matcher.cc +++ b/ensemble_matcher.cc @@ -4,6 +4,7 @@ #include "components/zucchini/ensemble_matcher.h" +#include <algorithm> #include <limits> #include "base/logging.h" @@ -18,7 +19,21 @@ EnsembleMatcher::EnsembleMatcher() = default; EnsembleMatcher::~EnsembleMatcher() = default; void EnsembleMatcher::Trim() { - // TODO(huangs): Add MultiDex handling logic when we add DEX support. + // Trim rule: If > 1 DEX files are found then ignore all DEX. This is done + // because we do not yet support MultiDex, under which contents can move + // across file boundary between "old" and "new" archives. When this occurs, + // forcing matches of DEX files and patching them separately can result in + // larger patches than naive patching. + auto is_match_dex = [](const ElementMatch& match) { + return match.exe_type() == kExeTypeDex; + }; + auto num_dex = std::count_if(matches_.begin(), matches_.end(), is_match_dex); + if (num_dex > 1) { + LOG(WARNING) << "Found " << num_dex << " DEX: Ignoring all."; + matches_.erase( + std::remove_if(matches_.begin(), matches_.end(), is_match_dex), + matches_.end()); + } } } // namespace zucchini diff --git a/type_dex.h b/type_dex.h new file mode 100644 index 0000000..508d331 --- /dev/null +++ b/type_dex.h @@ -0,0 +1,261 @@ +// Copyright 2018 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_TYPE_DEX_H_ +#define COMPONENTS_ZUCCHINI_TYPE_DEX_H_ + +#include <stdint.h> + +namespace zucchini { +namespace dex { +// Contains types that models DEX executable format data structures. +// See https://source.android.com/devices/tech/dalvik/dex-format + +// The supported versions are 035 and 037. + +enum class FormatId : uint8_t { + b, // 22b. + c, // 21c, 22c, 31c, 35c, 3rc. + h, // 21h. + i, // 31i. + l, // 51l. + n, // 11n. + s, // 21s, 22s. + t, // 10t, 20t, 21t, 22t, 30t, 31t. + x, // 10x, 11x, 12x, 22x, 23x, 32x. +}; + +struct Instruction { + Instruction() = default; + constexpr Instruction(uint8_t opcode_in, + uint8_t layout_in, + FormatId format_in, + uint8_t variant_in = 1) + : opcode(opcode_in), + layout(layout_in), + format(format_in), + variant(variant_in) {} + + // The opcode that identifies the instruction. + uint8_t opcode; + // Number of uint16_t units for the instruction. + uint8_t layout; + // Identifier that groups similar instructions, as quick filter. + FormatId format; + // Number of successive opcodes that have the same format. + uint8_t variant = 1; +}; + +constexpr Instruction kByteCode[] = { + {0x00, 1, FormatId::x}, + {0x01, 1, FormatId::x}, + {0x02, 2, FormatId::x}, + {0x03, 3, FormatId::x}, + {0x04, 1, FormatId::x}, + {0x05, 2, FormatId::x}, + {0x06, 3, FormatId::x}, + {0x07, 1, FormatId::x}, + {0x08, 2, FormatId::x}, + {0x09, 3, FormatId::x}, + {0x0A, 1, FormatId::x}, + {0x0B, 1, FormatId::x}, + {0x0C, 1, FormatId::x}, + {0x0D, 1, FormatId::x}, + {0x0E, 1, FormatId::x}, + {0x0F, 1, FormatId::x}, + {0x10, 1, FormatId::x}, + {0x11, 1, FormatId::x}, + {0x12, 1, FormatId::n}, + {0x13, 2, FormatId::s}, + {0x14, 3, FormatId::i}, + {0x15, 2, FormatId::h}, + {0x16, 2, FormatId::s}, + {0x17, 3, FormatId::i}, + {0x18, 5, FormatId::l}, + {0x19, 2, FormatId::h}, + {0x1A, 2, FormatId::c}, + {0x1B, 3, FormatId::c}, + {0x1C, 2, FormatId::c}, + {0x1D, 1, FormatId::x}, + {0x1E, 1, FormatId::x}, + {0x1F, 2, FormatId::c}, + {0x20, 2, FormatId::c}, + {0x21, 1, FormatId::x}, + {0x22, 2, FormatId::c}, + {0x23, 2, FormatId::c}, + {0x24, 3, FormatId::c}, + {0x25, 3, FormatId::c}, + {0x26, 3, FormatId::t}, + {0x27, 1, FormatId::x}, + {0x28, 1, FormatId::t}, + {0x29, 2, FormatId::t}, + {0x2A, 3, FormatId::t}, + {0x2B, 3, FormatId::t}, + {0x2C, 3, FormatId::t}, + {0x2D, 2, FormatId::x, 5}, + {0x32, 2, FormatId::t, 6}, + {0x38, 2, FormatId::t, 6}, + // {0x3E, 1, FormatId::x, 6}, unused + {0x44, 2, FormatId::x, 14}, + {0x52, 2, FormatId::c, 14}, + {0x60, 2, FormatId::c, 14}, + {0x6E, 3, FormatId::c, 5}, + // {0x73, 1, FormatId::x}, unused + {0x74, 3, FormatId::c, 5}, + // {0x79, 1, FormatId::x, 2}, unused + {0x7B, 1, FormatId::x, 21}, + {0x90, 2, FormatId::x, 32}, + {0xB0, 1, FormatId::x, 32}, + {0xD0, 2, FormatId::s, 8}, + {0xD8, 2, FormatId::b, 11}, + // {0xE3, 1, FormatId::x, 29}, unused +}; + +// Supported by MSVC, g++, and clang++. Ensures no gaps in packing. +#pragma pack(push, 1) + +// header_item: Appears in the header section. +struct HeaderItem { + uint8_t magic[8]; + uint32_t checksum; + uint8_t signature[20]; + uint32_t file_size; + uint32_t header_size; + uint32_t endian_tag; + uint32_t link_size; + uint32_t link_off; + uint32_t map_off; + uint32_t string_ids_size; + uint32_t string_ids_off; + uint32_t type_ids_size; + uint32_t type_ids_off; + uint32_t proto_ids_size; + uint32_t proto_ids_off; + uint32_t field_ids_size; + uint32_t field_ids_off; + uint32_t method_ids_size; + uint32_t method_ids_off; + uint32_t class_defs_size; + uint32_t class_defs_off; + uint32_t data_size; + uint32_t data_off; +}; + +// string_id_item: String identifiers list. +struct StringIdItem { + uint32_t string_data_off; +}; + +// type_id_item: Type identifiers list. +struct TypeIdItem { + uint32_t descriptor_idx; +}; + +// proto_id_item: Method prototype identifiers list. +struct ProtoIdItem { + uint32_t shorty_idx; + uint32_t return_type_idx; + uint32_t parameters_off; +}; + +// field_id_item: Field identifiers list. +struct FieldIdItem { + uint16_t class_idx; + uint16_t type_idx; + uint32_t name_idx; +}; + +// method_id_item: Method identifiers list. +struct MethodIdItem { + uint16_t class_idx; + uint16_t proto_idx; + uint32_t name_idx; +}; + +// class_def_item: Class definitions list. +struct ClassDefItem { + uint32_t class_idx; + uint32_t access_flags; + uint32_t superclass_idx; + uint32_t interfaces_off; + uint32_t source_file_idx; + uint32_t annotations_off; + uint32_t class_data_off; + uint32_t static_values_off; +}; + +// code_item: Header of a code item. +struct CodeItem { + uint16_t registers_size; + uint16_t ins_size; + uint16_t outs_size; + uint16_t tries_size; + uint32_t debug_info_off; + uint32_t insns_size; + // Variable length data follow for complete code item. +}; + +constexpr uint32_t kMaxItemListSize = 18; + +// map_item +struct MapItem { + uint16_t type; + uint16_t unused; + uint32_t size; + uint32_t offset; +}; + +// map_list +struct MapList { + uint32_t size; + MapItem list[kMaxItemListSize]; +}; + +// type_item +struct TypeItem { + uint16_t type_idx; +}; + +// annotation_set_ref_item +struct AnnotationSetRefItem { + uint32_t annotations_off; +}; + +// annotation_off_item +struct AnnotationOffItem { + uint32_t annotation_off; +}; + +// try_item +struct TryItem { + uint32_t start_addr; + uint16_t insn_count; + uint16_t handler_off; +}; + +constexpr uint16_t kTypeHeaderItem = 0x0000; +constexpr uint16_t kTypeStringIdItem = 0x0001; +constexpr uint16_t kTypeTypeIdItem = 0x0002; +constexpr uint16_t kTypeProtoIdItem = 0x0003; +constexpr uint16_t kTypeFieldIdItem = 0x0004; +constexpr uint16_t kTypeMethodIdItem = 0x0005; +constexpr uint16_t kTypeClassDefItem = 0x0006; +constexpr uint16_t kTypeMapList = 0x1000; +constexpr uint16_t kTypeTypeList = 0x1001; +constexpr uint16_t kTypeAnnotationSetRefList = 0x1002; +constexpr uint16_t kTypeAnnotationSetItem = 0x1003; +constexpr uint16_t kTypeClassDataItem = 0x2000; +constexpr uint16_t kTypeCodeItem = 0x2001; +constexpr uint16_t kTypeStringDataItem = 0x2002; +constexpr uint16_t kTypeDebugInfoItem = 0x2003; +constexpr uint16_t kTypeAnnotationItem = 0x2004; +constexpr uint16_t kTypeEncodedArrayItem = 0x2005; +constexpr uint16_t kTypeAnnotationsDirectoryItem = 0x2006; + +#pragma pack(pop) + +} // namespace dex +} // namespace zucchini + +#endif // COMPONENTS_ZUCCHINI_TYPE_DEX_H_ |