| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package dex.reader; |
| |
| import dex.structure.DexFile; |
| |
| import java.util.Arrays; |
| |
| public final class DexFileReader { |
| |
| // DEX constants |
| private int ENDIAN_CONSTANT = 0x12345678; |
| @SuppressWarnings("unused") |
| private int REVERSE_ENDIAN_CONSTANT = 0x78563412; |
| private final byte[] REF_MAGIC = new byte[] { |
| 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00}; |
| |
| // Header values |
| private DexBuffer b; |
| private byte[] magic = new byte[8]; |
| @SuppressWarnings("unused") |
| private int checksum = 0; |
| private byte[] signature = new byte[20]; |
| @SuppressWarnings("unused") |
| private int fileSize = 0; |
| @SuppressWarnings("unused") |
| private int headerSize = 0; |
| private int endianTag = 0; |
| |
| // Indices of offset and size items |
| private static final int LINK = 0; |
| private static final int MAP = 1; // no size! |
| private static final int STRING_IDS = 2; |
| private static final int TYPE_IDS = 3; |
| private static final int PROTO_IDS = 4; |
| private static final int FIELD_IDS = 5; |
| private static final int METHOD_IDS = 6; |
| private static final int CLASS_DEFS = 7; |
| private static final int DATA = 8; |
| |
| private int[] size = new int[9]; |
| private int[] off = new int[9]; |
| |
| |
| // |
| private String[] stringPool; |
| private int[] typeIds; // values are index of stringPool |
| private ProtIdItem[] protoIdItems; |
| private FieldIdItem[] fieldIdItems; |
| private MethodsIdItem[] methodIdItems; |
| private ClassDefItem[] classDefItems; |
| |
| // starting buffer at zero |
| public DexFile read(DexBuffer buffer) { |
| this.b = buffer; |
| readMagic(); |
| readChecksum(); |
| readSignature(); |
| readFileSize(); |
| readHeaderSize(); |
| readEndianTag(); |
| readSize(LINK); |
| readOffset(LINK); |
| readOffset(MAP); |
| readSize(STRING_IDS); |
| readOffset(STRING_IDS); |
| readSize(TYPE_IDS); |
| readOffset(TYPE_IDS); |
| readSize(PROTO_IDS); |
| readOffset(PROTO_IDS); |
| readSize(FIELD_IDS); |
| readOffset(FIELD_IDS); |
| readSize(METHOD_IDS); |
| readOffset(METHOD_IDS); |
| readSize(CLASS_DEFS); |
| readOffset(CLASS_DEFS); |
| readSize(DATA); |
| readOffset(DATA); |
| // from now on, index is not automatically on the desired position |
| readStrings(); |
| readTypeIds(); |
| readProtos(); |
| readFields(); |
| readMethods(); |
| readClasses(); |
| |
| return new DexFileImpl(b.createCopy(), stringPool, typeIds, |
| protoIdItems, fieldIdItems, methodIdItems, classDefItems); |
| } |
| |
| // MAGIC (8, U_BYTE) |
| // "dex\n035\0" |
| private void readMagic() { |
| b.readBytes(magic); |
| assert Arrays.equals(magic, REF_MAGIC) : "Not a DEX file"; |
| } |
| |
| // CHECKSUM (1, U_INT) |
| private void readChecksum() { |
| checksum = b.readUInt(); |
| } |
| |
| // SIGNATURE (20, U_BYTE) |
| private void readSignature() { |
| b.readBytes(signature); |
| } |
| |
| // FILE_SIZE (1, U_INT) |
| private void readFileSize() { |
| fileSize = b.readUInt(); |
| } |
| |
| // HEADER_SIZE (1, U_INT), //0x70 |
| private void readHeaderSize() { |
| headerSize = b.readUInt(); |
| } |
| |
| // ENDIAN_TAG (1, U_INT), //ENDIAN_CONSTANT |
| private void readEndianTag() { |
| endianTag = b.readUInt(); |
| // FIXME Support for big endian encoded dex files |
| assert endianTag == ENDIAN_CONSTANT : "Byteorder NOT in little endian"; |
| } |
| |
| private void readSize(int attribute) { |
| size[attribute] = b.readUInt(); |
| } |
| |
| private void readOffset(int attribute) { |
| off[attribute] = b.readUInt(); |
| } |
| |
| // reads the string pool |
| private void readStrings() { |
| int nStrings = size[STRING_IDS]; |
| b.setPosition(off[STRING_IDS]); // the first string offset is here |
| int[] stringDataOffsets = new int[nStrings]; |
| |
| for (int i = 0; i < stringDataOffsets.length; i++) { |
| stringDataOffsets[i] = b.readUInt(); |
| } |
| |
| stringPool = new String[nStrings]; |
| for (int i = 0; i < stringDataOffsets.length; i++) { |
| b.setPosition(stringDataOffsets[i]); // set buffer to offset |
| // Position |
| int lenght = b.readUleb128(); // read uleb128 |
| byte[] values = new byte[lenght]; |
| b.readBytes(values); |
| stringPool[i] = new String(values); |
| } |
| } |
| |
| private void readTypeIds() { |
| int nTypes = size[TYPE_IDS]; |
| b.setPosition(off[TYPE_IDS]); // the first element is here |
| typeIds = new int[nTypes]; |
| |
| for (int i = 0; i < typeIds.length; i++) { |
| typeIds[i] = b.readUInt(); |
| } |
| } |
| |
| static class ProtIdItem { |
| public int shorty_idx; |
| public int return_type_idx; |
| public int parameter_off; |
| } |
| |
| private void readProtos() { |
| int nProtos = size[PROTO_IDS]; |
| b.setPosition(off[PROTO_IDS]); |
| protoIdItems = new ProtIdItem[nProtos]; |
| |
| ProtIdItem item = null; |
| for (int i = 0; i < protoIdItems.length; i++) { |
| item = new ProtIdItem(); |
| item.shorty_idx = b.readUInt(); |
| item.return_type_idx = b.readUInt(); |
| item.parameter_off = b.readUInt(); |
| protoIdItems[i] = item; |
| } |
| } |
| |
| static class FieldIdItem { |
| public int class_idx; // defining class : index of type_ids |
| public int type_idx; // type of field : index of type_ids |
| public int name_idx; // name of field : index into string id (or |
| // directly stringpool) |
| } |
| |
| private void readFields() { |
| int nFields = size[FIELD_IDS]; |
| b.setPosition(off[FIELD_IDS]); |
| fieldIdItems = new FieldIdItem[nFields]; |
| |
| FieldIdItem item = null; |
| for (int i = 0; i < fieldIdItems.length; i++) { |
| item = new FieldIdItem(); |
| item.class_idx = b.readUShort(); |
| item.type_idx = b.readUShort(); |
| item.name_idx = b.readUInt(); |
| fieldIdItems[i] = item; |
| } |
| } |
| |
| static class MethodsIdItem { |
| public int class_idx; // defining class : index of typeIds |
| public int proto_idx; // proto of method : index of protoIdItems |
| public int name_idx; // name of method : index into string id (or |
| // directly stringpool) |
| } |
| |
| private void readMethods() { |
| int nMethods = size[METHOD_IDS]; |
| b.setPosition(off[METHOD_IDS]); |
| methodIdItems = new MethodsIdItem[nMethods]; |
| |
| MethodsIdItem item = null; |
| for (int i = 0; i < methodIdItems.length; i++) { |
| item = new MethodsIdItem(); |
| item.class_idx = b.readUShort(); |
| item.proto_idx = b.readUShort(); |
| item.name_idx = b.readUInt(); |
| methodIdItems[i] = item; |
| } |
| } |
| |
| public static class ClassDefItem { |
| public int class_idx; |
| public int access_flags; |
| public int superclass_idx; |
| public int interfaces_off; |
| public int source_file_idx; |
| public int annotations_off; |
| public int class_data_off; |
| public int static_values_off; |
| } |
| |
| private void readClasses() { |
| int nClassDefs = size[CLASS_DEFS]; |
| b.setPosition(off[CLASS_DEFS]); |
| classDefItems = new ClassDefItem[nClassDefs]; |
| |
| ClassDefItem item = null; |
| for (int i = 0; i < classDefItems.length; i++) { |
| item = new ClassDefItem(); |
| item.class_idx = b.readUInt(); |
| item.access_flags = b.readUInt(); |
| item.superclass_idx = b.readUInt(); |
| item.interfaces_off = b.readUInt(); |
| item.source_file_idx = b.readUInt(); |
| item.annotations_off = b.readUInt(); |
| item.class_data_off = b.readUInt(); |
| item.static_values_off = b.readUInt(); |
| classDefItems[i] = item; |
| } |
| } |
| } |