android逆向之frida安装与使用基础

这篇文章最后以分析UnCrackable-Level1.apk介绍frida脚本的使用,如果大佬们对前面介绍的adb、frida安装已经清楚,则可以直接拉到最后看UnCrackable-Level1.apk的分析。

一、安装frida

  1. 在电脑上安装adb

    安装adb是为了在电脑上通过adb与手机进行交互。手机需要通过usb连接(adb也支持通过wifi连接,但最开始都需要通过usb连接一次)

    windows电脑安装adb,可以通过安装android sdk,然后在android sdk目录中的platform-tools目录中找到adb.exe,将该目录设置为系统环境变量,即可在命令行中使用adb,具体安装方法可以百度或者Google一下参考

    mac安装adb可以参考如下链接

    https://www.jianshu.com/p/52e9b44460d0

    安装完adb后,通过数据线将手机与电脑进行连接,手机设置允许usb调试,然后执行adb devices命令查看手机是否已经跟电脑连接成功,如下所示

    image
  2. 在电脑上安装python3

    windows电脑可以在https://www.python.org/downloads/windows/下载python3安装包进行安装,如下所示

    image

    mac电脑安装python3,可以通过brew install python3指令进行安装,如下所示

    image
  3. 在电脑上安装frida-tools

    windows电脑与mac电脑都可以通过如下指令安装frida-tools

    pip3 install frida-tools

    如下所示

    image
  4. 下载frida-server发送到手机中并启动

    电脑上配置完后,现在需要下载frida-server保存到手机中,并启动frida-server,这样电脑上的frida客户端才能与手机中的frida服务端进行交互

    frida-server下载,可在github中下载,链接如下所示

    https://github.com/frida/frida/releases

    下载的frida-server可根据设备cpu的型号进行下载,如下所示

    image

    如果觉得github下载速度慢,可以在公众号回复“frida-server”百度云下载,四个版本都打包一起哈

    下载完android设备相对应的frida-server后,通过adb push将frida-server发送到android设备中(通常将frida-server保存在/data/local/tmp目录中),如下所示

    image

    修改frida-server的权限,使frida-server具有执行权限,如下所示

    image

    以后台模式运行frida-server,这样电脑上的frida CLI就能够与frida-server进行交互,我们可以在电脑上使用frida来与android设备中的app交互(当然前提是手机要通过usb数据线连接到电脑),如下所示

    image
    image

二、frida的使用

  1. frida tools主要有Frida CLI、frida-ps、frida-trace、frida-discover、frida-ls-devices、frida-kill等命令工具

    具体的命令工具使用可以参考上篇文章 ios逆向之frida安装与使用,链接如下

    frida tools命令工具的使用

  2. 除了frida命令行使用外,frida还可以通过python及JavaScript脚本来hook android设备中的应用程序

    接下来我们主要介绍如何通过python及JavaScript脚本来hook android应用程序,以分析UnCrackable-Level1.apk为例,找到UnCrackable-Level1.apk需要的正确的校验字符串

  3. 下载安装UnCrackable-Level1.apk

    可以通过如下github链接下载

    https://github.com/OWASP/owasp-mstg/tree/master/Crackmes/Android/Level_01

    如果觉得github下载速度慢,也可以在公众号回复"UnCrackable-Level1"进行下载。下载完以后通过adb指令 adb install UnCrackable-Level1.apk 将apk安装到手机中,如下所示

    image
  4. 运行UnCrackable-Level1,观察app的功能,可以发现该app主要是要校验我们输入的字符串是否正确。并出现了一个"Root detected!"弹框,表示app检测到我们的android设备已root,如下所示

    image
  5. 确认我们需要完成任务即绕过app的root检测找出app校验的正确的字符串是什么,现在可以开始分析app

  6. 静态分析app,可以通过jebjadxAndroidkiller等工具进行分析,这里我就使用jadx静态分析app。我们先分析app的检测android设备是否root的功能,如下所示

    image
    image
    image
    image

    顺带看一下app中的检测app是否被调试的方法

    image

    静态分析完app的root检测方法后,我们现在需要通过frida脚本绕过app的检测android设备是否root功能,代码如下所示(我贴出所有代码,包括后面字符串校验的代码)

    主要有两种方式绕过root检测方法

    方法一:hook root检测方法使它们的返回值始终是false,这样则检测不到我们的android设备已经root

    方法二:hook System.exit函数,即app虽然检测到我们的设备已经root,但是不让它退出,继续运行

    script.js

