當(dāng)前位置:首頁 > IT技術(shù) > 其他 > 正文

Frida 實(shí)現(xiàn) Hook 功能的強(qiáng)大能力
2022-05-31 17:12:04

技術(shù)分享 | Frida 實(shí)現(xiàn) Hook 功能的強(qiáng)大能力

??更多技術(shù)文章??

Frida 通過 C 語言將 QuickJS 注入到目標(biāo)進(jìn)程中,獲取完整的內(nèi)存操作權(quán)限,達(dá)到在程序運(yùn)行時(shí)實(shí)時(shí)地插入額外代碼和數(shù)據(jù)的目的。官方將調(diào)用代碼封裝為 python 庫,當(dāng)然你也可以直接通過其他的語言調(diào)用 Frida 中的 C 語言代碼進(jìn)行操作。

Frida安裝和啟動(dòng)

電腦端 Frida 安裝

pip install frida-tools
  • 如果在安裝中卡住,需要在 Frida 的 pypi 頁面下載對(duì)應(yīng)系統(tǒng)的 egg 文件,對(duì)應(yīng)頁面地址為:https://pypi.org/project/frida/#files ,并將該文件放置到個(gè)人文件夾路徑下,例如 C:Users當(dāng)前用戶名,再重新使用命令安裝。
  • 安裝完畢后可以通過命令frida --version來查看安裝的版本,確認(rèn)是否安裝成功。

手機(jī)端 Frida-server 安裝

  • 本次示例使用 Android App 作為目標(biāo)程序,所以需要電腦端安裝 SDK 環(huán)境,以便能夠連接手機(jī)進(jìn)行調(diào)試操作,還需在手機(jī)端準(zhǔn)備一個(gè) Frida-server,下載地址為:https://github.com/frida/frida/releases,下載匹配手機(jī) CPU 架構(gòu)和本地 Frida 版本的包。
  • 下載之后解壓文件,使用adb push命令將文件推送到手機(jī)端,建議放置在/data/local/tmp文件夾中,并修改該文件的權(quán)限為 755,以便之后進(jìn)行啟動(dòng)。

確認(rèn)環(huán)境運(yùn)行正常

  • 通過 Frida 提供的一些小工具,對(duì) Frida 的安裝運(yùn)行環(huán)境做簡(jiǎn)單的確認(rèn)。
  • 首先準(zhǔn)備一個(gè) Android 模擬器或者真機(jī),將上一步中提到的 Frida-server 推送到手機(jī)端中,在本示例中將放置在手機(jī)的/data/local/tmp文件夾內(nèi),并將文件命名為frida-server。
  • 通過adb shell命令連接手機(jī),運(yùn)行/data/local/tmp/frida-server &,將 Frida-server 放在系統(tǒng)后臺(tái)自動(dòng)運(yùn)行。
  • 在本地電腦終端中運(yùn)行frida-ps -U,結(jié)果如下展示手機(jī)中的進(jìn)程信息,說明環(huán)境已經(jīng)準(zhǔn)備完畢。
  PID  Name
----- --------------------------------------------------
1313 adbd
12621 android.process.acore
18037 android.process.media
14455 com.android.defcontainer
11656 com.android.deskclock

示例

目標(biāo)應(yīng)用介紹

  • 因?yàn)?Hook 需要通過分析源碼中的邏輯來實(shí)現(xiàn),所以先展示一下目標(biāo)應(yīng)用的源碼部分,方便分析其中的邏輯,找到 Hook 時(shí)要修改的方法和變量。
  • 代碼是簡(jiǎn)單的猜黑白游戲,通過按下黑或白按鈕,與電腦結(jié)果進(jìn)行對(duì)比,結(jié)果相同加 1 分,結(jié)果不同分?jǐn)?shù)清零,當(dāng)滿 100 分時(shí)打出勝利提示語。具體代碼如下:
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")
@Override
public 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")
@Override
protected 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 次的概率幾乎為零,如果想要達(dá)到勝利條件,Hook 就是一個(gè)比較好的方式。
  • 首先分析一下源碼,想要達(dá)到連贏 100 次的情況,可以有兩種解決辦法:一種是通過修改用來記錄連贏次數(shù)的變量winCount,將連贏記錄改成 99,這樣只需要再贏一次就可以獲得勝利;還有一種是通過修改getCPUResult方法的返回值,讓其固定返回一種可能性,這樣只需要選擇對(duì)應(yīng)的顏色就可以連續(xù)獲勝。接下來通過實(shí)現(xiàn)第一個(gè)方案,看看使用 Frida 如何達(dá)到想要的效果。

