生活随笔
收集整理的這篇文章主要介紹了
使用Frida 实现 Hook 功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Frida 通過 C 語言將 QuickJS 注入到目標進程中,獲取完整的內存操作權限,達到在程序運行時實時地插入額外代碼和數據的目的。官方將調用代碼封裝為 python 庫,當然你也可以直接通過其他的語言調用 Frida 中的 C 語言代碼進行操作。
Frida安裝和啟動
電腦端 Frida 安裝
- Frida 支持 python2 和 python3 版本,演示所使用的版本為 python3.8
pip install frida-tools
- 如果在安裝中卡住,需要在 Frida 的 pypi 頁面下載對應系統的 egg 文件,對應頁面地址為:https://pypi.org/project/frida/#files ,并將該文件放置到個人文件夾路徑下,例如 C:\Users\當前用戶名 ,再重新使用命令安裝。
- 安裝完畢后可以通過命令frida --version 來查看安裝的版本,確認是否安裝成功。
手機端 Frida-server 安裝
- 本次示例使用 Android App 作為目標程序,所以需要電腦端安裝 SDK 環境,以便能夠連接手機進行調試操作,還需在手機端準備一個 Frida-server,下載地址為:https://github.com/frida/frida/releases,下載匹配手機 CPU 架構和本地 Frida 版本的包。
- 下載之后解壓文件,使用adb push 命令將文件推送到手機端,建議放置在/data/local/tmp 文件夾中,并修改該文件的權限為 755,以便之后進行啟動。
確認環境運行正常
- 通過 Frida 提供的一些小工具,對 Frida 的安裝運行環境做簡單的確認。
- 首先準備一個 Android 模擬器或者真機,將上一步中提到的 Frida-server 推送到手機端中,在本示例中將放置在手機的/data/local/tmp 文件夾內,并將文件命名為frida-server 。
- 通過adb shell 命令連接手機,運行/data/local/tmp/frida-server & ,將 Frida-server 放在系統后臺自動運行。
- 在本地電腦終端中運行frida-ps -U ,結果如下展示手機中的進程信息,說明環境已經準備完畢。
PID Name
----- --------------------------------------------------1313 adbd12621 android.process.acore18037 android.process.media14455 com.android.defcontainer11656 com.android.deskclock...
示例
目標應用介紹
- 因為 Hook 需要通過分析源碼中的邏輯來實現,所以先展示一下目標應用的源碼部分,方便分析其中的邏輯,找到 Hook 時要修改的方法和變量。
- 代碼是簡單的猜黑白游戲,通過按下黑或白按鈕,與電腦結果進行對比,結果相同加 1 分,結果不同分數清零,當滿 100 分時打出勝利提示語。具體代碼如下:
package com.example.target_frida;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener {TextView winCountView;TextView battleInfoTextView;int winCount = 0;@SuppressLint("SetTextI18n")@Overridepublic void onClick(View view) {if (winCount > 100) {return;}if (view.getId() == R.id.tvButtonBlack) {if (!getCPUResult()) {winCount++;winCountView.setText(winCount + "");battleInfoTextView.setText("Right!");} else {winCount = 0;winCountView.setText(winCount + "");battleInfoTextView.setText("Wrong! Clean All!");}} else if (view.getId() == R.id.tvButtonWhite) {if (getCPUResult()) {winCount++;winCountView.setText(winCount + "");battleInfoTextView.setText("Right!");} else {winCount = 0;winCountView.setText(winCount + "");battleInfoTextView.setText("Wrong! Clean All!");}}if (winCount >= 100) {battleInfoTextView.setText("Win 100 times!!!");}}@SuppressLint("SetTextI18n")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);winCountView = findViewById(R.id.winCount);winCountView.setText(winCount + "");battleInfoTextView = findViewById(R.id.battleInfo);battleInfoTextView.setText("猜黑白!!!");Button buttonBlack = (Button) findViewById(R.id.tvButtonBlack);Button buttonWhite = (Button) findViewById(R.id.tvButtonWhite);buttonBlack.setOnClickListener(this);buttonWhite.setOnClickListener(this);}public boolean getCPUResult() {//true為白,false為黑return Math.random() > 0.5;}
}
Hook 需求分析
- 由于正常情況下,連贏 100 次的概率幾乎為零,如果想要達到勝利條件,Hook 就是一個比較好的方式。
- 首先分析一下源碼,想要達到連贏 100 次的情況,可以有兩種解決辦法:一種是通過修改用來記錄連贏次數的變量winCount ,將連贏記錄改成 99,這樣只需要再贏一次就可以獲得勝利;還有一種是通過修改getCPUResult 方法的返回值,讓其固定返回一種可能性,這樣只需要選擇對應的顏色就可以連續獲勝。接下來通過實現第一個方案,看看使用 Frida 如何達到想要的效果。
第一種實現:修改結果變量中保存的值
import timeimport frida, sysdate_str = time.strftime('%m-%d %H:%M:%S')def on_message(message, data):if message['type'] == 'send':print(f"[{date_str}] {message['payload']}")else:print(f"[{date_str}] {message}")def run_all():# Java.perform方法:當 js 附加到目標的進程中時被執行,運行其中定義的函數# Java.choose方法:通過完整類名,獲取它的實例,從而對實例中的數據進行修改# 通過 key:value 結構定義了兩個函數:# onMatch 對應的函數在命中一個實例的時候被調用,傳入函數中的參數 instance 就是被命中的實例# onComplete 函數會在所有實例遍歷完畢之后被調用,可以做一些后續處理操作jscode = """Java.perform(function () {Java.choose("com.example.target_frida.MainActivity",{onMatch:function(instance){console.log("winCount value is "+instance.winCount.value);instance.winCount.value=99;console.log("winCount value is "+instance.winCount.value);},onComplete:function(){console.log("Complete!!!")}});});"""# attach目標App進程target_app = 'com.example.target_frida'process = frida.get_usb_device().attach(target_app)# 將JS代碼注入進程,并附加監聽方法,用來獲取返回的日志信息script = process.create_script(jscode)script.on('message', on_message)# 打印起始日志print(f'[{date_str}] Start Frida on {target_app}')# 加載注入的JS代碼邏輯script.load()# 使用系統輸入語句阻止函數運行完畢自動退出sys.stdin.read()if __name__ == '__main__':run_all()
- 代碼中的 python 語句已經添加了注釋,Hook 的核心邏輯,JS 語句作為字符串保存在 jscode 變量中。
- 梳理一下整個 JS 語句的流程:通過Java.choose 函數獲取com.example.target_frida.MainActivity 類的實例。在獲取到實例時,首先使用console.log 語句將當前實例中的 winCount 變量值(使用 winCount.value)打印到日志中,之后直接通過賦值語句把變量值改為 99,再次輸出日志確認修改無誤,修改邏輯就完成了。
- 在手機端啟動 Frida-server 和被測 App,電腦端運行腳本,可以看到在命令行中輸出如下內容:
[04-15 18:19:57] Start Frida on com.example.target_fridawinCount value is 0winCount value is 99Complete!!!
- 這時在 App 中選擇一個顏色點擊,只要選中正確的顏色,就可以成功達到預期的 100 次連勝目標,如果沒能選中正確的顏色導致清零,可以再重復運行腳本修改一次數值,在這種情況下要達到預期的場景就很容易。
總結
第二個方案以及其他更多的可能性,就留給讀者自行探索,在這里送上 Frida 官方 JavaScript API 鏈接:https://frida.re/docs/javascript-api/ ,可以通過這個鏈接找到你所需要的 JS 函數。
通過示例可以看到 Frida 實現 Hook 功能的強大能力,它可以定位到類的實例,并且對實例中的數據進行直接的修改,達到場景構建的目的。
更多技術文章
總結
以上是生活随笔為你收集整理的使用Frida 实现 Hook 功能的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。