Reverse Engineering Android Apps to Bypass Root Detection Capabilities

0
128


Smartphone producers ship Android gadgets with a strict set of permissions and entry management programs to guard customers from safety dangers and forestall them from by accident damaging their gadgets. Nevertheless, these programs might really feel restrictive to customers who want to customise their gadget in a means that was not supposed by the producer.

With a view to achieve full entry to an Android gadget, it have to be rooted. Having root entry to a tool supplies the next advantages, amongst others: 

  • Putting in cellular apps discovered on third celebration App Shops
  • Sideloading purposes
  • Making use of customized themes/skins for purposes and the house display screen
  • Enhancing battery life
  • Enhancing efficiency
  • Modifying habits of cellular apps on the gadget

Having root entry to Android gadgets is a crucial part of cellular utility safety testing at NowSecure. With out it, safety researchers wouldn’t be capable of achieve perception into the internal workings of cellular apps as simply.

What Is Root Detection?

When customers have root entry, they’re in a position to tamper with each a part of the gadget. Whereas not each consumer of a rooted gadget might have malicious intent, some builders don’t wish to enable rooted gadgets to make use of their cellular apps. Permitting a cellular app to run on a rooted gadget opens it as much as all kinds of exploits. Delicate cellular apps resembling banking, medical, purchasing and authorities typically implement checks that decide if the app is operating on a tool with root privileges. Normally, these checks will stop the cellular app from functioning correctly. Typically, builders implement strict root detection capabilities that stop apps from operating in any respect on rooted gadgets.

Bypassing root detection is usually doable, nonetheless the quantity of ability that’s required can range drastically relying on the cellular app. There are various root detection methods that may be applied. Some apps use fundamental checks that may simply be discovered on-line, whereas others might use customized detection strategies which have by no means been seen earlier than. As a result of most root detection logic runs instantly on a tool, these methods can typically be uncovered via reverse engineering.

By way of a mixture of static and dynamic evaluation, you’ll be able to uncover what the basis detection is doing, when it’s known as, and the way to circumvent the checks. The steps won’t be precisely the identical for each app, nonetheless the method to bypass root detection is comparable normally.

Tutorial for Reverse Engineering Android Apps

Having the ability to bypass root detection is a useful ability that every one cellular safety researchers and cellular pen testers ought to have of their toolkit.

This tutorial covers the steps to reverse engineer Android apps and bypass three frequent root detection methods utilizing Frida. This tutorial makes use of a rudimentary check app, however the identical methods are relevant to real-world cellular apps. You possibly can obtain the check app right here:

Stipulations

  • A rooted Android gadget
  • An app with root detection
  • JADX-GUI
  • Frida Python Packages (frida, frida-tools)
  • Frida Server operating on the goal gadget (the way to)
  • Fundamental Data of Java and Javascript

Discovering the detection code with static evaluation

Upon launching the pattern cellular app, we will see the three root detection strategies that the app is utilizing. Since all three are true, root entry has been detected and the “This utility seems to be operating on a hacked gadget!” toast seems on the backside of the display screen. Since this toast seems each time the basis verify fails, we now have an thought of the place to start out trying within the decompiled code.

To decompile the code, begin JADX-GUI, choose “Open File” and choose the APK to be decompiled. As soon as the Android app is decompiled, begin trying to find phrases or phrases that may very well be associated to root detection. Some useful phrases may very well be: root detected, su, magisk, root verify, is rooted, jailbreak, and many others. Since we now have a warning toast that shows when root is detected, we will seek for the toast’s content material.

The search outcomes present us the situation within the decompiled code the place this warning toast is created. By clicking on the search outcome, we will navigate to the code that’s chargeable for performing the verify and deciding if this toast needs to be displayed.

The supply code exhibits {that a} perform known as checkForRoot known as. If the perform returns true, then root entry has been detected. Primarily based on this info, the code that we have to bypass should reside inside this perform.

Understanding and Bypassing the Detection Code

Contained in the checkForRoot perform, we see that three checks are carried out. These checks return boolean values and if any of them are true, then root was detected. These three checks correspond to the three values displayed on the app’s residence display screen, so we are going to verify them out one after the other. Our purpose might be to govern the app’s habits, so all these checks return false. With a view to manipulate the cellular app’s habits, we are going to depend on a dynamic evaluation software known as Frida. Frida makes use of numerous strategies to hook into an utility’s runtime and supplies an interface for researchers to view or manipulate how a program operates whereas it’s operating. On this tutorial, we are going to use Frida’s JavaScript API to implement our hooks.

