statement

All content in this article is only for learning and communication, not for any other purpose, and does not provide complete code. The content of captured packets, sensitive URLs, data interfaces, etc. have been desensitized. Commercial and illegal uses are strictly prohibited. All consequences of this have nothing to do with the author!

The author is not responsible for any accident caused by unauthorized use of the technology explained in this article. If there is any infringement, please contact the author on the official account [K Brother Crawler] to delete it immediately !

reverse goal

  • Device: Google Pixel4, Android 10, rooted
  • APP: UnCrackable-Level1.apk (can be obtained by replying to the APP on the official account)
  • The APP has detected root, if the phone is rooted, it will forcefully exit the APP. After root detection, you need to enter a string for verification.

Install ADB

adb (Android Debug Bridge) is the Android debugging bridge. After installation, you can interact with the mobile phone on the computer. Tools such as Android Studio will come with adb. Sometimes we do not want to download such a large tool, so here is an introduction to the Android SDK Platform. -Tools, it is a component of the Android SDK, it includes tools for interacting with the Android platform, mainly adb and fastboot, the official download address: https://developer.android.com/studio/releases/platform-tools , the download is complete Then add the directory to the environment variable, connect the phone via USB, and set the phone to allow USB debugging. Use the command adb version to view the version information, adb devices to view the currently connected device, as shown below shown:

01

Install Frida

Frida is a Hook and debugging framework based on Python + JavaScript. First, use the command on the computer pip install frida-tools to install the frida module (this command will install the latest version of frida and frida-tools by default, such as), and then download frida -server, download address: https://github.com/frida/frida/releases

The frida-server should be selected according to the frida version installed on your computer and the CPU architecture of the mobile phone. Use the command frida --version to view the frida version, use the command adb shell to enter the mobile phone, enter getprop ro.product.cpu.abi Check the CPU architecture, as shown in the figure below, my frida is version 15.2.2, and the mobile phone CPU is arm64, so I downloaded frida-server-15.2.2-android-arm64.xz .

Some lower versions of Android may have problems using higher versions of frida. If you encounter problems, try lowering the version of frida to solve them.

02

03

Transfer the downloaded frida-server to the /data/local/tmp/ directory using the adb push command, and give 777 read, write, and execute permissions, and then run frida-server directly. There will be any output, and of course you can use & etc. to make it run in the background.

04

Then open another cmd and use the command frida-ps -U to view the mobile phone process, if there is output, it is normal.

05

reverse analysis

Use the adb install command to install UnCrackable-Level1.apk, open the APP, root will be detected, and a prompt of Root detected! will appear, as shown in the following figure:

06

Use JEB, JADX, GDA and other tools to decompile apk, and directly search for keywords Root detected! to locate the detection place:

07

可以看到图中有三个检测方法c.a()c.b()c.c() ,其中一个返回为真,则弹出Root detected! ,然后There is another onClick method in front. If you click the OK button, it will trigger System.exit(0); , that is, exit the APP, and first click on the three detection methods to see:

a() The method determines whether it is rooted by detecting whether there is a su file in the Android system environment variable;

b() The method determines whether it is rooted by detecting whether Build.TAGS contains the string test-keys ;

c() The method judges whether it is rooted by detecting whether the specified file contains the specified file.

08

So here we have a variety of ways to pass detection:

Method 1: Hook the three detection methods, let them all return false, and the subsequent a method will not be executed, and the APP will not be exited:

 Java.perform(
    function(){
        console.log("[*] Hook begin")
        var vantagePoint = Java.use("sg.vantagepoint.a.c")
        vantagePoint.a.implementation = function(){
            console.log("[*] Hook vantagepoint.a.c.a")
            this.a();
            return false;
        }
        vantagePoint.b.implementation = function(){
            console.log("[*] Hook vantagepoint.a.c.b")
            this.b();
            return false;
        }
        vantagePoint.c.implementation = function(){
            console.log("[*] Hook vantagepoint.a.c.c")
            this.c();
            return false;
        }
    }
)

Method 2: Hook a() method, leave it empty, do nothing, do not pop up a dialog box, and do not exit the APP:

 Java.perform(
    function(){
        console.log("[*] Hook begin")
        var mainActivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
        mainActivity.a.implementation = function(){
            console.log("[*] Hook mainActivity.a")
        }
    }
)

Method 3: Hook onClick() method, click OK to not let it exit the APP, note that here is the Hook writing of the inner class:

 Java.perform(
    function(){
        console.log("[*] Hook begin")
        var mainActivity$1 = Java.use("sg.vantagepoint.uncrackable1.MainActivity$1");
        mainActivity$1.onClick.implementation = function(){
            console.log("[*] Hook mainActivity$1.onClick")
        }
    }
)

Method 4: Hook System.exit() method, click OK to not let it exit the APP:

 Java.perform(
    function(){
        console.log("[*] Hook begin")
        var javaSystem = Java.use("java.lang.System");
        javaSystem.exit.implementation = function(){
            console.log("[*] Hook system.exit")
        }
    }
)

After the root detection is passed, the APP also needs to enter a string, and the input error will prompt That's not it. Try again. , as shown in the following figure:

09

Analysis of the Java code, there is a if-else judgment, obj is the input string, a.a(obj) Judgment is true, it means the input is correct.

10

跟到a.a()方法, bArr字符串, equals()方法比较输入的str bArr Equal:

11

The value of bArr is mainly obtained after processing by the sg.vantagepoint.a.a.a() method. If you continue to follow up, you can find that it is the AES encryption algorithm:

12

