天天看點

frida入門總結

一、Frida概述

    Frida是一款輕量級HOOK架構,可用于多平台上,例如android、windows、ios等。

    frida分為兩部分,服務端運作在目标機上,通過注入程序的方式來實作劫持應用函數,另一部分運作在系統機器上。

    frida上層接口支援js、python、c等。

    Frida官方github位址為:frida官方github位址

    PS:雖然百度一下會發現frida教程有不少,但是涉及到native層的教程基本很少,然後對每一句Hook代碼解釋一下的更是少之又少,是以我還是厚着臉皮從自己的角度寫了這一篇!!!

二、Frida安裝

    1 、安裝python3.7并配置好環境變量(官方推薦python3以上版本至少為3.7),python安裝包官方下載下傳位址:https://www.python.org/downloads/。

    2 、安裝frida子產品,指令為

pip install frida

(配置了多個python版本環境的可以使用指令

python -m pip install frida

防止用

pip install frida

指令報錯)。

    3、安裝frida-tools子產品,指令同上,

pip install frida-tools

或者

python -m pip install frida-tools

    4、下載下傳運作在目标機上的frida-sever端,官方下載下傳位址:https://github.com/frida/frida/releases,下載下傳時要選擇對應的版本下載下傳,例如我的機器為arm32為架構,就選擇

frida-server-12.8.14-android-arm.xz

下載下傳。(可以在adb使用指令

cat /proc/cpuinfo

查詢)

    5、将第四步下載下傳好的檔案解壓,然後通過指令

adb push 你的電腦是存放位置 /data/local/tmp

将檔案傳輸到手機中,然後通過

adb shell

進入手機端,給檔案賦權777,并于root權限啟動。

frida入門總結
frida入門總結

    6、做完以上幾步後,新開一個指令行輸入指令

frida-ps -U

檢視手機程序,如果出現以下結果,則frida安裝成功。

frida入門總結

三、Frida Hook Java層

    1、編寫一個小demo用來hook,該demo關鍵部分代碼如下:

    2、現在我們将該apk安裝好,運作看一下未Hook前的顯示字元串!!!

    3、現在來編寫Hook的Python腳本,腳本代碼如下:

import frida  #導入frida子產品
import sys    #導入sys子產品

jscode = """  #從此處開始定義用來Hook的javascript代碼
    Java.perform(function(){  
        var MainActivity = Java.use('com.example.testfrida.MainActivity'); //獲得MainActivity類
        MainActivity.testFrida.implementation = function(){ //Hook testFrida函數,用js自己實作
            send('Statr! Hook!'); //發送資訊,用于回調python中的函數
            return 'Change String!' //劫持傳回值,修改為我們想要傳回的字元串
        }
    });
"""

def on_message(message,data): #js中執行send函數後要回調的函數
    print(message)
    
process = frida.get_remote_device().attach('com.example.testfrida') #得到裝置并劫持程序com.example.testfrida(該開始用get_usb_device函數用來擷取裝置,但是一直報錯找不到裝置,改用get_remote_device函數即可解決這個問題)
script = process.create_script(jscode) #建立js腳本
script.on('message',on_message) #加載回調函數,也就是js中執行send函數規定要執行的python函數
script.load() #加載腳本
sys.stdin.read()
           

    4、現在python腳本編寫完畢,我們來執行該腳本,首先手機端執行frida,然後通過指令

adb forward tcp:27043 tcp:27043

adb forward tcp:27042 tcp:27042

來轉發這兩個端口,接着在手機上運作該應用程式,在指令行中執行腳本,最後點選應用的按鈕,即可看到字元串已經被替換成我們要替換的了!!!

frida入門總結
frida入門總結

四、Frida Hook Native層

  4.1、Hook native層傳回值為int類型的demo

    1、還是先寫一個小demo,下面貼一下關鍵代碼(很簡單c語言代碼就不再解釋了,至于native層函數怎麼編寫,由于本篇主要不是講怎麼編寫so函數,就不過多叙述了,實在不會的可以看一下我的一篇部落格,我覺得寫得還是挺詳細的,部落格編寫native層函數連結:https://www.cnblogs.com/aWxvdmVseXc0/p/11564809.html)和未Hook前截圖:

frida入門總結

代碼:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_fridaso_FridaSoDefine */

#ifndef _Included_com_example_fridaso_FridaSoDefine
#define _Included_com_example_fridaso_FridaSoDefine
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_example_fridaso_FridaSoDefine
* Method:    FridaSo
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_fridaso_FridaSoDefine_FridaSo(JNIEnv *env, jclass obj, jint a, jint b)
{
    int c;
    c = a + b;
    return c;
}

#ifdef __cplusplus
}
#endif
#endif  
           
