Frida框架簡單使用

1.安裝

pip install frida-tools
pip install frida      

PS: frida安裝時需要編譯所以會卡在Running setup.py bdist_wheel for frida ...\,需要大約20多分鐘,並且沒有相關提示,需要耐心等待

2. Hook native api

首先運行以下命令確保frida可以監控到非自己的子進程

$ sudo sysctl kernel.yama.ptrace_scope=0

基本例子

import frida
import sys

# hook邏輯腳本
jscode = """
Interceptor.attach(ptr("%s"), {
    onEnter: function(args) {
        send(args[0].toInt32());
    }
});
"""

# 注入進程,attach傳入進程名稱(字符串)或者進程號(整數)
session = frida.attach("hello")
script = session.create_script(jscode % int(sys.argv[1], 16))
#int()函數把字符串表示的16進制數轉換成整數
#上面的jscode % int(sys.argv[1], 16)是python格式化字符串的語法

# 接收腳本信息的回調函數
# message是一個對象,type屬性爲send則表示send函數發送的信息,其內容在payload裏
# 下面這個on_message函數可以做固定用法,一般無需改動,當然也可直接打印message看看裏邊的內容
def on_message(message, data):
    if message['type'] == 'send':
        print(message['payload'])
    elif message['type'] == 'error':
        print(message['stack'])
# 應該是設置message事件的回調函數
script.on('message', on_message)
# 加載hook腳本
script.load()
# 保持主線程不結束(也可以使用time.sleep循環)
sys.stdin.read()

hook加載的基本流程

  1. frida.attach attach進程創建session,使用進程名(字符串)或者進程號(整數)指定進程
  2. session.create_script session創建hook腳本
  3. script.on 註冊用於通信的回調函數
  4. script.load加載腳本
  5. sys.stdin.read保持主線程運行

hook代碼(js)

1. hook函數

Interceptor.attach(ptr(''%s'), {
    onEnter: function(args) {
        send(args[0].toInt32());//這裏也可以直接修改args的值來改變傳入的參數
    onLeave: function onLeave(retval) {
    }
});

Interceptor類用於hook操作,其attach方法有兩個參數

  1. 要hook的函數在內存中加載的地址

    這個地址可以通過Module.findExportByName(鏈接庫名,函數名)來獲取鏈接庫中的導出函數地址

    ptr()接收一個字符串,用於構造一個指針

  2. 包含回調函數的對象

    onEnter在進入該函數時調用,args爲傳入該函數的參數;onLeave在函數返回時被調用,retval該函數的返回值

send函數用於hook腳本向外部發送信息,通過在外部python腳本中用script.on設置回調函數來接收信息,回調函數有兩個參數,第一個message就包含send發送的信息,第二個參數暫時沒有在官方文檔中找到用處。

詳細描述參考官方文檔Interceptorptr

2. 調用函數

var f = new NativeFunction(ptr("%s"), 'void', ['int']);
f(1911);

NativeFunction構造函數接受三個參數——函數地址返回值參數類型(數組表示), 然後就可以調用該函數

詳細描述參考官方文檔NativeFunction

3. 分配空間

var st = Memory.allocUtf8String("TESTMEPLZ!");

Memory對象下的方法可以操作被attach的進程的內存空間 ,該方法分配的空間似乎會在函數結束後被銷燬,比如在onEnter中分配的內存空間在onEnter結束後會被釋放,使用時需注意。

詳細描述參考官方文檔Memory

4. hook printf函數的一個測試

  1. 編寫hook腳本

    var write_address = Module.findExportByName('libc.so.6', 'write');
    //js字符串中的換行符必須要寫成\\n
    //給我們注入的字符串分配空間
    var st = Memory.allocUtf8String('你的write函數已經被劫持了\\n');
    if(write_address != null){
        send('write is at' + write_address)
        Interceptor.attach(write_address, {
            onEnter: function(args) {
                //輸出write的原始參數write(int fd, char* buff, int size)
                send(args[0].toString()+ ' ' + args[1].toString() + ' ' + args[2].toString());
                //把第二個參數替換成我們的字符串
                args[1] = st;
                //第三個參數改爲我們字符串的長度,utf8一個漢字爲3個字節,我們的字符串共計36字節
                args[2] = ptr('36');
            },
            onLeave: function(retval) {
            }
        });
    }else{
        send('no such function');
    }
    
  2. 運行(加載js腳本的過程同上文所述)

python通過write輸出的所有值都變成我們的字符串,下邊是python在write stdout(0x1)和stderr(0x2)
測試結果

3. 在Android系統中運行

  1. 下載frida-server,一般爲frida-server-12.3.5-android-arm64.xz ,根據Android系統的架構來選擇(arm,arm64,x86,x86_64)

  2. 用電腦連接Android手機,把frida-server放到手機裏,再通過adb啓動手機shell,運行frida-server需要root權限

    $ adb root
    $ adb push frida-server /data/local/tmp/ 
    $ adb shell "chmod 755 /data/local/tmp/frida-server"
    $ adb shell "/data/local/tmp/frida-server &"
    
  3. 將hook腳本中的frida.attach()替換成frida.get_usb_devicce().attach()就可以hook手機上的進程了,可以hook java方法,也可用hook Android系統程序的native api

4.hook java方法

hook代碼加載流程同上文hook native api相同,只是用於hook邏輯的js代碼不同

Java.perform(function () {
    // Function to hook is defined here
    var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
    
    // hook MainActivity類的onClick方法
    // java方法的hook採用了替換的方式,提供了調用原方法的途徑
    MainActivity.onClick.implementation = function (v) {
        //進入方法時的操作(操作參數)
        send('onClick');

        // 調用原方法
        this.onClick(v);

        // 退出原方法時的操作(操作返回值等)
        this.m.value = 0;
        this.n.value = 1;
        this.cnt.value = 999;

        // Log to the console that it's done, and we should have the flag!
        console.log('Done:' + JSON.stringify(this.cnt));
    };
});
  1. Java.perform()應該是對hook java層進行包裝,其參數爲一個函數,該函數爲具體hook邏輯

  2. Java.use()參數爲需要hook的類名

  3. classname.method.implementation需要被賦值爲一個函數,該函數會替換method(不同於hook本地api時有onEnteronLeave),在函數內使用this可以訪問該類,調用其方法和屬性。調用屬性時使用this.property.value, 而不是this.property,遇到有屬性和方法同名時屬性前加下劃線_,如this._property.value

  4. hook有重載的方法時,使用overload指出參數具體類型

    例如classname.method.overload('type1','type2').implementation

  5. 構造方法使用$init表示,例如classname.$init.overload('type1','type2').implementation

ps: 調用某些原方法可能無法正常工作,據說是系統原因,只有更換系統版本

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章