短視頻、直播數據實時採集接口,請查看文檔: TiToDatajava
免責聲明:本文檔僅供學習與參考,請勿用於非法用途!不然一切後果自負。python
安全工程師在拿到應用評測的任務以後,第一件事情是抓到他的收包發包,第二件事情應該就是拿到它的apk
,打開看看裏面是什麼內容,若是不幸它加了殼,可能打開就是這樣的場景,見下圖,什麼內容都看不到,這時候就要首先對它進行脫殼。
殼的種類很是多,根據其種類不一樣,使用的技術也不一樣,這裏稍微簡單分個類:android
先說最難的Dex2C
目前是沒有辦法還原的,只能跟蹤進行分析;VMP
虛擬機解釋執行保護的是映射表,只要心思細、功夫深,是能夠將映射表還原的;二代殼函數抽取目前是能夠從根本上進行還原的,dump
出全部的運行時的方法體,填充到dump
下來的dex
中去的,這也是fart
的核心原理;最後也就是目前咱們推薦的幾個內存中搜索和dump
出dex
的Frida
工具,在一些場景中能夠知足你們的需求。git
dex
地址是:https://github.com/r0ysue/frida_dumpgithub
# frida -U --no-pause -f com.xxxxxx.xxxxxx -l dump_dex.js ____ / _ | Frida 12.8.9 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://www.frida.re/docs/home/ Spawned `com.xxxxx.xxxxx`. Resuming main thread! [Google Pixel::com.xxxxx.xxxxx]-> [dlopen:] libart.so _ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE 0x7adcac4f74 [DefineClass:] 0x7adcac4f74 [find dex]: /data/data/com.xxxxx.xxxxx/files/7abfc00000_8341c4.dex [dump dex]: /data/data/com.xxxxx.xxxxx/files/7abfc00000_8341c4.dex [find dex]: /data/data/com.xxxxx.xxxxx/files/7ac4096000_6e6c8.dex [dump dex]: /data/data/com.xxxxx.xxxxx/files/7ac4096000_6e6c8.dex [find dex]: /data/data/com.xxxxx.xxxxx/files/7ac37c4028_8781c4.dex [dump dex]: /data/data/com.xxxxx.xxxxx/files/7ac37c4028_8781c4.dex
其核心邏輯原理就是下面一句話magic.indexOf("dex") == 0
,只要文件頭中含有魔數dex
,就把它dump
下來。算法
if (dex_maps[base] == undefined) { dex_maps[base] = size; var magic = ptr(base).readCString(); if (magic.indexOf("dex") == 0) { var process_name = get_self_process_name(); if (process_name != "-1") { var dex_path = "/data/data/" + process_name + "/files/" + base.toString(16) + "_" + size.toString(16) + ".dex"; console.log("[find dex]:", dex_path); var fd = new File(dex_path, "wb"); if (fd && fd != null) { var dex_buffer = ptr(base).readByteArray(size); fd.write(dex_buffer); fd.flush(); fd.close(); console.log("[dump dex]:", dex_path); } } } }
安卓只能使用繼承自BaseDexClassLoader
的兩種ClassLoader
,一種是PathClassLoader
,用於加載系統中已經安裝的apk
;一種就是DexClassLoader
,加載未安裝的jar
包或apk
。
能夠用objcetion
直接在堆上暴力搜索全部的dalvik.system.DexClassLoader
實例,效果見下圖:json
# android heap search instances dalvik.system.DexClassLoader
地址:https://github.com/hluwa/FRIDA-DEXDumpapi
dex
,採用暴力搜索dex035
便可找到。dex
,經過匹配一些特徵來找到,而後自動修復文件頭。效果很是好:數組
root@roysuekali:~/Desktop/FRIDA-DEXDump# python main.py [DEXDump]: found target [7628] com.xxxxx.xxxxx [DEXDump]: DexSize=0x8341c4, SavePath=./com.xxxxx.xxxxx/0x7abfc00000.dex [DEXDump]: DexSize=0x8341c4, SavePath=./com.xxxxx.xxxxx/0x7ac0600000.dex root@roysuekali:~/Desktop/FRIDA-DEXDump# du -h com.xxxxx.xxxxx/* 8.3M com.xxxxx.xxxxx/0x7abfc00000.dex 8.3M com.xxxxx.xxxxx/0x7ac0600000.dex root@roysuekali:~/Desktop/FRIDA-DEXDump# file com.xxxxx.xxxxx/* com.xxxxx.xxxxx/0x7abfc00000.dex: Dalvik dex file version 035 com.xxxxx.xxxxx/0x7ac0600000.dex: Dalvik dex file version 035
打開dump
下來的dex
,很是完整,能夠用jadx
直接解析。用010
打開能夠看到完整的文件頭——dexn035
,其實現代碼也是簡單粗暴,直接搜索:64 65 78 0a 30 33 35 00
:
Memory.scanSync(range.base, range.size, "64 65 78 0a 30 33 35 00").forEach(function (match) { var range = Process.findRangeByAddress(match.address); if (range != null && range.size < match.address.toInt32() + 0x24 - range.base.toInt32()) { return; } var dex_size = match.address.add("0x20").readInt(); if (range != null) { if (range.file && range.file.path && (range.file.path.startsWith("/data/app/") || range.file.path.startsWith("/data/dalvik-cache/") || range.file.path.startsWith("/system/"))) { return; } if (match.address.toInt32() + dex_size > range.base.toInt32() + range.size) { return; } }
還有一部分想要特徵匹配的功能還在實現中:
// @TODO improve fuzz if ( range.size >= 0x60 && range.base.readCString(4) != "dexn" && range.base.add(0x20).readInt() <= range.size //file_size // && range.base.add(0x24).readInt() == 112 //header_size && range.base.add(0x34).readInt() < range.size && range.base.add(0x3C).readInt() == 112 //string_id_off ) { result.push({ "addr": range.base, "size": range.base.add(0x20).readInt() }); }
既然直接使用Frida
的API
能夠暴力搜索內存,那麼別忘了咱們上面介紹過的objection
也能夠暴力搜內存。
# memory search "64 65 78 0a 30 33 35 00"
搜出來的offset
是:0x79efc00000
,大小是c4 41 83 00
,也就是0x8341c4
,轉化成十進制就是8602052
,最後dump
下來的內容與FRIDA-DEXDump
脫下來的如出一轍,拖到jdax
裏能夠直接解析。
在Frida
出現以前,沒有任何一款工具,能夠在語言級別支持直接在電腦上調用app
中的方法。像Xposed
是純Java
,根本就沒有電腦上運行的版本;各類Native
框架也是同樣,都是由C/C++/asm
實現,根本與電腦毫無關係。
而Frida
主要是一款在電腦上操做的工具,其自己就決定了其「高併發」、「多聯通」、「自動化」等特性:
app
中的算法;Frida
用於自動化的場景中,必然是不可能在終端敲frida-tools
裏的那些命令行工具的,有人說能夠將這些命令按順序寫成腳本,那爲啥不直接寫成python
腳本呢?枉費大鬍子叔叔(Frida
的做者oleavr的頭像)爲咱們寫好了Python bindings
,咱們只須要直接調用便可享受。Python bindings
在安裝好frida-tools
的時候已經默認安裝在咱們的電腦上了,能夠直接使用。
鏈接多臺設備很是簡單,若是是USB
口直接鏈接的,只要確保adb
已經鏈接上,若是是網絡調試的,也要用adb connect
鏈接上,而且都開啓frida server
,鍵入adb devices
或者frida-ls-devices
命令時多臺設備的id
都會出現,最終可使用frida.get_device(id)
的API
來選擇設備,以下圖所示。
互聯互通是指把app
中捕獲的內容傳輸到電腦上,電腦上處理結束後再發回給app
繼續處理。看似很簡單的一個功能,目前卻僅有Frida
能夠實現。
好比說咱們有這樣一個app
,其中最核心的地方在於判斷用戶是否爲admin
,若是是,則直接返回錯誤,禁止登錄。若是不是,則把用戶和密碼上傳到服務器上進行驗證登陸操做,其核心代碼邏輯以下:
public class MainActivity extends AppCompatActivity { EditText username_et; EditText password_et; TextView message_tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); password_et = (EditText) this.findViewById(R.id.editText2); username_et = (EditText) this.findViewById(R.id.editText); message_tv = ((TextView) findViewById(R.id.textView)); this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (username_et.getText().toString().compareTo("admin") == 0) { message_tv.setText("You cannot login as admin"); return; } //咱們hook的目標就在這裏 message_tv.setText("Sending to the server :" + Base64.encodeToString((username_et.getText().toString() + ":" + password_et.getText().toString()).getBytes(), Base64.DEFAULT)); } }); } }
運行起來的效果以下圖:
咱們的目標就是在電腦上「獲得」輸入框輸入的內容,而且修改其輸入的內容,而且「傳輸」給安卓機器,使其經過驗證。也就是說,咱們的目標是哪怕輸入admin
的帳戶名和密碼,也能夠繞過本地校驗,進行服務器驗證登錄的操做。
因此最終咱們的hook
代碼的邏輯就是,截取輸入,傳輸給電腦,暫停執行,獲得電腦傳回的數據以後,繼續執行,用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; send(string_to_send); // 將數據發送給kali主機的python代碼 recv(function (received_json_object) { string_to_recv = received_json_object.my_data console.log("string_to_recv: " + string_to_recv); }).wait(); //收到數據以後,再執行下去 return this.setText(string_to_recv); } });
在電腦上的處理流程是,將接受到的JSON
數據解析,提取出其中的密碼部分保持不變,而後將用戶名替換成admin
,這樣就實現了將admin
和password
發送給服務器的結果。咱們的代碼以下:
import time import frida def my_message_handler(message, payload): print message print payload if message["type"] == "send": print message["payload"] data = message["payload"].split(":")[1].strip() print 'message:', message data = data.decode("base64") # 解碼 user, pw = data.split(":") # 提取用戶名和密碼 data = ("admin" + ":" + pw).encode("base64") # 組成新的組合並編碼 print "encoded data:", data script.post({"my_data": data}) # 將JSON對象發送回去 print "Modified data sent" device = frida.get_usb_device() pid = device.spawn(["com.roysue.demo04"]) device.resume(pid) time.sleep(1) session = device.attach(pid) with open("s4.js") as f: script = session.create_script(f.read()) script.on("message", my_message_handler) # 註冊消息處理函數 script.load() raw_input()
一樣不少手機上沒法處理的數據,也能夠編碼後發送到電腦上進行處理,好比處理GBK
編碼的中文字符集數據,再好比對dump
下來的內存或so
進行二次解析還原等,這些在js
幾乎是沒法處理的(或難度很是大),可是到了電腦上就易如反掌,用python
導入幾個庫就能夠。
在一些(網絡)接口的模糊測試的場景中,一些字典和畸形數據的構造也會在電腦上完成,app
端最多做爲執行端接受和發送這些數據,這時候也須要使用到Frida
互聯互通動態修改的功能。
在腳本里定義一個導出函數,並用rpc.exports
的字典進行聲明:
function callSecretFun() { //定義導出函數 Java.perform(function () { //to-do 作本身想作的事情 //好比這裏是找到隱藏函數而且調用 Java.choose("com.roysue.demo02.MainActivity", { onMatch: function (instance) { console.log("Found instance: " + instance); console.log("Result of secret func: " + instance.secret()); }, onComplete: function () { } }); }); } rpc.exports = { callsecretfunction: callSecretFun //把callSecretFun函數導出爲callsecretfunction符號,導出名不能夠有大寫字母或者下劃線 };
在電腦上就能夠直接在py
代碼裏調用這個方法:
import time import frida def my_message_handler(message, payload): print message print payload device = frida.get_usb_device() pid = device.spawn(["com.roysue.demo02"]) device.resume(pid) time.sleep(1) session = device.attach(pid) with open("s3.js") as f: script = session.create_script(f.read()) script.on("message", my_message_handler) script.load() command = "" while 1 == 1: command = raw_input("Enter command:n1: Exitn2: Call secret functionnchoice:") if command == "1": break elif command == "2": #在這裏調用 script.exports.callsecretfunction()
最終效果就是按一下2
,function callSecretFun()
就會被執行一次,而且結果會顯示在電腦上的py
腳本里,以供後續繼續處理,很是方便。
筆者有一位朋友甚至將該接口使用python
的flask
框架暴露出去,讓網絡裏的每一個人均可以調用該方法,給本身的發包進行簽名,可用說是一個需求很是龐大的場景。
最後收集和整理一下你們在學習Frida
的過程當中可能會遇到的幾個高頻問題,以餮讀者。
Frida
從面世到如今已經有四五年了,大概17~18年那會兒開始火爆起來,大量的腳本和工具代碼都是那段時間寫出來的,而Frida
又升級特別快,新的Frida
對老的腳本兼容性不是很好,見下圖最新的Frida
運行老的腳本,日誌格式已經亂掉了,而老版本(12.4.8
)就沒問題,見圖2-18。若是要運行一些兩三年曆史的代碼,必然須要安裝兩三年前左右的版本,這樣才能跑起來,而且不出錯。
版本管理用pyenv
便可,熟練使用pyenv
能夠基本上知足同時安裝幾十個Frida
版本的需求。
幾個最基本的思路,首先frida-server
的文件名改掉,相似於frida-server-12.8.9-android-arm64
這樣的文件名,我通常改爲fs1289amd64
,固然讀者能夠想改爲啥就改爲啥。
有些反調試還會檢查端口,好比frida-server
的默認端口是27042
,這個端口通常不會有人用,若是27042
端口打開而且正在監聽,反調試就會工做,能夠把端口改爲非標準端口,方法下一小節就講。
最後還有一種經過Frida
內存特徵對maps
中elf
文件進行掃描匹配特徵的反調試方法,支持frida-gadget
和frida-server
,項目地址在這裏。
其核心代碼以下:
void *check_loop(void *) { int fd; char path[256]; char perm[5]; unsigned long offset; unsigned int base; long end; char buffer[BUFFER_LEN]; int loop = 0; unsigned int length = 11; //"frida:rpc"的內存佈局特徵 unsigned char frida_rpc[] = { 0xfe, 0xba, 0xfb, 0x4a, 0x9a, 0xca, 0x7f, 0xfb, 0xdb, 0xea, 0xfe, 0xdc }; for (unsigned char &m : frida_rpc) { unsigned char c = m; c = ~c; c ^= 0xb1; c = (c >> 0x6) | (c << 0x2); c ^= 0x4a; c = (c >> 0x6) | (c << 0x2); m = c; } //開始檢測frida反調試循環 LOGI("start check frida loop"); while (loop < 10) { fd = wrap_openat(AT_FDCWD, "/proc/self/maps", O_RDONLY, 0); if (fd > 0) { while ((read_line(fd, buffer, BUFFER_LEN)) > 0) { // 匹配frida-server和frida-gadget的內存特徵 if (sscanf(buffer, "%x-%lx %4s %lx %*s %*s %s", &base, &end, perm, &offset, path) != 5) { continue; } if (perm[0] != 'r') continue; if (perm[3] != 'p') continue; if (0 != offset) continue; if (strlen(path) == 0) continue; if ('[' == path[0]) continue; if (end - base <= 1000000) continue; if (wrap_endsWith(path, ".oat")) continue; if (elf_check_header(base) != 1) continue; if (find_mem_string(base, end, frida_rpc, length) == 1) { //發現其內存特徵 LOGI("frida found in memory!"); #ifndef DEBUG //殺掉本身的進程 wrap_kill(wrap_getpid(),SIGKILL); #endif //退出 break; } } } else { LOGI("open maps error"); } wrap_close(fd); //休息三秒,進入下一個檢查循環,也就是這個反調試一共會運做30秒,30秒後結束 loop++; sleep(3); } return nullptr; } void anti_frida_loop() { pthread_t t; //建立一個線程,執行反調試工做 if (pthread_create(&t, nullptr, check_loop, (void *) nullptr) != 0) { exit(-1); }; pthread_detach(t); }
想過這種反調試,得找到反調試在哪一個so
的哪裏,nop
掉建立check_loop
線程的地方,或者nop
掉kill
本身進程的地方,均可以。也能夠直接kill
掉反調試進程,筆者就曾經遇到過這種狀況,frida
命令注入後,app
調不起來,這時候用ps -e
命令查看多一個反調試進程,直接kill
掉那個進程後,app
就起來了,這個app
是使用的一個大廠的加固服務,這個進程就是殼的一部分。
好比將frida-server
啓動在6666
端口:
# ./fs1287amd64 -l 0.0.0.0:6666
使用frida-tools
工具和objection
分別鏈接的方法以下:
# frida-ps -H 192.168.1.102:6666 # objection -N -h 192.168.1.102 -p 6666 -g com.android.settings explore
效果如圖所示:
圖 鏈接非標準端口
在python bindings
中鏈接的話,會稍微複雜一點點,由於python bindings
只認adb
,因此要經過adb
命令將手機的6666
端口映射到電腦的27042
端口:
$ adb forward tcp:27042 tcp:6666
這樣python bindings
也能夠正常使用了。
byte[]``[B
ByteString.of
是用來把byte[]
數組轉成hex
字符串的函數, 安卓系統自帶ByteString
,app
裏面沒有也不要緊,能夠去系統裏面拿,這裏給個小案例:
var ByteString = Java.use("com.android.okhttp.okio.ByteString"); var j = Java.use("xxxxxxx.business.comm.j"); j.x.implementation = function() { var result = this.x(); console.log("j.x:", ByteString.of(result).hex()); return result; }; j.a.overload('[B').implementation = function(bArr) { this.a(bArr); console.log("j.a:", ByteString.of(bArr).hex()); };
hook
管理子進程常常有人會問,像那種com.xxx.xxx:push
、com.xxx.xxx:service
、com.xxx.xxx:notification
、com.xxx.xxx:search
這樣的進程如何hook
,或者說如何在其建立伊始進行hook
,由於這樣的進程通常都是由主進程fork()
出來的。
這種的就要用到Frida
最新的Child gating
機制,能夠參考個人過往的文章,官方的完整代碼在這裏。能夠在進程建立之初對該進程進行控制和hook
,已經不少人用了,效果很好,達成目標。
hook
混淆方法名有些方法名上了很強的混淆,如何處理?其實很簡單,能夠看上面ZenTracer
的源碼,hook
類的全部子類,hook
類的全部方法,而且hook
方法的全部重載。
hook
某些方法的時候,發現傳進來的參數居然是中文的,如何打印出來?若是是utf8
還好,Frida
的CLI
也是直接支持utf8
的,若是是GBK
字符集的,目前沒有找到在js
裏進行打印的方法,能夠send()
到電腦上進行打印。
hook
主動註冊使用Frida
來hook
JNI
的一些函數,打印出主動調用的執行路徑。下面是hook
Google play Market
的例子:
frida -U --no-pause -f com.android.vending -l hook_RegisterNatives.js ____ / _ | Frida 12.6.13 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/ Spawning `com.android.vending`... GetFieldID is at 0xf1108e4d _ZN3art3JNI10GetFieldIDEP7_JNIEnvP7_jclassPKcS6_ AllocObject is at 0xf10f1809 _ZN3art3JNI11AllocObjectEP7_JNIEnvP7_jclass GetMethodID is at 0xf10f3175 _ZN3art3JNI11GetMethodIDEP7_JNIEnvP7_jclassPKcS6_ NewStringUTF is at 0xf111fc71 _ZN3art3JNI12NewStringUTFEP7_JNIEnvPKc GetObjectClass is at 0xf10f2841 _ZN3art3JNI14GetObjectClassEP7_JNIEnvP8_jobject RegisterNatives is at 0xf11301fd _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi CallObjectMethod is at 0xf10f3745 _ZN3art3JNI16CallObjectMethodEP7_JNIEnvP8_jobjectP10_jmethodIDz GetStaticFieldID is at 0xf111949d _ZN3art3JNI16GetStaticFieldIDEP7_JNIEnvP7_jclassPKcS6_ GetStaticMethodID is at 0xf110e6d1 _ZN3art3JNI17GetStaticMethodIDEP7_JNIEnvP7_jclassPKcS6_ GetStringUTFChars is at 0xf11203e1 _ZN3art3JNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh ReleaseStringUTFChars is at 0xf11207fd _ZN3art3JNI21ReleaseStringUTFCharsEP7_JNIEnvP8_jstringPKc FindClass is at 0xf10ec7a1 _ZN3art3JNI9FindClassEP7_JNIEnvPKc Spawned `com.android.vending`. Resuming main thread! [Google Pixel XL::com.android.vending]-> [RegisterNatives] method_count: 0x6 [RegisterNatives] java_class: org.chromium.base.CommandLine name: nativeInit sig: ([Ljava/lang/String;)V fnPtr: 0xd454f349 module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130349 [RegisterNatives] java_class: org.chromium.base.CommandLine name: nativeHasSwitch sig: (Ljava/lang/String;)Z fnPtr: 0xd454f369 module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130369 [RegisterNatives] java_class: org.chromium.base.CommandLine name: nativeGetSwitchValue sig: (Ljava/lang/String;)Ljava/lang/String; fnPtr: 0xd454f3bd module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x1303bd [RegisterNatives] java_class: org.chromium.base.CommandLine name: nativeAppendSwitch sig: (Ljava/lang/String;)V fnPtr: 0xd454f461 module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130461 [RegisterNatives] java_class: org.chromium.base.CommandLine name: nativeAppendSwitchWithValue sig: (Ljava/lang/String;Ljava/lang/String;)V fnPtr: 0xd454f499 module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130499 [RegisterNatives] java_class: org.chromium.base.CommandLine name: nativeAppendSwitchesAndArguments sig: ([Ljava/lang/String;)V fnPtr: 0xd454f4f1 module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x1304f1 [RegisterNatives] method_count: 0x3 [RegisterNatives] java_class: org.chromium.base.EarlyTraceEvent name: nativeRecordEarlyEvent sig: (Ljava/lang/String;JJIJ)V fnPtr: 0xd454f94d module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x13094d [RegisterNatives] java_class: org.chromium.base.EarlyTraceEvent name: nativeRecordEarlyStartAsyncEvent sig: (Ljava/lang/String;JJ)V fnPtr: 0xd454fa3d module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130a3d [RegisterNatives] java_class: org.chromium.base.EarlyTraceEvent name: nativeRecordEarlyFinishAsyncEvent sig: (Ljava/lang/String;JJ)V fnPtr: 0xd454fae5 module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130ae5 [RegisterNatives] method_count: 0x4 [RegisterNatives] java_class: org.chromium.base.FieldTrialList name: nativeFindFullName sig: (Ljava/lang/String;)Ljava/lang/String; fnPtr: 0xd454fb8d module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130b8d [RegisterNatives] java_class: org.chromium.base.FieldTrialList name: nativeTrialExists sig: (Ljava/lang/String;)Z fnPtr: 0xd454fbff module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130bff [RegisterNatives] java_class: org.chromium.base.FieldTrialList name: nativeGetVariationParameter sig: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; fnPtr: 0xd454fc2f module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130c2f [RegisterNatives] java_class: org.chromium.base.FieldTrialList name: nativeLogActiveTrials sig: ()V fnPtr: 0xd454fd1d module_name: libcronet.76.0.3809.21.so module_base: 0xd441f000 offset: 0x130d1d [RegisterNatives] method_count: 0x2
源碼地址:https://github.com/lasting-yang/frida_hook_libart
JNI API
地址:https://github.com/chame1eon/jnitrace
hook
不少時候在帶殼hook
的時候,善用兩個frida
提供的延時hook
機制:
frida --no-pause
是進程直接執行,有時候會hook
不到,若是把--no-pause
拿掉,進入CLI
以後延遲幾秒再使用%resume
恢復執行,就會hook
到;js
中的setTimeout(func, delay[, ...parameters])
函數,會延時delay
毫秒來調用func
,有時候不加延時會hook
不到,加個幾百到幾千毫秒的延時就會hook
到。