Rikj000 / Belfius-Root

Simple Xposed module to support the Belfius app on Rooted devices!
https://xdaforums.com/t/release-v1-0-0-belfius-root.4680025/
GNU General Public License v3.0
4 stars 0 forks source link

⚡️ Enhancement - Run `findAndHookRootCheck` on `be.belfius.android.model.config.BaseServerMaintenanceInfo.B()` #5

Open Tutul- opened 1 week ago

Tutul- commented 1 week ago

I wanted to see if the hook could be improve so that the "app information" menu doesn't show the system as rooted.

I can confirm that the most important hook is indeed be.belfius.android.widget.security.security.utils.RootUtils$isDeviceRooted$2.b() that must return false for the majority of the application to avoid seeing the root status.

But I think that the be.belfius.android.model.config.BaseServerMaintenanceInfo.B() might be the one responsible for some extra "device rooted" that one screen might display. At the time of writing this, I wasn't able to find the setter for that boolean (it's true by default). The setter seem to be called from some kind of interface or wrapper and I wasn't able to track the call down.

But the two are clearly separated because the be.belfius.android.widget.MaintenanceState enum become DEVICE_ROOTED only when both the RootUtils$isDeviceRooted$2.b() and BaseServerMaintenanceInfo.B() return true (again, the current hook is sufficient because of the AND present in that code).

I still think that it might be interesting to isolate the code called by RootUtils$isDeviceRooted$2.b() that is responsible for detecting the root status of the device. But I wasn't able to de-compile correctly that obfuscated code

Below is a quick notepad I did while exploring the source code:

be.belfius.android.model.config.BaseServerMaintenanceInfo

private boolean isBlockRootedDevices = true;

public final void b(boolean z) {
    this.isBlockRootedDevices = z; // cannot trace it back
}

public final boolean B() {
    return this.isBlockRootedDevices; // <=== HOOK AND FORCE RETUR **FALSE**
}

be.belfius.android.widget.security.security.utils.RootUtils$isDeviceRooted$2

Seem to be the most important place to hook !!!

public final Boolean b() {
    boolean z;
    UC uc = new UC("root", "rootCheck");
    uc.b("startCheck");
    Iterator it2 = this.this$0.b.iterator();
    while (true) {
        if (!it2.hasNext()) {
            z = false;
            break;
        }
        auZ auz = (auZ) it2.next();
        String simpleName = auz.getClass().getSimpleName();
        uc.b("rootCheck: " + simpleName);
        if (auz.a(this.this$0.e)) {
            uc.b("rootCheck: true");
            z = true;
            break;
        }
    }
    uc.b("end");
    uc.b();
    TN.e.e("info_r", String.valueOf(z));
    return Boolean.valueOf(z); // <======== HOOK AND FORCE RETUR **FALSE**
}

be.belfius.android.widget.MaintenanceState

public enum MaintenanceState {
    MAINTENANCE,
    APP_VERSION_NOT_SUPPORTED,
    DEVICE_ROOTED, // <===================
    NO_CONNECTION,
    UNREACHABLE,
    NORMAL,
    TOUCHPOINT;
}

IF baseServerMaintenanceInfo.B() && ava.d() THEN return MaintenanceState.DEVICE_ROOTED

ava.d() ~= RootUtils$isDeviceRooted$2.b()

be.belfius.android.widget.model.AbstractServerMaintenanceInfoRepository$getServerMaintenanceInfo$1

public AbstractServerMaintenanceInfoRepository$getServerMaintenanceInfo$1(aqS<U> aqs, boolean z, boolean z2) {
    super(0);
    this.this$0 = aqs;
    this.$forceUpdate = z;
    this.$deviceRooted = z2; // <= also linked to RootUtils$isDeviceRooted$2.b()
}

be.belfius.android.widget.repository.AbstractLogonUseCase

IF MaintenanceState == DEVICE_ROOTED THEN throw new BlockRootedException

be.belfius.android.core.DevPreferences

Could force FORCE_NON_ROOTED but seem unable to override

Tutul- commented 1 week ago

Oh I don't know what changed but since the last two reboot, the info panel of the app doesn't show the device as rooted anymore.

Rikj000 commented 1 week ago

Hi @Tutul-,
thank you for your RE work!

I wanted to see if the hook could be improve so that the "app information" menu doesn't show the system as rooted.

Perhaps the module was not enabled yet?
For me it shows not rooted with the module enabled.

But the two are clearly separated because the be.belfius.android.widget.MaintenanceState enum become DEVICE_ROOTED only when both the RootUtils$isDeviceRooted$2.b() and BaseServerMaintenanceInfo.B() return true (again, the current hook is sufficient because of the AND present in that code).

This is a very interesting find.
Through your findings I'd like to update be.belfius.directmobile.android.root.BelfiusRoot as following:

That should make the hooking system more resilient / less prone to complete failure on an update of the Belfius app.

However, before I'd do that,
I want to test if hooking be.belfius.android.model.config.BaseServerMaintenanceInfo.B()
actually bypasses root on it's own though.

Since it would be better to not hook the function, if it does not result into bypassing the root check.

Tutul- commented 1 week ago

As I said, the setter for BaseServerMaintenanceInfo was never found used. The code that set it up is probably hiding in the heavily obfuscated part of the package, behind one or more interface or even behind pure object with invoke... Same for RootUtils$isDeviceRooted$2 in witch the call that actually detect the root status is hidden in the same place.

For now, the hook only manage to hide to the rest of the app the root status verdict. It's seem enough for now. But the actual root detection is still somewhere and the de-compiler I use seem to be unable to track completely the object nature (I see a lot of Object.invoke call on them making tracing of the execution flow tricky)

I did do more testing and your hook is sufficient for now. But because the second one is still not hidden, I fear that the one not hooked for now is linked to Belfius' server response and might become more relevant in future update... Also, without finding where those detection take place, the risk that the info is sent to the server is present and they might update their process so that the server can refuse any transactions (I mean, it surprising that the blockage is only done on the app for now).

Because I've a decompiled version of the APK, I can propose some assistance if required.

Btw, you should be aware that LSPosed project is mostly done and only fork of that project are still alive (the dev teams was harassed and decide to stop everything, the latest version of the official git isn't working anymore on my Android 14, I'm using a fork...)