Created
October 14, 2017 18:57
-
-
Save FrankSpierings/6e2608e22121b1aeaaa4588f13387dde to your computer and use it in GitHub Desktop.
Hook all overloads - Java/Android - Frida
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
function getGenericInterceptor(className, func, parameters) { | |
args = [] | |
for (i = 0; i < parameters.length; i++) { | |
args.push('arg_' + i) | |
} | |
var script = "result = this.__FUNCNAME__(__SEPARATED_ARG_NAMES__);\nlogmessage = '__CLASSNAME__.__FUNCNAME__(' + __SEPARATED_ARG_NAMES__ + ') => ' + result;\nconsole.log(logmessage);\nreturn result;" | |
script = script.replace(/__FUNCNAME__/g, func); | |
script = script.replace(/__SEPARATED_ARG_NAMES__/g, args.join(', ')); | |
script = script.replace(/__CLASSNAME__/g, className); | |
script = script.replace(/\+ \+/g, '+'); | |
args.push(script) | |
cb = Function.apply(null, args) | |
return cb | |
} | |
function hookall(className, func, cb) { | |
const clazz = Java.use(className); | |
overloads = clazz[func].overloads; | |
for (i in overloads) { | |
if (overloads[i].hasOwnProperty('argumentTypes')) { | |
var parameters = []; | |
for (j in overloads[i].argumentTypes) { | |
parameters.push(overloads[i].argumentTypes[j].className); | |
} | |
const cb = getGenericInterceptor(className, func, parameters); | |
clazz[func].overload.apply('this', parameters).implementation = cb; | |
} | |
} | |
} | |
if (Java.available) { | |
// Switch to the Java context | |
Java.perform(function() { | |
const JavaString = Java.use('java.lang.String'); | |
//Hook all init overloads | |
hookall('java.lang.StringBuilder', '$init', 'a'); | |
}) | |
} |
@wving5 Thanks for the info. Really helpful.
Here we go, created method that searches for a specific class.method
signature, maybe some one will need something like me so you will find it here :)
function getSpecificMethodOverload(methodFullName, args) {
var delimiter = methodFullName.lastIndexOf('.');
if (delimiter === -1) throw new Error('methodFullName is invalid');
var targetClass = methodFullName.slice(0, delimiter);
var targetMethod = methodFullName.slice(delimiter + 1, methodFullName.length);
var clazz = Java.use(targetClass);
if (!args) return null;
if (args.length === 0) return clazz[targetMethod].overload();
return clazz[targetMethod].overloads.find(overload => {
if (overload.argumentTypes && overload.argumentTypes.length === args.length) {
var argsFromOverload = overload.argumentTypes.map(argType => argType.className);
return JSON.stringify(argsFromOverload) === JSON.stringify(args)
}
});
}
// Usage:
var specificOverload = getSpecificMethodOverload('com.appname.classname.methodname', ['java.lang.Boolean', 'java.lang.String']);
P.S. There are many other stuff btw, you can search for an methods with specific modifier [public, private, etc..] or even method return value type.
Anyway tnx for help once again, have a nice life :)
thanks @SiriusED , finally write a util function:
https://github.com/crifan/JsFridaUtil/blob/main/frida/FridaAndroidUtil.js
// https://source.android.com/docs/core/runtime/dex-format?hl=zh-cn
// https://cmrodriguez.me/blog/methods/
static FridaDexTypeMapppingDict = {
"void": "V",
"boolean": "Z",
"char": "C",
"byte": "B",
"short": "S",
"int": "I",
"long": "J",
"float": "F",
"double": "D",
"char": "[C",
"byte[]": "[B",
"short[]": "[S",
"int[]": "[I",
"long[]": "[J",
"float[]": "[F",
"double[]": "[D",
"String[]": "[Ljava/lang/String;",
"Object[]": "[Ljava/lang/Object;",
// TODO: add more type
}
static findOverloadFunction(overloads, argTypeList, retType=null){
var foundOverloadFunc = null
var argTypeNum = argTypeList.length
// console.log("argTypeNum=" + argTypeNum)
overloads.find( function(curOverloadFunc) {
var overloadArgTypeList = curOverloadFunc.argumentTypes
// console.log("overloadArgTypeList=" + overloadArgTypeList)
if ((overloadArgTypeList) && (argTypeNum == overloadArgTypeList.length)){
var argsFromOverload = curOverloadFunc.argumentTypes.map(argType => argType.className)
// console.log("argsFromOverload=" + argsFromOverload)
var overloadArgListJsonStr = JSON.stringify(argsFromOverload)
// console.log("overloadArgListJsonStr=" + overloadArgListJsonStr)
var inputArgListJsonStr = JSON.stringify(argTypeList)
// console.log("inputArgListJsonStr=" + inputArgListJsonStr)
var isArgsSame = overloadArgListJsonStr === inputArgListJsonStr
// console.log("isArgsSame=" + isArgsSame)
if (isArgsSame){
if (retType){
var mappedTypeStr = retType
if (mappedTypeStr in FridaAndroidUtil.FridaDexTypeMapppingDict){
mappedTypeStr = FridaAndroidUtil.FridaDexTypeMapppingDict[mappedTypeStr]
// console.log("mapped mappedTypeStr=" + mappedTypeStr)
}
var overloadFuncRetType = curOverloadFunc.returnType
// console.log("overloadFuncRetType=" + overloadFuncRetType)
var overloadFuncRetTypeStr = overloadFuncRetType.toString()
// console.log("overloadFuncRetTypeStr=" + overloadFuncRetTypeStr)
if (mappedTypeStr === overloadFuncRetTypeStr){
foundOverloadFunc = curOverloadFunc
return foundOverloadFunc
} else {
// console.log("returnType not same: mapped=" + mappedTypeStr + " != current=" + overloadFuncRetTypeStr)
}
}
}
}
})
// console.log("foundOverloadFunc=" + foundOverloadFunc)
return foundOverloadFunc
}
call:
var clsName_afl__ExternalSyntheticApiModelOutline6 = "afl$$ExternalSyntheticApiModelOutline6"
var cls_afl__ExternalSyntheticApiModelOutline6 = Java.use(clsName_afl__ExternalSyntheticApiModelOutline6)
var mOverloads = cls_afl__ExternalSyntheticApiModelOutline6["m"].overloads
// var func_afl__ExternalSyntheticApiModelOutline6_m_1pg = cls_afl__ExternalSyntheticApiModelOutline6.m.overload("android.location.GnssMeasurement")
var curParaTypeList = ["android.location.GnssMeasurement"]
var curRetType = "double"
var func_afl__ExternalSyntheticApiModelOutline6_m_1pg_d = FridaAndroidUtil.findOverloadFunction(mOverloads, curParaTypeList, curRetType)
console.log("func_afl__ExternalSyntheticApiModelOutline6_m_1pg_d=" + func_afl__ExternalSyntheticApiModelOutline6_m_1pg_d)
if (func_afl__ExternalSyntheticApiModelOutline6_m_1pg_d) {
func_afl__ExternalSyntheticApiModelOutline6_m_1pg_d.implementation = function (gnssMeasurement0) {
var funcName = "afl$$ExternalSyntheticApiModelOutline6.m_1pg_d"
var funcParaDict = {
"gnssMeasurement0": gnssMeasurement0,
}
FridaAndroidUtil.printFunctionCallAndStack(funcName, funcParaDict)
var retDouble_1pg_d = this.m(gnssMeasurement0)
console.log(funcName + " => retDouble_1pg_d=" + retDouble_1pg_d)
return retDouble_1pg_d
}
}
// public static float m(GnssMeasurement gnssMeasurement0) {
// public static float afl$$ExternalSyntheticApiModelOutline6.m(android.location.GnssMeasurement)
// var func_afl__ExternalSyntheticApiModelOutline6_m_1pg = cls_afl__ExternalSyntheticApiModelOutline6.m.overload("android.location.GnssMeasurement")
var curParaTypeList = ["android.location.GnssMeasurement"]
var curRetType = "float"
var func_afl__ExternalSyntheticApiModelOutline6_m_1pg_f = FridaAndroidUtil.findOverloadFunction(mOverloads, curParaTypeList, curRetType)
console.log("func_afl__ExternalSyntheticApiModelOutline6_m_1pg_f=" + func_afl__ExternalSyntheticApiModelOutline6_m_1pg_f)
if (func_afl__ExternalSyntheticApiModelOutline6_m_1pg_f) {
func_afl__ExternalSyntheticApiModelOutline6_m_1pg_f.implementation = function (gnssMeasurement0) {
var funcName = "afl$$ExternalSyntheticApiModelOutline6.m_1pg_f"
var funcParaDict = {
"gnssMeasurement0": gnssMeasurement0,
}
FridaAndroidUtil.printFunctionCallAndStack(funcName, funcParaDict)
var retFloat_1pg_f = this.m(gnssMeasurement0)
console.log(funcName + " => retFloat_1pg_f=" + retFloat_1pg_f)
return retFloat_1pg_f
}
}
output:
argTypeNum=1
overloadArgTypeList=Landroid/location/GnssClock;
argsFromOverload=android.location.GnssClock
overloadArgListJsonStr=["android.location.GnssClock"]
inputArgListJsonStr=["android.location.GnssMeasurement"]
isArgsSame=false
overloadArgTypeList=Landroid/location/GnssMeasurement;
argsFromOverload=android.location.GnssMeasurement
overloadArgListJsonStr=["android.location.GnssMeasurement"]
inputArgListJsonStr=["android.location.GnssMeasurement"]
isArgsSame=true
mapped mappedTypeStr=D
overloadFuncRetType=D
overloadFuncRetTypeStr=D
foundOverloadFunc=function m(android.location.GnssMeasurement): double
func_afl__ExternalSyntheticApiModelOutline6_m_1pg_d=function m(android.location.GnssMeasurement): double
argTypeNum=1
overloadArgTypeList=Landroid/location/GnssClock;
argsFromOverload=android.location.GnssClock
overloadArgListJsonStr=["android.location.GnssClock"]
inputArgListJsonStr=["android.location.GnssMeasurement"]
isArgsSame=false
overloadArgTypeList=Landroid/location/GnssMeasurement;
argsFromOverload=android.location.GnssMeasurement
overloadArgListJsonStr=["android.location.GnssMeasurement"]
inputArgListJsonStr=["android.location.GnssMeasurement"]
isArgsSame=true
mapped mappedTypeStr=F
overloadFuncRetType=D
overloadFuncRetTypeStr=D
returnType not same: mapped=F != current=D
overloadArgTypeList=Landroid/location/GnssMeasurement;
argsFromOverload=android.location.GnssMeasurement
overloadArgListJsonStr=["android.location.GnssMeasurement"]
inputArgListJsonStr=["android.location.GnssMeasurement"]
isArgsSame=true
mapped mappedTypeStr=F
overloadFuncRetType=F
overloadFuncRetTypeStr=F
foundOverloadFunc=function m(android.location.GnssMeasurement): float
func_afl__ExternalSyntheticApiModelOutline6_m_1pg_f=function m(android.location.GnssMeasurement): float
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I believe the overloads array is created by Frida around here; https://github.com/frida/frida-java-bridge/blob/1443e3ef82e719de51c8eb586c27882f98bbf0c5/lib/class-factory.js#L331. It uses Java's introspection from JNI to populate this information. The Java types that are used are based on this: https://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp16432.