frida的用法--Hook Java代碼篇

frida是一款方便并且易用的跨平臺Hook工具弱卡,使用它不僅可以Hook Java寫的應(yīng)用程序,而且還可以Hook原生的應(yīng)用程序。

1. 準(zhǔn)備

frida分客戶端環(huán)境和服務(wù)端環(huán)境咕晋。在客戶端我們可以編寫Python代碼雹拄,用于連接遠(yuǎn)程設(shè)備,提交要注入的代碼到遠(yuǎn)程掌呜,接受服務(wù)端的發(fā)來的消息等滓玖。在服務(wù)端,我們需要用Javascript代碼注入到目標(biāo)進(jìn)程质蕉,操作內(nèi)存數(shù)據(jù)势篡,給客戶端發(fā)送消息等操作。我們也可以把客戶端理解成控制端饰剥,服務(wù)端理解成被控端殊霞。
假如我們要用PC來對Android設(shè)備上的某個進(jìn)程進(jìn)行操作,那么PC就是客戶端汰蓉,而Android設(shè)備就是服務(wù)端绷蹲。

1.1 準(zhǔn)備frida服務(wù)端環(huán)境

本文,服務(wù)端在Android平臺測試顾孽。服務(wù)端環(huán)境準(zhǔn)備步驟如下:

  1. 根據(jù)自己的平臺下載frida服務(wù)端并解壓
    https://github.com/frida/frida/releases

    frida_server

  2. 執(zhí)行以下命令將服務(wù)端推到手機(jī)的/data/local/tmp目錄
    adb push frida-server /data/local/tmp/frida-server

  3. 執(zhí)行以下命令修改frida-server文件權(quán)限
    adb shell chmod 777 /data/local/tmp/frida-server

注:Windows系統(tǒng)執(zhí)行命令可以在CMD中進(jìn)行祝钢;Linux和MacOS執(zhí)行命令可以在終端中進(jìn)行。adb是Android一個調(diào)試工具若厚,具體安裝方法不是本文的重點(diǎn)拦英。

1.2 準(zhǔn)備客戶端環(huán)境

在PC上安裝Python的運(yùn)行環(huán)境,安裝完成后執(zhí)行下面的命令安裝frida

pip install frida

1.3 客戶端命令參數(shù)

下面是frida客戶端命令行的參數(shù)解釋测秸,看一下就好

Usage: frida [options] target

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -D ID, --device=ID    connect to device with the given ID
  -U, --usb             connect to USB device
  -R, --remote          connect to remote frida-server
  -H HOST, --host=HOST  connect to remote frida-server on HOST
  -f FILE, --file=FILE  spawn FILE
  -n NAME, --attach-name=NAME
                        attach to NAME
  -p PID, --attach-pid=PID
                        attach to PID
  --debug               enable the Node.js compatible script debugger
  --enable-jit          enable JIT
  -l SCRIPT, --load=SCRIPT
                        load SCRIPT
  -c CODESHARE_URI, --codeshare=CODESHARE_URI
                        load CODESHARE_URI
  -e CODE, --eval=CODE  evaluate CODE
  -q                    quiet mode (no prompt) and quit after -l and -e
  --no-pause            automatically start main thread after startup
  -o LOGFILE, --output=LOGFILE
                        output to log file

如果將一個腳本注入到Android目標(biāo)進(jìn)程

frida -U -l myhook.js com.xxx.xxxx

參數(shù)解釋:

  • -U 指定對USB設(shè)備操作
  • -l 指定加載一個Javascript腳本
  • 最后指定一個進(jìn)程名疤估,如果想指定進(jìn)程pid,用-p選項。正在運(yùn)行的進(jìn)程可以用frida-ps -U命令查看

frida運(yùn)行過程中霎冯,執(zhí)行%resume重新注入铃拇,執(zhí)行%reload來重新加載腳本;執(zhí)行exit結(jié)束腳本注入

2. Hook Java方法

2.1 載入類

Java.use方法用于聲明一個Java類沈撞,在用一個Java類之前首先得聲明慷荔。比如聲明一個String類,要指定完整的類名:

var StringClass=Java.use("java.lang.String");

2.2 修改函數(shù)的實現(xiàn)

修改一個函數(shù)的實現(xiàn)是逆向調(diào)試中相當(dāng)有用的缠俺。修改一個函數(shù)的實現(xiàn)后显晶,如果這個函數(shù)被調(diào)用,我們的Javascript代碼里的函數(shù)實現(xiàn)也會被調(diào)用壹士。

