日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

安卓逆向_24( 一 ) --- Hook 框架 frida( Hook Java层 和 so层) )

發布時間:2024/7/23 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 安卓逆向_24( 一 ) --- Hook 框架 frida( Hook Java层 和 so层) ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

From:Hook 神器家族的 Frida 工具使用詳解:https://blog.csdn.net/FlyPigYe/article/details/90258758

詳解 Hook 框架 frida ( 信搶紅包 ):https://www.freebuf.com/company-information/180480.html

APP逆向神器之Frida【Android初級篇】:https://www.jianshu.com/p/2d755beb1c54

frida 官網文檔:https://frida.re/docs/home/

《FRIDA操作手冊》:https://python.ctolib.com/hookmaster-frida-all-in-one.html

FridaApp_Python的Hook腳本.zip:鏈接: https://pan.baidu.com/s/196-f9xggQ6QNNavmwiP8PA 提取碼: vzuh

?

利用Frida繞過Android App(途牛apk)的SSL Pinning:https://blog.csdn.net/weixin_44677409/article/details/106650473

Frida從入門到入門—安卓逆向菜鳥的 frida 使用說明:https://bbs.pediy.com/thread-226846.htm

嗶哩嗶哩視頻教程:
? ? ? ? frida?java?層?hook:https://www.bilibili.com/video/BV1UE411A7rW?p=78
? ? ? ? frida native?層?hook:https://www.bilibili.com/video/BV1UE411A7rW?p=79

關鍵字:frida hook? ??fridaapp.apk? ? frida工具使用詳解

?

Android 之 Frida 框架:https://mp.weixin.qq.com/s?__biz=MzU5Mjg5NjMyNA==&mid=2247484311&idx=1&sn=f18d19fd7cbf72c66beba9455caf24cc

?

雷電3版本模擬器上 frida-server 跑不起來。雷電4模擬器可以。https://blog.csdn.net/ugooo/article/details/112045478
但是 雷電4模擬器沒法設置代理進行抓包。?https://www.ldmnq.com/forum/thread-67291-1-1.html
所以抓包可以用雷電3,運行frida-server 可以用 雷電4

?

?

一、前言

?

? ? ? ? 說到逆向APP,很多人首先想到的都是反編譯,但是單看反編譯出來的代碼很難得知某個函數在被調用時所傳入的參數和它返回的值,極大地增加了逆向時的復雜度,有沒有什么辦法可以方便地知道被傳入的參數和返回值呢?答案是有的,這個方法就是Hook,Hook的原理簡單地說就是用一個新的函數替代掉原來的函數,在這個新的函數中你想做什么都可以,為所欲為。

? ? ? ? 在逆向過程中有一個 Hook 神器是必不可少的工具,之前已經介紹了 Xposed 和 Substrate 了,不了解的可以看這兩篇文章:Android中Hook神器Xposed工具介紹?和?Android中Hook神器SubstrateCydia工具介紹?這兩篇文章非常重要,一個是 Hook Java層的時候最常用的 Xposed ,?一個是Hook Native層常用的SubstrateCydia,可以看之前的文章比如寫微信插件等都采用了Xposed工具,因為個人覺得Xposed用起來比較爽,寫代碼比較方便。而對于SubstrateCydia工具可以 Hook Native 層。

? ? ? ? 那么有了這兩個神器為啥還要介紹 Frida工具呢?而且這個工具網上已經有介紹了,為什么還有介紹了,因為這個Frida工具對于逆向者操作破解來說非常方便,所謂方便是他的安裝環境和配置要求都非常簡單兼容性也非常好,因為最近在弄一個協議解密,無奈手機上安裝 Cydia 之后不兼容導致死機所以就轉向用了這個工具實現了 hook,所以覺得這個工具非常好用就單獨介紹一下。

? ? ? ? Frida 也是一個很常用的 Hook 工具,只需要編寫一段 Javascript 代碼就能輕松地對指定的函數進行 Hook,而且它基本上可以算是全平臺的(主流平臺全覆蓋),除了 Android 以外,iOS 和 PC 端的APP也可以用它來進行Hook,非常方便。

?

1、Hook是個什么鬼?

  Hook 翻譯過來就是 "鉤子" 的意思,鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。Hook 技術無論對安全軟件還是惡意軟件都是十分關鍵的一項技術,其本質就是 劫持函數調用 。

?

2、Frida框架的那些事

  frida 是一款基于 python 和 java 的 hook 框架,是一種動態插樁工具,可以插入代碼到原生App的內存空間中,動態的監視和修改其行行為,可運行在Android、iOS、Linux和windows等多個平臺。

插樁技術 是指將額外的代碼注入程序中以收集運行時的信息,可分為兩種:

  • 1. 源代碼插樁【Source Code Instrumentation(SCI)】:額外代碼注入到程序源代碼中。
  • 2. 二進制插樁【Binary Instrumentation】:額外代碼注入到二進制可執行文件中,其又可分為兩種:
    ? ? ● 靜態二進制插樁 ?【?Static Binary Instrumentation(SBI) 】:
    ?? ??? ? ? ? ? ?在程序執行前插入額外的代碼和數據,生成一個永久改變的可執行文件。
    ? ? ● 動態二進制插樁??【?Dynamic Binary Instrumentation(DBI) 】:
    ?? ??? ? ? ? ? ?在程序運行時實時地插入額外代碼和數據,對可執行文件沒有任何永久改變。

使用 Frida 框架到底能做什么呢 ?

  • (1)訪問進程的內存,提取我們感興趣的信息或敏感信息
  • (2)在應用程序運行時覆蓋一些功能,改變其程序運行邏輯
  • (3)從導入的類中調用函數
  • (4)在堆上查找對象實例并使用這些對象實例
  • (5)Hook,動態跟蹤、攔截變量和函數 等等。

?

?

二、環境安裝配置

 

如何讓 Frida 奔跑起來 ?

用到的 frida 框架分為兩部分:

  • 一部分是 "客戶端", 即:用于連接遠程設備,提交要注入的 JS 代碼到服務端,并接受服務端發來的消息;?
  • 另一部分是 "服務器端",?即:注入JS代碼到目標進程,操作內存數據,并將相關信息發送至給客戶端。

官網安裝說明

?

frida

? ? ? ? 環境要求
? ? ? ?   系統環境? – Windows、macOS、Linux
? ? ? ?   Python? ? ?– 最新的3.x版本,
? ? ? ?   Adb環境? ?– 請自行下載adb工具或安裝 android studio 工具? ? ? ?   
? ? ? ? ? ? 環境準備好了,讓我們來安裝 frida CLI 吧!
? ? ? ? ? ? 安裝 frida CLI 有很多種方法,這里只介紹兩種,即:pip 和 npm
? ? ? ??
? ? ? ? pip 安裝 frida  ? ??
? ? ? ?   ? ? 執行 pip install frida
? ? ? ?   ? ? 執行 pip install frida-tools
? ? ? ?   ? ? 執行 frida -version
? ? ? ??
? ? ? ? npm 安裝 frida,首先安裝 NodeJS
? ? ? ?   ? ? 執行 npm install frida
? ? ? ?   ? ? 執行 npm install frida-tools
? ? ? ?   ? ? 執行 frida --version

