using dex::CallSiteIdItem; using dex::ClassDef; using dex::FieldId; using dex::MapList; using dex::MapItem; using dex::MethodHandleItem; using dex::MethodId; using dex::ProtoId; using dex::StringId; using dex::TryItem; using dex::TypeId; using dex::TypeList;
static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong");
static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial");
static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial");
DexFile::DexFile(const uint8_t* base, const std::string& location,
uint32_t location_checksum, const OatDexFile* oat_dex_file,
std::shared_ptr<DexFileContainer> container)
: begin_(base),
data_(GetDataRange(base, container.get())),
location_(location),
location_checksum_(location_checksum),
header_(reinterpret_cast<const Header*>(base)),
string_ids_(GetSection<StringId>(&header_->string_ids_off_, container.get())),
type_ids_(GetSection<TypeId>(&header_->type_ids_off_, container.get())),
field_ids_(GetSection<FieldId>(&header_->field_ids_off_, container.get())),
method_ids_(GetSection<MethodId>(&header_->method_ids_off_, container.get())),
proto_ids_(GetSection<ProtoId>(&header_->proto_ids_off_, container.get())),
class_defs_(GetSection<ClassDef>(&header_->class_defs_off_, container.get())),
method_handles_(nullptr),
num_method_handles_(0),
call_site_ids_(nullptr),
num_call_site_ids_(0),
hiddenapi_class_data_(nullptr),
oat_dex_file_(oat_dex_file),
container_(std::move(container)),
hiddenapi_domain_(hiddenapi::Domain::kApplication) {
CHECK(begin_ != nullptr) << GetLocation(); // Check base (=header) alignment. // Must be 4-byte aligned to avoid undefined behavior when accessing // any of the sections via a pointer.
CHECK_ALIGNED(begin_, alignof(Header));
if (DataSize() < sizeof(Header)) { // Don't go further if the data doesn't even contain a header. return;
}
InitializeSectionsFromMapList();
}
DexFile::~DexFile() { // We don't call DeleteGlobalRef on dex_object_ because we're only called by DestroyJavaVM, and // that's only called after DetachCurrentThread, which means there's no JNIEnv. We could // re-attach, but cleaning up these global references is not obviously useful. It's not as if // the global reference table is otherwise empty!
}
bool DexFile::Init(std::string* error_msg) {
CHECK_GE(container_->End(), reinterpret_cast<const uint8_t*>(header_));
size_t container_size = container_->End() - reinterpret_cast<const uint8_t*>(header_); if (container_size < sizeof(Header)) {
*error_msg = StringPrintf("Unable to open '%s' : File size is too small to fit dex header",
location_.c_str()); returnfalse;
} if (!CheckMagicAndVersion(error_msg)) { returnfalse;
}
uint32_t expected_header_size = header_->GetExpectedHeaderSize(); if (header_->header_size_ != expected_header_size) {
*error_msg = StringPrintf("Unable to open '%s' : Header size is %u but %u was expected",
location_.c_str(),
header_->header_size_,
expected_header_size); returnfalse;
} if (container_size < header_->file_size_) {
*error_msg = StringPrintf("Unable to open '%s' : File size is %zu but the header expects %u",
location_.c_str(),
container_size,
header_->file_size_); returnfalse;
} returntrue;
}
ArrayRef<const uint8_t> DexFile::GetDataRange(const uint8_t* data, DexFileContainer* container) { // NB: This function must survive random data to pass fuzzing and testing.
CHECK(container != nullptr);
CHECK_GE(data, container->Begin());
CHECK_LE(data, container->End());
size_t size = container->End() - data; if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(data)) { auto header = reinterpret_cast<const DexFile::Header*>(data); if (size >= sizeof(HeaderV41) && header->header_size_ >= sizeof(HeaderV41)) { auto headerV41 = reinterpret_cast<const DexFile::HeaderV41*>(data);
data -= headerV41->header_offset_; // Allow underflow and later overflow.
size = headerV41->container_size_;
} else {
size = header->file_size_;
}
} // The returned range is guaranteed to be in bounds of the container memory. return {data, std::min<size_t>(size, container->End() - data)};
}
void DexFile::InitializeSectionsFromMapList() { // NB: This function must survive random data to pass fuzzing and testing.
static_assert(sizeof(MapList) <= sizeof(Header));
DCHECK_GE(DataSize(), sizeof(MapList)); if (header_->map_off_ == 0 || header_->map_off_ > DataSize() - sizeof(MapList)) { // Bad offset. The dex file verifier runs after this method and will reject the file. return;
} const uint8_t* map_list_raw = DataBegin() + header_->map_off_; if (map_list_raw < Begin()) { return;
} const MapList* map_list = reinterpret_cast<const MapList*>(map_list_raw); const size_t count = map_list->size_;
size_t map_limit =
(DataSize() - OFFSETOF_MEMBER(MapList, list_) - header_->map_off_) / sizeof(MapItem); if (count > map_limit) { // Too many items. The dex file verifier runs after // this method and will reject the file as it is malformed. return;
}
// Construct pointers to certain arrays without any checks. If they are outside the // data, the dex file verification should fail and these pointers should not be used. for (size_t i = 0; i < count; ++i) { const MapItem& map_item = map_list->list_[i]; if (map_item.type_ == kDexTypeMethodHandleItem) {
method_handles_ = GetSection<MethodHandleItem>(&map_item.offset_, container_.get());
num_method_handles_ = map_item.size_;
} elseif (map_item.type_ == kDexTypeCallSiteIdItem) {
call_site_ids_ = GetSection<CallSiteIdItem>(&map_item.offset_, container_.get());
num_call_site_ids_ = map_item.size_;
} elseif (map_item.type_ == kDexTypeHiddenapiClassData) {
hiddenapi_class_data_ =
GetSection<dex::HiddenapiClassData>(&map_item.offset_, container_.get());
} else { // Pointers to other sections are not necessary to retain in the DexFile struct. // Other items have pointers directly into their data.
}
}
}
const ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx, const dex::TypeIndex* signature_type_idxs,
uint32_t signature_length) const {
int32_t lo = 0;
int32_t hi = NumProtoIds() - 1; while (hi >= lo) {
int32_t mid = (hi + lo) / 2; const dex::ProtoIndex proto_idx = static_cast<dex::ProtoIndex>(mid); const ProtoId& proto = GetProtoId(proto_idx); int compare = return_type_idx.index_ - proto.return_type_idx_.index_; if (compare == 0) {
DexFileParameterIterator it(*this, proto);
size_t i = 0; while (it.HasNext() && i < signature_length && compare == 0) {
compare = signature_type_idxs[i].index_ - it.GetTypeIdx().index_;
it.Next();
i++;
} if (compare == 0) { if (it.HasNext()) {
compare = -1;
} elseif (i < signature_length) {
compare = 1;
}
}
} if (compare > 0) {
lo = mid + 1;
} elseif (compare < 0) {
hi = mid - 1;
} else { return &proto;
}
} return nullptr;
}
// Given a signature place the type ids into the given vector bool DexFile::CreateTypeList(std::string_view signature,
dex::TypeIndex* return_type_idx,
std::vector<dex::TypeIndex>* param_type_idxs) const { if (signature[0] != '(') { returnfalse;
}
size_t offset = 1;
size_t end = signature.size(); bool process_return = false; while (offset < end) {
size_t start_offset = offset; char c = signature[offset];
offset++; if (c == ')') {
process_return = true; continue;
} while (c == '[') { // process array prefix if (offset >= end) { // expect some descriptor following [ returnfalse;
}
c = signature[offset];
offset++;
} if (c == 'L') { // process type descriptors do { if (offset >= end) { // unexpected early termination of descriptor returnfalse;
}
c = signature[offset];
offset++;
} while (c != ';');
}
std::string_view descriptor(signature.data() + start_offset, offset - start_offset); constTypeId* type_id = FindTypeId(descriptor); if (type_id == nullptr) { returnfalse;
}
dex::TypeIndex type_idx = GetIndexForTypeId(*type_id); if (!process_return) {
param_type_idxs->push_back(type_idx);
} else {
*return_type_idx = type_idx; return offset == end; // return true if the signature had reached a sensible end
}
} returnfalse; // failed to correctly parse return type
}
const TryItem& ti = try_items[mid]; const uint32_t start = ti.start_addr_; const uint32_t end = start + ti.insn_count_;
if (address < start) {
max = mid;
} elseif (address >= end) {
min = mid + 1;
} else { // We have a winner! return mid;
}
} // No match. return -1;
}
// Read a signed integer. "zwidth" is the zero-based byte count.
int32_t DexFile::ReadSignedInt(const uint8_t* ptr, int zwidth) {
int32_t val = 0; for (int i = zwidth; i >= 0; --i) {
val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24);
}
val >>= (3 - zwidth) * 8; return val;
}
// Read an unsigned integer. "zwidth" is the zero-based byte count, // "fill_on_right" indicates which side we want to zero-fill from.
uint32_t DexFile::ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) {
uint32_t val = 0; for (int i = zwidth; i >= 0; --i) {
val = (val >> 8) | (((uint32_t)*ptr++) << 24);
} if (!fill_on_right) {
val >>= (3 - zwidth) * 8;
} return val;
}
// Read a signed long. "zwidth" is the zero-based byte count.
int64_t DexFile::ReadSignedLong(const uint8_t* ptr, int zwidth) {
int64_t val = 0; for (int i = zwidth; i >= 0; --i) {
val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56);
}
val >>= (7 - zwidth) * 8; return val;
}
// Read an unsigned long. "zwidth" is the zero-based byte count, // "fill_on_right" indicates which side we want to zero-fill from.
uint64_t DexFile::ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) {
uint64_t val = 0; for (int i = zwidth; i >= 0; --i) {
val = (val >> 8) | (((uint64_t)*ptr++) << 56);
} if (!fill_on_right) {
val >>= (7 - zwidth) * 8;
} return val;
}
// Checks that visibility is as expected. Includes special behavior for M and // before to allow runtime and build visibility when expecting runtime.
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) {
os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]",
dex_file.GetLocation().c_str(),
dex_file.GetHeader().checksum_, dex_file.GetLocationChecksum(),
dex_file.Begin(), dex_file.Begin() + dex_file.Size()); return os;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.