2.2.1 函數(shù)參數(shù)類型表示

不同的參數(shù)類型都有自己的表示方法

  1. 對于基本類型磷雇,直接用它在Java中的表示方法就可以了,不用改變墓卦,例如:
  • int
  • short
  • char
  • byte
  • boolean
  • float
  • double
  • long
  1. 基本類型數(shù)組倦春,用左中括號接上基本類型的縮寫

基本類型縮寫表示表:

基本類型 縮寫
boolean Z
byte B
char C
double D
float F
int I
long J
short S

例如:int[]類型,在重載時要寫成[I

  1. 任意類落剪,直接寫完整類名即可

例如:java.lang.String

  1. 對象數(shù)組睁本,用左中括號接上完整類名再接上分號

例如:[java.lang.String;

2.2.2 帶參數(shù)的構(gòu)造函數(shù)

修改參數(shù)為byte[]類型的構(gòu)造函數(shù)的實現(xiàn)

ClassName.$init.overload('[B').implementation=function(param){
    //do something
}

注:ClassName是使用Java.use定義的類;param是可以在函數(shù)體中訪問的參數(shù)

修改多參數(shù)的構(gòu)造函數(shù)的實現(xiàn)

ClassName.$init.overload('[B','int','int').implementation=function(param1,param2,param3){
    //do something
}

2.2.3 無參數(shù)構(gòu)造函數(shù)

ClassName.$init.overload().implementation=function(){
    //do something
}

調(diào)用原構(gòu)造函數(shù)

ClassName.$init.overload().implementation=function(){
    //do something
    this.$init();
    //do something
}

注意:當(dāng)構(gòu)造函數(shù)(函數(shù))有多種重載形式,比如一個類中有兩個形式的func:void func()void func(int)忠怖,要加上overload來對函數(shù)進(jìn)行重載呢堰,否則可以省略overload

2.2.4 一般函數(shù)

修改函數(shù)名為func,參數(shù)為byte[]類型的函數(shù)的實現(xiàn)

ClassName.func.overload('[B').implementation=function(param){
    //do something
    //return ...
}

2.2.5 無參數(shù)的函數(shù)

ClassName.func.overload().implementation=function(){
    //do something
}

注: 在修改函數(shù)實現(xiàn)時凡泣,如果原函數(shù)有返回值枉疼,那么我們在實現(xiàn)時也要返回合適的值

ClassName.func.overload().implementation=function(){
    //do something
    return this.func();
}

3. 調(diào)用函數(shù)

和Java一樣,創(chuàng)建類實例就是調(diào)用構(gòu)造函數(shù)鞋拟,而在這里用$new表示一個構(gòu)造函數(shù)骂维。

var ClassName=Java.use("com.luoye.test.ClassName");
var instance = ClassName.$new();

實例化以后調(diào)用其他函數(shù)

var ClassName=Java.use("com.luoye.test.ClassName");
var instance = ClassName.$new();
instance.func();

4. 類型轉(zhuǎn)換

Java.cast方法來對一個對象進(jìn)行類型轉(zhuǎn)換,如將variable轉(zhuǎn)換成java.lang.String

var StringClass=Java.use("java.lang.String");
var NewTypeClass=Java.cast(variable,StringClass);

5. Java.available字段

這個字段標(biāo)記Java虛擬機(jī)(例如: Dalvik 或者 ART)是否已加載, 操作Java任何東西之前贺纲,要確認(rèn)這個值是否為true

6. Java.perform方法

Java.perform(fn)在Javascript代碼成功被附加到目標(biāo)進(jìn)程時調(diào)用航闺,我們核心的代碼要在里面寫。格式:

Java.perform(function(){
//do something...
});

7. 實例講解

有了以上的基礎(chǔ)知識猴誊,我們就可以進(jìn)行編寫代碼了

7.1 修改返回值

7.1.1 場景

假設(shè)有以下的程序潦刃,給isExcellent方法傳入兩個值,通過計算懈叹,返回一個布爾值乖杠,表示是否優(yōu)秀。默認(rèn)情況下澄成,它是只會顯示是否優(yōu)秀:false的胧洒,因為我們默認(rèn)傳入的數(shù)很小:

exp1_before
public class MainActivity extends AppCompatActivity {
    private  String TAG="Crackme";
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView =findViewById(R.id.tv);
        textView.setText("是否優(yōu)秀:"+isExcellent(46,54));
    }

    private  boolean isExcellent(int chinese, int math){
        if( chinese + math >=180){
            return true;
        }
        else{
            return false;
        }
    }

}

我們編寫一個腳本來Hook isExcellent函數(shù),使它返回true墨状,顯示為是否優(yōu)秀:true

對于這種簡單的場景卫漫,直接修改返回值就可以了,因為只有結(jié)果是重要的歉胶。

7.1.2 代碼

想直接返回結(jié)果很簡單汛兜,直接在匿名方法里return即可。

if(Java.available){
    Java.perform(function(){
        var MainActivity = Java.use("com.luoyesiqiu.crackme.MainActivity");
        MainActivity.isExcellent.implementation=function(){
            return true;        
        }
    });

}
  • 將上面的代碼保存為:exp1.js

  • 執(zhí)行adb shell 'su -c /data/local/tmp/frida-server'啟動服務(wù)端

  • 運(yùn)行目標(biāo)App

  • 執(zhí)行frida -U -l exp1.js com.luoyesiqiu.crackme注入代碼

  • 按返回鍵返回桌面通今,再重新打開App,發(fā)現(xiàn)達(dá)到預(yù)期

  • 在命令行輸入exit粥谬,回車,停止注入代碼

exp1_after

注:這里為什么要打開兩次App辫塌?第一打開是為了讓frida能夠找到進(jìn)程漏策,第二次打開是為了驗證結(jié)果,即使Hook成功了臼氨,界面是有緩存的掺喻,并不能實時顯示Hook結(jié)果,所以需要重新打開App

7.2 修改參數(shù)

7.2.1 場景

假設(shè)有以下場景,isExcellent除了返回是否優(yōu)秀以外感耙,方法的內(nèi)部還把分?jǐn)?shù)打印出來褂乍。

