Methods Summary |
---|
private void | addExternalFieldReferences(ClassRef[] sparseRefs)Runs through the list of field references, inserting external
references into the appropriate ClassRef.
for (int i = 0; i < mFieldIds.length; i++) {
if (!mTypeIds[mFieldIds[i].classIdx].internal) {
FieldIdItem fieldId = mFieldIds[i];
FieldRef newFieldRef = new FieldRef(
classNameFromTypeIndex(fieldId.classIdx),
classNameFromTypeIndex(fieldId.typeIdx),
mStrings[fieldId.nameIdx]);
sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef);
}
}
|
private void | addExternalMethodReferences(ClassRef[] sparseRefs)Runs through the list of method references, inserting external
references into the appropriate ClassRef.
for (int i = 0; i < mMethodIds.length; i++) {
if (!mTypeIds[mMethodIds[i].classIdx].internal) {
MethodIdItem methodId = mMethodIds[i];
MethodRef newMethodRef = new MethodRef(
classNameFromTypeIndex(methodId.classIdx),
argArrayFromProtoIndex(methodId.protoIdx),
returnTypeFromProtoIndex(methodId.protoIdx),
mStrings[methodId.nameIdx]);
sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef);
}
}
|
private java.lang.String[] | argArrayFromProtoIndex(int idx)Returns an array of method argument type strings, given an index
into the proto_ids table.
ProtoIdItem protoId = mProtoIds[idx];
String[] result = new String[protoId.types.length];
for (int i = 0; i < protoId.types.length; i++) {
result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx];
}
return result;
|
private java.lang.String | classNameFromTypeIndex(int idx)Returns the class name, given an index into the type_ids table.
return mStrings[mTypeIds[idx].descriptorIdx];
|
public ClassRef[] | getExternalReferences()Returns an array with all of the class references that don't
correspond to classes in the DEX file. Each class reference has
a list of the referenced fields and methods associated with
that class.
// create a sparse array of ClassRef that parallels mTypeIds
ClassRef[] sparseRefs = new ClassRef[mTypeIds.length];
// create entries for all externally-referenced classes
int count = 0;
for (int i = 0; i < mTypeIds.length; i++) {
if (!mTypeIds[i].internal) {
sparseRefs[i] =
new ClassRef(mStrings[mTypeIds[i].descriptorIdx]);
count++;
}
}
// add fields and methods to the appropriate class entry
addExternalFieldReferences(sparseRefs);
addExternalMethodReferences(sparseRefs);
// crunch out the sparseness
ClassRef[] classRefs = new ClassRef[count];
int idx = 0;
for (int i = 0; i < mTypeIds.length; i++) {
if (sparseRefs[i] != null)
classRefs[idx++] = sparseRefs[i];
}
assert idx == count;
return classRefs;
|
public void | load()Loads the contents of the DEX file into our data structures.
parseHeaderItem();
loadStrings();
loadTypeIds();
loadProtoIds();
loadFieldIds();
loadMethodIds();
loadClassDefs();
markInternalClasses();
|
void | loadClassDefs()Loads the class defs list.
int count = mHeaderItem.classDefsSize;
mClassDefs = new ClassDefItem[count];
//System.out.println("reading " + count + " classDefs");
seek(mHeaderItem.classDefsOff);
for (int i = 0; i < count; i++) {
mClassDefs[i] = new ClassDefItem();
mClassDefs[i].classIdx = readInt();
/* access_flags = */ readInt();
/* superclass_idx = */ readInt();
/* interfaces_off = */ readInt();
/* source_file_idx = */ readInt();
/* annotations_off = */ readInt();
/* class_data_off = */ readInt();
/* static_values_off = */ readInt();
//System.out.println(i + ": " + mClassDefs[i].classIdx + " " +
// mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]);
}
|
void | loadFieldIds()Loads the field ID list.
int count = mHeaderItem.fieldIdsSize;
mFieldIds = new FieldIdItem[count];
//System.out.println("reading " + count + " fieldIds");
seek(mHeaderItem.fieldIdsOff);
for (int i = 0; i < count; i++) {
mFieldIds[i] = new FieldIdItem();
mFieldIds[i].classIdx = readShort() & 0xffff;
mFieldIds[i].typeIdx = readShort() & 0xffff;
mFieldIds[i].nameIdx = readInt();
//System.out.println(i + ": " + mFieldIds[i].nameIdx +
// " " + mStrings[mFieldIds[i].nameIdx]);
}
|
void | loadMethodIds()Loads the method ID list.
int count = mHeaderItem.methodIdsSize;
mMethodIds = new MethodIdItem[count];
//System.out.println("reading " + count + " methodIds");
seek(mHeaderItem.methodIdsOff);
for (int i = 0; i < count; i++) {
mMethodIds[i] = new MethodIdItem();
mMethodIds[i].classIdx = readShort() & 0xffff;
mMethodIds[i].protoIdx = readShort() & 0xffff;
mMethodIds[i].nameIdx = readInt();
//System.out.println(i + ": " + mMethodIds[i].nameIdx +
// " " + mStrings[mMethodIds[i].nameIdx]);
}
|
void | loadProtoIds()Loads the proto ID list.
int count = mHeaderItem.protoIdsSize;
mProtoIds = new ProtoIdItem[count];
//System.out.println("reading " + count + " protoIds");
seek(mHeaderItem.protoIdsOff);
/*
* Read the proto ID items.
*/
for (int i = 0; i < count; i++) {
mProtoIds[i] = new ProtoIdItem();
mProtoIds[i].shortyIdx = readInt();
mProtoIds[i].returnTypeIdx = readInt();
mProtoIds[i].parametersOff = readInt();
//System.out.println(i + ": " + mProtoIds[i].shortyIdx +
// " " + mStrings[mProtoIds[i].shortyIdx]);
}
/*
* Go back through and read the type lists.
*/
for (int i = 0; i < count; i++) {
ProtoIdItem protoId = mProtoIds[i];
int offset = protoId.parametersOff;
if (offset == 0) {
protoId.types = new int[0];
continue;
} else {
seek(offset);
int size = readInt(); // #of entries in list
protoId.types = new int[size];
for (int j = 0; j < size; j++) {
protoId.types[j] = readShort() & 0xffff;
}
}
}
|
void | loadStrings()Loads the string table out of the DEX.
First we read all of the string_id_items, then we read all of the
string_data_item. Doing it this way should allow us to avoid
seeking around in the file.
int count = mHeaderItem.stringIdsSize;
int stringOffsets[] = new int[count];
//System.out.println("reading " + count + " strings");
seek(mHeaderItem.stringIdsOff);
for (int i = 0; i < count; i++) {
stringOffsets[i] = readInt();
}
mStrings = new String[count];
seek(stringOffsets[0]);
for (int i = 0; i < count; i++) {
seek(stringOffsets[i]); // should be a no-op
mStrings[i] = readString();
//System.out.println("STR: " + i + ": " + mStrings[i]);
}
|
void | loadTypeIds()Loads the type ID list.
int count = mHeaderItem.typeIdsSize;
mTypeIds = new TypeIdItem[count];
//System.out.println("reading " + count + " typeIds");
seek(mHeaderItem.typeIdsOff);
for (int i = 0; i < count; i++) {
mTypeIds[i] = new TypeIdItem();
mTypeIds[i].descriptorIdx = readInt();
//System.out.println(i + ": " + mTypeIds[i].descriptorIdx +
// " " + mStrings[mTypeIds[i].descriptorIdx]);
}
|
void | markInternalClasses()Sets the "internal" flag on type IDs which are defined in the
DEX file or within the VM (e.g. primitive classes and arrays).
for (int i = mClassDefs.length -1; i >= 0; i--) {
mTypeIds[mClassDefs[i].classIdx].internal = true;
}
for (int i = 0; i < mTypeIds.length; i++) {
String className = mStrings[mTypeIds[i].descriptorIdx];
if (className.length() == 1) {
// primitive class
mTypeIds[i].internal = true;
} else if (className.charAt(0) == '[") {
mTypeIds[i].internal = true;
}
//System.out.println(i + " " +
// (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " +
// mStrings[mTypeIds[i].descriptorIdx]);
}
|
void | parseHeaderItem()Parses the interesting bits out of the header.
mHeaderItem = new HeaderItem();
seek(0);
byte[] magic = new byte[8];
readBytes(magic);
if (!verifyMagic(magic)) {
System.err.println("Magic number is wrong -- are you sure " +
"this is a DEX file?");
throw new DexDataException();
}
/*
* Read the endian tag, so we properly swap things as we read
* them from here on.
*/
seek(8+4+20+4+4);
mHeaderItem.endianTag = readInt();
if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) {
/* do nothing */
} else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){
/* file is big-endian (!), reverse future reads */
isBigEndian = true;
} else {
System.err.println("Endian constant has unexpected value " +
Integer.toHexString(mHeaderItem.endianTag));
throw new DexDataException();
}
seek(8+4+20); // magic, checksum, signature
mHeaderItem.fileSize = readInt();
mHeaderItem.headerSize = readInt();
/*mHeaderItem.endianTag =*/ readInt();
/*mHeaderItem.linkSize =*/ readInt();
/*mHeaderItem.linkOff =*/ readInt();
/*mHeaderItem.mapOff =*/ readInt();
mHeaderItem.stringIdsSize = readInt();
mHeaderItem.stringIdsOff = readInt();
mHeaderItem.typeIdsSize = readInt();
mHeaderItem.typeIdsOff = readInt();
mHeaderItem.protoIdsSize = readInt();
mHeaderItem.protoIdsOff = readInt();
mHeaderItem.fieldIdsSize = readInt();
mHeaderItem.fieldIdsOff = readInt();
mHeaderItem.methodIdsSize = readInt();
mHeaderItem.methodIdsOff = readInt();
mHeaderItem.classDefsSize = readInt();
mHeaderItem.classDefsOff = readInt();
/*mHeaderItem.dataSize =*/ readInt();
/*mHeaderItem.dataOff =*/ readInt();
|
byte | readByte()Reads a single signed byte value.
mDexFile.readFully(tmpBuf, 0, 1);
return tmpBuf[0];
|
void | readBytes(byte[] buffer)Fills the buffer by reading bytes from the DEX file.
mDexFile.readFully(buffer);
|
int | readInt()Reads a signed 32-bit integer, byte-swapping if necessary.
mDexFile.readFully(tmpBuf, 0, 4);
if (isBigEndian) {
return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) |
((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24);
} else {
return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) |
((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24);
}
|
short | readShort()Reads a signed 16-bit integer, byte-swapping if necessary.
mDexFile.readFully(tmpBuf, 0, 2);
if (isBigEndian) {
return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8));
} else {
return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8));
}
|
java.lang.String | readString()Reads a UTF-8 string.
We don't know how long the UTF-8 string is, so we have to read one
byte at a time. We could make an educated guess based on the
utf16_size and seek back if we get it wrong, but seeking backward
may cause the underlying implementation to reload I/O buffers.
int utf16len = readUnsignedLeb128();
byte inBuf[] = new byte[utf16len * 3]; // worst case
int idx;
for (idx = 0; idx < inBuf.length; idx++) {
byte val = readByte();
if (val == 0)
break;
inBuf[idx] = val;
}
return new String(inBuf, 0, idx, "UTF-8");
|
int | readUnsignedLeb128()Reads a variable-length unsigned LEB128 value. Does not attempt to
verify that the value is valid.
int result = 0;
byte val;
do {
val = readByte();
result = (result << 7) | (val & 0x7f);
} while (val < 0);
return result;
|
private java.lang.String | returnTypeFromProtoIndex(int idx)Returns a string representing the method's return type, given an
index into the proto_ids table.
ProtoIdItem protoId = mProtoIds[idx];
return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx];
|
void | seek(int position)Seeks the DEX file to the specified absolute position.
mDexFile.seek(position);
|
private static boolean | verifyMagic(byte[] magic)Verifies the given magic number.
return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC) ||
Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_API_13);
|