這篇文章最后以分析UnCrackable-Level1.apk介紹frida腳本的使用最欠,如果大佬們對前面介紹的adb、frida安裝已經(jīng)清楚,則可以直接拉到最后看UnCrackable-Level1.apk的分析。
一疏尿、安裝frida
-
在電腦上安裝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.reibang.com/p/52e9b44460d0
安裝完adb后造寝,通過數(shù)據(jù)線將手機(jī)與電腦進(jìn)行連接磕洪,手機(jī)設(shè)置允許usb調(diào)試,然后執(zhí)行adb devices命令查看手機(jī)是否已經(jīng)跟電腦連接成功诫龙,如下所示
image -
在電腦上安裝python3
windows電腦可以在https://www.python.org/downloads/windows/下載python3安裝包進(jìn)行安裝析显,如下所示
imagemac電腦安裝python3,可以通過brew install python3指令進(jìn)行安裝签赃,如下所示
image -
在電腦上安裝frida-tools
windows電腦與mac電腦都可以通過如下指令安裝frida-tools
pip3 install frida-tools
如下所示
image -
下載frida-server發(fā)送到手機(jī)中并啟動
電腦上配置完后谷异,現(xiàn)在需要下載frida-server保存到手機(jī)中分尸,并啟動frida-server,這樣電腦上的frida客戶端才能與手機(jī)中的frida服務(wù)端進(jìn)行交互
frida-server下載歹嘹,可在github中下載箩绍,鏈接如下所示
https://github.com/frida/frida/releases
下載的frida-server可根據(jù)設(shè)備cpu的型號進(jìn)行下載,如下所示
image如果覺得github下載速度慢尺上,可以在公眾號回復(fù)“frida-server”百度云下載材蛛,四個版本都打包一起哈
下載完android設(shè)備相對應(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以后臺模式運(yùn)行frida-server抽诉,這樣電腦上的frida CLI就能夠與frida-server進(jìn)行交互,我們可以在電腦上使用frida來與android設(shè)備中的app交互(當(dāng)然前提是手機(jī)要通過usb數(shù)據(jù)線連接到電腦)吐绵,如下所示
imageimage
二迹淌、frida的使用
-
frida tools主要有Frida CLI、frida-ps己单、frida-trace唉窃、frida-discover、frida-ls-devices纹笼、frida-kill等命令工具
具體的命令工具使用可以參考上篇文章 ios逆向之frida安裝與使用纹份,鏈接如下
-
除了frida命令行使用外,frida還可以通過python及JavaScript腳本來hook android設(shè)備中的應(yīng)用程序
接下來我們主要介紹如何通過python及JavaScript腳本來hook android應(yīng)用程序廷痘,以分析UnCrackable-Level1.apk為例蔓涧,找到UnCrackable-Level1.apk需要的正確的校驗字符串
-
下載安裝UnCrackable-Level1.apk
可以通過如下github鏈接下載
https://github.com/OWASP/owasp-mstg/tree/master/Crackmes/Android/Level_01
如果覺得github下載速度慢,也可以在公眾號回復(fù)"UnCrackable-Level1"進(jìn)行下載笋额。下載完以后通過adb指令 adb install UnCrackable-Level1.apk 將apk安裝到手機(jī)中元暴,如下所示
image -
運(yùn)行UnCrackable-Level1,觀察app的功能兄猩,可以發(fā)現(xiàn)該app主要是要校驗我們輸入的字符串是否正確茉盏。并出現(xiàn)了一個"Root detected!"彈框,表示app檢測到我們的android設(shè)備已root枢冤,如下所示
image 確認(rèn)我們需要完成任務(wù)即繞過app的root檢測和找出app校驗的正確的字符串是什么鸠姨,現(xiàn)在可以開始分析app
-
靜態(tài)分析app,可以通過jeb淹真、jadx讶迁、Androidkiller等工具進(jìn)行分析,這里我就使用jadx靜態(tài)分析app趟咆。我們先分析app的檢測android設(shè)備是否root的功能添瓷,如下所示
imageimageimageimage順帶看一下app中的檢測app是否被調(diào)試的方法
image靜態(tài)分析完app的root檢測方法后梅屉,我們現(xiàn)在需要通過frida腳本繞過app的檢測android設(shè)備是否root功能,代碼如下所示(我貼出所有代碼鳞贷,包括后面字符串校驗的代碼)
主要有兩種方式繞過root檢測方法
方法一:hook root檢測方法使它們的返回值始終是false坯汤,這樣則檢測不到我們的android設(shè)備已經(jīng)root
方法二:hook System.exit函數(shù),即app雖然檢測到我們的設(shè)備已經(jīng)root搀愧,但是不讓它退出惰聂,繼續(xù)運(yùn)行
script.js
setImmediate(function(){ //防止超時
console.log("[*] Staring script");
Java.perform(function(){ //JavaScript代碼成功被附加到目標(biāo)進(jìn)程時調(diào)用,我們要hook app的代碼都在Java.perform底下寫咱筛,是個固定格式
//我們通過兩種方法來繞過app的檢測android設(shè)備是否root搓幌,如果root則退出app
//方法一 修改我們上面分析的3個root檢測函數(shù)的返回值 使他們始終返回false
console.log("[*] Hooking calls to root detect");
//1.修改方法a的返回值,使返回值為false迅箩。方法a通過檢測PATH(android系統(tǒng)環(huán)境變量)中是否有su文件來判斷android系統(tǒng)是否被root
var rootDetect = Java.use("sg.vantagepoint.a.c"); //Java.use用于聲明一個Java類 這里我們聲明root檢測的類
//類.函數(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通過檢測Build.TAGS中是否包含字符串"test-keys"來判斷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通過檢測指定路徑下是否包含指定的文件來判斷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檢測到android設(shè)備root時則會調(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表示超時
pid = device.spawn([PACKAGE]) #啟動指定包名的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ù)
-
下面是執(zhí)行繞過app root檢測方法腳本后,得到的結(jié)果奕塑,如下所示
imageimageimageimage -
為了驗證app root檢測方法的正確性堂污,我們也可以到android設(shè)備中上面檢測方法涉及的目錄查看是否存在相應(yīng)的文件,如下所示
imageimageimage -
我們已經(jīng)分析完app的root檢測功能龄砰,現(xiàn)在分析app的verify算法盟猖,查看app字符串校驗的算法,確認(rèn)app需要的正確的字符串是啥换棚,如下所示
imageimageimage 分析完verify校驗算法后式镐,我們即可通過frida hook最后的aes解密算法,得到它解密后的返回值再轉(zhuǎn)成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);//執(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;
}
-
下面是執(zhí)行完aes解密方法腳本后,得到的結(jié)果颇蜡,如下所示
圖片.png
圖片.png
圖片.png
- 綜上所述价说,我們已經(jīng)通過frida腳本完成了繞過app的檢測及hook app校驗字符串的方法從而得到真正的字符串。感興趣的大佬可以試一試风秤,完成項目代碼可以在公眾號回復(fù)“codeUncrackable1Test”鳖目,通過百度云下載。