exp2_before
public class MainActivity extends AppCompatActivity {
    private  String TAG="Crackme";
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView =findViewById(R.id.tv);
        textView.append("是否優(yōu)秀:"+isExcellent(46,54)+"\n");
    }

    private  boolean isExcellent(int chinese, int math){
        textView.append("語文+數(shù)學(xué)總分:"+(chinese+math)+"\n");
        if( chinese + math >=180){
            return true;
        }
        else{
            return false;
        }
    }
}

這種情況下我們不可能只返回是否優(yōu)秀吧,顯示的總分很低即硼,但是卻返回優(yōu)秀逃片,是很尷尬的...所以我們要修改isExcellent方法的參數(shù),使其通過計算打印和返回合理的值只酥。

7.2.2 代碼

if(Java.available){
    Java.perform(function(){
        var MainActivity = Java.use("com.luoyesiqiu.crackme.MainActivity");
        MainActivity.isExcellent.overload("int","int").implementation=function(chinese,math){
            return this.isExcellent(95,96);      
        }
    });

}

上面的代碼褥实,通過overload方法重載參數(shù),修改isExcellent方法實現(xiàn)裂允,并在實現(xiàn)函數(shù)里調(diào)用原來的方法损离,得到新的返回值

  • 將上面的代碼保存為:exp2.js

  • 執(zhí)行adb shell 'su -c /data/local/tmp/frida-server'啟動服務(wù)端(如果上面啟動的服務(wù)端還開著可省略這一步)

  • 運(yùn)行目標(biāo)App

  • 執(zhí)行frida -U -l exp2.js com.luoyesiqiu.crackme注入代碼

  • 按返回鍵,再重新打開App,發(fā)現(xiàn)達(dá)到預(yù)期

  • 在命令行輸入exit绝编,回車僻澎,停止注入代碼

exp2_after

8. 配合Python腳本注入

在本文剛開始的時候說到,我們可以編寫Python代碼來配合Javascript代碼注入瓮增。下面我們來看看怎棱,怎么使用,先看一段代碼:

# -*- coding: UTF-8 -*-

import frida, sys

jscode = """
if(Java.available){
    Java.perform(function(){
        var MainActivity = Java.use("com.luoyesiqiu.crackme.MainActivity");
        MainActivity.isExcellent.overload("int","int").implementation=function(chinese,math){
            console.log("[javascript] isExcellent be called.");
            send("isExcellent be called.");
            return this.isExcellent(95,96);      
        }
    });

}
"""

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)
pass

