/* Software Name : AsmDex * Version : 1.0 * * Copyright © 2012 France Télécom * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ package org.ow2.asmdex.lowLevelUtils; import java.io.IOException; import java.util.Arrays; /** * Reads the basic structure of a DEX file and builds a representation of this file. * * @author Pierre Crégut * @author Julien Névo (little modifications) */ public class BasicDexFileReader { /** * Constant representing the absence of an index in a table especially for super class. */ public final static int NO_INDEX = 0xFFFFFFFF; /** * The stream of code. */ protected DalvikValueReader reader; private byte[] magic = new byte[8]; private static final byte[] MAGIC_TEMPLATE = { 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 }; // Offset of the format version number in the sequence above. private static final int OFFSET_FORMAT_VERSION_NUMBER_IN_MAGIC_SEQUENCE = 4; private static final int LENGTH_FORMAT_VERSION_NUMBER_IN_MAGIC_SEQUENCE = 3; /** * Offset to the file size field */ protected final static int FILE_SIZE_OFFSET = 32; /** * Header size */ protected final static int HEADER_NOMINAL_SIZE = 0x70; /** * Standard endianess marker */ protected final static int STANDARD_ENDIAN_VALUE = 0x12345678; /** * Reverse endian marker */ protected final static int REVERSE_ENDIAN_VALUE = 0x78563412; /** * ADLER 32 checksum of the DEX file */ public long adler32Checksum; /** * Signature of the DEX file */ public byte[] signature = new byte[20]; /** * Standard or reverse endianess */ protected boolean isStandardEndian; /** * Format version number */ public byte[] formatVersionNumber = new byte[LENGTH_FORMAT_VERSION_NUMBER_IN_MAGIC_SEQUENCE]; // Set from the Magic array. /** * Size of the link table (but undefined otherwise) */ public int linkTableSize; /** * Size of string ids table */ protected int stringIdsSize; /** * Offset of string ids table */ protected int stringIdsOffset; /** * Size of type ids table */ protected int typeIdsSize; /** * Offset of type ids table */ protected int typeIdsOffset; /** * Size of prototypes ids table */ protected int protoIdsSize; /** * Offset of prototype ids table */ protected int protoIdsOffset; /** * Size of field ids table */ protected int fieldIdsSize; /** * Offset of field ids table */ protected int fieldIdsOffset; /** * Size of method ids table */ protected int methodIdsSize; /** * Offset of method ids table */ protected int methodIdsOffset; /** * Size of class definition table */ protected int classDefinitionsSize; // Class_defs_size in the documentation. /** * Offset of class definition table */ protected int classDefinitionsOffset; /** * The Strings Constant Pool. */ protected String[] constantPoolStrings; /** * The Types Constant Pool. */ protected String[] constantPoolTypes; /** * Constructor. */ public BasicDexFileReader() { } /** * Takes a byte array corresponding to a Dex file and populate the structure. * @param dexBytes the code * @throws IOException */ public void parse(byte[] dexBytes) throws IllegalArgumentException, IOException /*, RefNotFoundException */ { // Get a DalvikValueReader on the input stream. reader = new DalvikValueReader(dexBytes, FILE_SIZE_OFFSET); readHeader(); readStrings(); readTypes(); } /** * Reads the Header of the previously loaded Dex file. * @throws IllegalArgumentException */ protected void readHeader() throws IllegalArgumentException { // Checks the Magic sequence at offset 0. reader.bytes(magic); if (!Arrays.equals(magic, MAGIC_TEMPLATE)) { throw new IllegalArgumentException("Unknown bytecode version"); } // Copy the format version number from the Magic sequence. for (int i = 0; i < formatVersionNumber.length; i++) { formatVersionNumber[i] = magic[i + OFFSET_FORMAT_VERSION_NUMBER_IN_MAGIC_SEQUENCE]; } adler32Checksum = reader.uint(); // Reads the Adler32 checksum. reader.bytes(signature); // Reads the 20-byte signature. reader.skipInt(); // Skips the file size, not needed. // Compare the Header size with its nominal value (according to documentation). int header_size = reader.sint(); if (header_size != HEADER_NOMINAL_SIZE) { throw new IllegalArgumentException("Bad header size: " + Integer.toHexString(header_size)); } // Check the validity of the Endian value. int endianTag = reader.sint(); if (endianTag == STANDARD_ENDIAN_VALUE) { isStandardEndian = true; } else if (endianTag == REVERSE_ENDIAN_VALUE) { isStandardEndian = false; } else { throw new IllegalArgumentException("Unknown Endian Value."); } // Read the following sizes/offsets for various structures. linkTableSize = reader.sint(); reader.skipInt(); /* link_off = */ reader.skipInt(); /* map_off = */ stringIdsSize = reader.sint(); stringIdsOffset = reader.sint(); typeIdsSize = reader.sint(); typeIdsOffset = reader.sint(); protoIdsSize = reader.sint(); protoIdsOffset = reader.sint(); fieldIdsSize = reader.sint(); fieldIdsOffset = reader.sint(); methodIdsSize = reader.sint(); methodIdsOffset = reader.sint(); classDefinitionsSize = reader.sint(); classDefinitionsOffset = reader.sint(); reader.skipInt(); /* data_size = */ reader.skipInt(); /* data_off = */ } /** * Reads the string index table, and populates constantPoolStrings. * The table must have its offset set before (by readHeader). */ protected void readStrings() { reader.seek(stringIdsOffset); // Move to the table offset. int[] stringOffsets = new int[stringIdsSize]; // Gets the offset of each Strings. for (int i = 0; i < stringIdsSize; i++) { stringOffsets[i] = reader.sint(); } // Now gets each Strings and fills the ConstantPoolStrings. // This is done by parsing the "string_data_item" structure. // The size of the string is followed by the string itself. constantPoolStrings = new String[stringIdsSize]; for (int i = 0; i < stringIdsSize; i++) { reader.seek(stringOffsets[i]); int size = reader.uleb128(); String string = reader.utf8String(); constantPoolStrings[i] = string; // Checks the String length validity. if (string.length() != size) { StringBuilder b = new StringBuilder(); for (int k = stringOffsets[i]; k < reader.getPos(); k++) { b.append(" ").append(Integer.toHexString(reader.peek(k))); } System.out.println("Mismatched size for string " + string + " " + size + " / " + string.length() + b); } } } /** * Reads the types index table, and populates constantPoolTypes. * The table must have its offset set before (by readHeader), and readStrings must have * been called. */ protected void readTypes() { constantPoolTypes = new String[typeIdsSize]; for (int i = 0; i < typeIdsSize; i++) { reader.seek(getOffsetTypeIdItem(i)); int index = reader.uint(); constantPoolTypes[i] = constantPoolStrings[index]; } } // ------------------------------------ // Some utility methods // ------------------------------------ /** * Size of a class definition in the pool in bytes */ public final static int CLASS_DEF_ITEM_SIZE = 8 * 4; /** * Size of a field definition in the pool in bytes */ public final static int FIELD_ID_ITEM_SIZE = 8; /** * Size of a string decl in the pool in bytes */ public final static int STRING_ID_ITEM_SIZE = 4; /** * Size of a type declaration in the pool in bytes */ public final static int TYPE_ID_ITEM_SIZE = 4; /** * Size of a method declaration in the pool in bytes */ public final static int METHOD_ID_ITEM_SIZE = 8; /** * Size of a prototype definition in the pool in bytes */ public final static int PROTO_ID_ITEM_SIZE = 12; /** * Returns the offset (from the beginning of the file) of a Field Id Item * (field_id_item structure) whose index is given. * The Dex file header <i>must</i> have been parsed. * @param itemIndex index of the Field Id Item. * @return the offset of the Field Id Item. */ public int getOffsetFieldIdItem(int itemIndex) { return fieldIdsOffset + itemIndex * FIELD_ID_ITEM_SIZE; } /** * Returns the offset (from the beginning of the file) of a String Id Item * (string_id_item structure) whose index is given. * The Dex file header <i>must</i> have been parsed. * @param itemIndex index of the String Id Item. * @return the offset of the String Id Item. */ public int getOffsetStringIdItem(int itemIndex) { return stringIdsOffset + itemIndex * STRING_ID_ITEM_SIZE; } /** * Returns the offset (from the beginning of the file) of a Type Id Item * (type_id_item structure) whose index is given. * The Dex file header <i>must</i> have been parsed. * @param itemIndex index of the Type Id Item. * @return the offset of the Type Id Item. */ public int getOffsetTypeIdItem(int itemIndex) { return typeIdsOffset + itemIndex * TYPE_ID_ITEM_SIZE; } /** * Returns the offset (from the beginning of the file) of a Method Id Item * (method_id_item structure) whose index is given. * The Dex file header <i>must</i> have been parsed. * @param itemIndex index of the Method Id Item. * @return the offset of the Method Id Item. */ public int getOffsetMethodIdItem(int itemIndex) { return methodIdsOffset + itemIndex * METHOD_ID_ITEM_SIZE; } /** * Returns the offset (from the beginning of the file) of a Prototype Id Item * (proto_id_item structure) whose index is given. * The Dex file header <i>must</i> have been parsed. * @param itemIndex index of the Method Id Item. * @return the offset of the Method Id Item. */ public int getOffsetProtoIdItem(int itemIndex) { return protoIdsOffset + itemIndex * PROTO_ID_ITEM_SIZE; } /** * Returns the offset (from the beginning of the file) of a Class Id Item * (class_def_item structure) whose index is given. * The Dex file header <i>must</i> have been parsed. * @param itemIndex index of the Method Id Item. * @return the offset of the Method Id Item. */ public int getClassDefinitionOffset(int itemIndex) { return classDefinitionsOffset + itemIndex * CLASS_DEF_ITEM_SIZE; } // ------------------------------------ // Getters. // ------------------------------------ /** * Endianess of the file * @return true if standard endian */ public boolean isStandardEndian() { return isStandardEndian; } /** * Number of string ids in the pool * @return number */ public int getStringIdsSize() { return stringIdsSize; } /** * Position of string table * @return position */ public int getStringIdsOffset() { return stringIdsOffset; } /** * Number of type ids. * @return positive or null number */ public int getTypeIdsSize() { return typeIdsSize; } /** * Position of type id table * @return positive or null number */ public int getTypeIdsOffset() { return typeIdsOffset; } /** * Number of prototype ids * @return positive or null number */ public int getProtoIdsSize() { return protoIdsSize; } /** * Position of prototype ids table * @return offset */ public int getProtoIdsOffset() { return protoIdsOffset; } /** * Number of field declaration * @return positive or null number */ public int getFieldIdsSize() { return fieldIdsSize; } /** * Position of the field table * @return offset */ public int getFieldIdsOffset() { return fieldIdsOffset; } /** * Number of methods declared * @return positive or null number */ public int getMethodIdsSize() { return methodIdsSize; } /** * Position of the method table * @return offset */ public int getMethodIdsOffset() { return methodIdsOffset; } /** * Number of class declaration * @return positive or null number */ public int getClassDefinitionsSize() { return classDefinitionsSize; } /** * Position of the class table * @return offset */ public int getClassDefinitionsOffset() { return classDefinitionsOffset; } /** * Version of the format used by the file * @return enumeration */ public byte[] getFormatVersionNumber() { return formatVersionNumber; } }