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

技術(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)境做簡單的確認(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é)果進(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 和被測 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ù)期的場景就很容易。

總結(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á)到場景構(gòu)建的目的独令。

更多技術(shù)文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市好芭,隨后出現(xiàn)的幾起案子燃箭,更是在濱河造成了極大的恐慌,老刑警劉巖舍败,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件招狸,死亡現(xiàn)場離奇詭異,居然都是意外死亡邻薯,警方通過查閱死者的電腦和手機(jī)裙戏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弛说,“玉大人挽懦,你說我怎么就攤上這事∧救耍” “怎么了信柿?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長醒第。 經(jīng)常有香客問我渔嚷,道長,這世上最難降的妖魔是什么稠曼? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任形病,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘漠吻。我一直安慰自己量瓜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布途乃。 她就那樣靜靜地躺著绍傲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耍共。 梳的紋絲不亂的頭發(fā)上烫饼,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音试读,去河邊找鬼杠纵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛钩骇,可吹牛的內(nèi)容都是我干的比藻。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼伊履,長吁一口氣:“原來是場噩夢啊……” “哼韩容!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唐瀑,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤群凶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后哄辣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體请梢,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年力穗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毅弧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡当窗,死狀恐怖够坐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情崖面,我是刑警寧澤元咙,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站巫员,受9級(jí)特大地震影響庶香,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜简识,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一赶掖、第九天 我趴在偏房一處隱蔽的房頂上張望感猛。 院中可真熱鬧,春花似錦奢赂、人聲如沸陪白。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拷泽。三九已至,卻和暖如春袖瞻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拆吆。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國打工聋迎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枣耀。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓霉晕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捞奕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牺堰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容