diff options
-rw-r--r-- | disassembler_dex.cc | 230 | ||||
-rw-r--r-- | disassembler_dex.h | 37 | ||||
-rw-r--r-- | type_dex.h | 30 |
3 files changed, 266 insertions, 31 deletions
diff --git a/disassembler_dex.cc b/disassembler_dex.cc index 2fa11f8..a7316ba 100644 --- a/disassembler_dex.cc +++ b/disassembler_dex.cc @@ -463,7 +463,7 @@ class ItemReferenceReader : public ReferenceReader { // NTTT|NTT|NTTTT|N|NTT... // where |N| is an uint32_t representing the number of items in each sub-list, // and "T" is a fixed-size item (|item_width|) of type "T". On success, stores -// the offset of each |T| into |reference_list|, and returns true. Otherwise +// the offset of each |T| into |item_offsets|, and returns true. Otherwise // (e.g., on finding any structural problem) returns false. bool ParseItemOffsets(ConstBufferView image, const dex::MapItem& map_item, @@ -493,13 +493,86 @@ bool ParseItemOffsets(ConstBufferView image, return true; } +// Parses AnnotationDirectoryItems of the format (using RegEx) "(AF*M*P*)*", +// where: +// A = AnnotationsDirectoryItem (contains class annotation), +// F = FieldAnnotation, +// M = MethodAnnotation, +// P = ParameterAnnotation. +// On success, stores the offsets of each class, field, method and parameter +// annotation for each item into |*_annotation_offsets|. Otherwise on finding +// structural issues returns false. +bool ParseAnnotationsDirectoryItems( + ConstBufferView image, + const dex::MapItem& annotations_directory_map_item, + std::vector<offset_t>* annotations_directory_item_offsets, + std::vector<offset_t>* field_annotation_offsets, + std::vector<offset_t>* method_annotation_offsets, + std::vector<offset_t>* parameter_annotation_offsets) { + // Sanity check: |image| should at least fit + // |annotations_directory_map_item.size| copies of "A". + if (!image.covers_array(annotations_directory_map_item.offset, + annotations_directory_map_item.size, + sizeof(dex::AnnotationsDirectoryItem))) { + return false; + } + BufferSource source = std::move( + BufferSource(image).Skip(annotations_directory_map_item.offset)); + annotations_directory_item_offsets->clear(); + field_annotation_offsets->clear(); + method_annotation_offsets->clear(); + parameter_annotation_offsets->clear(); + + // Helper to process sublists. + auto parse_list = [&source, image](uint32_t unsafe_size, size_t item_width, + std::vector<offset_t>* item_offsets) { + DCHECK(Is32BitAligned( + base::checked_cast<offset_t>(source.begin() - image.begin()))); + if (!source.covers_array(0, unsafe_size, item_width)) + return false; + item_offsets->reserve(item_offsets->size() + unsafe_size); + for (uint32_t i = 0; i < unsafe_size; ++i) { + item_offsets->push_back( + base::checked_cast<offset_t>(source.begin() - image.begin())); + source.Skip(item_width); + } + return true; + }; + + annotations_directory_item_offsets->reserve( + annotations_directory_map_item.size); + for (uint32_t i = 0; i < annotations_directory_map_item.size; ++i) { + if (!source.AlignOn(image, 4U)) + return false; + // Parse header. + annotations_directory_item_offsets->push_back( + base::checked_cast<offset_t>(source.begin() - image.begin())); + dex::AnnotationsDirectoryItem unsafe_annotations_directory_item; + if (!source.GetValue(&unsafe_annotations_directory_item)) + return false; + // Parse sublists. + if (!(parse_list(unsafe_annotations_directory_item.fields_size, + sizeof(dex::FieldAnnotation), field_annotation_offsets) && + parse_list(unsafe_annotations_directory_item.annotated_methods_size, + sizeof(dex::MethodAnnotation), + method_annotation_offsets) && + parse_list( + unsafe_annotations_directory_item.annotated_parameters_size, + sizeof(dex::ParameterAnnotation), + parameter_annotation_offsets))) { + return false; + } + } + return true; +} + /******** CachedItemListReferenceReader ********/ // A class that takes sorted |item_offsets|, and emits all member variable of // interest (MVIs) that fall inside |[lo, hi)|. The MVI of each item has // location of |rel_location| from item offset, and has target extracted with -// |mapper| (which performs validation). By the "atomicity assumption", [|lo, -// hi)| never cut across an MVI. +// |mapper| (which performs validation). By the "atomicity assumption", +// [|lo, hi)| never cut across an MVI. class CachedItemListReferenceReader : public ReferenceReader { public: // A function that takes an MVI's location and emit its target offset. @@ -515,7 +588,9 @@ class CachedItemListReferenceReader : public ReferenceReader { end_it_(item_offsets.cend()), mapper_(mapper) { cur_it_ = std::upper_bound(item_offsets.cbegin(), item_offsets.cend(), lo); - if (cur_it_ != item_offsets.begin() && *(cur_it_ - 1) >= lo) + // Adding |rel_location_| is necessary as references can be offset from the + // start of the item. + if (cur_it_ != item_offsets.begin() && *(cur_it_ - 1) + rel_location_ >= lo) --cur_it_; } @@ -752,19 +827,46 @@ std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const { {{2, TypeTag(kCodeToFieldId), PoolTag(kFieldId)}, &DisassemblerDex::MakeReadCodeToFieldId16, &DisassemblerDex::MakeWriteFieldId16}, + {{4, TypeTag(kAnnotationsDirectoryToFieldId), PoolTag(kFieldId)}, + &DisassemblerDex::MakeReadAnnotationsDirectoryToFieldId32, + &DisassemblerDex::MakeWriteFieldId32}, {{2, TypeTag(kCodeToMethodId), PoolTag(kMethodId)}, &DisassemblerDex::MakeReadCodeToMethodId16, &DisassemblerDex::MakeWriteMethodId16}, + {{4, TypeTag(kAnnotationsDirectoryToMethodId), PoolTag(kMethodId)}, + &DisassemblerDex::MakeReadAnnotationsDirectoryToMethodId32, + &DisassemblerDex::MakeWriteMethodId32}, + {{4, TypeTag(kAnnotationsDirectoryToParameterMethodId), + PoolTag(kMethodId)}, + &DisassemblerDex::MakeReadAnnotationsDirectoryToParameterMethodId32, + &DisassemblerDex::MakeWriteMethodId32}, {{4, TypeTag(kProtoIdToParametersTypeList), PoolTag(kTypeList)}, &DisassemblerDex::MakeReadProtoIdToParametersTypeList, &DisassemblerDex::MakeWriteAbs32}, {{4, TypeTag(kClassDefToInterfacesTypeList), PoolTag(kTypeList)}, &DisassemblerDex::MakeReadClassDefToInterfacesTypeList, &DisassemblerDex::MakeWriteAbs32}, + {{4, TypeTag(kAnnotationsDirectoryToParameterAnnotationSetRef), + PoolTag(kAnnotationSetRefList)}, + &DisassemblerDex:: + MakeReadAnnotationsDirectoryToParameterAnnotationSetRef, + &DisassemblerDex::MakeWriteAbs32}, {{4, TypeTag(kAnnotationSetRefListToAnnotationSet), PoolTag(kAnnotionSet)}, &DisassemblerDex::MakeReadAnnotationSetRefListToAnnotationSet, &DisassemblerDex::MakeWriteAbs32}, + {{4, TypeTag(kAnnotationsDirectoryToClassAnnotationSet), + PoolTag(kAnnotionSet)}, + &DisassemblerDex::MakeReadAnnotationsDirectoryToClassAnnotationSet, + &DisassemblerDex::MakeWriteAbs32}, + {{4, TypeTag(kAnnotationsDirectoryToFieldAnnotationSet), + PoolTag(kAnnotionSet)}, + &DisassemblerDex::MakeReadAnnotationsDirectoryToFieldAnnotationSet, + &DisassemblerDex::MakeWriteAbs32}, + {{4, TypeTag(kAnnotationsDirectoryToMethodAnnotationSet), + PoolTag(kAnnotionSet)}, + &DisassemblerDex::MakeReadAnnotationsDirectoryToMethodAnnotationSet, + &DisassemblerDex::MakeWriteAbs32}, {{4, TypeTag(kClassDefToClassData), PoolTag(kClassData)}, &DisassemblerDex::MakeReadClassDefToClassData, &DisassemblerDex::MakeWriteAbs32}, @@ -1010,6 +1112,84 @@ DisassemblerDex::MakeReadAnnotationSetRefListToAnnotationSet(offset_t lo, annotation_set_ref_list_offsets_, std::move(mapper)); } +std::unique_ptr<ReferenceReader> +DisassemblerDex::MakeReadAnnotationsDirectoryToClassAnnotationSet(offset_t lo, + offset_t hi) { + // dex::AnnotationsDirectoryItem::class_annotations_off mapper. + auto mapper = base::BindRepeating(ReadTargetOffset32, image_); + return std::make_unique<CachedItemListReferenceReader>( + lo, hi, offsetof(dex::AnnotationsDirectoryItem, class_annotations_off), + annotations_directory_item_offsets_, std::move(mapper)); +} + +std::unique_ptr<ReferenceReader> +DisassemblerDex::MakeReadAnnotationsDirectoryToFieldId32(offset_t lo, + offset_t hi) { + auto mapper = base::BindRepeating( + ReadTargetIndex<decltype(dex::FieldAnnotation::field_idx)>, image_, + field_map_item_, sizeof(dex::FieldIdItem)); + return std::make_unique<CachedItemListReferenceReader>( + lo, hi, offsetof(dex::FieldAnnotation, field_idx), + annotations_directory_item_field_annotation_offsets_, std::move(mapper)); +} + +std::unique_ptr<ReferenceReader> +DisassemblerDex::MakeReadAnnotationsDirectoryToFieldAnnotationSet(offset_t lo, + offset_t hi) { + // dex::FieldAnnotation::annotations_off mapper. + auto mapper = base::BindRepeating(ReadTargetOffset32, image_); + return std::make_unique<CachedItemListReferenceReader>( + lo, hi, offsetof(dex::FieldAnnotation, annotations_off), + annotations_directory_item_field_annotation_offsets_, std::move(mapper)); +} + +std::unique_ptr<ReferenceReader> +DisassemblerDex::MakeReadAnnotationsDirectoryToMethodId32(offset_t lo, + offset_t hi) { + auto mapper = base::BindRepeating( + ReadTargetIndex<decltype(dex::MethodAnnotation::method_idx)>, image_, + method_map_item_, sizeof(dex::MethodIdItem)); + return std::make_unique<CachedItemListReferenceReader>( + lo, hi, offsetof(dex::MethodAnnotation, method_idx), + annotations_directory_item_method_annotation_offsets_, std::move(mapper)); +} + +std::unique_ptr<ReferenceReader> +DisassemblerDex::MakeReadAnnotationsDirectoryToMethodAnnotationSet( + offset_t lo, + offset_t hi) { + // dex::MethodAnnotation::annotations_off mapper. + auto mapper = base::BindRepeating(ReadTargetOffset32, image_); + return std::make_unique<CachedItemListReferenceReader>( + lo, hi, offsetof(dex::MethodAnnotation, annotations_off), + annotations_directory_item_method_annotation_offsets_, std::move(mapper)); +} + +std::unique_ptr<ReferenceReader> +DisassemblerDex::MakeReadAnnotationsDirectoryToParameterMethodId32( + offset_t lo, + offset_t hi) { + auto mapper = base::BindRepeating( + ReadTargetIndex<decltype(dex::ParameterAnnotation::method_idx)>, image_, + method_map_item_, sizeof(dex::MethodIdItem)); + return std::make_unique<CachedItemListReferenceReader>( + lo, hi, offsetof(dex::ParameterAnnotation, method_idx), + annotations_directory_item_parameter_annotation_offsets_, + std::move(mapper)); +} + +std::unique_ptr<ReferenceReader> +DisassemblerDex::MakeReadAnnotationsDirectoryToParameterAnnotationSetRef( + offset_t lo, + offset_t hi) { + // dex::ParameterAnnotation::annotations_off mapper. + auto mapper = base::BindRepeating(ReadTargetOffset32, image_); + return std::make_unique<CachedItemListReferenceReader>( + lo, hi, offsetof(dex::ParameterAnnotation, annotations_off), + annotations_directory_item_parameter_annotation_offsets_, + std::move(mapper)); +} + std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToStringId16( offset_t lo, offset_t hi) { @@ -1376,29 +1556,31 @@ bool DisassemblerDex::ParseHeader() { } if (map_item_map_.count(dex::kTypeAnnotationSetItem)) annotation_set_map_item_ = *map_item_map_[dex::kTypeAnnotationSetItem]; - - // Iteratively extract variable length lists. Any failure would indicate - // invalid DEX. Success indicates that no structural problem is found. - // However, contained references data read from parsed items still require - // validation. - if (!ParseItemOffsets(image_, type_list_map_item_, sizeof(dex::TypeItem), - &type_list_offsets_)) { - return false; + if (map_item_map_.count(dex::kTypeAnnotationsDirectoryItem)) { + annotations_directory_map_item_ = + *map_item_map_[dex::kTypeAnnotationsDirectoryItem]; } - if (!ParseItemOffsets(image_, annotation_set_ref_list_map_item_, - sizeof(dex::AnnotationSetRefItem), - &annotation_set_ref_list_offsets_)) { - return false; - } - if (!ParseItemOffsets(image_, annotation_set_map_item_, - sizeof(dex::AnnotationOffItem), - &annotation_set_offsets_)) { + + // Iteratively parse variable length lists, annotations directory items, and + // code items blocks. Any failure would indicate invalid DEX. Success + // indicates that no structural problem is found. However, contained + // references data read from parsed items still require validation. + if (!(ParseItemOffsets(image_, type_list_map_item_, sizeof(dex::TypeItem), + &type_list_offsets_) && + ParseItemOffsets(image_, annotation_set_ref_list_map_item_, + sizeof(dex::AnnotationSetRefItem), + &annotation_set_ref_list_offsets_) && + ParseItemOffsets(image_, annotation_set_map_item_, + sizeof(dex::AnnotationOffItem), + &annotation_set_offsets_) && + ParseAnnotationsDirectoryItems( + image_, annotations_directory_map_item_, + &annotations_directory_item_offsets_, + &annotations_directory_item_field_annotation_offsets_, + &annotations_directory_item_method_annotation_offsets_, + &annotations_directory_item_parameter_annotation_offsets_))) { return false; } - - // 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; diff --git a/disassembler_dex.h b/disassembler_dex.h index 8c6f691..ecc4be6 100644 --- a/disassembler_dex.h +++ b/disassembler_dex.h @@ -73,21 +73,21 @@ class DisassemblerDex : public Disassembler { kMethodIdToProtoId, // kProtoId kCodeToFieldId, // kFieldId - // kAnnotationsDirectoryToFieldId, + kAnnotationsDirectoryToFieldId, kCodeToMethodId, // kMethodId - // kAnnotationsDirectoryToMethodId, - // kAnnotationsDirectoryToParameterMethodId, + kAnnotationsDirectoryToMethodId, + kAnnotationsDirectoryToParameterMethodId, kProtoIdToParametersTypeList, // kTypeList kClassDefToInterfacesTypeList, - // kAnnotationsDirectoryToParameterAnnotationSetRef, // kAnnotationSetRef, + kAnnotationsDirectoryToParameterAnnotationSetRef, // kAnnotationSetRef, kAnnotationSetRefListToAnnotationSet, // kAnnotationSet, - // kAnnotationsDirectoryToClassAnnotationSet, - // kAnnotationsDirectoryToFieldAnnotationSet, - // kAnnotationsDirectoryToMethodAnnotationSet, + kAnnotationsDirectoryToClassAnnotationSet, + kAnnotationsDirectoryToFieldAnnotationSet, + kAnnotationsDirectoryToMethodAnnotationSet, kClassDefToClassData, // kClassData @@ -179,6 +179,23 @@ class DisassemblerDex : public Disassembler { std::unique_ptr<ReferenceReader> MakeReadAnnotationSetRefListToAnnotationSet( offset_t lo, offset_t hi); + std::unique_ptr<ReferenceReader> + MakeReadAnnotationsDirectoryToClassAnnotationSet(offset_t lo, offset_t hi); + std::unique_ptr<ReferenceReader> MakeReadAnnotationsDirectoryToFieldId32( + offset_t lo, + offset_t hi); + std::unique_ptr<ReferenceReader> + MakeReadAnnotationsDirectoryToFieldAnnotationSet(offset_t lo, offset_t hi); + std::unique_ptr<ReferenceReader> MakeReadAnnotationsDirectoryToMethodId32( + offset_t lo, + offset_t hi); + std::unique_ptr<ReferenceReader> + MakeReadAnnotationsDirectoryToMethodAnnotationSet(offset_t lo, offset_t hi); + std::unique_ptr<ReferenceReader> + MakeReadAnnotationsDirectoryToParameterMethodId32(offset_t lo, offset_t hi); + std::unique_ptr<ReferenceReader> + MakeReadAnnotationsDirectoryToParameterAnnotationSetRef(offset_t lo, + offset_t hi); std::unique_ptr<ReferenceReader> MakeReadCodeToStringId16(offset_t lo, offset_t hi); std::unique_ptr<ReferenceReader> MakeReadCodeToStringId32(offset_t lo, @@ -236,12 +253,18 @@ class DisassemblerDex : public Disassembler { // Optionally supported (not all DEX files have these). dex::MapItem annotation_set_ref_list_map_item_ = {}; dex::MapItem annotation_set_map_item_ = {}; + dex::MapItem annotations_directory_map_item_ = {}; // Sorted list of offsets of parsed items in |image_|. std::vector<offset_t> code_item_offsets_; std::vector<offset_t> type_list_offsets_; std::vector<offset_t> annotation_set_ref_list_offsets_; std::vector<offset_t> annotation_set_offsets_; + std::vector<offset_t> annotations_directory_item_offsets_; + std::vector<offset_t> annotations_directory_item_field_annotation_offsets_; + std::vector<offset_t> annotations_directory_item_method_annotation_offsets_; + std::vector<offset_t> + annotations_directory_item_parameter_annotation_offsets_; DISALLOW_COPY_AND_ASSIGN(DisassemblerDex); }; @@ -227,6 +227,36 @@ struct AnnotationOffItem { uint32_t annotation_off; }; +// field_annotation +struct FieldAnnotation { + uint32_t field_idx; + uint32_t annotations_off; +}; + +// method_annotation +struct MethodAnnotation { + uint32_t method_idx; + uint32_t annotations_off; +}; + +// parameter_annotation +struct ParameterAnnotation { + uint32_t method_idx; + uint32_t annotations_off; +}; + +// annotations_directory_item +struct AnnotationsDirectoryItem { + uint32_t class_annotations_off; + uint32_t fields_size; + uint32_t annotated_methods_size; + uint32_t annotated_parameters_size; + // FieldAnnotation field_annotations[fields_size]; + // MethodAnnotation method_annotations[annotated_methods_size]; + // ParameterAnnotation parameter_annotations[annotated_parameters_size]; + // All *Annotation are 8 bytes each. +}; + // try_item struct TryItem { uint32_t start_addr; |