su Binary Examine

Understanding the su binary verify

The primary verify that the applying performs is contained in doesSuBinaryExist.

This perform creates an array of file paths the place an su binary may probably be positioned. The code then initializes a File class for every of those paths and checks if the file could be discovered utilizing an exists name.This verify is often utilized in root detection, as a result of an su binary is utilized by customers or purposes to achieve sudo or root entry to a tool.With a view to bypass this verify, we should trick the app into considering that the su binary doesn’t exist on the gadget. Since we all know that it makes use of exists to seek out the binary, that needs to be our goal.

Bypassing the su binary verify

Since we wish to modify the exists methodology, we first have to hook into the category that calls the strategy. On this case, exists is being known as from contained in the File class. By trying on the record of lessons imported into the MainActivity class on the high of the file, we will get hold of the File class title that we’re concerned about.

To begin writing the hook, we have to open up an empty JavaScript file. We are able to begin hooking the File class with:

const File = Java.use('java.io.File');

Subsequent, we have to state which methodology we wish to modify. We are able to do that for the exists methodology like this:

File.exists.implementation = perform () {}

This line of code permits us to overwrite the habits of what ought to occur when a name is made to exists. Any code that we wish to run needs to be positioned inside the perform that we now have simply outlined.

With a view to perceive the way to implement the bypass, we have to perceive how this methodology works. We are able to do that by taking a peek on the Android documentation. The documentation explains that the exists methodology returns true if the trail exists and false if it doesn’t. On this case, all we have to do is verify if the file path being checked ends with su, and if it does, then we drive the strategy to return false. If it isn’t checking for su, then we will proceed the strategy name as regular utilizing this.exists.

With a view to decide what path exists is being known as on, we’ll refer as soon as once more to the Android documentation and see if the File class can present entry to that info. In response to the docs, the File class has a getPath methodology that returns the file path as a string. We are able to manually name this methodology and use the output to verify if the decision needs to be bypassed or not.

The complete hook seems to be like this:

Java.carry out(perform(){
   // Su Exists bypass
   const File = Java.use('java.io.File');
   File.exists.implementation = perform () {
       const filePath = this.getPath();
      
       if (filePath.endsWith("su")){
           console.log(`Bypassing exists() name to: ${filePath}`);
           return false;
       }
       console.log(`Calling exists() on: ${filePath}`);
       return this.exists();
   };
})

Observe: The hook is wrapped in Java.carry out(), as a result of this ensures that the Java Digital Machine might be initialized earlier than we begin loading our hooks. If this name isn’t included, you might expertise sudden habits.

Save the JavaScript file and spawn your app with Frida utilizing the next command:

frida -U -f com.instance.rootbypass -l root_bypass.js

The habits of every argument is as follows:

  • -U = use the gadget related through USB
  • -f = the bundle title of the applying you might be testing
  • -l = the JavaScript file to be loaded

If the hook was written correctly, the goal app ought to spawn and the terminal ought to output the next:

The console output exhibits us that the su checks have been efficiently bypassed and all different exists calls carried on as regular. We are able to additionally confirm that the bypass labored by having a look on the app’s display screen.

That’s one down and two to go!

which su Examine

Understanding the which su verify

One other means that purposes can verify if the su binary exists on a tool is by utilizing the which command. In our decompiled code, we will see that it runs which su to attempt to uncover the file path for the binary.

This code executes a which su by utilizing the Runtime class. If the binary is discovered, the command sends the file path to stdout, but when it finds nothing, nothing might be printed to the display screen. The basis detection methodology then returns true if which su sends something to stdout, in any other case it returns false.


As a result of most root detection logic runs instantly on a tool, these methods can typically be uncovered via reverse engineering.

Bypassing the which su verify

As soon as once more, we have to consult with the Android documentation, however this time we have to see how the exec methodology operates. In response to the docs, it seems that there are a number of variations of exec that may be known as, so we have to be sure that we’re hooking the model that our goal app makes use of. On this case, the  which su command is represented by an array of strings, so we have to craft a hook utilizing the documentation for the overload that takes a single array of strings as a parameter. 

