說到逆向APP,不少人首先想到的都是反編譯,可是單看反編譯出來的代碼很可貴知某個函數在被調用時所傳入的參數和它返回的值,極大地增長了逆向時的複雜度,有沒有什麼辦法能夠方便地知道被傳入的參數和返回值呢?javascript
答案是有的,這個方法就是Hook,Hook的原理簡單地說就是用一個新的函數替代掉原來的函數,在這個新的函數中你想作什麼均可以,隨心所欲。java
本文中的Frida就是一個很經常使用的Hook工具,只須要編寫一段Javascript代碼就能輕鬆地對指定的函數進行Hook,並且它基本上能夠算是全平臺的(主流平臺全覆蓋),除了Android之外,iOS和PC端的APP也能夠用它來進行Hook,很是方便。python
那麼怎麼使用呢?首先咱們在Frida官方文檔中的Installation頁能夠看到,咱們須要有Python環境,而且用pip安裝一個叫frida-tools的庫,而後才能夠開始使用。git
Python環境相信你們都有了,直接打開命令行,執行一波pip install frida-tools
吧。github
安裝完畢之後,由於這一頁文檔的下半部分用於測試剛裝好的庫是否可用的話過於麻煩,咱們這裏就直接使用frida-ps
命令來測試吧。shell
看起來是沒問題了,而後咱們怎麼Hook Android手機上的APP呢?別急,還須要在手機上作一些操做你才能這麼作。api
咱們須要有一臺已經Root了的Android手機,由於不一樣型號的手機Root方法存在差別,本文中就不指導如何對手機進行Root操做了,請自行經過搜索引擎查找方法。實在沒有能夠Root的Android手機的話能夠選擇使用模擬器,推薦使用Genymotion之類系統較爲原生的模擬器,並將Android版本選擇在6.0以上,不然可能會出現一些奇奇怪怪的問題。bash
手機準備好了以後,找到Frida文檔中Tutorials欄裏的Android頁,開始進行Frida的手機端準備工做。網絡
文檔中能看到,Frida官方最近的大部分測試都是在運行着Android 9的Pixel 3上進行的,因此理論上來說若是你在使用中遇到任何奇怪的問題,均可能是你手機的系統致使,因此這裏再次建議,使用較爲原生和偏高版本的系統(建議至少6.0)進行操做。架構
接着往下看,咱們須要在手機上以Root權限運行一個叫frida-server的東西,這個東西須要在Frida官方的GitHub倉庫中下載,文檔中有連接能夠直接跳轉。
打開GitHub以後你會發現,這裏有不少個不一樣的版本,應該下載哪個呢?
能夠看到這一排的文件中,末尾處都有個系統和CPU架構的標識,咱們直接看Android的。這裏標了Android的一共有四個,而後X86的由於不是手機使用的CPU架構,能夠直接pass掉,剩下的就是arm和arm64兩種了,那麼咱們須要怎麼判斷本身的手機/模擬器屬於哪種CPU架構的呢?
查CPU架構的方法不少,這裏介紹一個比較方便快捷的——使用一個名叫Device Info HW的APP。
安裝後打開它,在芯片欄中咱們能夠看到一個叫ABI的東西,右邊就是咱們手機的CPU架構了,以下:
因此這裏我須要下載的是arm64版本的frida-server,下載後解壓出來一個沒後綴的文件,而後咱們須要將這個文件放入手機中運行起來。先把這個文件的名字改爲frida-server
,而後在這個文件所在的目錄下打開命令行(Windows下爲Shift+鼠標右鍵,選擇「在此處打開CMD/PowerShell」),執行如下命令:
adb root
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"
複製代碼
若是你的手機和個人同樣,直接這麼運行會提示權限不足的話,能夠先進入adb shell
,在執行su
命令獲取Root權限後(手機端可能會彈出Root受權提示),再運行/data/local/tmp/frida-server &
啓動frida-server。
啓動後,咱們先照慣例來測試一下是否能正常使用了,和前面同樣,使用frida-ps
命令,但在後面加一個-U
參數,這個參數的意思是讓它對USB鏈接的設備操做,若是不出意外的話,你應該能看到與不加-U
參數時大相徑庭的顯示。
至此,全部準備工做均已完成。
小提示:在手機重啓後須要從新運行一次frida-server,但能夠不從新執行adb push
操做,由於文件已經放進去了。
終於到了喜聞樂見的實戰環節了,就拿Frida官方文檔中的提到的CTF APP來開刀吧,找到文檔中Examples欄裏的Android頁,通過幾回跳轉後下載APP安裝、打開,會看到這樣的一個界面:
這個APP是一個石頭剪子布的遊戲,點擊下面三個按鈕分別選擇石頭、剪子、布,玩起來的時候是這麼一個效果(加號後面的是得分值,正常狀況下連勝會每次在原來的基礎上+1):
咱們先作個比較簡單的操做吧,讓咱們的每次出招都必勝~先複製一下文檔中的代碼,建一個.py文件粘貼進去,將this.cnt.value = 999;
這一條刪除或註釋掉,而後運行這個python腳本,在注入完成後,無論你怎麼點,你都是一定勝利的,以下圖:
注:圖中左下方顯示的是Hook時產生的日誌,其中value是得分值。
可是這樣子弄,若是咱們須要讓分值達到很高的話,就須要點不少次了,怎麼讓它一次就加到999呢?很簡單,直接把得分值也給改了就行了,咱們把前面去掉的this.cnt.value = 999;
再改回來,而後從新運行一遍這個腳本。
正常狀況下這個分值會是一個+999,這裏顯示成這樣是由於這個樣例APP太老了,不兼容新版本系統,致使出現這種狀況,換舊版本系統可解,因此這裏不糾結這個問題。
單看這麼一通操做是否是以爲很懵?複製過來的代碼是幹啥的都不知道,若是換一個APP咋搞?不慌,我把這個代碼的意思一行一行地給你解釋一遍,你就知道怎麼用了。
首先import不用說了吧,你們都懂,直接看on_message這個函數。這個on_message的用途是接收下面Javascript代碼中調用send函數傳出的日誌,一般咱們能夠不用管它,直接複製出來用就好了,或者可使用console.log打日誌,效果也是差很少的。
而後是jscode這個變量,這個變量其實建議使用一個單獨的.js文件代替,由於這樣的話可使用各類編輯器、IDE的JavaScript代碼格式化、智能提示等功能,寫起來會舒服不少。若是你要替換掉的話,改爲讀JS代碼文件以後read出內容字符串賦值給jscode就好了。
接着是JS代碼中的部分。
先看看Java.perform
,在Frida官方文檔的javascript-api頁中能夠看到,它的用途是確保當前線程已鏈接到VM用的,因此咱們直接照着這麼用就好了;
而後看看Java.use
這個函數,它的用途是獲取一個指向某個類的指針,參數中的com.example.seccon2015.rock_paper_scissors.MainActivity
就是咱們須要Hook的那個類;
接着就是真正執行Hook的部分了,這個代碼中使用了一個MainActivity.onClick.implementation
,意思就是Hook前面獲取到的類中的onClick方法,後面跟着的賦值函數的部分,函數的參數爲對應要Hook方法的參數,內部執行的部分就是在對應方法被調用時所執行的代碼,這裏它是先打了一個onClick
日誌,而後調用了原始方法(若是不調用的話原始方法不會被執行),接着它將m、n、cnt(變量具體含義請自行反編譯APP後查看代碼)的值作了修改,最後,它又打了一個攜帶着cnt變量值的日誌。
最後是一些常規操做,frida.get_usb_device().attach()
是獲取指定APP(參數爲包名)當前運行着的進程,process.create_script(jscode)
、script.load()
是將前面的JS代碼注入進去,script.on('message', on_message)
是設置消息傳出時的回調,最後的sys.stdin.read()
是輸出日誌用的。
總之,除了JS代碼部分,其餘的其實只是個殼子,核心的Hook操做邏輯全在JS代碼中,咱們在使用時通常只改JS代碼部分和指定包名的部分就能夠了。
看了這一篇文章後你應該會對使用Frida對Android手機上Java層的Hook有所瞭解了吧,若是以爲玩Frida官方文檔中的這個石頭剪子布APP不夠刺激,還能夠看看我前面的《當你寫爬蟲遇到APP的請求有加密參數時該怎麼辦?【初級篇-秒殺模式】》這篇文章,裏面使用的DEMO APP是有SSL Pinning、且對代碼進行了混淆的,但願你可以觸類旁通,本身寫出一個幹掉這個SSL Pinning的腳本。(若是還不會的話能夠看更前面的《當你寫爬蟲抓不到APP請求包的時候該怎麼辦?【高級篇-混淆致使通用Hook工具失效】》這篇文章)
發送消息「Frida Android初級篇」到個人公衆號【小周碼字】便可得到代碼和APP的下載地址~
這個時代各類東西變化太快,而網絡上的垃圾信息又不少,你須要有一個良好的知識獲取渠道,不少時候早就是一種優點,還不趕忙關注個人公衆號並置頂/星標一波~