android逆向之frida安裝與使用基礎(chǔ)

這篇文章最后以分析UnCrackable-Level1.apk介紹frida腳本的使用,如果大佬們對(duì)前面介紹的adb、frida安裝已經(jīng)清楚,則可以直接拉到最后看UnCrackable-Level1.apk的分析。

一、安裝frida

  1. 在電腦上安裝adb

    安裝adb是為了在電腦上通過adb與手機(jī)進(jìn)行交互。手機(jī)需要通過usb連接(adb也支持通過wifi連接,但最開始都需要通過usb連接一次)

    windows電腦安裝adb,可以通過安裝android sdk,然后在android sdk目錄中的platform-tools目錄中找到adb.exe,將該目錄設(shè)置為系統(tǒng)環(huán)境變量,即可在命令行中使用adb,具體安裝方法可以百度或者Google一下參考

    mac安裝adb可以參考如下鏈接

    http://www.itdecent.cn/p/52e9b44460d0

    安裝完adb后,通過數(shù)據(jù)線將手機(jī)與電腦進(jìn)行連接,手機(jī)設(shè)置允許usb調(diào)試,然后執(zhí)行adb devices命令查看手機(jī)是否已經(jīng)跟電腦連接成功,如下所示

    image
  2. 在電腦上安裝python3

    windows電腦可以在https://www.python.org/downloads/windows/下載python3安裝包進(jìn)行安裝,如下所示

    image

    mac電腦安裝python3,可以通過brew install python3指令進(jìn)行安裝,如下所示

    image
  3. 在電腦上安裝frida-tools

    windows電腦與mac電腦都可以通過如下指令安裝frida-tools

    pip3 install frida-tools

    如下所示

    image
  4. 下載frida-server發(fā)送到手機(jī)中并啟動(dòng)

    電腦上配置完后,現(xiàn)在需要下載frida-server保存到手機(jī)中,并啟動(dòng)frida-server,這樣電腦上的frida客戶端才能與手機(jī)中的frida服務(wù)端進(jìn)行交互

    frida-server下載,可在github中下載,鏈接如下所示

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

    下載的frida-server可根據(jù)設(shè)備cpu的型號(hào)進(jìn)行下載,如下所示

    image

    如果覺得github下載速度慢,可以在公眾號(hào)回復(fù)“frida-server”百度云下載,四個(gè)版本都打包一起哈

    下載完android設(shè)備相對(duì)應(yīng)的frida-server后,通過adb push將frida-server發(fā)送到android設(shè)備中(通常將frida-server保存在/data/local/tmp目錄中),如下所示

    image

    修改frida-server的權(quán)限,使frida-server具有執(zhí)行權(quán)限,如下所示

    image

    以后臺(tái)模式運(yùn)行frida-server,這樣電腦上的frida CLI就能夠與frida-server進(jìn)行交互,我們可以在電腦上使用frida來(lái)與android設(shè)備中的app交互(當(dāng)然前提是手機(jī)要通過usb數(shù)據(jù)線連接到電腦),如下所示

    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腳本來(lái)hook android設(shè)備中的應(yīng)用程序

    接下來(lái)我們主要介紹如何通過python及JavaScript腳本來(lái)hook android應(yīng)用程序,以分析UnCrackable-Level1.apk為例,找到UnCrackable-Level1.apk需要的正確的校驗(yàn)字符串

  3. 下載安裝UnCrackable-Level1.apk

    可以通過如下github鏈接下載

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

    如果覺得github下載速度慢,也可以在公眾號(hào)回復(fù)"UnCrackable-Level1"進(jìn)行下載。下載完以后通過adb指令 adb install UnCrackable-Level1.apk 將apk安裝到手機(jī)中,如下所示

    image
  4. 運(yùn)行UnCrackable-Level1,觀察app的功能,可以發(fā)現(xiàn)該app主要是要校驗(yàn)我們輸入的字符串是否正確。并出現(xiàn)了一個(gè)"Root detected!"彈框,表示app檢測(cè)到我們的android設(shè)備已root,如下所示

    image
  5. 確認(rèn)我們需要完成任務(wù)即繞過app的root檢測(cè)找出app校驗(yàn)的正確的字符串是什么,現(xiàn)在可以開始分析app

  6. 靜態(tài)分析app,可以通過jeb、jadxAndroidkiller等工具進(jìn)行分析,這里我就使用jadx靜態(tài)分析app。我們先分析app的檢測(cè)android設(shè)備是否root的功能,如下所示

    image
    image
    image
    image

    順帶看一下app中的檢測(cè)app是否被調(diào)試的方法

    image

    靜態(tài)分析完app的root檢測(cè)方法后,我們現(xiàn)在需要通過frida腳本繞過app的檢測(cè)android設(shè)備是否root功能,代碼如下所示(我貼出所有代碼,包括后面字符串校驗(yàn)的代碼)

    主要有兩種方式繞過root檢測(cè)方法

    方法一:hook root檢測(cè)方法使它們的返回值始終是false,這樣則檢測(cè)不到我們的android設(shè)備已經(jīng)root

    方法二:hook System.exit函數(shù),即app雖然檢測(cè)到我們的設(shè)備已經(jīng)root,但是不讓它退出,繼續(xù)運(yùn)行

    script.js

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

    Java.perform(function(){ //JavaScript代碼成功被附加到目標(biāo)進(jìn)程時(shí)調(diào)用,我們要hook app的代碼都在Java.perform底下寫,是個(gè)固定格式
        //我們通過兩種方法來(lái)繞過app的檢測(cè)android設(shè)備是否root,如果root則退出app

        //方法一 修改我們上面分析的3個(gè)root檢測(cè)函數(shù)的返回值 使他們始終返回false

        console.log("[*] Hooking calls to root detect");
        //1.修改方法a的返回值,使返回值為false。方法a通過檢測(cè)PATH(android系統(tǒng)環(huán)境變量)中是否有su文件來(lái)判斷android系統(tǒng)是否被root
        var rootDetect = Java.use("sg.vantagepoint.a.c"); //Java.use用于聲明一個(gè)Java類 這里我們聲明root檢測(cè)的類
        //類.函數(shù).overload(參數(shù)類型).implementation = function(形參名稱){
        rootDetect.a.implementation = function(){ //這里我們需要hook的是rootDetect類中的a方法 a方法沒有參數(shù)因此overload可以不用寫
        //function中不用寫形參名稱
            console.log("-----hook su finder-----");
            var suFinder = this.a(); //執(zhí)行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函數(shù)始終返回false
        }

        //2.修改方法b的返回值,使返回值為false。方法b通過檢測(cè)Build.TAGS中是否包含字符串"test-keys"來(lái)判斷android系統(tǒng)是否被root
        rootDetect.b.implementation = function(){ //這里我們需要hook的是rootDetect類中的b方法 b方法沒有參數(shù)因此overload可以不用寫
        //function中不用寫形參名稱
            console.log("-----hook test-keys finder------");
            var testKeysFinder = this.b(); //執(zhí)行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函數(shù)始終返回false
        }

        //3.修改方法c的返回值,使返回值為false。方法c通過檢測(cè)指定路徑下是否包含指定的文件來(lái)判斷android系統(tǒng)是否被root
        rootDetect.c.implementation = function(){ //這里我們需要hook的是rootDetect類中的c方法 c方法沒有參數(shù)因此overload可以不用寫
        //function中不用寫形參名稱
            console.log("-----hook superuser file finder-----");
            var superuserFileFinder = this.c(); //執(zhí)行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函數(shù)始終返回false
        }


        //方法二 修改System.exit函數(shù)使app不退出 當(dāng)app檢測(cè)到android設(shè)備root時(shí)則會(huì)調(diào)用System.exit函數(shù)退出app
        /*console.log("[*] Hooking calls to System.exit");
        var exitClass = Java.use("java.lang.System"); //聲明System類
        exitClass.exit.implementation = function(){ //這里我們需要hook的是System類中的exit方法 exit方法沒有參數(shù)因此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<decryptValue.length;i++){
                secret+=String.fromCharCode(decryptValue[i]);
            }
            console.log("secret is : " + secret);
            return decryptValue;
        }
    });
})

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腳本內(nèi)容
    device = frida.get_usb_device(1000) #連接usb設(shè)備 1000表示超時(shí)
    pid = device.spawn([PACKAGE]) #啟動(dòng)指定包名的app
    session = device.attach(pid) #附加到app
    script = session.create_script(jscode) #創(chuàng)建frida javaScript腳本
    script.load() #加載腳本
    device.resume(pid) #恢復(fù)app運(yùn)行
    sys.stdin.read() #阻塞接收數(shù)據(jù)
  1. 下面是執(zhí)行繞過app root檢測(cè)方法腳本后,得到的結(jié)果,如下所示

    image
    image
    image
    image
  2. 為了驗(yàn)證app root檢測(cè)方法的正確性,我們也可以到android設(shè)備中上面檢測(cè)方法涉及的目錄查看是否存在相應(yīng)的文件,如下所示

    image
    image
    image
  3. 我們已經(jīng)分析完app的root檢測(cè)功能,現(xiàn)在分析app的verify算法,查看app字符串校驗(yàn)的算法,確認(rèn)app需要的正確的字符串是啥,如下所示

    image
    image
    image
  4. 分析完verify校驗(yàn)算法后,我們即可通過frida hook最后的aes解密算法,得到它解密后的返回值再轉(zhuǎn)成String后,就是我們想得到的正確的校驗(yàn)字符串,代碼如下

//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);//執(zhí)行原函數(shù)得到返回值byte[]
var secret = '';
for(var i=0;i<decryptValue.length;i++){//將返回值byte[]轉(zhuǎn)成String
  secret+=String.fromCharCode(decryptValue[i]);
}
console.log("secret is : " + secret);//打印正確的字符串
return decryptValue;
}
  1. 下面是執(zhí)行完aes解密方法腳本后,得到的結(jié)果,如下所示


    圖片.png

    圖片.png

    圖片.png
  1. 綜上所述,我們已經(jīng)通過frida腳本完成了繞過app的檢測(cè)及hook app校驗(yàn)字符串的方法從而得到真正的字符串。感興趣的大佬可以試一試,完成項(xiàng)目代碼可以在公眾號(hào)回復(fù)“codeUncrackable1Test”,通過百度云下載。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容