執行一波?pip install frida-tools,安裝完畢以后,測試是否安裝正確,因為官網安裝文檔的下半部分是用于測試剛裝好的庫是否可用,但是比較麻煩,這里可以直接使用?frida-ps?命令來測試(顯示本機的進程)

如果加上參數 U 表示的是 usb 連接的設備。(?模擬器也是 -U 參數)

frida-ps -aU

看起來是沒問題了,然后我們怎么Hook Android手機上的 APP 呢?別急,還需要在手機上做一些操作你才能這么做。

我們需要有一臺已經Root了的Android手機,因為不同型號的手機Root方法存在差異,本文中就不指導如何對手機進行Root操作了,請自行通過搜索引擎查找方法。實在沒有可以Root的Android手機的話可以選擇使用模擬器,推薦使用Genymotion之類系統較為原生的模擬器,并將Android版本選擇在6.0以上,否則可能會出現一些奇奇怪怪的問題。

手機準備好了之后,找到 Frida 文檔中 Tutorials 欄里的Android頁,開始進行Frida的手機端準備工作。

文檔中能看到,Frida官方最近的大部分測試都是在運行著Android 9的Pixel 3上進行的,所以理論上來講如果你在使用中遇到任何奇怪的問題,都可能是你手機的系統導致,所以這里再次建議,使用較為原生和偏高版本的系統(建議至少6.0)進行操作

?

Frida 環境

1. 手機運行服務端 2. 電腦端運行客戶端,電腦端再進行端口轉發adb forward tcp:27042 tcp:27042adb forward tcp:27043 tcp:27043

?

frida-server

環境要求
? ? ? ?   手機一部,本文主要講述 Android系統。手機必須被 Root,具體 Root 方法請自行查詢? ? ? ??
????????  下載 frida-server ,地址:https://github.com/frida/frida/releases

?

