Created
October 2, 2016 00:53
-
-
Save mar-v-in/7be2290a9483939328ad8f0445d8aed8 to your computer and use it in GitHub Desktop.
Android ART native method call tracer for frida.re
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
/** | |
* Called synchronously when about to call _ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc. | |
* | |
* @this {object} - Object allowing you to store state for use in onLeave. | |
* @param {function} log - Call this function with a string to be presented to the user. | |
* @param {array} args - Function arguments represented as an array of NativePointer objects. | |
* For example use Memory.readUtf8String(args[0]) if the first argument is a pointer to a C string encoded as UTF-8. | |
* It is also possible to modify arguments by assigning a NativePointer object to an element of this array. | |
* @param {object} state - Object allowing you to keep state across function calls. | |
* Only one JavaScript function will execute at a time, so do not worry about race-conditions. | |
* However, do not use this to store function arguments across onEnter/onLeave, but instead | |
* use "this" which is an object for keeping state local to an invocation. | |
*/ | |
onEnter: function (log, args, state) { | |
function StringId(ptr) { // struct | |
try { | |
return { | |
'string_data_off_': Memory.readU32(ptr) | |
}; | |
} catch (err) { | |
log("Error creating StringId at "+ptr+": "+err); | |
return null; | |
}} | |
function TypeId(ptr) { // struct | |
try { | |
return { | |
'descriptor_idx_': Memory.readU32(ptr) | |
}; | |
} catch (err) { | |
log("Error creating TypeId at "+ptr+": "+err); | |
return null; | |
}} | |
function MethodId(ptr) { // struct | |
try { | |
return { | |
'class_idx_': Memory.readU16(ptr), | |
'proto_idx_': Memory.readU16(ptr.add(2)), | |
'name_idx_': Memory.readU32(ptr.add(4)) | |
}; | |
} catch (err) { | |
log("Error creating MethodId at "+ptr+": "+err); | |
return null; | |
}} | |
function ClassDef(ptr) { // struct | |
try { | |
return { | |
'class_idx_': Memory.readU16(ptr), | |
'access_flags_': Memory.readU32(ptr.add(4)), | |
'superclass_idx_': Memory.readU16(ptr.add(8)), | |
'interfaces_off_': Memory.readU32(ptr.add(12)), | |
'source_file_idx_': Memory.readU32(ptr.add(16)), | |
'annotations_off_': Memory.readU32(ptr.add(20)), | |
'class_data_off_': Memory.readU32(ptr.add(24)), | |
'static_values_off_': Memory.readU32(ptr.add(28)), | |
}; | |
} catch (err) { | |
log("Error creating ClassDef at "+ptr+": "+err); | |
}} | |
function DexFileHeader(ptr) { // struct | |
try { | |
return { | |
'magic_': Memory.readByteArray(ptr, 8), | |
'checksum_': Memory.readU32(ptr.add(8)), | |
'signature_': Memory.readByteArray(ptr.add(12), 20), | |
'file_size_': Memory.readU32(ptr.add(32)), | |
'header_size_': Memory.readU32(ptr.add(36)), | |
'string_ids_size_': Memory.readU32(ptr.add(56)) | |
}; | |
} catch (err) { | |
log("Error creating DexFileHeader at "+ptr+": "+err); | |
return null; | |
}} | |
function DexFile(ptr) { // class | |
try { | |
var begin_ = Memory.readPointer(ptr.add(4)); | |
var size_ = Memory.readU32(ptr.add(8)); | |
//var location_ = Memory.readPointer(ptr.add(12)); | |
var location_checksum_ = Memory.readU32(ptr.add(24)); | |
var mem_map_ = Memory.readPointer(ptr.add(28)); | |
var header_ = Memory.readPointer(ptr.add(32)); | |
var string_ids_ = Memory.readPointer(ptr.add(36)); | |
var type_ids_ = Memory.readPointer(ptr.add(40)); | |
var field_ids_ = Memory.readPointer(ptr.add(44)); | |
var method_ids_ = Memory.readPointer(ptr.add(48)); | |
var proto_ids_ = Memory.readPointer(ptr.add(52)); | |
var class_defs_ = Memory.readPointer(ptr.add(56)); | |
function GetHeader() { | |
return DexFileHeader(header_); | |
} | |
function GetMethodId(idx) { | |
return MethodId(method_ids_.add(idx*8)); | |
} | |
function GetClassDef(idx) { | |
return ClassDef(class_defs_.add(idx*32)); | |
} | |
function GetTypeId(idx) { | |
return TypeId(type_ids_.add(idx*4)); | |
} | |
function NumStringIds() { | |
return GetHeader().string_ids_size_; | |
} | |
function GetStringId(idx) { | |
if (idx >= NumStringIds()) { | |
log(idx + " overflows "+NumStringIds()); | |
return null; | |
} | |
return StringId(string_ids_.add(idx*4)); | |
} | |
function GetStringData(string_id) { | |
if (string_id == null) return null; | |
return Memory.readUtf8String(begin_.add(string_id.string_data_off_).add(1)); | |
} | |
function StringDataByIdx(idx) { | |
return GetStringData(GetStringId(idx)); | |
} | |
function GetMethodName(method_id) { | |
return StringDataByIdx(method_id.name_idx_); | |
} | |
function GetTypeDescriptor(type_id) { | |
return StringDataByIdx(type_id.descriptor_idx_); | |
} | |
return { | |
"GetMethodId": GetMethodId, | |
"GetTypeId": GetTypeId, | |
"GetStringId": GetStringId, | |
"GetClassDef": GetClassDef, | |
"GetTypeDescriptor": GetTypeDescriptor, | |
"GetMethodName": GetMethodName | |
}; | |
} catch (err) { | |
log("Error creating DexFile at "+ptr+": "+err); | |
return null; | |
}} | |
function DexCache(ptr) { // class | |
var dex_file_ = Memory.readPointer(ptr.add(32)); | |
function GetDexFile() { | |
return DexFile(dex_file_); | |
} | |
return { | |
"GetDexFile": GetDexFile | |
}; | |
} | |
function Class(ptr) { // class | |
var dex_cache_ = Memory.readPointer(ptr.add(16)); | |
var dex_class_def_index_ = Memory.readU32(declClass.add(88)); | |
function GetDexCache() { | |
return DexCache(dex_cache_); | |
} | |
function GetDexClassDefIndex() { | |
return dex_class_def_index_; | |
} | |
function GetDexFile() { | |
return GetDexCache().GetDexFile(); | |
} | |
function GetClassDef() { | |
return GetDexFile().GetClassDef(GetDexClassDefIndex()); | |
} | |
function GetDescriptor() { | |
var dex_file = GetDexFile(); | |
var type_id = dex_file.GetTypeId(GetClassDef().class_idx_); | |
return dex_file.GetTypeDescriptor(type_id); | |
} | |
return { | |
"GetDexCache": GetDexCache, | |
"GetDescriptor": GetDescriptor | |
}; | |
} | |
function ArtMethod(ptr) { // class | |
var declaring_class_ = Memory.readPointer(ptr); | |
var dex_method_index_ = Memory.readU32(ptr.add(20)); | |
function GetDeclaringClass() { | |
return Class(declaring_class_); | |
} | |
function GetDexCache() { | |
return GetDeclaringClass().GetDexCache(); | |
} | |
function GetDexFile() { | |
return GetDexCache().GetDexFile(); | |
} | |
function GetDexMethodIndex() { | |
return dex_method_index_; | |
} | |
function GetName() { | |
var dex_method_idx = GetDexMethodIndex(); | |
var dex_file = GetDexFile(); | |
var name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); | |
return name; | |
} | |
return { | |
"GetName": GetName, | |
"GetDeclaringClass": GetDeclaringClass | |
}; | |
} | |
function readArtString(s) { | |
return Memory.readUtf16String(s.add(16), Memory.readS32(s.add(8))); | |
} | |
var artMethod = args[0]; | |
var declClass = Memory.readPointer(artMethod); | |
var declClassNameString = Memory.readPointer(declClass.add(28)); | |
try { | |
var artMethod = ArtMethod(args[0]); | |
log(artMethod.GetDeclaringClass().GetDescriptor() + "->" + artMethod.GetName()); | |
} catch (err) { | |
log(err); | |
} | |
}, | |
/** | |
* Called synchronously when about to return from _ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc. | |
* | |
* See onEnter for details. | |
* | |
* @this {object} - Object allowing you to access state stored in onEnter. | |
* @param {function} log - Call this function with a string to be presented to the user. | |
* @param {NativePointer} retval - Return value represented as a NativePointer object. | |
* @param {object} state - Object allowing you to keep state across function calls. | |
*/ | |
onLeave: function (log, retval, state) { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment