Jack O'Sullivan
October 5 2020
In the first part of this post, I discussed one method to bypass RootBeer Library through code manipulation. However, installing and running a modified/patched application is not always possible. This happens when an application contains protections such as anti-tampering and/or integrity checks.
If patching an application is not working, then how can we change an application’s behaviour? In this post, I’ll discuss how to change an application’s process during runtime through dynamic instrumentation using Frida. The goal of this post is the same, and that is to bypass all the root-detection checks used by RootBeer library.
Let’s start by identifying the functions responsible for the root-detection checks used by RootBeer. As you can see below, the doInBackground() function of the RootCheckTask (com.scottyab.rootbeer.sample.CheckRootTask) class can be used to identify the different functions/checks used by the application.
The goal here is to “hook” these functions by injecting a JavaScript code into the application’s process, and change their return value to “false”.
As a simple example, let’s force RootBeer to give the “NOT ROOTED” result. This can be done with the following JS code to hook the isRooted() function and change its return value to “false”.
Java.perform(function(){
// Create an instance of the RootBeer class
var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
// Hook the isRooted() function
RootBeer.isRooted.overload().implementation = function(){
// Change the return value to "false"
return false
};
});
Once the script has been saved, execute Frida and load the script.
Injecting the JS code into RootBeer’s process resulted into the following:
If we want to bypass all the checks, we need to hook all the relevant functions and force their return value to “false”. The following quick and dirty script can be used to obtain the result that we’re after:
Java.perform(function(){
var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
var Utils = Java.use("com.scottyab.rootbeer.util.Utils");
RootBeer.detectRootManagementApps.overload().implementation = function(){
return false;
};
RootBeer.detectPotentiallyDangerousApps.overload().implementation = function(){
return false;
};
RootBeer.detectTestKeys.overload().implementation = function(){
return false;
};
RootBeer.checkForBusyBoxBinary.overload().implementation = function(){
return false;
};
RootBeer.checkForSuBinary.overload().implementation = function(){
return false;
};
RootBeer.checkSuExists.overload().implementation = function(){
return false;
};
RootBeer.checkForRWPaths.overload().implementation = function(){
return false;
};
RootBeer.checkForDangerousProps.overload().implementation = function(){
return false;
};
RootBeer.checkForRootNative.overload().implementation = function(){
return false;
};
RootBeer.detectRootCloakingApps.overload().implementation = function(){
return false;
};
Utils.isSelinuxFlagInEnabled.overload().implementation = function(){
return false;
};
RootBeer.checkForMagiskBinary.overload().implementation = function(){
return false;
};
RootBeer.isRooted.overload().implementation = function(){
return false;
};
});
Tip: You can edit and save your script while Frida is running. This way, you don’t have to keep executing the same Frida command every time.
After loading the above script with Frida and running the application, it can be seen that all checks were bypassed.
Successfully Bypassed All RootBeer’s Checks
Final Thoughts
It should be noted that Frida will not work at all times as there are applications which use protections, such as anti-tampering and anti-reversing techniques, that will detect Frida’s presence. If this is the case, you need to find a way to bypass these additional protections first or look for another method which does not involve Frida.
Lastly, if you’re not confident in writing your own Frida script, do not worry because there are ready-to-use Frida scripts which are hosted in Frida CodeShare.
For more information, contact our team.