第一種實(shí)現(xiàn):修改結(jié)果變量中保存的值

  • 首先展示修改代碼,然后再進(jìn)行逐步講解:
import time
import frida, sys

date_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方法:當(dāng) js 附加到目標(biāo)的進(jìn)程中時(shí)被執(zhí)行,運(yùn)行其中定義的函數(shù)
# Java.choose方法:通過完整類名,獲取它的實(shí)例,從而對(duì)實(shí)例中的數(shù)據(jù)進(jìn)行修改
# 通過 key:value 結(jié)構(gòu)定義了兩個(gè)函數(shù):
# onMatch 對(duì)應(yīng)的函數(shù)在命中一個(gè)實(shí)例的時(shí)候被調(diào)用,傳入函數(shù)中的參數(shù) instance 就是被命中的實(shí)例
# onComplete 函數(shù)會(huì)在所有實(shí)例遍歷完畢之后被調(diào)用,可以做一些后續(xù)處理操作
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目標(biāo)App進(jìn)程
target_app = 'com.example.target_frida'
process = frida.get_usb_device().attach(target_app)
# 將JS代碼注入進(jìn)程,并附加監(jiān)聽方法,用來獲取返回的日志信息
script = process.create_script(jscode)
script.on('message', on_message)
# 打印起始日志
print(f'[{date_str}] Start Frida on {target_app}')
# 加載注入的JS代碼邏輯
script.load()
# 使用系統(tǒng)輸入語句阻止函數(shù)運(yùn)行完畢自動(dòng)退出
sys.stdin.read()


if __name__ == '__main__':
run_all()
  • 代碼中的 python 語句已經(jīng)添加了注釋,Hook 的核心邏輯,JS 語句作為字符串保存在 jscode 變量中。
  • 梳理一下整個(gè) JS 語句的流程:通過Java.choose函數(shù)獲取com.example.target_frida.MainActivity類的實(shí)例。在獲取到實(shí)例時(shí),首先使用console.log語句將當(dāng)前實(shí)例中的 winCount 變量值(使用 winCount.value)打印到日志中,之后直接通過賦值語句把變量值改為 99,再次輸出日志確認(rèn)修改無誤,修改邏輯就完成了。
  • 在手機(jī)端啟動(dòng) Frida-server 和被測(cè) App,電腦端運(yùn)行腳本,可以看到在命令行中輸出如下內(nèi)容:
[04-15 18:19:57] Start Frida on com.example.target_frida
winCount value is 0
winCount value is 99
Complete!!!

這時(shí)在 App 中選擇一個(gè)顏色點(diǎn)擊,只要選中正確的顏色,就可以成功達(dá)到預(yù)期的 100 次連勝目標(biāo),如果沒能選中正確的顏色導(dǎo)致清零,可以再重復(fù)運(yùn)行腳本修改一次數(shù)值,在這種情況下要達(dá)到預(yù)期的場(chǎng)景就很容易。

總結(jié)

第二個(gè)方案以及其他更多的可能性,就留給讀者自行探索,在這里送上 Frida 官方 JavaScript API 鏈接:https://frida.re/docs/javascript-api/ ,可以通過這個(gè)鏈接找到你所需要的 JS 函數(shù)。
通過示例可以看到 Frida 實(shí)現(xiàn) Hook 功能的強(qiáng)大能力,它可以定位到類的實(shí)例,并且對(duì)實(shí)例中的數(shù)據(jù)進(jìn)行直接的修改,達(dá)到場(chǎng)景構(gòu)建的目的。
???更多技術(shù)文章??

本文摘自 :https://blog.51cto.com/u

開通會(huì)員,享受整站包年服務(wù)立即開通 >