Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 1975 lines (1669 sloc) 62.1 KB
//--------------------------------------
//--- 010 Editor v3.1.3 - v6.0.2 Binary Template
//
// File: DEXTemplate.bt
// Author: Jon Larimer <jlarimer@gmail.com>
// Tim Strazzere <strazz@gmail.com>
// Revision: 2.1.1
// Purpose: A template for analyzing Dalvik VM (Android) DEX files
//
// License: This file is released into the public domain. People may
// use it for any purpose, commercial or otherwise.
//--------------------------------------
// Version 2.1.1 (2015-10-17)
//
// FIXED:
// - Annotation item's should note be optimized and have a correct
// size of 4, not 12. Thanks jcase!
// Version 2.1 (2015-9-17)
//
// FIXED:
// - Check bounds on debug information offset found inside code_items.
//
// Version 2.0 (2015-1-22)
//
// ADDED:
// - Check for data injected inside of dex after the map section.
// This was seen ITW; checksum, signature and filesize from the
// dex header where set properly to "include" this data.
//
// Version 1.9 (2015-1-06)
//
// ADDED:
// - Prompt user to check source file oddities
//
// Version 1.8 (2014-2-11)
//
// FIXED:
// - Small warning about type_item array not being created when the size
// is zero. Not a large issue but creating unwanted info in the
// output.
//
// Version 1.7 (2013-9-17)
//
// ADDED:
// - Added Adler32 checksum parsing for odex contents.
// - Added reading class descriptor inside odex sections.
// FIXED:
// - Odex parsing in general, offsets where not being properly
// padded, now they are.
// - File size attribute is not properly parsed and checked for
// odex files.
// - Fixed the Alder32 and SHA1 checks when file is odex, significant
// speed increases in doing so.
// - SHA1 should just not be run against Odex since it should always
// fail.
//
// Version 1.6 (2013-9-13)
//
// FIXED:
// - Warnings where not being output to the "output" box and only being
// displayed in the status box, so I wrote a small wrapper hack to make
// this data display in both places since more than one warning is likely
// to occur at a time. This will prompt you at the end to check the output
// box to see what actually went wrong, opposed to only see the last thing
// that went wrong. (Normally you want to see the first thing that broke!)
//
// Version 1.5 (2013-9-12)
//
// FIXED:
// - Added a warning and (red) color change to the following header fields;
// - file size vs expected file size
// - SHA1 from header vs actual SHA1
// - Alder32 checksum from header vs actual checksum
//
// Version 1.4 (2013-8-12)
//
// FIXED:
// - Fixed linked section to only color if section is not null or zero
// - Color the header_size to red if not expected 0x70 size, indicated
// "big ego"/section stuffer attack
//
// Version 1.3 (2013-2-7)
//
// FIXED:
// - Actually fixed the on-demand structs issue. Template appears to
// run much faster (previous update introduced bugs)
// - Properly configured the optimize=true|false flags for structs
//
// Version 1.2 (2012-12-17)
//
// FIXED:
// - Attempting to convert some structs over to on-demand structs,
// this should reduce some memory overhead according to 010Editor
// docs.
//
// Version 1.1 (2012-10-08)
//
// FIXED:
// - Same set of known issues below, though I've added some simple
// coloring to aid with visual inspection of APKS.
// - Linked section is called out in red
// - If there is a larger than normal header, it will be colored red
// and bundled into the "extra_padding" section of the header for
// easier inspection
//
// NEW ISSUES
// - Only color the linked section if it isn't null
//
//
// Version 1.0 (2011-05-10)
//
// KNOWN ISSUES:
// - display of floats and doubles in encoded_value is incorrect
// - display of MUTF-8 strings may be incorrect
// - doesn't handle big endian (byte swapped) files
// - doesn't disassemble DEX instructions
// - only decodes partial information from optimized (ODEX/DexOpt) files
// - could use a bit of refactoring and removing duplicate code
// - not decoding items in the map_list structure - they're already
// referenced through other items in the header so adding them
// in the map_list would be reduntant
LittleEndian();
#define NO_INDEX (0xFFFFFFFF)
#define ENDIAN_CONSTANT (0x12345678)
#define REVERSE_ENDIAN_CONSTANT (0x78563412);
// offset used when seeking within a dex file inside of an odex wrapper
local int odexpad = 0;
// utility type to show the SHA1 hash in the value column
typedef ubyte SHA1[20] <read=SHA1Read, format=hex>;
local int warnings = 0;
local string temp_warning;
// A hack to get warning messages to both "Warn" (show in status) and output to the "output" window
void PrintWarning(string message) {
Warning(temp_warning);
// Ensure new-line, "Warning" statuses should not have them
SPrintf(temp_warning, "%s\n", message);
Printf(temp_warning);
// Hack to trigger a more generic "look at warnings in output"
warnings++;
}
string SHA1Read(SHA1 sig) {
string ret;
string tmp;
int i;
for(i = 0; i<20; i++) {
SPrintf(tmp, "%.2X", sig[i]);
ret += tmp;
}
return ret;
}
// utility for reading/checking the magic value
typedef struct {
char dex[3];
char newline;
char ver[3];
char zero;
// XXX not checking the version, but it should be 035
if((Strcmp(dex, "dex") && Strcmp(dex, "dey")) ||
newline != '\n' ||
zero != 0) {
PrintWarning("Invalid DEX file");
return -1;
}
} dex_magic <read=DexMagicRead, size=8>;
string DexMagicRead(dex_magic &m) {
string s;
SPrintf(s, "%s %s", m.dex, m.ver);
return s;
}
//////////////////////////////////////////////////
// LEB128 stuff
//////////////////////////////////////////////////
// struct to read a uleb128 value. uleb128's are a variable-length encoding for
// a 32 bit value. some of the uleb128/sleb128 code was adapted from dalvik's
// libdex/Leb128.h
typedef struct {
ubyte val <comment="uleb128 element">;
if(val > 0x7f) {
ubyte val <comment="uleb128 element">;
if (val > 0x7f) {
ubyte val <comment="uleb128 element">;
if(val > 0x7f) {
ubyte val <comment="uleb128 element">;
if(val > 0x7f) {
ubyte val <comment="uleb128 element">;
}
}
}
}
} uleb128 <read=ULeb128Read, comment="Unsigned little-endian base 128 value">;
// get the actual uint value of the uleb128
uint uleb128_value(uleb128 &u) {
local uint result;
local ubyte cur;
result = u.val[0];
if(result > 0x7f) {
cur = u.val[1];
result = (result & 0x7f) | (uint)((cur & 0x7f) << 7);
if(cur > 0x7f) {
cur = u.val[2];
result |= (uint)(cur & 0x7f) << 14;
if(cur > 0x7f) {
cur = u.val[3];
result |= (uint)(cur & 0x7f) << 21;
if(cur > 0x7f) {
cur = u.val[4];
result |= (uint)cur << 28;
}
}
}
}
return result;
}
typedef struct uleb128 uleb128p1;
int uleb128p1_value(uleb128 &u) {
return (int)uleb128_value(u) - 1;
}
string ULeb128Read(uleb128 &u) {
local string s;
s = SPrintf(s, "0x%X", uleb128_value(u));
return s;
}
// sleb128
typedef struct {
ubyte val <comment="sleb128 element">;
if(val > 0x7f) {
ubyte val <comment="sleb128 element">;
if (val > 0x7f) {
ubyte val <comment="sleb128 element">;
if(val > 0x7f) {
ubyte val <comment="sleb128 element">;
if(val > 0x7f) {
ubyte val <comment="sleb128 element">;
}
}
}
}
} sleb128 <read=SLeb128Read, comment="Signed little-endian base 128 value">;
// get the actual uint value of the uleb128
int sleb128_value(sleb128 &u) {
local int result;
local ubyte cur;
result = u.val[0];
if(result <= 0x7f) {
result = (result << 25) >> 25;
} else {
cur = u.val[1];
result = (result & 0x7f) | ((uint)(cur & 0x7f) << 7);
if(cur <= 0x7f) {
result = (result << 18) >> 18;
} else {
cur = u.val[2];
result |= (uint)(cur & 0x7f) << 14;
if(cur <= 0x7f) {
result = (result << 11) >> 11;
} else {
cur = u.val[3];
result |= (uint)(cur & 0x7f) << 21;
if(cur <= 0x7f) {
result = (result << 4) >> 4;
} else {
cur = u.val[4];
result |= (uint)cur << 28;
}
}
}
}
return result;
}
string SLeb128Read(sleb128 &u) {
local string s;
s = SPrintf(s, "%i", sleb128_value(u));
return s;
}
//////////////////////////////////////////////////
// encoded_value type
//////////////////////////////////////////////////
typedef enum <ubyte> {
VALUE_BYTE = 0x00,
VALUE_SHORT = 0x02,
VALUE_CHAR = 0x03,
VALUE_INT = 0x04,
VALUE_LONG = 0x06,
VALUE_FLOAT = 0x10,
VALUE_DOUBLE = 0x11,
VALUE_STRING = 0x17,
VALUE_TYPE = 0x18,
VALUE_FIELD = 0x19,
VALUE_METHOD = 0x1a,
VALUE_ENUM = 0x1b,
VALUE_ARRAY = 0x1c,
VALUE_ANNOTATION = 0x1d,
VALUE_NULL = 0x1e,
VALUE_BOOLEAN = 0x1f
} VALUE;
// variable-width integer used by encoded_value types
typedef struct (int size, VALUE type) {
local int s = size + 1;
local VALUE t = type;
local int i;
for(i=0; i<s; i++) {
ubyte val <comment="Encoded value element">;
}
} EncodedValue <read=EncodedValueRead, optimize=false>;
string EncodedValueRead(EncodedValue &v) {
local string s = "";
switch(v.t) {
case VALUE_BYTE:
case VALUE_SHORT:
case VALUE_INT:
case VALUE_LONG:
case VALUE_FLOAT:
case VALUE_DOUBLE:
SPrintf(s, "0x%X", EncodedValueValue(v));
break;
case VALUE_STRING:
s = StringIdRead(EncodedValueValue(v));
break;
case VALUE_TYPE:
s = LongTypeIdRead(EncodedValueValue(v));
break;
case VALUE_FIELD:
s = FieldIdRead(EncodedValueValue(v));
break;
case VALUE_ENUM:
s = FieldIdRead(EncodedValueValue(v));
break;
case VALUE_ARRAY:
case VALUE_ANNOTATION:
case VALUE_BOOLEAN:
case VALUE_NULL:
s = "NULL";
break;
}
return s;
}
int64 EncodedValueValue(EncodedValue &v) {
local int shift = 0;
local int i;
local int64 ret;
if(v.s == 1) {
return v.val;
}
for(i=0; i<v.s; i++) {
ret |= (uint64)v.val[i] << shift;
shift += 8;
}
return ret;
}
typedef struct {
VALUE value_type:5 <comment="Value type">;
ubyte value_arg:3 <comment="Value clarifying argument">;
local string valstr = "";
local string typestr = "";
switch (value_type) {
case VALUE_BYTE:
ubyte value <comment="Byte value">;
SPrintf(valstr, "0x%.2X", value);
typestr = "byte";
break;
case VALUE_SHORT:
// value_arg has size-1, either 0 or 1
EncodedValue value(value_arg, value_type) <comment="Short value">;
SPrintf(valstr, "%i", EncodedValueValue(value));
typestr = "short";
break;
case VALUE_CHAR:
EncodedValue value(value_arg, value_type) <comment="Char value">;
SPrintf(valstr, "'%c'", EncodedValueValue(value));
typestr = "char";
break;
case VALUE_INT:
EncodedValue value(value_arg, value_type) <comment="Int value">;
SPrintf(valstr, "%i", EncodedValueValue(value));
typestr = "int";
break;
case VALUE_LONG:
EncodedValue value(value_arg, value_type) <comment="Long value">;
SPrintf(valstr, "%li", EncodedValueValue(value));
typestr = "long";
break;
case VALUE_FLOAT: // XXX this doesn't work
EncodedValue value(value_arg, value_type) <comment="Float value">;
SPrintf(valstr, "%f", EncodedValueValue(value));
typestr = "float";
break;
case VALUE_DOUBLE:
EncodedValue value(value_arg, value_type) <comment="Double value">;
SPrintf(valstr, "%li", EncodedValueValue(value));
typestr = "double";
break;
case VALUE_STRING:
EncodedValue value(value_arg, value_type) <comment="String value">;
valstr = "\"" + GetStringById(EncodedValueValue(value)) + "\"";
typestr = "string";
break;
case VALUE_TYPE:
EncodedValue value(value_arg, value_type) <comment="Type value">;
valstr = GetLongTypeById(EncodedValueValue(value));
typestr = "type";
break;
case VALUE_FIELD:
EncodedValue value(value_arg, value_type) <comment="Field value">;
valstr = GetFieldById(EncodedValueValue(value));
typestr = "field";
break;
case VALUE_METHOD:
EncodedValue value(value_arg, value_type) <comment="Method value">;
valstr = GetMethodById(EncodedValueValue(value));
typestr = "method";
break;
case VALUE_ENUM:
EncodedValue value(value_arg, value_type) <comment="Enum value">;
valstr = GetFieldById(EncodedValueValue(value));
typestr = "enum";
break;
case VALUE_ARRAY:
struct encoded_array array <comment="Encoded array">;
break;
case VALUE_ANNOTATION:
struct encoded_annotation annotation <comment="Encoded annotation">;
break;
case VALUE_NULL:
// no additional bytes used by null
typestr = valstr = "NULL";
break;
case VALUE_BOOLEAN:
// no additional bytes used by boolean
typestr = "boolean";
if(value_arg == 0) {
valstr = "false";
} else {
valstr = "true";
}
break;
default:
SPrintf(temp_warning, "Unknown type for encoded value 0x%X", value_type);
PrintWarning(temp_warning);
break;
}
} encoded_value <read=EncodedValueStructRead, optimize=false>;
string EncodedValueStructRead(encoded_value &v) {
return v.typestr + ": " + v.valstr;
}
typedef struct {
uleb128 size <comment="Number of elements in array">;
encoded_value values[uleb128_value(size)] <comment="Encoded value element">;
} encoded_array <read=EncodedArrayRead>;
// show first 5 elements of the array
string EncodedArrayRead(encoded_array &a) {
local int count = 5;
local int dots = 1;
local int i;
if(uleb128_value(a.size) < 5) {
count = uleb128_value(a.size);
dots = 0;
}
string val = "[";
for(i=0; i<count; i++) {
val += EncodedValueStructRead(a.values[i]);
if(i < (count-1) || dots) {
val += ", ";
}
}
if(dots) {
val += "...";
}
val += "]";
return val;
}
typedef struct {
uleb128 name_idx <read=StringIdReadUleb, comment="String ID of annotation element name">;
encoded_value value <comment="Encoded value">;
} annotation_element <read=AnnotationElementRead, optimize=false>;
string AnnotationElementRead(annotation_element &e) {
string name = GetStringById(uleb128_value(e.name_idx));
return name + " = " + e.value.valstr;
}
typedef struct {
uleb128 type_idx <read=LongTypeIdReadUleb, comment="Annotation type">;
uleb128 size <comment="Number of name-value mappings in annotation">;
if(uleb128_value(size) > 0) {
annotation_element elements[uleb128_value(size)] <comment="Encoded annotation contents">;
}
} encoded_annotation <read=EncodedAnnotationRead>;
string EncodedAnnotationRead(encoded_annotation &a) {
string s;
SPrintf(s, "%i annotations for %s", uleb128_value(a.size), GetLongTypeById(uleb128_value(a.type_idx)));
return s;
}
//////////////////////////////////////////////////
// dex file header
//////////////////////////////////////////////////
typedef struct {
dex_magic magic <comment="Magic value">;
local uint file_start = 0;
if(odex) {
file_start = dexopt_header.dex_offset;
}
// Temp variable for expected file_length
local uint temp_expected_file_size = ReadUInt(file_start + 32);
// If the size is less than the actual file - lets just set it to be filesize and let other
// things fail nicely than hit out of bound errors
if(temp_expected_file_size > FileSize()) {
temp_expected_file_size = FileSize();
}
local uint temp_checksum = Checksum(CHECKSUM_ADLER32, file_start + 12, temp_expected_file_size - 12, -1, -1);
if(temp_checksum != ReadUInt(FTell())) {
SetBackColor(cLtRed);
SPrintf(temp_warning, "Alder32 checksum did not match, expected %08X but got %08X", ReadUInt(FTell()), temp_checksum);
PrintWarning(temp_warning);
}
uint checksum <format=hex, comment="Alder32 checksum of rest of file">;
// Ensure we reset color to not bleed
SetBackColor(cLtGreen);
// Odex files kill the sha1, but the above checksum is changed
if(!odex) {
// Preemptively read the sha1 in for comparison
local string temp_expected_sha1, temp_char;
local int i;
for(i = 0; i < 20; i++) {
SPrintf(temp_char, "%.2X", ReadUByte(FTell()+i));
temp_expected_sha1 += temp_char;
}
local string temp_prepared_sha1 = "";
ChecksumAlgStr(CHECKSUM_SHA1, temp_prepared_sha1, file_start + 32, temp_expected_file_size - 32, "", -1, -1);
if(temp_expected_sha1 != temp_prepared_sha1) {
SetBackColor(cLtRed);
SPrintf(temp_warning, "Sha1 checksum did not match, expected %s but got %s", temp_expected_sha1, temp_prepared_sha1);
PrintWarning(temp_warning);
}
}
SHA1 signature <comment="SHA-1 signature of rest of file">;
// Ensure we reset color to not bleed
SetBackColor(cLtGreen);
local uint temp_file_size = ReadUInt(FTell());
if(temp_file_size > FileSize()) {
PrintWarning("File size appears be to smaller than expected - invalid dex file");
SetBackColor(cLtRed);
} else if(odex && temp_file_size != dexopt_header.dex_length) {
PrintWarning("File size does not match what odex header says it should be");
SetBackColor(cLtRed);
} else if(!odex && temp_file_size < FileSize()) {
// This could still technically be a valid dex file according to code paths in the verifier
PrintWarning("File size appears be to larger than expected");
SetBackColor(cLtRed);
}
uint file_size <comment="File size in bytes">;
// Ensure we reset color to not bleed
SetBackColor(cLtGreen);
if(ReadUInt(FTell()) != 0x70) {
SetBackColor(cLtRed);
}
uint header_size <comment="Header size in bytes">;
if(header_size > 0x70) {
PrintWarning("Header size appears be to larger than expected");
}
// Ensure we reset color to not bleed
SetBackColor(cLtGreen);
uint endian_tag <format=hex, comment="Endianness tag">;
if(endian_tag != ENDIAN_CONSTANT) {
// XXX we don't handle big endian files
SPrintf(temp_warning, "Invalid endian_tag %.8X, should be %.8X", endian_tag, ENDIAN_CONSTANT);
PrintWarning(temp_warning);
}
if(ReadUInt(FTell()) != 0 && ReadUInt(FTell() + 4) != 0) {
SetBackColor(cLtRed);
}
uint link_size <comment="Size of link section">;
uint link_off <comment="File offset of link section">;
if(link_size != 0 || link_off != 0) {
PrintWarning("A link section appears to be set, this is not supported");
}
// Ensure we reset color to not bleed
SetBackColor(cLtGreen);
uint map_off <comment="File offset of map list">;
uint string_ids_size <comment="Count of strings in the string ID list">;
uint string_ids_off <comment="File offset of string ID list">;
uint type_ids_size <comment="Count of types in the type ID list">;
uint type_ids_off <comment="File offset of type ID list">;
uint proto_ids_size <comment="Count of items in the method prototype ID list">;
uint proto_ids_off <comment="File offset of method prototype ID list">;
uint field_ids_size <comment="Count of items in the field ID list">;
uint field_ids_off <comment="File offset of field ID list">;
uint method_ids_size <comment="Count of items in the method ID list">;
uint method_ids_off <comment="File offset of method ID list">;
uint class_defs_size <comment="Count of items in the class definitions list">;
uint class_defs_off <comment="File offset of class definitions list">;
uint data_size <comment="Size of data section in bytes">;
uint data_off <comment="File offset of data section">;
// Anything after the rest of this, but before the other sections is essentiall
// just "padding", so lets create an array variable to contain it and color it
// properly
if(header_size > 0x70) {
SetBackColor(cLtRed);
char extra_padding[header_size - 0x70];
}
} header_item <size=readHeaderItemSize>;
int readHeaderItemSize(header_item &item) {
return ReadUInt(startof(item)+36);
}
//////////////////////////////////////////////////
// odex file header
//////////////////////////////////////////////////
// Keeping this enum, but it shouldn't ever happen that the flag
// will be anything other than 0x0 or DEX_OPT_FLAG_BIG
// https://android.googlesource.com/platform/dalvik.git/+/57fd399d1265ec627d28a15b3d4b98e5f239ac88%5E!/
typedef enum <uint> {
DEX_FLAG_VERIFIED = 0x1,
DEX_OPT_FLAG_BIG = 0x2,
DEX_OPT_FLAG_FIELDS = 0x4,
DEX_OPT_FLAG_INVOCATIONS = 0x8
} DEX_OPT_FLAGS;
typedef struct {
dex_magic magic <comment="Magic value">;
uint dex_offset <comment="File offset of DEX header">;
uint dex_length <comment="Size of DEX file">;
uint deps_offset <comment="File offset of optimized DEX dependency table">;
uint deps_length <comment="Size of DEX dependency table">;
uint opt_offset <comment="File offset of optimized data tables">;
uint opt_length <comment="Size of optimized data tables">;
DEX_OPT_FLAGS flags <read=DexOptFlagsRead, comment="Flags">;
// This is a potentially sloppy way of getting the size of the section to be checksum'ed,
// though I haven't bothered to look up how the padding is done on the end of the
// deps + opt section
local uint temp_checksum = Checksum(CHECKSUM_ADLER32, deps_offset, FileSize() - deps_offset, -1, -1);
if(temp_checksum != ReadUInt(FTell())) {
SetBackColor(cLtRed);
SPrintf(temp_warning, "Alder32 checksum for optimized dependancies did not match, expected %08X but got %08X", ReadUInt(FTell()), temp_checksum);
PrintWarning(temp_warning);
}
uint checksum <comment="Alder32 checksum of dependency table and optimization table">;
} dexopt_header_item <size=40>;
string DexOptFlagsRead(DEX_OPT_FLAGS f) {
string ret = "";
string flags = "";
DEX_OPT_FLAGS i = 1;
while(i <= DEX_OPT_FLAG_INVOCATIONS) {
if (f & i) {
flags += EnumToString(i) + " ";
}
i = i << 1;
}
SPrintf(ret, "(0x%X) %s", f, flags);
return ret;
}
//////////////////////////////////////////////////
// strings
//////////////////////////////////////////////////
typedef struct {
uleb128 utf16_size <comment="Size of string in UTF-16 code units">;
string data <comment="A string in MUTF-8 format">;
} string_item;
typedef struct {
uint string_data_off <comment="File offset of string data">;
local int64 pos = FTell();
FSeek(odexpad + string_data_off);
string_item string_data <comment="String item">;
FSeek(pos);
} string_id_item <read=StringDataReader, optimize=false>;
string StringDataReader(string_id_item &i) {
return i.string_data.data;
}
typedef struct (int size) {
local int s = size;
string_id_item string_id[size] <comment="String ID">;
} string_id_list <read=StringIDListRead, comment="String ID list">;
string StringIDListRead(string_id_list &l) {
string s;
s = SPrintf(s, "%d strings", l.s);
return s;
}
//////////////////////////////////////////////////
// type IDs
//////////////////////////////////////////////////
typedef struct {
uint descriptor_idx <read=StringIdRead, comment="String ID for this type descriptor">;
} type_id_item <read=TypeIDRead, optimize=false>;
string TypeIDRead(type_id_item &i) {
return GetLongTypeDescriptor(GetStringById(i.descriptor_idx));
}
typedef struct (int size) {
local int s = size;
type_id_item type_id[size] <comment="Type ID">;
} type_id_list <read=TypeIDListRead, optimize=true>;
string TypeIDListRead(type_id_list &l) {
string s;
s = SPrintf(s, "%d types", l.s);
return s;
}
//////////////////////////////////////////////////
// type list
//////////////////////////////////////////////////
typedef struct {
ushort type_idx <comment="Index into type_ids list">;
} type_item <size=2>;
typedef struct {
uint size <comment="Number of entries in type list">;
if(size != 0) {
type_item list[size] <read=TypeItemRead, comment="Type entry">;
}
} type_item_list <read=TypeItemListRead, optimize=true>;
string TypeItemRead(type_item &t) {
return GetTypeById(t.type_idx);
}
string TypeItemListRead(type_item_list &l) {
string s = "";
string tmp;
int i;
for(i = 0; i < l.size; i++) {
s += GetTypeById(l.list[i].type_idx);
if(i+1 < l.size) {
s += ", ";
}
}
return s;
}
string GetLongTypeDescriptor(string descriptor) {
local string desc = "";
local string post = "";
local int i = 0;
local int len = Strlen(descriptor);
// array descriptors
while(descriptor[i] == '[') {
post += "[]";
i++;
if(i >= len) return "ERROR";
}
if(descriptor[i] == 'L') {
// fully qualified class descriptors
i++;
while(i < len) {
if(descriptor[i] == '/') desc += ".";
else if(descriptor[i] == ';') break;
else desc += descriptor[i];
i++;
}
} else {
// simple type descriptors
switch(descriptor[i]) {
case 'V': desc = "void"; break;
case 'Z': desc = "boolean"; break;
case 'B': desc = "byte"; break;
case 'S': desc = "short"; break;
case 'C': desc = "char"; break;
case 'I': desc = "int"; break;
case 'J': desc = "long"; break;
case 'F': desc = "float"; break;
case 'D': desc = "double"; break;
}
}
return desc + post;
}
//////////////////////////////////////////////////
// protoypes
//////////////////////////////////////////////////
typedef struct {
uint shorty_idx <read=StringIdRead, comment="String ID of short-form descriptor">;
uint return_type_idx <read=TypeIdRead, comment="Type ID of the return type">;
uint parameters_off <comment="File offset of parameter type list">;
if(parameters_off != 0) {
local int64 pos = FTell();
FSeek(odexpad + parameters_off);
type_item_list parameters <comment="Prototype parameter data">;
FSeek(pos);
}
} proto_id_item <read=ProtoIDItemRead, optimize=false, size=12>;
string ProtoIDItemRead(proto_id_item &i) {
return GetPrototypeSignature(i);
}
typedef struct (int size) {
local int s = size;
proto_id_item proto_id[size] <comment="Prototype ID">;
} proto_id_list <read=ProtoIDListRead, optimize=true>;
string ProtoIDListRead(proto_id_list &l) {
string s;
s = SPrintf(s, "%d prototypes", l.s);
return s;
}
string GetParameterListString(type_item_list &l) {
local string s = "(";
local string tmp;
local int i;
for(i = 0; i < l.size; i++) {
s += GetLongTypeDescriptor(GetTypeById(l.list[i].type_idx));
if(i+1 < l.size) {
s += ", ";
}
}
return s + ")";
}
string GetPrototypeSignature(proto_id_item &item) {
string ret = GetLongTypeDescriptor(GetTypeById(item.return_type_idx));
string params = "()";
if(exists(item.parameters)) {
params = GetParameterListString(item.parameters);
}
return ret + " " + params;
}
//////////////////////////////////////////////////
// fields
//////////////////////////////////////////////////
typedef struct {
ushort class_idx <read=LongTypeIdRead, comment="Type ID of the class that defines this field">;
ushort type_idx <read=LongTypeIdRead, comment="Type ID for the type of this field">;
uint name_idx <read=StringIdRead, comment="String ID for the field's name">;
} field_id_item <read=FieldIdItemRead, optimize=false, size=8>;
string FieldIdItemRead(field_id_item &i) {
local string type = GetLongTypeDescriptor(GetTypeById(i.type_idx));
local string class = GetLongTypeDescriptor(GetTypeById(i.class_idx));
local string name = GetStringById(i.name_idx);
return type + " " + class + "." + name;
}
typedef struct (int size) {
local int s = size;
field_id_item field_id[size] <comment="Field ID">;
} field_id_list <read=FieldIDListRead, optimize=true>;
string FieldIDListRead(field_id_list &l) {
string s;
s = SPrintf(s, "%d fields", l.s);
return s;
}
//////////////////////////////////////////////////
// methods
//////////////////////////////////////////////////
typedef struct {
ushort class_idx <read=LongTypeIdRead, comment="Type ID of the class that defines this method">;
ushort proto_idx <read=ProtoIdxRead, comment="Prototype ID for this method">;
uint name_idx <read=StringIdRead, comment="String ID for the method's name">;
} method_id_item <read=MethodIdItemRead, optimize=false, size=8>;
string ProtoIdxRead(ushort p) {
string s;
SPrintf(s, "(0x%X) %s", p, GetPrototypeSignature(dex_proto_ids.proto_id[p]));
return s;
}
string MethodIdItemRead(method_id_item &m) {
local string retval = GetLongTypeDescriptor(GetTypeById(dex_proto_ids.proto_id[m.proto_idx].return_type_idx));
local string classname = GetLongTypeDescriptor(GetStringById(dex_type_ids.type_id[m.class_idx].descriptor_idx));
local string methodname = GetStringById(m.name_idx);
local string params = "()";
if(exists(dex_proto_ids.proto_id[m.proto_idx].parameters)) {
params = GetParameterListString(dex_proto_ids.proto_id[m.proto_idx].parameters);
}
return retval + " " + classname + "." + methodname + params;
}
typedef struct (int size) {
local int s = size;
method_id_item method_id[size] <comment="Method ID">;
} method_id_list <read=MethodIDListRead, optimize=true>;
string MethodIDListRead(method_id_list &l) {
string s;
s = SPrintf(s, "%d methods", l.s);
return s;
}
//////////////////////////////////////////////////
// annotations
//////////////////////////////////////////////////
typedef struct {
uint field_idx <read=FieldIdRead>;
uint annotations_off;
if(annotations_off != 0) {
local int64 pos = FTell();
FSeek(odexpad + annotations_off);
struct annotation_set_item field_annotations;
FSeek(pos);
}
} field_annotation <read=FieldAnnotationRead, optimize=false>;
string FieldAnnotationRead(field_annotation &f) {
return GetFieldById(f.field_idx);
}
typedef struct {
uint method_idx <read=MethodIdRead>;
uint annotations_off;
if(annotations_off != 0) {
local int64 pos = FTell();
FSeek(odexpad + annotations_off);
struct annotation_set_item method_annotations;
FSeek(pos);
}
} method_annotation <read=MethodAnnotationRead, optimize=false>;
string MethodAnnotationRead(method_annotation &m) {
return GetMethodById(m.method_idx);
}
typedef struct {
uint method_idx <read=MethodIdRead>;
uint annotations_off;
if(annotations_off != 0) {
local int64 pos = FTell();
FSeek(odexpad + annotations_off);
struct annotation_set_ref_list annotations_list;
FSeek(pos);
}
} parameter_annotation <read=ParameterAnnotationRead, optimize=false>;
string ParameterAnnotationRead(parameter_annotation &p) {
return GetParameterListString(dex_proto_ids.proto_id[dex_method_ids.method_id[p.method_idx].proto_idx].parameters);
}
typedef enum <ubyte> {
VISIBILITY_BUILD = 0x0,
VISIBILITY_RUNTIME = 0x1,
VISIBILITY_SYSTEM = 0x2
} VISIBILITY;
typedef struct {
VISIBILITY visibility <comment="Visibility of item">;
encoded_annotation annotation <comment="Encoded annotation contents">;
} annotation_item <read=AnnotationItemRead, optimize=false>;
string AnnotationItemRead(annotation_item &i) {
return EncodedAnnotationRead(i.annotation);
}
typedef struct {
uint annotation_off <comment="File offset of this annotation entry">;
if(annotation_off != 0) {
local int64 pos = FTell();
FSeek(odexpad + annotation_off);
annotation_item item <comment="Annotation item">;
FSeek(pos);
}
} annotation_off_item <read=AnnotationOffItemRead, optimize=false, size=4>;
string AnnotationOffItemRead(annotation_off_item &i) {
if(exists(i.item)) {
return AnnotationItemRead(i.item);
}
}
typedef struct {
uint size <comment="Number of entries in set">;
if(size > 0) {
annotation_off_item entries[size] <comment="Annotation entry elements">;
}
} annotation_set_item <read=AnnotationSetItemRead, optimize=false>;
string AnnotationSetItemRead(annotation_set_item &i) {
local string s;
SPrintf(s, "%i annotation entries", i.size);
return s;
}
typedef struct {
uint class_annotations_off <comment="File offset to class annotations">;
if(class_annotations_off != 0) {
local int64 pos = FTell();
FSeek(odexpad + class_annotations_off);
annotation_set_item class_annotations <comment="Class annotations">;
FSeek(pos);
}
uint fields_size <comment="Number of fields annotated by this item">;
uint methods_size <comment="Number of methods annotated by this item">;
uint parameters_size <comment="Number of method parameter lists annotated by this item">;
if(fields_size > 0) {
field_annotation field_annotations[fields_size] <comment="List of field annotations">;
}
if(methods_size > 0) {
method_annotation method_annotations[methods_size] <comment="List of method annotations">;
}
if(parameters_size > 0) {
parameter_annotation parameter_annotations[parameters_size] <comment="List of method parameter annotations">;
}
} annotations_directory_item <read=AnnotationsDirectoryItemRead, optimize=true, size=16>;
string AnnotationsDirectoryItemRead(annotations_directory_item &i) {
local string s;
local int classes = 0;
if(exists(i.class_annotations)) {
classes = i.class_annotations.size;
}
SPrintf(s, "%i class annotations, %i field annotations, %i method annotations, %i parameter annotations",
classes, i.fields_size, i.methods_size, i.parameters_size);
return s;
}
typedef struct {
uint annotations_off <comment="File offset of annotation">;
if(annotations_off != 0) {
local int64 pos = FTell();
FSeek(odexpad + annotations_off);
struct annotation_set_item item <comment="Annotation set item">;
FSeek(pos);
}
} annotation_set_ref_item <optimize=false>;
typedef struct {
uint size <comment="Number of entries in annotation list">;
if(size > 0) {
annotation_set_ref_item list[size] <comment="Annotation set elements">;
}
} annotation_set_ref_list;
//////////////////////////////////////////////////
// classes
//////////////////////////////////////////////////
// access flags. some of these mean different things for different items (class/field/method)
typedef enum <uint> {
ACC_PUBLIC = 0x1,
ACC_PRIVATE = 0x2,
ACC_PROTECTED = 0x4,
ACC_STATIC = 0x8,
ACC_FINAL = 0x10,
ACC_SYNCHRONIZED = 0x20,
ACC_VOLATILE = 0x40, // field
//ACC_BRIDGE = 0x40, // method
ACC_TRANSIENT = 0x80, // field
//ACC_VARARGS = 0x80, // method
ACC_NATIVE = 0x100,
ACC_INTERFACE = 0x200,
ACC_ABSTRACT = 0x400,
ACC_STRICT = 0x800,
ACC_SYNTHETIC = 0x1000,
ACC_ANNOTATION = 0x2000,
ACC_ENUM = 0x4000,
ACC_CONSTRUCTOR = 0x10000,
ACC_DECLARED_SYNCHRONIZED = 0x20000
} ACCESS_FLAGS <read=AccessFlagsRead>;
string AccessFlagsRead(ACCESS_FLAGS f) {
string ret = "";
string flags = "";
ACCESS_FLAGS i = 1;
while(i <= ACC_DECLARED_SYNCHRONIZED) {
if (f & i) {
flags += EnumToString(i) + " ";
}
i = i << 1;
}
SPrintf(ret, "(0x%X) %s", f, flags);
return ret;
}
string AccessFlagsReadUleb(uleb128 &f) {
return AccessFlagsRead(uleb128_value(f));
}
typedef enum {
AF_CLASS, AF_FIELD, AF_METHOD
} AF_TYPE;
string GetFriendlyAccessFlag(int flag, AF_TYPE type) {
switch (flag) {
case ACC_PUBLIC: return "public";
case ACC_PRIVATE: return "private";
case ACC_PROTECTED: return "protected";
case ACC_STATIC: return "static";
case ACC_FINAL: return "final";
case ACC_SYNCHRONIZED: return "synchronized";
case ACC_VOLATILE:
if(type == AF_FIELD) return "volatile";
else return "bridge"; // 0x40 is 'bridge' for methods
case ACC_TRANSIENT:
if(type == AF_FIELD) return "transient";
else return "varargs"; // 0x80 is 'varargs' for methods
case ACC_NATIVE: return "native";
case ACC_INTERFACE: return "interface";
case ACC_ABSTRACT: return "abstract";
case ACC_STRICT: return "strict";
case ACC_SYNTHETIC: return "synthetic";
case ACC_ANNOTATION: return "annotation";
case ACC_ENUM: return "enum";
case ACC_CONSTRUCTOR: return "constructor";
case ACC_DECLARED_SYNCHRONIZED: return "declared-synchronized";
}
return "ERROR";
}
string GetFriendlyAccessFlags(ACCESS_FLAGS f, AF_TYPE type) {
string flags = "";
ACCESS_FLAGS i = 1;
while(i <= ACC_DECLARED_SYNCHRONIZED) {
if (f & i) {
flags += GetFriendlyAccessFlag(i, type) + " ";
}
i = i << 1;
}
return flags;
}
// encoded fields
typedef struct (int previd) {
local int p = previd;
uleb128 field_idx_diff <comment="Field ID for this field, represented as the difference from the previous index">;
uleb128 access_flags <read=AccessFlagsReadUleb, comment="Access flags">;
} encoded_field <read=EncodedFieldRead, optimize=false>;
string EncodedFieldRead(encoded_field &f) {
local int realid = f.p + uleb128_value(f.field_idx_diff);
return GetFriendlyAccessFlags(uleb128_value(f.access_flags), AF_FIELD) + GetFieldById(realid);
}
typedef struct (int size) {
local int s = size;
local int i;
local int fieldid = 0;
for(i=0; i<size; i++) {
encoded_field field(fieldid) <comment="Encoded field">;
fieldid = fieldid + uleb128_value(field.field_idx_diff);
}
} encoded_field_list <read=EncodedFieldListRead>;
string EncodedFieldListRead(encoded_field_list &l) {
local string s;
SPrintf(s, "%i fields", l.s);
return s;
}
// encoded methods
typedef struct (int previd) {
local int p = previd;
uleb128 method_idx_diff <comment="Method ID for this method, represented as the difference from the previous index">;
uleb128 access_flags <read=AccessFlagsReadUleb, comment="Access flags">;
uleb128 code_off <comment="File offset to the code for this method">;
if(uleb128_value(code_off) != 0) {
local int64 pos = FTell();
FSeek(odexpad + uleb128_value(code_off));
struct code_item code <comment="Code structure for this method">;
FSeek(pos);
}
} encoded_method <read=EncodedMethodRead, optimize=false>;
string EncodedMethodRead(encoded_method &m) {
local int realid = m.p + uleb128_value(m.method_idx_diff);
return GetFriendlyAccessFlags(uleb128_value(m.access_flags), AF_METHOD) + GetMethodById(realid);
}
typedef struct (int size) {
local int s = size;
local int i;
local int methodid = 0;
for(i=0; i<size; i++) {
encoded_method method(methodid) <comment="Encoded method">;
methodid = methodid + uleb128_value(method.method_idx_diff);
}
} encoded_method_list <read=EncodedMethodListRead>;
string EncodedMethodListRead(encoded_method_list &l) {
local string s;
SPrintf(s, "%i methods", l.s);
return s;
}
typedef struct {
uleb128 static_fields_size <comment="The number of static fields">;
uleb128 instance_fields_size <comment="The number of instance fields">;
uleb128 direct_methods_size <comment="The number of direct methods">;
uleb128 virtual_methods_size <comment="The number of virtual methods">;
if(uleb128_value(static_fields_size) > 0) {
encoded_field_list static_fields(uleb128_value(static_fields_size)) <comment="Encoded sequence of static fields">;
}
if(uleb128_value(instance_fields_size) > 0) {
encoded_field_list instance_fields(uleb128_value(instance_fields_size)) <comment="Encoded sequence of instance fields">;
}
if(uleb128_value(direct_methods_size) > 0) {
encoded_method_list direct_methods(uleb128_value(direct_methods_size)) <comment="Encoded sequence of direct methods">;
}
if(uleb128_value(virtual_methods_size) > 0) {
encoded_method_list virtual_methods(uleb128_value(virtual_methods_size)) <comment="Encoded sequence of virtual methods">;
}
} class_data_item <read=ClassDataItemRead>;
string ClassDataItemRead(class_data_item &i) {
local string s;
SPrintf(s, "%i static fields, %i instance fields, %i direct methods, %i virtual methods",
uleb128_value(i.static_fields_size), uleb128_value(i.instance_fields_size),
uleb128_value(i.direct_methods_size), uleb128_value(i.virtual_methods_size));
return s;
}
typedef struct {
local int64 pos;
uint class_idx <read=LongTypeIdRead, comment="Type ID for this class">;
ACCESS_FLAGS access_flags <comment="Access flags">;
uint superclass_idx <read=LongTypeIdRead, comment="Type ID for this class's superclass">;
uint interfaces_off <comment="File offset to interface list">;
if(interfaces_off != 0) {
pos = FTell();
FSeek(odexpad + interfaces_off);
type_item_list interfaces <read=InterfacesRead, comment="Interface data">;
FSeek(pos);
}
uint source_file_idx <read=StringIdRead, comment="String ID for the name of the file with this class defined">;
uint annotations_off <comment="File offset to the annotation structure for this class">;
if(annotations_off != 0) {
pos = FTell();
FSeek(odexpad + annotations_off);
annotations_directory_item annotations <comment="Annotation data">;
FSeek(pos);
}
uint class_data_off <comment="File offset to the class data for this class">;
if(class_data_off != 0) {
pos = FTell();
FSeek(odexpad + class_data_off);
class_data_item class_data <comment="Class data">;
FSeek(pos);
}
uint static_values_off <comment="File offset to static field data">;
if(static_values_off != 0) {
pos = FTell();
FSeek(odexpad + static_values_off);
struct encoded_array_item static_values <comment="Static values">;
FSeek(pos);
}
} class_def_item <read=ClassDefItemRead, optimize=false, size=32>;
string ClassDefItemRead(class_def_item &i) {
local string classname = GetLongTypeById(i.class_idx);
local string flags = GetFriendlyAccessFlags(i.access_flags, AF_CLASS);
return flags + classname;
}
string InterfacesRead(type_item_list &l) {
string s = "";
int i;
for(i = 0; i < l.size; i++) {
s += GetLongTypeDescriptor(GetTypeById(l.list[i].type_idx));
if(i+1 < l.size) {
s += ", ";
}
}
return s;
}
typedef struct (int size) {
local int s = size;
class_def_item class_def[size] <comment="Class ID">;
} class_def_item_list <read=ClassDefItemListRead>;
string ClassDefItemListRead(class_def_item_list &l) {
string s;
s = SPrintf(s, "%d classes", l.s);
return s;
}
typedef struct {
uint start_addr <comment="Start address of code block covered by this entry, in 16-bit code units from the start of the first instruction">;
ushort insn_count <comment="Number of 16-bit code units covered by this entry">;
ushort handler_off <comment="Offset to the catch_handler_item for this entry">;
} try_item <optimize=false, size=8>;
typedef struct {
sleb128 size <comment="Number of catch types in the list">;
local int s = sleb128_value(size);
local int numhandlers = 0;
if(s != 0) {
numhandlers = Abs(s);
struct encoded_type_addr_pair handlers[numhandlers] <comment="Exception handler pairs">;
}
if(s <= 0) {
uleb128 catch_all_addr <comment="Address of catch-all handler">;
numhandlers++;
}
} encoded_catch_handler <read=EncodedCatchHandlerRead, optimize=false>;
string EncodedCatchHandlerRead(encoded_catch_handler &h) {
local string s;
SPrintf(s, "%i handlers", h.numhandlers);
return s;
}
typedef struct {
uleb128 size <comment="Number of handler lists">;
encoded_catch_handler list[uleb128_value(size)] <comment="List of handler lists">;
} encoded_catch_handler_list <read=EncodedCatchHandlerListRead>;
string EncodedCatchHandlerListRead(encoded_catch_handler_list &l) {
local string s;
SPrintf(s, "%i handler lists", uleb128_value(l.size));
return s;
}
typedef struct {
ushort registers_size <comment="Number of registers used by this code">;
ushort ins_size <comment="Number of words of incoming arguments for this code's method">;
ushort outs_size <comment="Number of words of outgoing arguments for this code's method">;
ushort tries_size <comment="Number of try_item entries for this code">;
uint debug_info_off <comment="File offset for the debug information">;
if(debug_info_off != 0) {
if(odexpad + debug_info_off > FileSize()) {
PrintWarning("Debug info offset appears to be outside of file bounds - skipping!");
} else if(odexpad + debug_info_off < 0) {
PrintWarning("Debug info offset appears to be before file bounds - skipping!");
} else {
local int64 pos = FTell();
FSeek(odexpad + debug_info_off);
struct debug_info_item debug_info <comment="Debug information for this method">;
FSeek(pos);
}
}
uint insns_size <comment="Size of instruction list, in 16-bit code units">;
if(insns_size != 0) {
ushort insns[insns_size] <comment="Instruction">;
}
if(tries_size != 0) {
if (insns_size & 1 == 1) {
ushort padding <comment="Padding...">;
}
try_item tries[tries_size] <comment="Array of code try block addresses">;
encoded_catch_handler_list handlers <comment="Array of catch types and handler addresses">;
}
} code_item <read=CodeItemRead>;
string CodeItemRead(code_item &i) {
local string s;
SPrintf(s, "%i registers, %i in arguments, %i out arguments, %i tries, %i instructions",
i.registers_size, i.ins_size, i.outs_size, i.tries_size, i.insns_size);
return s;
}
typedef struct {
uleb128 type_idx <read=LongTypeIdReadUleb, comment="Type ID for the type of exception to catch">;
uleb128 addr <comment="Bytecode address of exception handler">;
} encoded_type_addr_pair <read=EncodedTypeAddrPairRead, optimize=false>;
string EncodedTypeAddrPairRead(encoded_type_addr_pair &p) {
string s;
SPrintf(s, "%s at 0x%X", GetLongTypeById(uleb128_value(p.type_idx)), uleb128_value(p.addr));
return s;
}
typedef struct {
encoded_array value <comment="The encoded array value">;
} encoded_array_item <read=EncodedArrayItemRead>;
string EncodedArrayItemRead(encoded_array_item &i) {
local string s;
SPrintf(s, "%i items: %s", uleb128_value(i.value.size), EncodedArrayRead(i.value));
return s;
}
enum <ushort> TYPE_CODES {
TYPE_HEADER_ITEM = 0x0000,
TYPE_STRING_ID_ITEM = 0x0001,
TYPE_TYPE_ID_ITEM = 0x0002,
TYPE_PROTO_ID_ITEM = 0x0003,
TYPE_FIELD_ID_ITEM = 0x0004,
TYPE_METHOD_ID_ITEM = 0x0005,
TYPE_CLASS_DEF_ITEM = 0x0006,
TYPE_MAP_LIST = 0x1000,
TYPE_TYPE_LIST = 0x1001,
TYPE_ANNOTATION_SET_REF_LIST = 0x1002,
TYPE_ANNOTATION_SET_ITEM = 0x1003,
TYPE_CLASS_DATA_ITEM = 0x2000,
TYPE_CODE_ITEM = 0x2001,
TYPE_STRING_DATA_ITEM = 0x2002,
TYPE_DEBUG_INFO_ITEM = 0x2003,
TYPE_ANNOTATION_ITEM = 0x2004,
TYPE_ENCODED_ARRAY_ITEM = 0x2005,
TYPE_ANNOTATIONS_DIRECTORY_ITEM = 0x2006
};
//////////////////////////////////////////////////
// debug info
//////////////////////////////////////////////////
typedef enum <ubyte> {
DBG_END_SEQUENCE = 0x00,
DBG_ADVANCE_PC = 0x01,
DBG_ADVANCE_LINE = 0x02,
DBG_START_LOCAL = 0x03,
DBG_START_LOCAL_EXTENDED = 0x04,
DBG_END_LOCAL = 0x05,
DBG_RESTART_LOCAL = 0x06,
DBG_SET_PROLOGUE_END = 0x07,
DBG_SET_EPILOGUE_BEGIN = 0x08,
DBG_SET_FILE = 0x09
} DBG_OPCODE;
typedef struct {
DBG_OPCODE opcode <comment="Debug opcode">;
local string args = "";
switch (opcode) {
case DBG_END_SEQUENCE:
break;
case DBG_ADVANCE_PC:
uleb128 addr_diff <comment="Amount to add to address register">;
SPrintf(args, "%i", uleb128_value(addr_diff));
break;
case DBG_ADVANCE_LINE:
sleb128 line_diff <comment="Amount to change line register by">;
SPrintf(args, "%i", sleb128_value(line_diff));
break;
case DBG_START_LOCAL:
uleb128 register_num <comment="Register that will contain local">;
uleb128p1 name_idx <read=StringIdReadUlebp1, comment="String index of name">;
uleb128p1 type_idx <read=LongTypeIdReadUlebp1,comment="Type index of type">;
SPrintf(args, "%i, %s, %s", uleb128_value(register_num), StringIdReadUlebp1(name_idx),
LongTypeIdReadUlebp1(type_idx));
break;
case DBG_START_LOCAL_EXTENDED:
uleb128 register_num <comment="Register that will contain local">;
uleb128p1 name_idx <read=StringIdReadUlebp1, comment="String index of name">;
uleb128p1 type_idx <read=LongTypeIdReadUlebp1, comment="Type index of type">;
uleb128p1 sig_idx <read=StringIdReadUlebp1, comment="String index of type signature">;
SPrintf(args, "%i, %s, %s, %s", uleb128_value(register_num), StringIdReadUlebp1(name_idx),
LongTypeIdReadUlebp1(type_idx), StringIdReadUlebp1(sig_idx));
break;
case DBG_END_LOCAL:
uleb128 register_num <comment="Register that contained local">;
SPrintf(args, "%i", uleb128_value(register_num));
break;
case DBG_RESTART_LOCAL:
uleb128 register_num <comment="Register to restart">;
SPrintf(args, "%i", uleb128_value(register_num));
break;
case DBG_SET_PROLOGUE_END:
case DBG_SET_EPILOGUE_BEGIN:
break;
case DBG_SET_FILE:
uleb128p1 name_idx <read=StringIdReadUlebp1, comment="String index of source file name">;
SPrintf(args, "%s", StringIdReadUlebp1(name_idx));
}
} debug_opcode <read=DebugOpcodeRead, optimize=false>;
#define DBG_FIRST_SPECIAL 0x0a
#define DBG_LINE_BASE -4
#define DBG_LINE_RANGE 15
string DebugOpcodeRead(debug_opcode &opcode) {
local string s;
if(opcode.opcode >= DBG_FIRST_SPECIAL) {
local ubyte adjusted = opcode.opcode - DBG_FIRST_SPECIAL;
SPrintf(s, "Special opcode: line + %i, address + %i", DBG_LINE_BASE + (adjusted % DBG_LINE_RANGE), (adjusted / DBG_LINE_RANGE));
} else {
s = EnumToString(opcode.opcode);
}
if(opcode.args != "") {
s += " (" + opcode.args + ")";
}
return s;
}
typedef struct {
uleb128 line_start <comment="Initial value for state machine 'line' register">;
uleb128 parameters_size <comment="Number of encoded parameter names">;
if(uleb128_value(parameters_size) > 0) {
uleb128p1 parameter_names[uleb128_value(parameters_size)] <comment="String ID of method parameter names", optimize=false>; // actually uleb128p1
}
do {
debug_opcode opcode <comment="A debug opcode">;
} while (opcode.opcode != DBG_END_SEQUENCE);
} debug_info_item;
//////////////////////////////////////////////////
// map list
//////////////////////////////////////////////////
typedef struct {
TYPE_CODES type;
ushort unused;
uint size;
uint offset;
} map_item <read=MapItemRead, optimize=true, size=12>;
string MapItemRead(map_item &m) {
string s;
SPrintf(s, "%s", EnumToString(m.type));
return s;
}
typedef struct {
uint size;
map_item list[size];
} map_list_type <read=MapListTypeRead>;
string MapListTypeRead(map_list_type &t) {
local string s;
SPrintf(s, "%i items", t.size);
return s;
}
//////////////////////////////////////////////////
// utility functions for reading various strings
// note: strings are stored in a format called MUTF-8, and its
// possible they won't always display correctly in the 010 UI
//////////////////////////////////////////////////
// read a value from the string table
string StringIdRead(int id) {
if(id == NO_INDEX) {
return "NO_INDEX";
}
local string s;
SPrintf(s, "(0x%.X) \"%s\"", id, GetStringById(id));
return s;
}
string StringIdReadUleb(uleb128 &id) {
return StringIdRead(uleb128_value(id));
}
string StringIdReadUlebp1(uleb128p1 &id) {
return StringIdRead(uleb128p1_value(id));
}
// read a value from the type table, return short form
string TypeIdRead(int id) {
return GetIdAndNameString(id, GetTypeById(id));
}
// read a value from the type table, return the long form
string LongTypeIdRead(int id) {
return GetIdAndNameString(id, GetLongTypeById(id));
}
string LongTypeIdReadUleb(uleb128 &id) {
return LongTypeIdRead(uleb128_value(id));
}
string LongTypeIdReadUlebp1(uleb128p1 &id) {
return LongTypeIdRead(uleb128p1_value(id));
}
string FieldIdRead(int id) {
return GetIdAndNameString(id, GetFieldById(id));
}
string MethodIdRead(int id) {
return GetIdAndNameString(id, GetMethodById(id));
}
string GetIdAndNameString(int id, string name) {
local string s;
SPrintf(s, "(0x%X) %s", id, name);
return s;
}
// read a string from the string table
string GetStringById(int id) {
if(id == NO_INDEX) {
return "NO_INDEX";
}
if(exists(dex_string_ids.string_id[id])) {
return dex_string_ids.string_id[id].string_data.data;
} else {
return "*** NO STRING";
}
}
string GetTypeById(int id) {
if(id == NO_INDEX) {
return "NO_INDEX";
}
if(exists(dex_type_ids.type_id[id])) {
return GetStringById(dex_type_ids.type_id[id].descriptor_idx);
} else {
return "*** NO TYPE";
}
}
string GetLongTypeById(int id) {
return GetLongTypeDescriptor(GetTypeById(id));
}
string GetMethodById(int id) {
if(id == NO_INDEX) {
return "NO_INDEX";
}
if(exists(dex_method_ids.method_id[id])) {
return MethodIdItemRead(dex_method_ids.method_id[id]);
} else {
return "*** NO METHOD";
}
}
string GetFieldById(int id) {
if(id == NO_INDEX) {
return "NO_INDEX";
}
if(exists(dex_field_ids.field_id[id])) {
return FieldIdItemRead(dex_field_ids.field_id[id]);
} else {
return "*** NO FIELD";
}
}
//////////////////////////////////////////////////
// dexopt stuff
//////////////////////////////////////////////////
typedef enum<uint> {
DEX_CHUNK_CLASS_LOOKUP = 0x434c4b50,
DEX_CHUNK_REGISTER_MAPS = 0x524d4150,
DEX_CHUNK_END = 0x41454e44
} DEX_CHUNK_TYPE;
typedef struct {
DEX_CHUNK_TYPE type <comment="Chunk type">;
uint size <comment="Size of chunk, in bytes">;
local int realsize = (size + 7) & ~7;
if(type == DEX_CHUNK_CLASS_LOOKUP) {
struct dex_class_lookup class_lookup_table <comment="DexOpt class lookup hash table">;
} else if (type == DEX_CHUNK_REGISTER_MAPS) {
ubyte chunkbytes[realsize];
} else if (type == DEX_CHUNK_END) {
//ubyte chunkbytes[realsize];
} else {
SPrintf(temp_warning, "Unknown chunk type 0x%X", type);
PrintWarning(temp_warning);
return;
}
} dexopt_opt_chunk <read=DexOptChunkRead, optimize=false>;
typedef struct {
local int count = 0;
do {
dexopt_opt_chunk chunk;
count++;
} while(chunk.type != DEX_CHUNK_END);
} dexopt_opt_table <read=DexoptOptTableRead>;
string DexoptOptTableRead(dexopt_opt_table &t) {
local string s;
SPrintf(s, "%i items", t.count);
return s;
}
string DexOptChunkRead(dexopt_opt_chunk &c) {
local string s;
SPrintf(s, "%s chunk: %i bytes", EnumToString(c.type), c.size);
return s;
}
typedef struct {
int offset;
} class_descriptor <read=readClassDescriptor, optimize=false>;
string readClassDescriptor(class_descriptor &item) {
if(item.offset != 0)
return ReadString(sizeof(dexopt_header) + item.offset);
return "";
}
typedef struct {
uint class_descriptor_hash <comment="Class descriptor hash code">;
class_descriptor class_descriptor_item <comment="Class descriptor item">;
int class_definition_offset <comment="File offset of class definition">;
} dex_class_lookup_entry <read=DexClassLookupEntryRead, optimize=false, size=12>;
string DexClassLookupEntryRead(dex_class_lookup_entry &e) {
local string s;
SPrintf(s, "0x%X: (descriptor 0x%X, definition 0x%X)", e.class_descriptor_hash, e.class_descriptor_item.offset, e.class_definition_offset);
return s;
}
typedef struct {
int size <comment="Size of dex_class_lookup structure">;
int num_entries <comment="Number of entries in class lookup hashtable">;
if(num_entries > 0) {
dex_class_lookup_entry table[num_entries] <comment="Class lookup table">;
}
} dex_class_lookup;
//////////////////////////////////////////////////
// main stuff
//////////////////////////////////////////////////
// first check file type - dex files start with 'dex', odex files start with 'dey'
local int odex = 0;
local char tmp[3];
ReadBytes(tmp, 0, 3);
FSeek(0);
if(!Strcmp(tmp, "dey")) {
odex = 1;
}
// dexopt files start with a dexopt header
if(odex) {
dexopt_header_item dexopt_header <comment="DexOpt file header">;
odexpad = dexopt_header.dex_offset;
FSeek(odexpad);
}
// main dex header and structs
SetBackColor(cLtGreen);
header_item dex_header <comment="Dex file header">;
FSeek(dex_header.string_ids_off + odexpad);
SetBackColor(cLtYellow);
string_id_list dex_string_ids(dex_header.string_ids_size) <comment="String ID list">;
FSeek(dex_header.type_ids_off + odexpad);
SetBackColor(cLtPurple);
type_id_list dex_type_ids(dex_header.type_ids_size) <comment="Type ID list">;
FSeek(dex_header.proto_ids_off + odexpad);
SetBackColor(cLtBlue);
proto_id_list dex_proto_ids(dex_header.proto_ids_size) <comment="Method prototype ID list">;
FSeek(dex_header.field_ids_off + odexpad);
SetBackColor(cYellow);
field_id_list dex_field_ids(dex_header.field_ids_size) <comment="Field ID list">;
FSeek(dex_header.method_ids_off + odexpad);
SetBackColor(cDkYellow);
method_id_list dex_method_ids(dex_header.method_ids_size) <comment="Method ID list">;
FSeek(dex_header.class_defs_off + odexpad);
SetBackColor(cLtGray);
class_def_item_list dex_class_defs(dex_header.class_defs_size) <comment="Class definitions list">;
// map list, we don't really do anything with it though
SetBackColor(cLtAqua);
if(dex_header.map_off != 0) {
FSeek(odexpad + dex_header.map_off);
map_list_type dex_map_list <comment="Map list">;
}
if(odex) {
if(dexopt_header.deps_offset != 0) {
FSeek(dexopt_header.deps_offset);
ubyte dexopt_deps[dexopt_header.deps_length] <comment="DexOpt dependency table">;
}
if(dexopt_header.opt_offset != 0) {
FSeek(dexopt_header.opt_offset);
dexopt_opt_table opt_table <comment="DexOpt OPT tabls">;
}
}
//////////////////////////////////////////////////
// Post template completion checks and functions
//////////////////////////////////////////////////
// Check for code injected before the "end" (according to header) of dex file; hidden after the map section, which must be last
local int injection_location = dex_header.map_off + (dex_map_list.size * 12) + 4;
local int injection_size = dex_header.file_size - injection_location;
if(injection_size > 0) {
SPrintf(temp_warning, "There appears to be extra data injected after the map list @ [ %d ] with a size of [ %d ] that is before the end of the dex file.", injection_location, injection_size);
PrintWarning(temp_warning);
SetBackColor(cLtRed);
FSeek(injection_location);
ubyte injected_data[injection_size];
}
// Check for source code name removal/obfuscation
int CheckSourceFileName(int source_file_index) {
local string temp_str = StringIdRead(source_file_index);
if(sizeof(temp_str) <= 6) {
return -1;
}
temp_str = SubStr(temp_str, sizeof(temp_str) - 7, -1);
return Strcmp(temp_str, ".java\"");
}
if( MessageBox( idYes | idNo,
"Check for oddities?",
"Check for source file issues?")
== idYes ) {
local int class_idx = 0;
for(class_idx = 0; class_idx < sizeof(dex_class_defs) / sizeof(dex_class_defs.class_def); class_idx++) {
if(dex_class_defs.class_def[class_idx].source_file_idx != NO_INDEX && CheckSourceFileName(dex_class_defs.class_def[class_idx].source_file_idx) != 0) {
SPrintf(temp_warning, "Source file name for class [ %s ] does not appear to be a \".java\" file!", LongTypeIdRead(dex_class_defs.class_def[class_idx].class_idx));
PrintWarning(temp_warning);
}
}
}
//////////////////////////////////////////////////
// Output error stuff
//////////////////////////////////////////////////
// It's not really useful to see just the last warning, so inform us how many warnings we should see in output
if(warnings > 1) {
Warning("%d warnings have occured and logged to the output box!", warnings);
}
// This will make the template show "Template executed successfully."
if(warnings != 0) {
SPrintf(temp_warning, "%d warnings found, template may not have run successfully!", warnings);
return temp_warning;
}