javascriptjavaandroidreverse-engineeringfrida

Setting a member in current class using Frida


I am hooking a certain function in Frida which uses the code:

this.carrier.getId()

However, at this point in time this.carrier has not been set yet, which causes the app to crash.
So I am thinking of manually setting this member in the current function in the class. So that carrier will exist by the time the code takes place.
The problem is that I encounter a problem by doing that.

So far this is what I got:

Java.perform(function () {
    var SignUpActivity = Java.use('com.app.features.authentication.SignUpActivity');
    SignUpActivity.validatePhoneNumber.implementation = function() {
        
        var Carrier = Java.use("com.app.Carrier");
        this.carrier = Carrier.$new();
        console.log(this.carrier) // This prints "[object Object]"
        console.log(this.carrier.setId) // This prints "undefined"
        this.carrier.setId(123); // crashes

    };
});

Code of carrier:

package com.app;

import android.os.Parcel;
import android.os.Parcelable;

public class Carrier implements Parcelable {

    private int id;
    private String name;
    private String officeTerminalAddress;

    public Carrier() {
    }

    protected Carrier(Parcel parcel) {
        this.id = parcel.readInt();
        this.name = parcel.readString();
        this.officeTerminalAddress = parcel.readString();
    }

    public int getId() {
        return this.id;
    }

    public void setId(int i) {
        this.id = i;
    }

}

Solution

  • Looks like the common problem in Frida that the way to access fields is different in Frida.

    Frida uses JavaScript code so it can't handle non-JavaScript objects directly. Therefore it wraps "native" objects (Android Java objects in this case) in JavaScript objects.

    If you now call in Frida this.carrier you are getting the Frida JavaScript wrapper, not the Java Carrier instance you are aiming.

    Of course the Frida JavaScript wrapper does not has the methods you try to call, therefore this.carrier.setId(123); will always fail.

    Accessing a Java field with Frida

    To access a field you always have to call .value on it to get the actual value:

    So if you want this.carrier you have to use this.carrier.value.

    In case there is a name collision with a method of the same name Frida by default assigns the name to the method. If you want to access the field instead add an underscore an the beginning of the field name. If there would exist a method named carrier you could access the field this way: this._carrier.value

    Reference to Frida help pages

    This is also described on the Frida pages, e.g. here:

    Note we use this.m.value = 0 instead of this.m = 0 to set the field’s value. If there is also a method in this class called m, we need to use this._m.value = 0 to set the value of field m. In general, when looking at the properties of objects it will be necessary to use .value to access the values those fields refer to.

    Complete simplified code

    But in your case you can simplify everything by just using a local variable:

    Java.perform(function () {
        var SignUpActivity = Java.use('com.app.features.authentication.SignUpActivity');
        SignUpActivity.validatePhoneNumber.implementation = function() {
            
            const Carrier = Java.use("com.app.Carrier");
            const c = Carrier.$new();
            c.setId(123);
            this._carrier.value = c;
        };
    });