/* 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;
}
}