setImmediate(function(){ //防止超时
    console.log("[*] Staring script");

    Java.perform(function(){ //JavaScript代码成功被附加到目标进程时调用,我们要hook app的代码都在Java.perform底下写,是个固定格式
        //我们通过两种方法来绕过app的检测android设备是否root,如果root则退出app

        //方法一 修改我们上面分析的3个root检测函数的返回值 使他们始终返回false

        console.log("[*] Hooking calls to root detect");
        //1.修改方法a的返回值,使返回值为false。方法a通过检测PATH(android系统环境变量)中是否有su文件来判断android系统是否被root
        var rootDetect = Java.use("sg.vantagepoint.a.c"); //Java.use用于声明一个Java类 这里我们声明root检测的类
        //类.函数.overload(参数类型).implementation = function(形参名称){
        rootDetect.a.implementation = function(){ //这里我们需要hook的是rootDetect类中的a方法 a方法没有参数因此overload可以不用写
        //function中不用写形参名称
            console.log("-----hook su finder-----");
            var suFinder = this.a(); //执行a方法并返回a的返回值Boolean值
            console.log("su finder original return value is: ",suFinder.toString()); //打印返回值
            suFinder = false; //修改返回值为false
            console.log("su finder new return value is: ",suFinder.toString()); //打印修改的返回值
            return suFinder; //返回我们修改的值 使a函数始终返回false
        }

        //2.修改方法b的返回值,使返回值为false。方法b通过检测Build.TAGS中是否包含字符串"test-keys"来判断android系统是否被root
        rootDetect.b.implementation = function(){ //这里我们需要hook的是rootDetect类中的b方法 b方法没有参数因此overload可以不用写
        //function中不用写形参名称
            console.log("-----hook test-keys finder------");
            var testKeysFinder = this.b(); //执行b方法并返回b的返回值Boolean值
            console.log("test-keys finder original return value is: ",testKeysFinder.toString()); //打印返回值
            testKeysFinder = false; //修改返回值为false
            console.log("test-keys finder new return value is: ",testKeysFinder.toString()); //打印修改的返回值
            return testKeysFinder; //返回我们修改的值 使b函数始终返回false
        }

        //3.修改方法c的返回值,使返回值为false。方法c通过检测指定路径下是否包含指定的文件来判断android系统是否被root
        rootDetect.c.implementation = function(){ //这里我们需要hook的是rootDetect类中的c方法 c方法没有参数因此overload可以不用写
        //function中不用写形参名称
            console.log("-----hook superuser file finder-----");
            var superuserFileFinder = this.c(); //执行c方法并返回c的返回值Boolean值
            console.log("superuser file finder original return value is: ",superuserFileFinder.toString()); //打印返回值
            superuserFileFinder = false; //修改返回值为false
            console.log("superuser file finder new return value is: ",superuserFileFinder.toString()); //打印修改的返回值
            return superuserFileFinder; //返回我们修改的值 使c函数始终返回false
        }


        //方法二 修改System.exit函数使app不退出 当app检测到android设备root时则会调用System.exit函数退出app
        /*console.log("[*] Hooking calls to System.exit");
        var exitClass = Java.use("java.lang.System"); //声明System类
        exitClass.exit.implementation = function(){ //这里我们需要hook的是System类中的exit方法 exit方法没有参数因此overload可以不用写
        //function中不用写形参名称
            console.log("[*] System.exit called");
        }*/


        //hook app正确字符串生成算法得到解密后的正确字符串
        console.log("[*] Hooking calls to AES decrypt");
        var aesDecrypt = Java.use("sg.vantagepoint.a.a");
        aesDecrypt.a.overload('[B',"[B").implementation = function(arg1,arg2){
            console.log("[*]Hook a.Class");
            var decryptValue = this.a(arg1,arg2);
            var secret = '';
            for(var i=0;i

verifyString.py

# -*- coding: utf-8 -*-

import frida
import sys

PACKAGE = 'owasp.mstg.uncrackable1' #需要hook的app包名

if __name__ == '__main__':
    jscode = open('script.js','r',encoding='UTF-8').read() #获取js脚本内容
    device = frida.get_usb_device(1000) #连接usb设备 1000表示超时
    pid = device.spawn([PACKAGE]) #启动指定包名的app
    session = device.attach(pid) #附加到app
    script = session.create_script(jscode) #创建frida javaScript脚本
    script.load() #加载脚本
    device.resume(pid) #恢复app运行
    sys.stdin.read() #阻塞接收数据
  1. 下面是执行绕过app root检测方法脚本后,得到的结果,如下所示

    image
    image
    image
    image
  2. 为了验证app root检测方法的正确性,我们也可以到android设备中上面检测方法涉及的目录查看是否存在相应的文件,如下所示

    image
    image
    image
  3. 我们已经分析完app的root检测功能,现在分析app的verify算法,查看app字符串校验的算法,确认app需要的正确的字符串是啥,如下所示

    image
    image
    image
  4. 分析完verify校验算法后,我们即可通过frida hook最后的aes解密算法,得到它解密后的返回值再转成String后,就是我们想得到的正确的校验字符串,代码如下

//hook app正确字符串生成算法得到解密后的正确字符串
console.log("[*] Hooking calls to AES decrypt");
var aesDecrypt = Java.use("sg.vantagepoint.a.a");
aesDecrypt.a.overload('[B',"[B").implementation = function(arg1,arg2){
console.log("[*]Hook a.Class");
var decryptValue = this.a(arg1,arg2);//执行原函数得到返回值byte[]
var secret = '';
for(var i=0;i
  1. 下面是执行完aes解密方法脚本后,得到的结果,如下所示


    图片.png

    图片.png

    图片.png
  1. 综上所述,我们已经通过frida脚本完成了绕过app的检测及hook app校验字符串的方法从而得到真正的字符串。感兴趣的大佬可以试一试,完成项目代码可以在公众号回复“codeUncrackable1Test”,通过百度云下载。

你可能感兴趣的:(android逆向之frida安装与使用基础)