Frida 基础操作2
1.FRIDA 初級
1.1.Frida遠程過程調用(rpc)
有時候分析一個應用的算法時找到了算法的實現方法但方法的邏輯特別復雜或被混淆了怎么辦?
這時候我們可以選擇一個簡單而粗暴的方式,直接剛,啊呸,直接調。
frida支持在js代碼中使用rpc.exports關鍵字對js函數進行導出,導出方法可在python代碼中通過script.exports.導出符號()進行調用。配合之前說過的frida的方法主動調用,可以很好的完成對應用中的算法或核心方法進行主動單獨調用。
python代碼
# -*- coding: UTF-8 -*- import frida import sys# 目標包名 appPacknName = "cn.gemini.k.fridatest" scriptFile = "hook_script.js"# 輸出日志的回調方法 def on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)device = frida.get_usb_device() # spawn模式,找到目標包名并重啟,在啟動前注入腳本 pid = device.spawn([appPacknName]) session = device.attach(pid) # 注意這里需要將device.attach(pid)這句代碼寫在前面,這樣執行才符合預期(啟動時程序白屏,等待下面這行代碼來恢復執行) # 其實在https://www.jianshu.com/p/b833fba1bffe這篇文章中有提到 device.resume(pid)# 方式一: 通過js文件創建hook代碼 with open(scriptFile, encoding='UTF-8') as f:script = session.create_script(f.read()) # 方式二: 直接將hook代碼寫在python文件中 # script = session.create_script(js_code)script.on("message", on_message) script.load() # 把js代碼注入到目標應用中 # 避免結束 # sys.stdin.read()# frida RPC測試 script.exports.exporthook10() # exportmain為js文件中的導出符號1.2.Frida手動加載dex
破解應用時如果想在應用中執行我們自己寫的Java類代碼通過frida怎么實現?
可以將自定義的Java類編譯成一個dex文件,懶得單獨編譯也可以直接去apk文件中獲取,需要注意的是有些apk可能存在多個dex文件,這里需要找一下一定要是含有我們Java類的那個dex。
dex文件準備好后使用adb工具push到設備的"/data/local/tmp/“目錄下方便我們的frida代碼加載,之后通過Java.openClassFile(”/data/local/tmp/my.dex").load()對目標dex進行加載。這樣就可以通過Java.use調用我們直接寫的方法了。
1.3.Frida Hook動態加載的dex
如果需要hook的類所在dex是應用在運行過程中動態加載的怎么hook?
首先怎么判斷是否為動態加載:內存中確實存在該類,但apk中的dex卻找不到該類,那么可能就是動態加載的。
這種情況下通過frida的一般hook流程是hook不上這個類的,因為frida使用的默認classloader與加載該類的classloader是不一樣,此時就需要通過frida的enumerateClassLoaders方法來枚舉當前進程的classloader,再通過loader.findClass方法在每個枚舉到的classloader中尋找是否存在我們想要的類,找到后再通過Java.classFactory.loader=loader來切換一下當前frida使用的classloader,切換完成后就可以通過Java.use進行類的查找了。
1.4.Frida?;厮?/h3>
一般在定位應用的某個功能點時用的較多,常見用法是對一些系統API進行hook,比如系統加解密API、接受用戶輸入的系統API等,當程序執行到被hook的方法時再通過對當時的堆棧進行打印并分析上下棧信息即可得知該功能的代碼實現所在位置。
function PrintJavaStacks1(){var thread = Java.use("java.lang.Thread");var tOBJ = thread.$new();var stack = tOBJ.currentThread().getStackTrace();var at = ""for(var i = 0; i < stack.length; i++){at += stack[i].toString()+"\n";}console.log(at); }1.5.Frida Hook時機
有些場景下的hook要求我們在APP應用啟動前就要進行hook,比如分析被加過固的應用又或是需要hook的方法是一個靜態方法,在程序啟動時就被初始化,這種情況該如何控制frida的hook時機呢?
前面說過frida支持兩種注入模式,一種是直接對已啟動的目標應用進行附加注入。另一種是以掛起的方式重啟目標應用,應用啟動時會先進入等待模式,觀察設備發現應用是處于白屏狀態的,實際上是在等待我們喚醒主線程來繼續完成啟動。
對于需要在APP應用啟動前就hook的場景我們就需要選擇第二種注入模式。
以掛起的方式啟動應用可以使用"frida -U -f com.xxx.xxx -l hook.js “來實現,之后需要我們通過手動輸入%resume的方式來讓主線程開始運行。
該命令沒有加”–no-pause"參數,如果加上該參數程序啟動時就不會進入等待而是直接啟動。加與不加的區別就是啟動時不等待與等待。
參數"-f"表示,需要重啟這個應用并且attach上去,與之對應的是"-n"或"-p"參數,分別是通過指定進程名或進程id來attach到正在運行的應用。
參數"-l"表示本次需要加載的js注入代碼。
如果是在python代碼中要以掛起的方式啟動則可以使用spawn()方法,喚醒的方法是resume(),需要指定pid。
1.6.Frida程序與App數據交互
主要涉及到兩個方法send()/recv();
send:向Frida應用程序發送JSON可序列化消息
recv:接收來自Frida應用程序發送的一條消息
wait:直到消息已經被recv接收,并且recv的回調方法已經執行完畢并返回
js注入程序通過send方法將應用的數據發送給PC端的frida應用程序,然后frida應用程序對接收到的數據進行各種處理修改后再發送給js注入程序,這樣就可以完成python對app數據動態修改的效果了??赡苡行┬』锇闀?#xff0c;為什么要傳到PC端去處理,之后又回傳給js代碼而不直接通過js代碼處理呢?
主要是考慮到手機性能的問題,如果需要處理的數據比較復雜,那么使用PC去跑則效果更好。
python客戶端處理代碼
注入的js代碼
Java.perform(function () {var tv_class = Java.use("android.widget.TextView");tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) {var string_to_send = x.toString();var string_to_recv;console.log("send"+string_to_send);send(string_to_send); // 將數據發送給PC端recv(function (received_json_object) {string_to_recv = received_json_object.my_dataconsole.log("string_to_recv: " + string_to_recv);}).wait(); //收到數據之后,再執行下去var string = Java.use("java.lang.String");string_to_recv = string.$new(string_to_recv);//將收到的數據轉化為String類型return this.setText(string_to_recv);} });1.7.Frida類型轉換、參數構造
1.7.1.類型轉換
我們在hook某個方法分析其參數值時,如果參數不是string類型,那么打印出來的很可能就是[object Object],這種情況我們就需要對參數做一些轉換處理才能打印出來真實的值。
- 打印HashMap或Map類型的參數x
frida給我們提供了類型強轉方法:Java.cast()
var map = Java.use("java.util.HashMap"); var args_x = Java.cast(x,map); //將參數x轉換為HashMap類型 console.log(args_x.toString()); //調用HashMap的toString方法- 打印char[]類型的參數x
- 打印byte[]類型的參數x
1.7.2.參數構造
frida獲取應用context
我們在主動調用app的某個方法時經常會遇到參數是一個context對象,此時我們就可以通過下面的方式獲取context對象。
frida構造任意類型的數組
frida還給我們提供了構造任意類型數組的方法java.array()
用法格式:Java.array(‘type’,[value1,value2,…])
使用構造char[]類型:
使用構造Object[]類型:
var objectclass = Java.use("java.lang.Object"); var objectArray = Java.array('char',[objectclass.class]);1.8.打印non-ascii
應用場景一般是apk被混淆了,混淆后的方法名都是一些亂碼或不顯示的非ASCII字符,這時候因為我們沒法確定方法名所以就沒法指定需要hook的方法,這時候可以通過先編碼打印出來,再用編碼后的字符串去hook即可解決方法名亂碼的問題。
https://api-caller.com/2019/03/30/frida-note/#non-ascii
示例方法:
js代碼:
Java.perform(function x() {var cls = Java.use("com.example.hooktest.MainActivity");var methods = cls.class.getDeclaredMethods(); // getDeclaredMethods方法會獲取該類的所有方法,包括私有的,公有的,保護的for (var i in methods) { // 遍歷獲取到的所有方法console.log(methods[i].toString()); // 打印原始方法名console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/, "$1"))); // 過濾處理并編碼}// %D6%8F == ?cls[decodeURIComponent("%D6%8F")].implementation = function (x) {console.log("original call: fun(" + x + ")");var result = this[decodeURIComponent("%D6%8F")](900);return result;}} )1.9.收集的一些工具方法
string轉byte[]
frida讀寫std::string
function readStdString(str){const isTiny = (str.readU8()&1) === 0;if (isTiny)return str.add(1).readUtf8String();return str.add(2*Provess.pointerSize).readPointer().readUtf8String(); }function writeStdString(str, content){const isTiny = (str.readU8()&1) === 0;if (isTiny)str.add(1).writeUtf8String(content);elsestr.add(2*Process.pointerSize).readPointer().writeUtf8String(content); }總結
以上是生活随笔為你收集整理的Frida 基础操作2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Frida之安装和使用教程
- 下一篇: 逆向工具清单