Here you can directly Hook sg.vantagepoint.a.a.a() to get the encrypted value directly, which is the correct string we want. Since the ASCII code is returned here, we also need to use it in the JavaScript code String.fromCharCode() Convert it to normal characters, the Hook code is as follows:

 Java.perform(
    function(){
        var cryptoAES = Java.use("sg.vantagepoint.a.a");
        cryptoAES.a.implementation = function(bArr, bArr2){
            console.log("[*] Hook cryptoAES")
            var secret = "";
            var decryptValue = this.a(bArr, bArr2);
            console.log("[*] DecryptValue:", decryptValue)
            for (var i=0; i < decryptValue.length; i++){
              secret += String.fromCharCode(decryptValue[i]);
            }
            console.log("[*] Secret:", secret)
            return decryptValue;
        }
    }
)

There are two ways to run the hook script. One is to use it in combination with Python, and the other is to use the script directly through the frida command. There is also a timing problem when injecting the hook code. Sometimes you need to start the hook when the APP starts, and sometimes you can wait for the APP to start and load. Then Hook, in this example, if the first and second methods are used for root detection, that is, the three Hook detection methods or the a method, then the Hook needs to be hooked when the APP is started. If the third and fourth methods are used method, namely Hook onClick() or System.exit() method, then wait for the APP to start and then Hook it.

Use with Python

First, let's take a look at how to use it with Python. The JavaScript code is as follows (frida-hook.js):

 /* ==================================
# @Time    : 2022-08-29
# @Author  : 微信公众号:K哥爬虫
# @FileName: frida-hook.js
# @Software: PyCharm
# ================================== */


Java.perform(
    function(){
        console.log("[*] Hook begin")

        // 方法一:Hook 三个检测方法,让它们都返回 false,不再执行后续的 a 方法,就不会退出 APP 了
        // var vantagePoint = Java.use("sg.vantagepoint.a.c")
        // vantagePoint.a.implementation = function(){
        //     console.log("[*] Hook vantagepoint.a.c.a")
        //     this.a();
        //     return false;
        // }
        // vantagePoint.b.implementation = function(){
        //     console.log("[*] Hook vantagepoint.a.c.b")
        //     this.b();
        //     return false;
        // }
        // vantagePoint.c.implementation = function(){
        //     console.log("[*] Hook vantagepoint.a.c.c")
        //     this.c();
        //     return false;
        // }

        // 方法二:Hook a() 方法,置空,什么都不做,不弹出对话框,也不退出 APP
        // var mainActivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
        // mainActivity.a.implementation = function(){
        //    console.log("[*] Hook mainActivity.a")
        // }

        // 方法三:Hook onClick() 方法,点击 OK 后不让其退出 APP
        // var mainActivity$1 = Java.use("sg.vantagepoint.uncrackable1.MainActivity$1");
        // mainActivity$1.onClick.implementation = function(){
        //     console.log("[*] Hook mainActivity$1.onClick")
        // }

        // 方法四:Hook System.exit 方法,点击 OK 后不让其退出 APP
        var javaSystem = Java.use("java.lang.System");
        javaSystem.exit.implementation = function(){
            console.log("[*] Hook system.exit")
        }

        var cryptoAES = Java.use("sg.vantagepoint.a.a");
        cryptoAES.a.implementation = function(bArr, bArr2){
            console.log("[*] Hook cryptoAES")
            var secret = "";
            var decryptValue = this.a(bArr, bArr2);
            console.log("[*] DecryptValue:", decryptValue)
            for (var i=0; i < decryptValue.length; i++){
              secret += String.fromCharCode(decryptValue[i]);
            }
            console.log("[*] Secret:", secret)
            return decryptValue;
        }
    }
)

The Python code is as follows (frida-hook.py):

 # ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2022-08-29
# @Author  : 微信公众号:K哥爬虫
# @FileName: frida-hook.py
# @Software: PyCharm
# ==================================


import sys
import frida


def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)


with open("./frida-hook.js", "r", encoding="utf-8") as fp:
    hook_string = fp.read()

# 方式一:attach 模式,已经启动的 APP
process = frida.get_usb_device(-1).attach("Uncrackable1")
script = process.create_script(hook_string)
script.on("message", on_message)
script.load()
sys.stdin.read()

# 方式二,spawn 模式,重启 APP
# device = frida.get_usb_device(-1)
# pid = device.spawn(["owasp.mstg.uncrackable1"])
# process = device.attach(pid)
# script = process.create_script(hook_string)
# script.on("message", on_message)
# script.load()
# device.resume(pid)
# sys.stdin.read()

In the Python code, the attach mode hooks the existing process, the spawn mode will restart the APP, start a new process and suspend it, and inject frida code at the same time of startup, which is suitable for some hooks before the process starts, and the attach mode is passed in. is the APP name, and the spawn mode passes in the APP package name. There are many ways to view the APP name and package name. Here are two frida commands, frida-ps -Uai : List installed programs, frida-ps -Ua : List the running programs, as shown in the figure below, in this example Uncrackable1 is the APP name, owasp.mstg.uncrackable1 is the package name:

13

Run the Python code. Note that frida-server should also be started on the mobile phone. After the root detection is passed, enter the string casually. Click VERIFY to hook the correct string to I want to believe , and enter the correct character again. string, the verification is successful.

14

frida command

Instead of using Python, you can also use the frida command directly. Like the previous Python, there are two modes, the same one is the APP name and the other is the package name:

frida -U Uncrackable1 -l .\frida-hook.js : attach mode, inject frida code after APP starts;

frida -U -f owasp.mstg.uncrackable1 -l .\frida-hook.js --no-pause : In spawn mode, restart the APP, and inject frida code at the same time.

15

So far, we have bypassed root detection perfectly and successfully found the correct string.


K哥爬虫
166 声望154 粉丝

Python网络爬虫、JS 逆向等相关技术研究与分享。