As a result of  we additionally want entry to the argument in our hook, we will hook the argument by including it to the perform signature that we create. The setup for the brand new hook seems to be like this:

const Runtime = Java.use('java.lang.Runtime');
Runtime.exec.overload('[Ljava.lang.String;').implementation = function(commandArray){}

Now that the skeleton for our hook has been created, we need to give it functionality. Because we want to bypass commands that reference the su binary, we should loop over all the words in the command and see if we find su. The command is stored as an array of strings, so this can be done with a simple for loop. If su is found, we need to swap that word for a string that does not exist on the device and exec the substituted command. The code to should look like this:

Java.perform(function(){
   // Shell "which su" Bypass
   const Runtime = Java.use('java.lang.Runtime');
   Runtime.exec.overload('[Ljava.lang.String;').implementation = function(commandArray){
       for (var i = 0; i < commandArray.length; i++) {
           if (commandArray[i] == "su") {
               console.log("Bypassing command referencing 'su'!");
               var clonedArray = commandArray.slice();
               clonedArray[i] = "NotARealBinary";
               return this.exec(clonedArray);
           }
       }
       console.log(`Calling exec() on: ${cmd}`)
       return this.exec(cmd);
   }
})

This new code could be positioned contained in the Java.carry out() perform from the opposite hook. When the app is launched with frida, the app ought to now show two bypassed checks:

Root Utility Examine

Understanding the basis utility verify

Some apps resembling Magisk are generally used to help within the rooting course of. Having any of those apps put in on a tool signifies that it has probably been rooted.

This methodology checks if any of the three offered root utility bundle names are put in on the gadget and, if a bundle is detected, it returns true.

Bypassing the basis utility verify

At first look, this looks as if it needs to be a straightforward hook to put in writing. All one ought to must do is hook the decision to getPackageInfo from the PackageManager class and return false. Nevertheless, if you happen to write a hook for the PackageManager class like this, your hook won’t ever run:

const PackageManager = Java.use("android.content material.pm.PackageManager");
PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = perform (packageName, flags) {console.log("check");}

It’s because the PackageManager is an summary class. Which means you must seek for a category that extends the PackageManger class and write a hook for that. Fortunately, Android is open supply, so we will simply see all of the lessons that stretch the PackageManager class! If we pull up the Android supply code, click on on PackageManager, after which click on “References” on the backside, we will see a listing of lessons that stretch it!

The primary class that extends PackageManager known as ApplicationPackageManager. Utilizing the information gained from the earlier bypasses, we have to search for the arguments and return worth for getPackageInfo within the Android docs. As soon as we perceive how the strategy works and have written the skeleton, the bypass should verify if the goal bundle matches what we now have put in on our gadget and insert a pretend bundle title. The completed hook ought to seem like this:

Java.carry out(perform(){
   // Root Utility bypass
   const PackageManager = Java.use('android.app.ApplicationPackageManager');
   PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = perform (packageName, flags) {
       if (packageName.contains("magisk")){
           console.log(`Bypassing getPackageInfo() on: ${packageName}`);
           return this.getPackageInfo("this.bundle.does.not.exist", flags);
       }
       console.log(`Calling getPackageInfo() on: ${packageName} with flags=${flags}`);
       return this.getPackageInfo(packageName, flags);
   };
})

Ensure that the hook takes into consideration the right overload of getPackageInfo and the perform signature accepts each parameters. When this hook is mixed with the earlier two, we must always see the terminal ought to seem like this:

And if all of the bypasses work as anticipated, our app gained’t be capable of detect that it’s operating on a rooted gadget!

Conclusion

Whereas this tutorial solely exhibits three root detection strategies, this method of mixing static evaluation with dynamic instrumentation could be utilized to all strategies. As you encounter extra root detection methods and add them to your Frida bypass script, you’ll be able to create a dependable software to bypass root detection methods throughout quite a lot of cellular apps.

To save lots of time analyzing cellular apps, pen testers and others can faucet the NowSecure Workstation preconfigured {hardware} and software program that compresses cellular app vulnerability assessments all the way down to mere hours and permits repeatable testing. You can even meet the NowSecure consultants in our sequence of Tech Talks — register immediately.



LEAVE A REPLY

Please enter your comment!
Please enter your name here