乐趣区

关于python:APP-逆向百例Frida-初体验root-检测与加密字符串定位

申明

本文章中所有内容仅供学习交换应用,不用于其余任何目标,不提供残缺代码,抓包内容、敏感网址、数据接口等均已做脱敏解决,严禁用于商业用途和非法用处,否则由此产生的所有结果均与作者无关!

本文章未经许可禁止转载,禁止任何批改后二次流传,擅自应用本文解说的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K 哥爬虫】分割作者立刻删除!

逆向指标

  • 设施:Google Pixel4,Android 10,已 root
  • APP:UnCrackable-Level1.apk(可在公众号回复 APP 获取)
  • APP 检测了 root,如果手机 root 了,会强制退出 APP,过了 root 检测后,还须要输出一个字符串进行校验。

装置 ADB

adb(Android Debug Bridge)即安卓调试桥,装置后能够在电脑上与手机进行交互,Android Studio 等工具外面会自带 adb,有时候咱们并不想下载这么大的工具,所以这里介绍一下 Android SDK Platform-Tools,它是 Android SDK 的一个组件,它包含与 Android 平台交互的工具,次要是 adb 和 fastboot,官网下载地址:https://developer.android.com…,下载实现后将该目录增加到环境变量,USB 连贯手机,手机上设置容许 USB 调试,应用命令 adb version 可查看版本信息,adb devices 能够查看以后连贯的设施,如下图所示:

装置 Frida

Frida 是一款基于 Python + JavaScript 的 Hook 与调试框架,首先电脑端应用命令 pip install frida-tools 装置 frida 模块(此命令默认会装置最新版的 frida 和 frida-tools,如),而后下载 frida-server,下载地址:https://github.com/frida/frid…

frida-server 要依据你电脑端装置的 frida 版本和手机的 CPU 架构来抉择对应的,应用命令 frida --version 能够查看 frida 版本,应用命令 adb shell 进入手机,输出 getprop ro.product.cpu.abi 查看 CPU 架构,如下图所示,我这里 frida 是 15.2.2 版本,手机 CPU 为 arm64,所以我下载的是 frida-server-15.2.2-android-arm64.xz

某些 Android 低版本应用高版本 frida 可能有问题,遇到问题可尝试升高 frida 版本来解决。

将下载好的 frida-server 应用 adb push 命令传到手机的 /data/local/tmp/ 目录下,并给予 777 读、写、执行的权限,而后间接运行 frida-server,失常不会有任何输入,当然也能够应用 & 等形式让其在后盾运行。

而后另开一个 cmd 应用命令 frida-ps -U 可查看手机过程,有输入则失常。

逆向剖析

应用 adb install 命令装置 UnCrackable-Level1.apk,关上该 APP,会检测到 root,呈现 Root detected! 的提醒,如下图所示:

应用 JEB、JADX、GDA 等工具反编译 apk,间接搜寻关键字 Root detected! 即可定位到检测的中央:

能够看到图中有三个检测办法 c.a()c.b()c.c(),其中一个返回为真,则弹出 Root detected!,而后后面还有一个 onClick 办法,如果点击 OK 按钮,则触发 System.exit(0);,即退出 APP,先点进三个检测办法看看:

a() 办法通过检测 Android 零碎环境变量中是否有 su 文件来判断是否被 root;

b() 办法通过检测 Build.TAGS 中是否蕴含字符串 test-keys 来判断是否被 root;

c() 办法通过检测指定门路下是否蕴含指定的文件来判断是否被 root。

所以咱们这里就有多种过掉检测的办法:

办法一:Hook 三个检测办法,让它们都返回 false,不再执行后续的 a 办法,就不会退出 APP 了:

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;
        }
    }
)

办法二:Hook a() 办法,置空,什么都不做,不弹出对话框,也不退出 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")
        }
    }
)

办法三:Hook onClick() 办法,点击 OK 后不让其退出 APP,留神这里是外部类的 Hook 写法:

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")
        }
    }
)

办法四:Hook System.exit() 办法,点击 OK 后不让其退出 APP:

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

root 检测过掉之后,APP 还要输出一个字符串,输出谬误会提醒 That's not it. Try again.,如下图所示:

剖析 Java 代码,有一个 if-else 判断,obj 为输出的字符串,a.a(obj) 判断为真,就示意输出正确。

跟到 a.a() 办法,能够看到 bArr 是内置的字符串,通过 equals() 办法比拟输出的 str 是否和 bArr 相等:

bArr 的值,次要通过 sg.vantagepoint.a.a.a() 办法解决后失去,持续跟进去能够发现是 AES 加密算法:

这里就能够间接 Hook sg.vantagepoint.a.a.a(),间接拿到加密后的值,也就是咱们要的正确字符串,因为这里返回的是 ASCII 码,所以咱们还须要在 JavaScript 代码中应用 String.fromCharCode() 将其转换成失常字符,Hook 代码如下:

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;
        }
    }
)

运行 Hook 脚本有两种形式,一是联合 Python 应用,二是间接通过 frida 命令应用脚本,注入 Hook 代码也有个机会问题,有时候须要在 APP 启动就开始 Hook,有时候能够等 APP 启动加载结束了再 Hook,本例中,过 root 检测的时候,如果采纳第一、二种办法,即 Hook 三个检测办法或者 a 办法,那就须要在 APP 启动的时候就 Hook,如果采纳第三、四种办法,即 Hook onClick() 或者 System.exit() 办法,那么等 APP 启动了再 Hook 也能够。

联合 Python 应用

首先来看一下联合 Python 怎么应用,JavaScript 代码如下(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;
        }
    }
)

Python 代码如下(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()

Python 代码中,attach 模式 Hook 曾经存在的过程,spawn 模式会重启 APP,启动一个新的过程并挂起,在启动的同时注入 frida 代码,实用于在过程启动前的一些 Hook,attach 模式传入的是 APP 名称,spawn 模式传入的是 APP 包名,查看 APP 名称和包名的办法有很多,这里介绍两个 frida 命令,frida-ps -Uai:列出装置的程序,frida-ps -Ua:列出正在运行中的程序,如下图所示,本例中 Uncrackable1 就是 APP 名称,owasp.mstg.uncrackable1 就是包名:

运行 Python 代码,留神手机端也要启动 frida-server,过掉 root 检测后,先轻易输出字符串,点击 VERIFY 就会 Hook 到正确的字符串为 I want to believe,再次输出正确的字符串,即可验证胜利。

frida 命令

不应用 Python,也能够间接应用 frida 命令来实现,和后面 Python 一样也有两种模式,同样的一个是 APP 名一个是包名:

frida -U Uncrackable1 -l .\frida-hook.js:attach 模式,APP 启动后注入 frida 代码;

frida -U -f owasp.mstg.uncrackable1 -l .\frida-hook.js --no-pause:spawn 模式,重启 APP,启动的同时注入 frida 代码。

至此,咱们完满绕过了 root 检测,并胜利找到了正确的字符串。

退出移动版