# 查找USB設(shè)備并附加到目標(biāo)進(jìn)程
session = frida.get_usb_device().attach('com.luoyesiqiu.crackme')
# 在目標(biāo)進(jìn)程里創(chuàng)建腳本
script = session.create_script(jscode)
# 注冊消息回調(diào)
script.on('message', on_message)
print('[*] Start attach')
# 加載創(chuàng)建好的javascript腳本
script.load()
# 讀取系統(tǒng)輸入
sys.stdin.read()
  • 將上面的代碼绷跑,保存為exp3.py

  • 執(zhí)行adb shell 'su -c /data/local/tmp/frida-server'啟動服務(wù)端(如果上面啟動的服務(wù)端還開著可省略這一步)

  • 運(yùn)行目標(biāo)App

  • 執(zhí)行python exp3.py注入代碼

  • 按返回鍵拳恋,再重新打開App,發(fā)現(xiàn)達(dá)到預(yù)期

  • Ctrl+C停止腳本和停止注入代碼

上面是一段Python代碼,我們來分析它的步驟:

  1. 通過調(diào)用frida.get_usb_device()方法來得到一個連接中的USB設(shè)備(Device類)實例
  2. 調(diào)用Device類的attach()方法來附加到目標(biāo)進(jìn)程并得到一個會話(Session類)實例砸捏,該方法有一個參數(shù)谬运,參數(shù)是需要注入的進(jìn)程名或者進(jìn)程pid。如果需要Hook的代碼在App的啟動期執(zhí)行垦藏,那么在調(diào)用attach方法前需要先調(diào)用Device類的spawn()方法梆暖,這個方法也有一個參數(shù),參數(shù)是進(jìn)程名掂骏,該方法調(diào)用后會重啟對應(yīng)的進(jìn)程轰驳,并返回新的進(jìn)程pid。得到新的進(jìn)程pid后弟灼,我們可以將這個進(jìn)程pid傳遞給attach()方法來實現(xiàn)附加级解。
  3. 接著調(diào)用Session類的create_script()方法創(chuàng)建一個腳本,傳入需要注入的javascript代碼并得到Script類實例
  4. 調(diào)用Script類的on()方法添加一個消息回調(diào)田绑,第一個參數(shù)是信號名勤哗,乖乖傳入message就行,第二個是回調(diào)函數(shù)
  5. 最后調(diào)用Script類的load()方法來加載剛才創(chuàng)建的腳本掩驱。

注:如果想在javascript輸出日志芒划,可以調(diào)用console.log()方法冬竟。如果想給客戶端發(fā)送消息,可以在javascript代碼里調(diào)用send()方法民逼,并在客戶端Python代碼里注冊一個消息回調(diào)來接收服務(wù)端發(fā)來的消息泵殴。

可以看到,結(jié)合python代碼缴挖,使注入更加的靈活了袋狞。如果想看Python端frida模塊的代碼焚辅,可以訪問:https://github.com/frida/frida-python/blob/master/frida/core.py

9. 參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末映屋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子同蜻,更是在濱河造成了極大的恐慌棚点,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件湾蔓,死亡現(xiàn)場離奇詭異瘫析,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)默责,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門贬循,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人桃序,你說我怎么就攤上這事杖虾。” “怎么了媒熊?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵奇适,是天一觀的道長。 經(jīng)常有香客問我芦鳍,道長嚷往,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任柠衅,我火速辦了婚禮皮仁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菲宴。我一直安慰自己贷祈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布裙顽。 她就那樣靜靜地躺著付燥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪愈犹。 梳的紋絲不亂的頭發(fā)上键科,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天闻丑,我揣著相機(jī)與錄音,去河邊找鬼勋颖。 笑死嗦嗡,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饭玲。 我是一名探鬼主播侥祭,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茄厘!你這毒婦竟也來了矮冬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤次哈,失蹤者是張志新(化名)和其女友劉穎胎署,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窑滞,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琼牧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哀卫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巨坊。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖此改,靈堂內(nèi)的尸體忽然破棺而出趾撵,到底是詐尸還是另有隱情,我是刑警寧澤带斑,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布鼓寺,位于F島的核電站,受9級特大地震影響勋磕,放射性物質(zhì)發(fā)生泄漏妈候。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一挂滓、第九天 我趴在偏房一處隱蔽的房頂上張望苦银。 院中可真熱鬧,春花似錦赶站、人聲如沸幔虏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽想括。三九已至,卻和暖如春烙博,著一層夾襖步出監(jiān)牢的瞬間瑟蜈,已是汗流浹背烟逊。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铺根,地道東北人宪躯。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像位迂,于是被迫代替她去往敵國和親访雪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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