frida / frida-java-bridge

Java runtime interop from Frida
318 stars 118 forks source link

[Android] Method Instrumentation Causes StackOverflow and App Crash #303

Open Vishalsng112 opened 8 months ago

Vishalsng112 commented 8 months ago

System Information:

Operating System: Linux, Ubuntu 22.04 Device: Android Studio Emulator Android Version: 10

App Information:

App Name: Clock App Link: https://f-droid.org/en/packages/com.chen.deskclock/ App Version: 1.2.2

Issue Description: I am trying to instrument the Clock app to log function entry and exit messages. However, I've encountered an issue where the app crashes due to stack overflow, particularly when trying to perform actions on an enabled alarm. Below is function of instrumentation. This behavior is found in some other apps too. If someone already knows the solution to this issue, please help me out.

Note: I think overloads. implementation is not able to resolve overloaded functions

function tracefunction(func) {
    //  Input example: com.chen.deskclock.data.WidgetModel.updateWidgetCount
    //  extract function name and class name
    var javaFunc = func.split(".")[func.split(".").length - 1];
    var javaClass = func.replace("." + javaFunc, "");
    try {
        var hook = Java.use(javaClass);
    }
    catch (e) {
        console.log("trace class failed");
        return;
    }

    if (typeof hook[javaFunc] != "undefined") {
        //  get method overload count of the method
        var overloadCount = hook[javaFunc].overloads.length;
        for (var i=0; i<overloadCount; i++) {
            hook[javaFunc].overloads[i].implementation = function () {
            var retval;
            console.log("\n*** entered " + func);
            retval = this[javaFunc].apply(this, arguments); // sometimes causes recursive call infinitly and causes app to crash.
            console.log("\n*** exiting " + func);
            return retval;
            }
        }
    }
}

The link to download the Frida-script and apk is here

Steps to Reproduce:

Instrument the app using Frida (latest version 16.1.4). Create an alarm within the app. Attempt to perform any action on the alarm, such as enabling or disabling it.

Expected Behavior: The app should allow instrumentation to log function entry and exit messages without crashing.

Actual Behavior: When instrumented, some functions within the app are causing a stack overflow, leading to an app crash. This issue does not occur without instrumentation.

I appreciate any guidance or solutions to address this issue, as I aim to instrument the Clock app without causing it to crash.

Vishalsng112 commented 8 months ago

A particular issue has been identified in the overload function within class-factory.js. Specifically, when multiple functions share the same signature and the argument type matches, the code currently returns the first match. This alteration in the program's execution order has been discovered during testing on 30 applications. Notably, 22 of these applications are experiencing crashes as a result of this bug.

How to deal with this problem?

    value (...args) {
      const overloads = this._o;

      const numArgs = args.length;
      const signature = args.join(':');

      for (let i = 0; i !== overloads.length; i++) {
        const method = overloads[i];
        const { argumentTypes } = method;

        if (argumentTypes.length !== numArgs) {
          continue;
        }

        const s = argumentTypes.map(t => t.className).join(':');
        if (s === signature) {
          return method;
        }
      }

      throwOverloadError(this.methodName, this.overloads, 'specified argument types do not match any of:');
    }
  }
oleavr commented 8 months ago

This is a user error, not a bug. You are using .overload() when replacing the method, but inside your replacement, you are not doing this when calling the original.

You should call .overload() and assign the returned value to a variable. Then you should use this variable both when assigning to .implementation, and when calling it inside the replacement.

For example:

const method = SomeClass.someMethod.overload(…);
method.implementation = function (...args) {
  return method.call(this, ...args);
};