安裝方法(?先要看構架:adb shell getprop ro.product.cpu.abi

什么是 Android ABI ?

ABI 就是 Application binary interface 的意思,即應用程序二進制接口。他定義了一套規則,允許編譯好的二進制目標代碼能在所有兼容該ABI的操作系統中無需改動就能運行。不同的Android手機使用不同的CPU,因此需要提供對應的二進制接口交互規則(即對應的ABI文件)才能進行交互。目前,有部分CPU是能支持多種交互規則,但這是在犧牲性能的前提下所做的兼容。

ABI架構

API架構說明
armeabiv-v7a第7代及以上的 ARM 處理器。目前,大部分手機的CPU都支持此ABI交互規則
arm64-v8a第8代64位ARM處理器,支持此ABI的手機還比較少
armeabi第5代、第6代的ARM處理器,早期手機用得較多
x86平板、模擬器用得比較多
x86_6464位的平板

ABI 與 CPU 的聯系

在 Android 手機上安裝一個應用時,只有手機CPU架構支持的ABI架構對應的.so文件會被安裝。如果支持多個ABI架構,會按照優先級進行安裝。

CPU架構支持的ABI架構對應的.so文件安裝優先級
ARMv5armeabi
ARMv7armeabi,armeabi-v7a
ARMv8armeabi,armeabi-v7a,arm64-v8a
MIPSmips
MIPS64mips,mips64
x86x86,armeabi,armeabi-v7a
x86_64:armeabi,x86,x86_64

CPU大都是向前兼容的,但是會按優先級來選擇ABI。如x86架構的CPU會優先選擇x86目錄下的.so包,當不存在時,才會選擇armeabi-v7a目錄下的.so包,如果仍然不存在,最后才會選擇armeabi目錄下的.so文件。
注:x86設備運行armeabi下的so庫時會損失性能。

?

打開 GitHub 之后你會發現,這里有很多個不同的版本,應該下載哪一個呢?

可以看到末尾處都有個 系統CPU架構 的標識,我們直接看 Android 的。

這里標了Android的一共有四個,怎么判斷自己的手機/模擬器屬于哪一種CPU架構的呢?

查CPU架構的方法很多,這里介紹一個比較方便快捷的——使用一個名叫 Device Info HW 的 APP。

Device Info HW 下載地址:https://www.cr173.com/soft/845060.html

? ? ? ? ? ? ? ? 下載的文件名的格式是: frida-server-(version)-(platform)-(cpu).xz?
? ? ? ? ? ? ? ? 例如:手機是 nexus6p, cpu 為 arm64
? ? ? ? ? ? ? ? 需要下載的是: frida-server-11.0.13-android-x86_64.xz;
? ? ? ? ? ? ? ? 注意, frida-server 的版本一定要跟 frida CLI 的版本一致。

????????  執行以下命令
????????????????adb devices
????????????????adb push path/to/frida-server /data/local/tmp
????????????????adb shell
????????????????su
????????????????cd /data/local/tmp
????????????????chmod a+x ./frida-server? ? ? ? ?或者? ?chmod 777?./frida-server
????????????????./frida-server? ? ? ? ? ? ? ? ? ? ? ? ? ? 或者? ?./frida-server &

?

frida tools

Frida 提供的工具,frida-trace,frida-ps,frida,frida-discover,這些工具都位于 python 的 Scripts 路徑下,

frida 的工具共有 6 個:

(1)frida 命令是一個交互式解釋器(REPL),用于實現 Hook 代碼的動態注入,他的交互形式跟 IPython 很類似。? ? ? ? ?

? ? ? ? ? 命令幫助:

示例:

將一個腳本注入到 Android 目標進程:frida -U -l myhook.js com.xxx.xxxx

參數解釋:

  • -U 指定對 USB 設備操作
  • -l? ?指定加載一個 Javascript 腳本
  • 最后指定一個進程名,如果想指定進程 pid,用?-p?選項。正在運行的進程可以用?frida-ps -U?命令查看

frida 運行過程中,執行?%resume?重新注入,執行?%reload?來重新加載腳本;執行?exit?結束腳本注入

?

(2) frida-ps: 用于列出進程的一個命令行工具,當我們需要跟遠程系統進行交互的時候,這個是非常有用的。

(備注:這里的 com.android.chrome 就是通過 frida-ps 命令查詢到的遠程客戶端進程。)

另外還有四個分別是: frida-trace,frida-discover,?frida-ls-devices,?frida-kill

由于不是經常用到,這邊就不一一詳細介紹了, 感興趣的可以去 frida 的官網查看他們的詳細介紹和用法。

?

常用 Java API

官網也有文檔說明:https://www.frida.re/docs/javascript-api,

  由于 frida 的注入腳本是 Java,因此在開始 frida 初探之前,有必要對 Java 注入相關的 API 做一個簡單介紹, 后面我們都將通過 js 腳本來操作設備上的 Java 代碼。

  當我們使用 Java.use() 獲取 Java類之后,我們就可以通過 class.method.implementations = function() {} 的形式 hook 某類的某方法,不管是實例方法還是靜態方法都可以。

  另外,由于 js 代碼注入時可能會出現超時的錯誤, 為了防止這個問題,我們通常還需要在最外面包裝一層setImmediate(function(){}) 的代碼,如下所示:

?

?

初探 Frida:Hello World 程序

?

apk 下載地址:https://github.com/OWASP/owasp-mstg/tree/master/Crackmes/Android

github 下載網速有點感人。鏈接: https://pan.baidu.com/s/1zYd4PmMQNlGqhzEUtXee6w 提取碼: 6y8e

CTF之安卓逆向 uncrackme level1https://blog.csdn.net/nini_boom/article/details/104565943/

Frida Hook app簡單教程:https://blog.csdn.net/PLA12147111/article/details/100525588

?

  • 注入模式
  • 嵌入模式
  • 預加載模式
  • 注入模式

    • 大部分情況下,我們都是附加到一個已經運行到進程,或者是在程序啟動到時候進行劫持,然后再在目標進程中運行我們的代碼邏輯。這種方式就是Frida最常用的使用方式,因此我們的文檔也把大部分的篇幅集中在這種方式上。
    • 注入模式的大致實現思路是這樣的,帶有GumJS的Frida核心引擎被打包成一個動態連接庫,然后把這個動態連接庫注入到目標進程中,同時提供了一個雙向通信通道,這樣你的控制端就可以和注入的模塊進行通信了,在不需要的時候,還可以在目標進程中把這個注入的模塊給卸載掉。
    • 除了上述功能,Frida還提供了枚舉已經安裝的App列表,運行的進程列表已經已經連接的設備列表,這里所說的設備列表通常就是frida-server 所在的設備。frida-server 是一個守護進程,通過TCP和Frida核心引擎通信,默認的監聽端口是27042。

    嵌入模式

    • 在實際使用的過程中,你會發現在沒有 root 過的iOS Android設備上你是沒有辦法對進程進行注入的,也就是說第一種注入模式失效了,這個時候嵌入模式就派上用場了。Frida提供了一個動態連接庫組件 frida-gadget, 你可以把這個動態庫集成到你程序里面來使用Frida的動態執行功能。一旦你集成了gadget,你就可以和你的程序使用Frida進行交互,并且使用 frida-trace 這樣的功能,同時也支持從文件自動加載Js文件執行JS邏輯。
    • 關于 Gadget 更多功能,請參考原文鏈接(https://www.frida.re/docs/modes)

    預加載模式

    • 大家應該熟悉 LD_PRELOAD,或者 DYLDINSERTLIBRARIES吧, 如果有 JS_PRELOAD 這種東西是不是更爽了呢!事實上對于我們上面討論的 Gadget 是支持這種模式的, 這才是Gadget 的強大之處,通過這種模式你就可以達到自動加載Js文件并執行的目的了。
    • 譯者注:這種模式我沒測試,我猜測主要目的是說可以執行那些在程序入口點執行之前就需要執行的JS邏輯,而注入模式是在入口點跑過之后才能執行JS邏輯的,這個是最大的區別吧,大家最好再看下原文,避免被我誤導)

    ?

      通過上面的介紹,現在終于可以來真格的了。而這里所說的 Hello World 程序,和我們日??淳幊虝f的有所不同,我們上網找了一個 OWASP Uncrackable Crackme Level 1 的 APP,并對其 "防Root檢測"?功能進行了Hook。

    將 UnCrackable-Level1.apk 安裝到安卓設備( 可以直接拖進模擬器安裝?):adb install UnCrackable-Level1.apk

    打開運行,存在 root 檢測,由于我的模擬器是已經 root,所以點擊 OK,程序它就自己退出了。

    ***********? 注意:這里使用的是 "雷電模擬器4",3版本運行?frida-server-12.11.17-android-x86 報錯??***********

    ?

    ?

    方法 1( 使用 jadx-gui?或者 jeb 打開并自動反編譯?apk ):

    ?

    使用 jadx-gui? 或者 jeb 打開 apk ,自動反編譯,然后查看 java 源碼。這里使用 jadx-gui 打開 apk 并自動反編譯。。。

    關鍵函數:

    在 MainActivity 類中可以看到,函數 a 打開了一個對話框,并設置了一個 onClickListener監聽,當按 ok 按鈕時,onClick 事件函數就會被觸發。onClick事件函數只是簡單的使用 system.exit ( 0 ) 來退出 app 。

    因此只需要不調用 system.exit ( 0 ) 阻止 App退出即可。所以需要 hook 住 onClick 方法。。。。。

    frida_hook_java.py 代碼:

    ? ? ? ? hook 匿名內部類:
    ? ? ? ? https://stackoverflow.com/questions/60236107/how-to-hook-into-onclick-method-inside-a-method-using-frida

    import sys import fridajs_code = ''' Java.perform(function(){console.log("[*] START...")var mClass = Java.use("sg.vantagepoint.uncrackable1.MainActivity$1")mClass.onClick.implementation=function() {console.log("[*] Clicked ")} }); '''def on_message(msg, data): # js中執行 send 函數后要回調的函數if msg['type'] == 'send':print(f'[*] {msg["payload"]}')else:print(msg)if __name__ == '__main__':# get_remote_device 獲取遠程設備 (get_usb_device)  attach 附加進程process = frida.get_remote_device().attach('owasp.mstg.uncrackable1')script = process.create_script(js_code)script.on('message', on_message) # 綁定一個事件script.load()sys.stdin.read()pass

    說明:on_message 的用途是接收 Javascript 代碼中調用 send 函數傳出的日志,可以不用管它,或者可以使用 console.log 打日志,效果也是差不多的。然后就是 jscode 這個變量,這個變量其實建議使用一個單獨的 .js 文件代替,因為這樣的話可以使用各種編輯器、IDE的JavaScript代碼格式化、智能提示等功能,寫起來會舒服很多。如果你要替換掉的話,改成讀 JS 代碼文件之后 read 出內容字符串賦值給 jscode 就行了。這里直接使用一個變量保存 js ,沒有使用 單獨的 js 文件保存 js 代碼。。。

    import sys import fridadef read_file_all(file_path):with open(file_path, 'r', encoding='utf-8') as f:text = f.read()return textdef on_message(message, data):if message['type'] == 'error':print("[!] " + message['stack'])elif message['type'] == 'send':print(message['payload'])if data is not None:print("[data] " + format_bytes(data))else:print(message)def format_bytes(byte_array):string = '['for b in byte_array:string = string + str(b) + ','return string[:len(string) - 1] + "]"if __name__ == '__main__': device = frida.get_usb_device()process = device.attach("com.xxxx.xxxx")js_code = read_file_all("hookMD5.js")script = process.create_script(js_code)script.on('message', on_message)script.load()sys.stdin.read()

    打開兩個 cmd 窗口,一個執行 frida-server 對應的版本,一個執行 端口轉發,如圖:

    然后在模擬器中打開 apk,再執行上面的 python 代碼,可以看到,點擊 OK 后 apk 不會退出,hook 成功

    ?

    ?

    方法 2(?dex2jar classes.dex -o classes.jar ):

    ?

    將 APK 文件擴展名修改為.zip,然后找到 classes.dex 文件將其編譯為 classes.jar 文件。

    dex2jar classes.dex -o classes.jar

    編譯完后,使用 JD-GUI 打開 classes.jar 文件,查找關鍵字,這里的關鍵字是 “Root detected”。

    a 方法中關鍵代碼,可以看到 new 了一個 b ,然后把 b 類的實例轉換成 OnClickListener。?

    js 代碼

    現在讓我們運行此 App 程序,它依然彈出了 Root檢測的對話框。

    現在讓我們來運行 frida 注入程序,然后點擊 OK 按鈕,神奇的事情發生了,App 并沒有退出,我們正常的進入了App界面。

    frida注入界面:

    frida 注入后的 App 界面

      至此,我們以一個實例展示了frida框架的強大和神奇之處,這個Hook功能雖然簡單,但是同樣體現了整個的Hook過程,所謂麻雀雖小,但五臟俱全。

    ?

    ?

    示例 2:crackme 自毀程序密碼

    Frida Hook和Xposed Hook 再搞Crackme :https://www.52pojie.cn/thread-1315865-1-1.html

    阿里 crackme 的新解法 --- frida:https://www.52pojie.cn/thread-608505-1-1.html

    一個簡單 CrackMe 的逆向總結:https://blog.csdn.net/u012551350/article/details/98871295

    crackme.apk:鏈接: https://pan.baidu.com/s/15Fwl0_MeZAr9ejGA9vh9sw? ? 提取碼: 6666

    安裝完?crackme.apk 之后,打開 app ,隨意輸入,然后點擊 按鈕

    使用 jadx-gui 打開 apk,查看 AndroidManifest.xml

    找到?com.yaotong.crackme.MainActivity,然后進入?MainActivity 查看源碼

    只要?securityCheck 函數 返回 true 即可。。。

    使用 frida 開始 hook

    import frida # 導入frida模塊 import sys # 導入sys模塊# 從此處開始定義用來Hook的javascript代碼 js_code = """ Java.perform(function() {// 獲取當前安卓設備的安卓版本var v = Java.androidVersion;send('version:' + v);//獲取該應用加載的類var class_names = Java.enumerateLoadedClassesSync(); for (var i = 0; i < class_names.length; i++){send('class name:' + class_names[i])}var MainActivity = Java.use('com.yaotong.crackme.MainActivity'); //獲得MainActivity類var java_string = Java.use('java.lang.String');MainActivity.securityCheck.implementation = function(java_string){ send('I am here'); // 發送信息,用于回調python中的函數return true; //劫持返回值,修改為我們想要返回的字符串}}); """def on_message_1(msg, data):if msg['type'] == 'send':print(f'[*] {msg["payload"]}')else:print(msg)def on_message(msg, data): # js中執行send函數后要回調的函數print(msg)# 得到設備并劫持進程 com.example.testfrida # (剛開始用get_usb_device函數用來獲取設備,但是一直報錯找不到設備,改用get_remote_device函數即可解決這個問題) process = frida.get_remote_device().attach('com.yaotong.crackme') script = process.create_script(js_code) # 創建js腳本 script.on('message', on_message) # 加載回調函數,也就是js中執行send函數規定要執行的python函數 script.load() # 加載腳本 sys.stdin.read()

    上傳 frida-server ,添加權限 chmod 777 frida-server-xxxx-android-x86,最后運行 frida-server。

    在打開一個 cmd 窗口,執行端口轉發:?adb forward tcp:27042 tcp:27042

    效果:任意輸入,點擊按鈕都可以通過。

    ?

    frida 遠程連接手機 --- frida局域網連接 遠程注入

    frida 遠程連接手機 frida局域網連接 遠程注入:https://www.jianshu.com/p/ed5ff5e9b952

    大家使用frida 進行hook的時候大部分時候是用usb數據線去連接。
    可是總是有一些比較坑爹的時候 比如設備根本不能連接數據線 比如一些iot設備 或者 數據線連接時斷時續比較感人,這個時候就可以用到frida 遠程連接了

    大家好,我是王鐵頭 一個乙方安全公司搬磚的菜雞,持續更新移動安全,iot安全,編譯原理相關原創視頻文章
    視頻演示:https://space.bilibili.com/430241559

    主要使用場景:

  • usb無法使用
  • usb連接不穩定
  • usb同時hook多個設備
  • 使用依賴

  • pc和遠程設備要安裝frida環境
  • 處在同一局域網環境下 (非局域網我自己沒測試 測試了我再來改)
  • frida_server要以root權限執行
  • ?

    frida遠程連接的兩種方式

    ?

    1. 命令行遠程連接:

    1) 遠程設備端啟動 frida_server。這里端口可以加參數去修改 如果不加參數默認是 27042

    //這里的 6666端口可以自定義 不加的話是 27042 frida:/data/local/tmp # ./fs_1413_a64 -l 0.0.0.0:6666

    2) pc 端執行 frida -H 主機IP:端口

    這里演示一下 查看進程 和 注入腳本的命令。查看進程

    //查看進程的命令 frida-ps -H 192.168.2.102:6666

    frida 遠程連接手機

    注入腳本

    //注入腳本的命令 frida -H 192.168.2.102:6666 -f com.wangtietou.test_activity -l C:\\Users\\wangtietou\\Desktop\\hook_activity.js --no-pause

    這里的腳本很簡單 就是在主界面加載的時候 打印一行日志
    ====> on create
    腳本代碼:

    var str_name_class = "com.wangtietou.test_activity.MainActivity";Java.perform(function() {var obj = Java.use(str_name_class);obj.onCreate.implementation = function (arg){console.log('====> on create');return this.onCreate(arg);} });

    執行結果:

    ?

    2. python 腳本遠程連接

    這里除了命令行直接執行js腳本 大家還經常使用 Python腳本使用frida
    這里的腳本是hook Test類的 t1方法 當調用t1方法的時候 會打印一行日志:
    ====> t1

    1) usb 連接時候的 python 腳本的注入代碼

    import frida import sysrdev = frida.get_usb_device() session = rdev.attach("com.wangtietou.test_activity") #要hook的程序包名scr = """ var str_name_class = "com.wangtietou.test_activity.Test";Java.perform(function() {var obj = Java.use(str_name_class);obj.t1.implementation = function (){console.log('====> t1');return this.t1();} }); """script = session.create_script(scr) def on_message(message ,data):print (message) script.on("message" , on_message) script.load() sys.stdin.read()

    2) 無線連接時候的 python 腳本的注入代碼

    import frida import sysstr_host = '192.168.2.102:6666' manager = frida.get_device_manager() remote_device = manager.add_remote_device(str_host) session = remote_device.attach("com.wangtietou.test_activity")scr = """ var str_name_class = "com.wangtietou.test_activity.Test";Java.perform(function() {var obj = Java.use(str_name_class);obj.t1.implementation = function (){console.log('====> t1');return this.t1();} }); """script = session.create_script(scr) def on_message(message ,data):print (message) script.on("message" , on_message) script.load() sys.stdin.read()

    這兩個腳本大部分代碼都是相同的 不同點在于連接設備的代碼部分

    連接 usb 的設備代碼

    rdev = frida.get_usb_device() session = rdev.attach("com.wangtietou.test_activity")

    連接遠程設備的代碼

    str_host = '192.168.2.102:6666' manager = frida.get_device_manager() remote_device = manager.add_remote_device(str_host) session = remote_device.attach("com.wangtietou.test_activity")

    這里執行注入的 python 腳本,先啟動設備端的 frida_server

    執行遠程注入腳本:

    成功執行。持續更新移動安全,iot安全,編譯原理相關原創視頻文章。。。視頻演示:[https://space.bilibili.com/430241559]

    ?

    ?

    三、Java 層 Hook 操作案例分析

    ?

    下面主要從以下幾個方面來介紹:

    • 第一、如何修改Java層的函數參數和返回值
    • 第二、如何打印Java層的方法堆棧信息
    • 第三、如何攔截native層的函數參數和返回值

    對于 Java 層會注重介紹,因為我們用過 Xposed 工具之后都知道,比如參數是自定義類型怎么Hook等。

    因為 Frida 大致原理是手機端安裝一個 server 程序,然后把手機端的端口轉到 PC端,PC端寫 python 腳本進行通信,而 python 腳本中需要 hook 的代碼采用 javascript 語言。

    所以這么看來我們首先需要安裝 PC端的 python 環境,安裝 frida 直接運行命令:pip install frida?

    安裝完成之后,我們再去官網下載對應版本的手機端程序 frida-server:https://github.com/frida/frida/releases?

    注意:frida-server 版本 和 PC端安裝的 frida 版本必須一致,不然運行報錯的。其實這里看到真的實現hook功能的是手機端的frida-server,這個也是開源的大家可以研究他的原理。我們也看到這個工具和 IDA 是不是很類似,也是把手機端的端口轉發到PC端進行通信而已。

    有了 frida-server 之后就好辦了,直接 push 到手機目錄下,然后修改一下文件的屬性即可:

    adb push C:\frida-server /data/local/tmpchmod 777 /data/local/tmp/frida-server# 然后直接運行這個程序: /data/local/tmp# ./frida-server# 然后 端口轉發 adb forward tcp:27042 tcp:27042 adb forward tcp:27043 tcp:27043

    接下來就開始 在PC端開始編寫 hook 程序進行操作了:

    這里代碼也非常簡單,因為安裝好了 frida 模塊,直接導入模塊,然后調用 api 獲取設備的 session 然后 hook 程序包名,接著就可以執行 js 腳本代碼進行 hook 操作,然后打印消息:

    這里用了 python 的 print 函數打印,其實如果想要打印可以在上面的 js 腳本中使用 console.log 也是可以的,看自己的習慣了。

    可以看到腳本的大致流程就是:最外面用 python 引用 frida 庫進行和設備通信,然后編寫 js 腳本執行 hook 操作。

    所以這里最主要的還是 js 腳本也就是需要理解 js 語法了。

    下面就開始分部拆解操作,看看如何涵蓋我們平常使用的 hook 案例。

    Utils 類:

    CoinMoney 類:

    ?

    ?

    第一個案例:hook類的構造方法

    ?

    ? ? ? ? 我們有時候想 hook 一個類的構造方法,在 Xposed 中直接用 findConstructor 方法就可以了,因為構造方法可能有多種重載形式,所以需要用參數作為區分。這里 hook 案例的 CoinMoney 類 的 構造方法:

    ? ? ? ? 首先腳本中使用 Java.use 方法通過類名獲取類類型,構造方法是固定寫法:$init;這個要記住,然后因為需要重載所以用overload(......)形式即可,參數和參數之間用逗號隔開即可。后面就是攔截之后的操作了,這里方法參數可以自定義變量名,因為js是弱語言,不對類型做強檢查,當然這里還有其他獲取參數的方法后面會介紹。

    ? ? ? ? 這里使用 send 來發送打印消息即可,當然也可以用 console.log 形式打印日志,代碼編寫完了,下面就開始運行看效果,運行也很簡單,直接 python frida.py:

    在這之前一定要先打開 hook 的應用,不然會報錯提示找不到這個程序進程:

    這時候在運行看到了就成功了,我們把構造方法的參數打印出來了,那么這里 hook 就成功了。所以可以看到這個操作是不是比Xposed 工具更方便呢。但是他也有弊端后面會總結的。

    ?

    ?

    第二、hook類的普通方法( 靜態、私有、公開?)

    ?

    這里的普通方法包括了 靜態方法私有方法?、公開方法 等,這個操作和上面的構造方法其實很類似

    代碼如下:

    這個就是把構造方法的固定寫法 $init 改成了需要 hook? 的方法名即可。如果方法有重載形式還是用 overload 進行區分即可,比如這里我們 hook 了 Uitls.getPwd(String pwd) 方法:

    然后這里我們看到可以用一個隱含的變量 arguments 獲取參數,這個是保存了方法的參數信息是系統自帶的。所以我們有兩種方式獲取方法的參數信息。運行看一下效果:

    看到打印消息,hook成功了。所以這里就把 hook 方法獲取參數的案例都介紹完了。

    總結一下很簡單:

    • 構造方法使用固定寫法 $init。
    • 其他方法全部用 方法名 即可。
    • 如果方法有重載形式需要用 overload 形式操作參數用逗號分隔。
    • 獲取參數可以自定義參數名或者用系統隱含的 arguments 變量獲取。
    • 當然,上面的所有操作之前,都需要用 Java.use 通過類名獲取類型。

    ?

    ?

    第三、修改方法的參數和返回值

    ?

    我們在使用 Xposed 進行 hook 的時候最常用的可能就是修改參數和返回值來實現插件和外掛功能了,在Frida中其實也可以做到但是和 Xposed不一樣,我們從上面的代碼可以看到,沒有像 Xposed 的 before 方法和 after 方法,而 Frida 直接是你可以在function 中調用原來的方法這樣來進行參數修改,比如這里我要修改上面的方法參數和返回值:

    因為 Frida 中沒有 before 和 after 方法,但是可以直接調用原來的方法其實 Xposed 中也可以可以直接調用原來的方法的,但是不怎么常用,只要可以調用原來的方法,那么參數和返回值就可以隨意修改了,這里我們把參數改成 jiangwei212,返回值后面追加 yyyy ,看打印的日志:

    其實這么做比 before 和 after 形式更為方便,而且可以在原始方法調用前做一些事情和后面做一些事情。

    ?

    ?

    第四、構造和修改自定義類型對象和屬性

    ?

    我們在 Xposed 寫外掛的時候也會遇到這種比較常見的問題,就是方法的參數不是基本類型是自定義類型,然后也想修改他的屬性值或者調用他的一個方法我們會使用反射來進行操作,而在返回值的時候,想構造一個自定義類型的對象也是直接用反射實例化一個對象進行操作的。其實在這里因為js中也是支持反射操作的,所以就很簡單了:

    這里構造一個對象其實很簡單直接固定寫法 $new 即可,然后有了對象也可以直接調用其對應的方法即可,然后就是如何修改一個對象類型的字段值呢?這個就要用反射了:

    這里我們攔截了 getCoinMoney 方法,參數是 CoinMoney 類型,我們想修改他的money字段值,這時候我們直接調用他的方法沒什么問題,但是如果直接調用字段值或者修改就會出現失敗了,所以只能通過反射去修改字段值,不過要先獲取這個對象對應的class類型,用Java.cast接口就可以,然后獲取反射字段直接修改即可,這里要注意不管字段是private還是public的寫法都是一樣的,都是這段代碼大家要注意把這段代碼記住即可。我們看看hook之后的結果:

    如果沒有用反射去操作直接獲取字段值打印就是object了。

    ?

    ?

    第五、打印方法的堆棧信息

    ?

    我們在破解過程中有時候通過拋出異常來打印堆棧信息跟蹤代碼效率會更高,Xposed 中操作很方便直接 Java 代碼用 Log.xxx 方法打印堆棧信息即可,但是在 Frida 中有點麻煩了,因為他是js代碼不好操作,第一次想到的辦法就是自己寫一個打印堆棧信息的類然后弄成一個dex之后,把這個dex注入到程序中,因為Frida支持把一個dex文件注入到原始程序中運行的,注入之后在需要打印堆棧信息的方法中調用這個dex中的那個方法就可以了。具體怎么注入本文不多介紹了。當時覺得這種方案太麻煩了,那么還有其他方案嗎?其實還是有的,因為我們既然可以構造一個對象那么為什么不直接構造一個 Exception 對象呢?其實操作很簡單,首先我們用 Java.use 方法獲取類型變量:var Exception = Java.use("java.lang.Exception"); 然后是js中支持 throw 語法的,直接在需要打印堆棧信息的方法中調用即可:

    不過這個是真得拋出異常了,沒有捕獲住,所以程序崩潰,我們在開發 Android 應用的時候如果程序崩潰了最快的查看異常信息的方法就是用日志過濾方式:adb logcat -s AndroidRuntime

    這樣我們就把堆棧信息打印出來了,其實這里可以看到這個是真的一個崩潰異常了,因為沒有 catch 所以直接用系統崩潰日志就可以查看了。這種方式最簡單粗暴了。對于跟蹤代碼非常有用的。

    ?

    到這里我們就把所有可能遇到的情形Java層hook操作都介紹完了,主要包括以下幾種常見情形:

    • 第一、Hook類的構造方法和普通方法,注意構造方法是固定寫法$init即可,獲取參數可以通過自定義參數名也可以直接用系統隱含的arguments變量獲取即可。
    • 第二、修改方法的參數和返回值,直接調用原始方法傳入需要修改的參數值和直接修改返回值即可。
    • 第三、構造對象使用固定寫法$new即可。
    • 第四、如果需要修改對象的字段值需要用反射去進行操作。
    • 第五、堆棧信息打印直接調用Java的Exception類即可,通過adb logcat -s AndroidRuntime來過濾日志信息查看崩潰堆棧信息。

    總結:記得用 Java.use 方法獲取類的類型,如果遇到重載的方法用 overload 實現即可。

    ?

    示例代碼:

    # -*- coding: UTF-8 -*-import sys import fridajs_code = """ if (Java.available) {Java.perform(function () {var MainActivity = Java.use("com.lanshifu.demo_module.ui.activity.DemoMainActivity");MainActivity.testCrash.overload("int").implementation = function (chinese, math) {console.log("[javascript] testCrash method be called.");send("hook testCrash method success>>>");return this.testCrash(12345678);}//聲明一個Java類var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");//hook 無參構造MyClass.$init.overload().implementation = function () {//調用原來構造this.$init();send("hook 無參構造 ");}//hook 有參構造,入參是 int[]MyClass.$init.overload('[I').implementation = function (param) {send("hook 有參構造 init(int[] i) method ");}//hook 多個參數構造,入參是 int,intMyClass.$init.overload("int", "int").implementation = function (param1, param2) {send("hook success ");send(param1);send(param2);//修改返回值return 100;}//void函數MyClass.method1.overload().implementation = function () {send("hook method1 ");this.method1();}//有入參的函數MyClass.method2.overload("int", "int").implementation = function (param1, param2) {send("hook method2 ");//實例化一個類并調用它的method3方法var ClassName = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");var instance = ClassName.$new();instance.method3(1, 2, 3);this.method2(100, 100)}//MyClass.method3.overload("int", "int", "int").implementation = function (param1, param2, param3) {send("hook method3 ");this.method3(100, 100, 100)}}); } """def on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)pass# 查找 USB設備并附加到目標進程 session = frida.get_usb_device().attach('com.lanshifu.demo_module') script = session.create_script(js_code) # 在目標進程里創建腳本 script.on('message', on_message) # 注冊消息回調 print('[*] Start attach') script.load() # 加載創建好的javascript腳本 sys.stdin.read() # 讀取系統輸入

    ?

    ?

    四. Native層 Hook 操作案例分析

    ?

    ? ? ? ? 下面繼續來看 Frida 更強大的地方就是 hook native 代碼,說的強大不是因為功能,而是便捷程度,我們之前 hook native 可能用Cydia 比較多,但是都知道 Cydia 和 Xposed 一樣都有兼容問題,環境安裝配置太麻煩了,而 Frida 還是只需要幾行 js 代碼即可搞定,

    ? ? ? ? 這里 hook native 還是用兩個案例介紹:一個是hook導出的函數,一個是hook未導出的函數,通過獲取參數和修改返回值來演示,這里我們不自己寫 native 代碼了,直接用之前破解快手的數據請求的 so文件,他有一個函數在底層獲取字符串信息,還有一個是最近正在研究的資訊類app的加密算法so,我們修改他的函數返回值。

    ?

    ?

    第一、hook未導出函數功能

    ?

    未導出的函數我們需要手動的計算出函數地址,然后將其轉化成一個 NativePointer 的對象然后進行 hook 操作,那么如何計算一個函數地址呢?這個很簡單只要得到 so 的內存基地址加上函數的相對地址就可以了。基地址獲取直接查看程序對應的 maps 文件即可:

    相對地址直接用 IDA 打開 so 文件就可以查看,比如這里我們通過靜態分析之后想 hook 這個 sub_5070 函數:

    然后我們 F5 查看函數對應的 C 語言代碼查看參數信息:

    這里看到是三個參數,那么計算了后的實際地址就是 0x7816A000+5070=0x7816F070,不過這個地址不是最后的地址,因為thumb 和 arm 指令的區分,地址最后一位的奇偶性來進行標志,所以這里還需加1也就是最終的0x7816F071,這一點很重要不管使用 Cydia 還是 Frida 都要注意最后計算的絕對地址要 +1,不然會報錯的:

    這里 hook 之后有兩個回調方法一個是進入函數之前,一個是執行完之后,這個和 Xposed 非常類似了,我們打印參數,不過這個和之前Hook Java層就不一樣了,因為在C中大部分都是和地址指針相關,特別是常見的字符串信息,我們如果要正確的打印字符串值就需要借助 Memory系統類來通過指針獲取字符串信息了,這個類非常重要,在后面修改返回值也是用它寫內存值的。我們先看看這個函數原始返回值是什么:

    這個是加密之后的值了,然后我們獲取到參數了,而通過 IDA 分析之后發現這個函數最終的結果不是通過 return 來返回的,而是通過第三個指針參數返回的,因為 C中有一個參數傳值功能,就是直接操作指針就可以傳回結果,這個在C中經常用到,因為一個函數返回值只有一處要是一個函數有多個返回值就沒辦法了,所以可以通過參數指針來傳遞。所以如果我們想修改函數的最終結果,需要修改參數指針的內存段數據,我們先把那個內存段數據獲取到打印出來,這里因為通過靜態分析知道最終的結果是16個字節數據,所以這里不能在用讀取內存字符串方法了,而是讀取純的字節數據:

    然后在把返回值修改了,返回值修改也很簡單,直接重寫那段內存值就可以了,比如這里修改成1111:

    所以看到了C語言中很多地方都在直接操作內存也就是地址,特別需要借助 Memory類,他有很多方法,包括內存拷貝等。具體用到的可以去官網查詢:https://www.frida.re/docs/javascript-api/#memory;然后我們看hook結果:

    我們hook到了他的參數信息,第一個參數是需要加密的字符串信息我們是通過Memory方法獲取字符串的,因為本身這個參數是一個字符串指針,第二個參數應該是字符串長度,第三個參數是操作結果值的指針,然后看到我們獲取到的結果值就是原始加密的信息。說明我們獲取成功了,然后再看看我們修改之后的1111值,通過日志查看:

    看到了在 Java 成通過 native 訪問得到的簽名信息已經被修改成了1111了,說明我們成功了。到這里我們就成功的,在hook native的時候一定要注意函數的絕對地址要計算對,最后一定要記住+1,函數的返回值有可能不是通過return而是參數指針傳遞的,操作內存的時候用 Memory類即可。

    ?

    ?

    在雷電模擬器上 hook native層? ?

    ?

    apk 和 源碼地址:鏈接: https://pan.baidu.com/s/1pcd-sHa9tgGqwCxjpVMrAQ 提取碼: 6ztm

    模擬器運行 apk 截圖:

    注意:雷電模擬器是 x86 架構,所以需要使用 IDA Pro 打開 x86 的 so 文件,找到 函數偏移地址,然后再通過? "?基址 + 函數偏移地址 " 得到函數加載到內存后的真實地址,hook 這個函數的真實地址。。。

    函數偏移地址:

    frida_hook_native.py 代碼:

    import sys import fridajs_code = ''' setImmediate(function(){send("start");// 遍歷模塊,找基址Process.enumerateModules({onMatch:function(exp){if(exp.name == "libnative-lib.so"){send("enumerateModules find");send(exp.name + "|" + exp.base + "|" + exp.size + "|" + exp.path);send(exp);return "stop";} },onComplete:function(){send("enumerateModules stop");}});// hook 導出函數var exports = Module.enumerateExportsSync("libnative-lib.so");for (var i = 0; i < exports.length; i++){send("name:" + exports[i].name + " address:" + exports[i].address); }// 通過模塊名直接查找基址var baseAddress = Module.findBaseAddress("libnative-lib.so");Interceptor.attach(baseAddress.add(0x000091D0), {onEnter:function(args){send(args[2]);send(args[3]);send(args[4]);//send(Memory.readCString(args[2]));//send(Memory.readCString(args[3]));//send(Memory.readCString(args[4]));},onLeave:function(ret_val){console.log(ret_val);send(ret_val)}}); }) '''def message(msg, data):if msg['type'] == 'send':print(f'[*] {msg["payload"]}')else:print(msg)if __name__ == '__main__':# get_remote_device 獲取遠程設備 (get_usb_device)  attach 附加進程process = frida.get_remote_device().attach('com.example.frida_native_demo')script = process.create_script(js_code)script.on('message', message) # 綁定一個事件script.load()sys.stdin.read()pass

    打開兩個 cmd 窗口,一個執行 frida-server 對應的版本,一個執行 端口轉發,如圖:

    模擬器中打開 apk,再執行上面的 python 代碼:

    ?

    ?

    第二、hook導出函數功能

    ?

    這部分內容很簡單了,比上面的簡單是因為不需要手動的計算函數地址,因為是導出的,所以直接可以得到導出的函數名即可,因為C語言中沒有重載的形式,而C++中有,所以有時候發現導出的函數名和正常的函數名前面加上了一串數據作為區分那應該是 C++ 代碼寫的。有了so文件和導出的函數名就不需要構造 NativePoniter 了:

    這個看到比上面自己手動找函數地址方便多了吧,打印參數都一樣的代碼了。這里通過函數名可以知道就是一個native函數了,那么他第一個參數肯定是JNIEnv指針,第二個參數是jclass類型,這個是標準的如果是靜態方法第二個參數沒啥用,后面的參數就是真的傳遞到native層的值了,比如這里Java層的方法:

    那么按照上面的說明native層的函數就是4個參數了:

    的確是這樣的,后面兩個參數才是我們想要的值,我們通過IDA查看這個函數:

    然后我們用F5查看偽代碼他的返回值:

    用 env 指針調用了 NewStringUTF 返回一個 jstring 對象了,好了到這里我們先不說返回值修改的問題,先看看hook參數信息:

    但是我們看到我們打印的返回值是個空也就是空指針,而如果這里我們想hook他的返回值怎么辦呢?如果是一個正常的返回字符串信息,我們可以直接用Memory的方法構造出來Memory.allocUtf8String("XXXXX")一個內存字符串信息,然后直接返回一個指針地址即可,但是現在這里是返回一個jstring對象,其實這個我們通過查看jni.h文件可以知道jstring是C++中定義的對象:

    而基本類型就是基本數據類型:

    這個修改沒有任何問題的,那么現在問題是修改非基本類型,比如這里的如何返回jstring對象呢?這里我能想到的一個辦法就是通過獲取NewStringUTF函數指針,通過NativeFunction方法獲取函數,然后調用

    這里看到代碼邏輯沒什么問題,現在缺的就是 NewStringUTF 的函數地址了,這個因為在 so 中沒法查看,所以怎么辦呢?不著急我們在看看 JNIEnv 的定義:

    他是一個結構體,再看看那個函數地址:

    我們已經有了 JNIEnv 結構體指針了,每個函數指針都是 int 類型也就是 四個字節,所以從 JNIEnv 指針開始依次計算就可以得到NewStringUTF 函數對應的地址了。不過都說了找不到方法的時候就去官網找,JNIEnv 變量其實有對應的方法,這里構造 jstring 方法其實很簡單:?

    這個比找 函數指針 方便多了,其實 env 有很多方法在這里都有對應的 api。

    所以到這里我們發現了 Frida 在 Hook 底層函數返回 jni 中的類型的時候有點麻煩了,但是 Cydia 就不會了,因為他是 Android 工程,可以引用 jni.h 頭文件的,比如我們用 Cydia 來修改這個函數的返回值:

    看到了吧,這樣就很方便了因為是 Android 工程,所以可以直接應用 jni.h 頭文件,然后直接調用 NewStringUTF 方法返回了,看看 hook 的結果:

    也修改成功了。所以這里看到Frida也不是萬能的,要看什么問題怎么去分析了。

    ?

    ?

    ?

    五、技術總結

    ?

    到這里我們就把Frida常用的功能和hook常見的用法都說明完了,下面就來總結一下:

    ?

    ?

    第一、Java層代碼Hook操作

    ?

    1、hook方法包括構造方法和對象方法,構造方法固定寫法是$init,普通方法直接是方法名,參數可以自己定義也可以使用系統隱含的變量arguments獲取。

    2、修改方法的參數和返回值,直接調用原始方法通過傳入想要修改的參數來做到修改參數的目的,以及修改返回值即可。

    3、構造對象和修改對象的屬性值,直接用反射進行操作,構造對象用固定寫法的$new即可。

    4、直接用Java的Exception對象打印堆棧信息,然后通過adb logcat -s AndroidRuntime來查看異常信息跟蹤代碼。

    總結:獲取對象的類類型是Java.use方法,方法有重載的話用overload(.......)解決。

    ?

    ?

    第二、Native層代碼Hook操作

    ?

    1、hook導出的函數直接用so文件名和函數名即可。

    2、hook未導出的函數需要計算出函數在內存中的絕對地址,通過查看maps文件獲取so的基地址+函數的相對地址即可,最后不要忘了+1操作。

    總結:Native中最常用的就是內存地址指針了,所以如果要正確的獲取值一定要用Memory類作為輔助,特別是字符串信息。

    ?

    ?

    frida hook jni_onload

    ?

    ************************ 打印 jni_onload 里面的注冊函數 ************************

    :https://www.bilibili.com/video/BV1UE411A7rW?p=79? ? 28' 40''

    以調試模式

    hook.js 代碼:

    打印結果:

    ?

    ?

    frida 簡單使用

    From:https://www.cnblogs.com/tjp40922/p/11357660.html

    示例代碼:

    # get_front_app.py.py # 得到android手機當前最前端Activity所在的進程import frida rdev = frida.get_remote_device() front_app = rdev.get_frontmost_application() print (front_app)# enum_process.py內容如下: # 枚舉android手機所有的進程import frida rdev = frida.get_remote_device() processes = rdev.enumerate_processes() for process in processes:print (process)# 枚舉某個進程加載的所有模塊以及模塊中的導出函數 import frida rdev = frida.get_remote_device() session = rdev.attach("com.tencent.mm") #如果存在兩個一樣的進程名可以采用rdev.attach(pid)的方式 modules = session.enumerate_modules() for module in modules:print (module)export_funcs = module.enumerate_exports()print ("\tfunc_name\tRVA")for export_func in export_funcs:print ("\t%s\t%s"%(export_func.name,hex(export_func.relative_address)))# 枚舉某個進程加載的所有模塊以及模塊中的導出函數 from __future__ import print_function import frida, sysdef on_message(message, data):print(message)jscode = """Process.enumerateModules({onMatch:function(exp){send(exp.name);},onComplete:function(){send("stop");} }) """process = frida.get_usb_device().attach('com.yinghuan.aiyou') script = process.create_script(jscode) script.on('message', on_message) script.load() sys.stdin.read()# hook android的native函數 import frida import sys rdev = frida.get_remote_device() session = rdev.attach("com.tencent.mm") scr = """ Interceptor.attach(Module.findExportByName("libc.so" , "open"), {onEnter: function(args) {send("open("+Memory.readCString(args[0])+","+args[1]+")");},onLeave:function(retval){} }); """ script = session.create_script(scr) def on_message(message ,data):print (message) script.on("message" , on_message) script.load() sys.stdin.read()# hook android的java層函數 # 如下代碼為hook微信(測試版本為6.3.13,不同版本由于混淆名字 # 的隨機生成的原因或者代碼改動導致類名不一樣)com.tencent.mm.sdk.platformtools.ay類 # 的隨機數生成函數,讓微信猜拳隨機(type=2),而搖色子總是為6點(type=5)import frida import sys rdev = frida.get_remote_device() session = rdev.attach("com.tencent.mm")scr = """ Java.perform(function () { var ay = Java.use("com.tencent.mm.sdk.platformtools.ay"); ay.pu.implementation = function(){var type = arguments[0];send("type="+type);if (type == 2){return this.pu(type);}else{return 5;} };}); """script = session.create_script(scr) def on_message(message ,data):print message script.on("message" , on_message) script.load() sys.stdin.read()# 通過frida向android進程注入 dex import frida, sys, optparse, re def on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)jscode = """ Java.perform(function () {var currentApplication = Java.use("android.app.ActivityThread").currentApplication();var context = currentApplication.getApplicationContext();var pkgName = context.getPackageName();var dexPath = "%s";var entryClass = "%s";Java.openClassFile(dexPath).load();console.log("inject " + dexPath +" to " + pkgName + " successfully!")Java.use(entryClass).%s("%s");console.log("call entry successfully!") }); """def checkRequiredArguments(opts, parser):missing_options = []for option in parser.option_list:if re.match(r'^\[REQUIRED\]', option.help) and eval('opts.' + option.dest) == None:missing_options.extend(option._long_opts)if len(missing_options) > 0:parser.error('Missing REQUIRED parameters: ' + str(missing_options))if __name__ == "__main__":usage = "usage: python %prog [options] arg\n\n" \"example: python %prog -p com.android.launcher " \"-f /data/local/tmp/test.apk " \"-e com.parker.test.DexMain/main " \"\"hello fridex!\""parser = optparse.OptionParser(usage)parser.add_option("-p", "--package", dest="pkg", type="string",help="[REQUIRED]package name of the app to be injected.")parser.add_option("-f", "--file", dest="dexPath", type="string",help="[REQUIRED]path of the dex")parser.add_option("-e", "--entry", dest="entry", type="string",help="[REQUIRED]the entry function Name.")(options, args) = parser.parse_args()checkRequiredArguments(options, parser)if len(args) == 0:arg = ""else:arg = args[0]pkgName = options.pkgdexPath = options.dexPathentry = options.entry.split("/")if len(entry) > 1:entryClass = entry[0]entryFunction = entry[1]else:entryClass = entry[0]entryFunction = "main"process = frida.get_usb_device(1).attach(pkgName)jscode = jscode%(dexPath, entryClass, entryFunction, arg)script = process.create_script(jscode)script.on('message', on_message)print('[*] Running fridex')script.load()sys.stdin.read()# 通過注入拋出異常代碼實現跟蹤程序調用棧在<<Android 軟件安全與逆向分析>>這本書中第八章有介紹通過重打包寫入異常代碼進行棧跟蹤,但是這樣比較麻煩,使用frida注入更方便。

    ?

    ?

    六、Hook 家族神器的對比

    ?

    下面繼續來看看 Frida,Xposed,SubstrateCydia 這三個 Hook 神器的區別和優缺點:

    ?

    第一、Xposed的優缺點

    優點:在編寫Java層hook插件的時候非常好用,這一點完全優越于Frida和SubstrateCydia,因為他也是Android項目,可以直接編寫Java代碼調用各類api進行操作。而且可以安裝到手機上直接使用。

    缺點:配置安裝環境繁瑣,兼容性差,在Hook底層的時候就很無助了。

    ?

    第二、Frida的優缺點

    優點:在上面我們可以看到他的優點在于配置環境很簡單,操作也很便捷,對于破解者開發階段非常好用。支持Java層和Native層hook操作,在Native層hook如果是非基本類型的話操作有點麻煩。

    缺點:因為他只適用于破解者在開發階段,也就是他沒法像Xposed用于實踐生產中,比如我寫一個微信外掛用Frida寫肯定不行的,因為他無法在手機端運行。也就是破解者用的比較多。

    ?

    第三、SubstrateCydia的優缺點

    優點:可以運行在手機端,和Xposed類似可以用于實踐生產中。支持Java層和Native層的hook操作,但是Java層hook不怎么常用,用的比較多的是 Native 層 hook 操作,因為他也是Android工程可以引用系統api,操作更為方便。

    缺點:和Xposed一樣安裝配置環境繁瑣,兼容性差。

    以上這三個工具可以說是現在用的最多的 hook工具了,總結一句話就是寫Java層Hook還是Xposed方便,寫 Native 層 Hook 還是Cydia 了,而對于破解者開發那還是 Frida 最靠譜了。但是不管怎么樣,寫外掛最難的也是最重要的不是寫代碼而是尋找 hook點,也就是逆向分析 app 找到那個地方,然后寫 hook 代碼實現插件功能。

    ?

    ?

    《Android應用安全防護和逆向分析》

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    總結

    以上是生活随笔為你收集整理的安卓逆向_24( 一 ) --- Hook 框架 frida( Hook Java层 和 so层) )的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。