在前面的文章中介紹了Frida基本API的使用,在這篇文章中介紹一些更加強大的API。同時簡單介紹下HOOK 系統函數的利器frida-trace。
內存,內存還是內存。
Java是極其重要的API。無論想對so層亦或java層進行攔截,通常都須編Java.perform。
還有一些API可參考官方文檔,Java.isMainThread()、Java.registerClass(spec)、Java.deoptimizeEverything()、Java.enumerateMethods(query)等,具體用法可參考官方文檔。在12.8.20版本上沒有Java.enumerateMethods(query)這個API。
function frida_Java() { Java.perform(function () { //作為判斷用 if(Java.available) { //注入的邏輯代碼 console.log(&34;,Java.androidVersion); }else{ //未能正常加載JAVA VM console.log(&34;); } //枚舉當前加載的所有類 Java.enumerateLoadedClasses({ //每一次回調此函數時其參數className就是類的信息 onMatch: function (className) { //輸出類字符串 console.log(&34;,className); }, //枚舉完畢所有類之後的回調函數 onComplete: function (){ //輸出類字符串 console.log(&34;); } }); //枚舉當前加載的Java VM類加載器 Java.enumerateClassLoaders({ //回調函數,參數loader是類加載的信息 onMatch: function (loader) { console.log(&34;,loader); }, //枚舉完畢所有類加載器之後的回調函數 onComplete: function () { console.log(&34;); } }); var values = Java.array(&39;, [ 1003, 1005, 1007 ]); var JString = Java.use(&39;); var str = JString.$new(Java.array(&39;, [ 0x48, 0x65, 0x69 ])); });} setImmediate(frida_Java,0);
部分運行結果如下:
Java.vm對象十分常用,比如想要拿到JNI層的JNIEnv對象,可以使用getEnv()。
function frida_Java() { Java.perform(function () { Interceptor.attach(Module.getExportByName(null, &39;), { onEnter: function (args) { this.fileDescriptor = args[0].toInt32(); }, onLeave: function (retval) { console.log(&34;+JSON.stringify(Java.vm.getEnv())); retval.replace(1337); console.log(&34;+retval); } }); });} setImmediate(frida_Java,0);
部分運行結果:
該對象功能十分強大,函數原型是Interceptor.attach(target, callbacks):參數target是需要攔截的位置的函數地址,也就是填某個so層函數的地址即可對其攔截,target是一個NativePointer參數,用來指定你想要攔截的函數的地址,NativePointer是一個指針。需要注意的是對於Thumb函數需要對函數地址+1,callbacks則是它的回調函數,分別是以下兩個回調函數:
很多時候,我們需要hook系統函數read,查看文件描述符,緩存等信息。
function frida_Java() { Java.perform(function () { Interceptor.attach(Module.getExportByName(null, &39;), { onEnter: function (args) { console.log(&39;); console.log(&39; + JSON.stringify(this.context)); console.log(&39; + this.returnAddress); console.log(&39; + this.threadId); console.log(&39; + this.depth); console.log(&39; + this.err); // Save arguments for processing in onLeave. this.fd = args[0].toInt32(); this.buf = args[1]; this.count = args[2].toInt32(); console.log(&34;+this.fd+&34;+this.buf+&34;+this.count); }, onLeave: function (retval) { console.log(&34;+JSON.stringify(Java.vm.getEnv())); retval.replace(1337); console.log(&34;+retval); } }); });} setImmediate(frida_Java,0);
運行結果如下:
關於this比較重要的屬性在表格中列了出來:
屬性含義returnAddress返回地址,類型是NativePointercontext上下文:具有鍵pc和sp的對象,它們是分別為ia32/x64/arm指定EIP/RIP/PC和ESP/RSP/SP的NativePointer對象。其他處理器特定的鍵也可用,例如eax、rax、r0、x0等。也可以通過分配給這些鍵來更新寄存器值errno當前errno值lastError當前作業系統錯誤值threadId作業系統線程IDdepth相對於其他調用的調用深度
相當於替換掉原本的函數,用替換時的實現替換目標處的函數。如果想要完全或部分替換現有函數的實現,則通常使用此函數。示例代碼如下,替換open函數,列印出打開文件的文件描述符和文件路徑。
var openPtr = Module.getExportByName(&39;, &39;);var open = new NativeFunction(openPtr, &39;, [&39;, &39;]);Interceptor.replace(openPtr, new NativeCallback(function (pathPtr, flags) { var path = pathPtr.readUtf8String(); log(&34;&39;&39;); var fd = open(pathPtr, flags); log(&39; + fd); return fd;}, &39;, [&39;, &39;]));
運行結果如下:
官網首頁就給出了frida-trace的用法,可見其功能強大。這裡以windows記事本為例,將記事本打開的文件路徑列印出來。Frida-trace是一個動態跟蹤函數調用的工具,其強大之處在於能夠hook系統函數。
在Windwos下打開文件函數使用的是CreateFileW函數,
可以看出CreateFileW是在Kernel32.dll中導出的。
使用 frida-trace-iCreateFileWnotepad.exe開啟跟蹤,修改相應的文件如下:
使用readUtf16String是因為Windwos使用Unicode字符編碼。通過記事本打開Warcraft目錄下的ij115.dll。結果如下,打開路徑被成功列印出來。
Frida hook系統API是如此的簡單,不得不說,Frida is so great。
更多Frida內容,歡迎關注我的微信公眾號。