frida入門總結

    2、接下來我們來寫python hook腳本,我們需要hook native層這個函數,達到傳回值修改為0的效果。寫到這裡需要說明一下關于so檔案當中的函數,分為導出函數和未導出函數兩種,導出函數打開IDA後能夠在導出表中找到的函數就是導出函數,未導出函數則在導出表中尋找不到,一般來說靜态編寫的native函數都能在導出表中尋找到,而動态加載的則無法在導出表中發現!!!

frida入門總結

代碼如下:(跟上面hook java層重複的代碼不在注釋詳講了!!!)

import frida
import sys

jscode = """
Java.perform(function(){
    //下面這一句代碼是指定要Hook的so檔案名和要Hook的函數名,函數名就是上面IDA導出表中顯示的那個函數名
    Interceptor.attach(Module.findExportByName("libfridaso.so","Java_com_example_fridaso_FridaSoDefine_FridaSo"),{
        //onEnter: function(args)顧名思義就是進入該函數前要執行的代碼,其中args是傳入的參數,一般so層函數第一個參數都是JniEnv,第二個參數是jclass,從第三個參數開始才是我們java層傳入的參數
        onEnter: function(args) {
            send("Hook start");
            send("args[2]=" + args[2]); //列印我們java層第一個傳入的參數
            send("args[3]=" + args[3]); //列印我們java層傳入的第二個參數
        },
        onLeave: function(retval){ //onLeave: function(retval)是該函數執行結束要執行的代碼,其中retval參數即是傳回值
            send("return:"+retval); //列印傳回值
            retval.replace(0); //替換傳回值為0
        }
    });
});
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print('[*] {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach('com.example.fridaso')
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()
           

    3、最後在手機端執行frida-server,轉發端口,開啟應用,執行腳本,點選按鈕,即可看到傳回值已經被修改成了0,效果圖如下:

frida入門總結
frida入門總結

  4.2、Hook native層傳回值為String類型的demo

    1、上面已經寫了怎麼Hook修改native層函數傳回值為int類型的情況,使用

replace()

函數直接修改即可,但是傳回情況為字元串則不一樣,在c語言中,傳回值為字元串其實是傳回了一個

char *

(字元串指針),是以簡單的替換是無法取效果的,具體怎麼修改傳回值,接着看下面,下面還是貼上demo的關鍵代碼和未Hook前截圖:

代碼如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_fridasostring_fridaSoString */

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_fridasostring_fridaSoString */

#ifndef _Included_com_example_fridasostring_fridaSoString
#define _Included_com_example_fridasostring_fridaSoString
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_example_fridasostring_fridaSoString
* Method:    FridaSo
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_fridasostring_fridaSoString_FridaSo(JNIEnv *env, jclass obj, jstring str)
{
    return str;
}

#ifdef __cplusplus
}
#endif
#endif
           

未Hook前運作截圖:

    2、接下來是python Hook腳本(隻解釋與上面有差異的代碼),Hook的函數具體函數名還是使用IDA去尋找

python代碼:

import frida
import sys

jscode = """
Java.perform(function(){
    Interceptor.attach(Module.findExportByName("libfridaso.so","Java_com_example_fridasostring_fridaSoString_FridaSo"),{
        onEnter: function(args) {
            send("Hook start");
            send("args[2]=" + args[2]);
        },
        onLeave: function(retval){
            send("return:"+retval);
            var env = Java.vm.getEnv(); //擷取env對象,也就是native函數的第一個參數
            var jstrings = env.newStringUtf("tamper"); //因為傳回的是字元串指針,使用我們需要構造一個newStringUtf對象,用來代替這個指針
            retval.replace(jstrings); //替換傳回值
        }
    });
});
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print('[*] {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach('com.example.fridasostring')
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()
           

    3、運作腳本後,點選按鈕,我們可以看到字元串已經被替換成了

tamper

,如下所示:

frida入門總結
frida入門總結

    4、關于為導出函數的Hook,大體上差不多,差别在于需要通過ida找到偏移值計算位址,而不是像導出函數這麼友善罷了,但原理都是差不多的,就不在細說了!!!

五、相關附件

    python Hook代碼已經demo下載下傳連結:https://pan.baidu.com/s/1ZCIeJXzeTpQ8uJ9Ew5nnGQ

    提取碼:z94i

繼續閱讀