Skip to content

Instantly share code, notes, and snippets.

@FrankSpierings
Created October 14, 2017 18:57
Show Gist options
  • Save FrankSpierings/6e2608e22121b1aeaaa4588f13387dde to your computer and use it in GitHub Desktop.
Save FrankSpierings/6e2608e22121b1aeaaa4588f13387dde to your computer and use it in GitHub Desktop.
Hook all overloads - Java/Android - Frida
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');
})
}
@FrankSpierings
Copy link
Author

The last bit of the code I mentioned shows you how to obtain the overloads.

// Give this function the overloads array and it will return their signature.
function getOverloads(overloads) {
    var results = []
    for (i in overloads) {
        if (overloads[i].hasOwnProperty('argumentTypes')) {
            var parameters = []
            for (j in overloads[i].argumentTypes) {
                parameters.push("'" + overloads[i].argumentTypes[j].className + "'")
            }
        }
        // results.push(overloads[i].name + '(' + parameters.join(', ') + ')')
        results.push('.overload(' + parameters.join(', ') + ')')
    }
    return results.join('\n')
}

Overriding the specific implementation of an overload should create the hook for only that version of the method. I have been out of the Frida stuff for quite some time, so I can't code it by heart (or give you exact coding advice).

@SiriusED
Copy link

SiriusED commented Feb 12, 2022

@wving5 Ok I got it, big thanks. So...... It does have some property after all...
overloads[0].argumentTypes

But what kind of type is on overloads array inside? How can I know about overload prototype or some documentation? I did not find anything about this types in Java \ JavaScript docs....

Can you, maybe, share some link to read about this weird types, because if I know about this property on a overload object I would implement this in 5 min after I need this :) But the real issue was I din't even know what kind of the type has overload(s)...

So I would appreciate to get some link about this kind of types, I assume those are internal Frida types or something, I'm right?

@FrankSpierings
Copy link
Author

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.

@SiriusED
Copy link

@wving5 Thanks for the info. Really helpful.

@SiriusED
Copy link

SiriusED commented Feb 12, 2022

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 :)

@crifan
Copy link

crifan commented Mar 26, 2025

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