Frida API高級
在前面的文章中介紹了Frida基本API的使用,在這篇文章中介紹一些更加強(qiáng)大的API。同時簡單介紹下HOOK 系統(tǒng)函數(shù)的利器frida-trace。
內(nèi)存,內(nèi)存還是內(nèi)存。
Java對象
Java對象
Java是極其重要的API。無論想對so層亦或java層進(jìn)行攔截,通常都須編Java.perform。
Java.available: 該函數(shù)一般用來判斷當(dāng)前進(jìn)程是否加載了JavaVM,Dalvik或ART虛擬機(jī)
Java.androidVersion: 顯示Android系統(tǒng)版本號
Java.enumerateLoadedClasses(callbacks): ? 枚舉當(dāng)前加載的所有類信息,它有一個回調(diào)函數(shù)分別是onMatch、onComplete函數(shù)。這是一個異步的方法,有一個同步的API是Java.enumerateLoadedClassesSync。
Java.enumerateClassLoaders(callbacks): ?枚舉Java VM中存在的類加載器,其有一個回調(diào)函數(shù),分別是onMatch: function (loader)與onComplete: ?function ()。這是一個異步的方法,有一個同步的API是Java.enumerateClassLoadersSync()。
Java.array(type, elements): 根據(jù)指定的元素類型創(chuàng)建一個Java數(shù)組。
還有一些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("",Java.androidVersion);
? ? ?}else{
? ? ? ? ?//未能正常加載JAVA VM
? ? ? ? ?console.log("error");
? ? ?}
? ? ?//枚舉當(dāng)前加載的所有類
? ? ?Java.enumerateLoadedClasses({
? ? ? ? ?//每一次回調(diào)此函數(shù)時其參數(shù)className就是類的信息
? ? ? ? ?onMatch: function (className)
????????? {
? ? ? ? ? ? ?//輸出類字符串
? ? ? ? ? ? ?console.log("",className);
? ? ? ? ?},
? ? ? ? ?//枚舉完畢所有類之后的回調(diào)函數(shù)
? ? ? ? ?onComplete: function ()
{
? ? ? ? ? ? ?//輸出類字符串
? ? ? ? ? ? ?console.log("輸出完畢");
? ? ? ? ?}
? ? ?});
? ? ?//枚舉當(dāng)前加載的Java VM類加載器
? ? ?Java.enumerateClassLoaders({
? ? ? ?//回調(diào)函數(shù),參數(shù)loader是類加載的信息
? ? ? ?onMatch: function (loader)
????????{
? ? ? ? ? ?console.log("",loader);
? ? ? ?},
? ? ? ?//枚舉完畢所有類加載器之后的回調(diào)函數(shù)
? ? ? ?onComplete: function ()
???????{
? ? ? ? ? ?console.log("end");
? ? ? ?}
? ?});
? ?var values = Java.array('int', [ 1003, 1005, 1007 ]);
? var JString = Java.use('java.lang.String');
? ?var str = JString.$new(Java.array('byte', [ 0x48, 0x65, 0x69 ]));
?});
} ? ? ?
setImmediate(frida_Java,0);
部分運(yùn)行結(jié)果如下:

Java.vm
Java.vm對象十分常用,比如想要拿到JNI層的JNIEnv對象,可以使用getEnv()。
function frida_Java() {
?Java.perform(function () {
? ? ? ?Interceptor.attach(Module.getExportByName(null, 'read'), {
? ? ? ? ?onEnter: function (args) {
? ? ? ? ? ?this.fileDescriptor = args[0].toInt32();
? ? ? ? ?},
? ? ? ? ?onLeave: function (retval) {
? ? ? ? ? ?console.log("Env對象"+JSON.stringify(Java.vm.getEnv()));
? ? ? ? ? ?retval.replace(1337);
? ? ? ? ? ?console.log("retval"+retval);
? ? ? ? ?}
? ? ? ?});
?});
} ? ? ?
setImmediate(frida_Java,0);
部分運(yùn)行結(jié)果:

Intercepter對象

該對象功能十分強(qiáng)大,函數(shù)原型是Interceptor.attach(target, callbacks):參數(shù)target是需要攔截的位置的函數(shù)地址,也就是填某個so層函數(shù)的地址即可對其攔截,target是一個NativePointer參數(shù),用來指定你想要攔截的函數(shù)的地址,NativePointer是一個指針。需要注意的是對于Thumb函數(shù)需要對函數(shù)地址+1,callbacks則是它的回調(diào)函數(shù),分別是以下兩個回調(diào)函數(shù):
onEnter: ?function (args): callback function given one argument args that can be ?used to read or write arguments as an array of NativePointer objects.
onLeave: ?function (retval): callback function given one argument retval that is a ?NativePointer-derived object containing the raw return value. You may ?call?retval.replace(1337)?to replace the return value with the integer 1337, or?retval.replace(ptr("0x1234"))?to ?replace with a pointer. Note that this object is recycled across ?onLeave calls, so do not store and use it outside your callback. Make a ?deep copy if you need to store the contained value, e.g.: ?ptr(retval.toString()).
很多時候,我們需要hook系統(tǒng)函數(shù)read,查看文件描述符,緩存等信息。
function frida_Java() {
?Java.perform(function () {
? ? ? ?Interceptor.attach(Module.getExportByName(null, 'read'), {
? ? ? ? ?onEnter: function (args) {
? ? ? ? ? ?console.log('Context information:');
? ? ? ? ? ?console.log('Context ?: ' + JSON.stringify(this.context));
? ? ? ? ? ?console.log('Return ? : ' + this.returnAddress);
? ? ? ? ? ?console.log('ThreadId : ' + this.threadId);
? ? ? ? ? ?console.log('Depth ? ?: ' + this.depth);
? ? ? ? ? ?console.log('Errornr ?: ' + this.err);
? ? ? ? ? ?// Save arguments for processing in onLeave.
? ? ? ? ? ?this.fd = args[0].toInt32();
? ? ? ? ? ?this.buf = args[1];
? ? ? ? ? ?this.count = args[2].toInt32();
? ? ? ? ? ?console.log("fd:"+this.fd+" buf:"+this.buf+" count:"+this.count);
? ? ? ? ?},
? ? ? ? ?onLeave: function (retval) {
? ? ? ? ? ?console.log("Env對象"+JSON.stringify(Java.vm.getEnv()));
? ? ? ? ? ?retval.replace(1337);
? ? ? ? ? ?console.log("retval"+retval);
? ? ? ? ?}
? ? ? ?});
?});
} ? ? ?
setImmediate(frida_Java,0);
運(yùn)行結(jié)果如下:

關(guān)于this比較重要的屬性在表格中列了出來:
屬性含義returnAddress返回地址,類型是NativePointercontext上下文:具有鍵pc和sp的對象,它們是分別為ia32/x64/arm指定EIP/RIP/PC和ESP/RSP/SP的NativePointer對象。其他處理器特定的鍵也可用,例如eax、rax、r0、x0等。也可以通過分配給這些鍵來更新寄存器值errno當(dāng)前errno值lastError當(dāng)前操作系統(tǒng)錯誤值threadId操作系統(tǒng)線程IDdepth相對于其他調(diào)用的調(diào)用深度
Interceptor.replace
相當(dāng)于替換掉原本的函數(shù),用替換時的實現(xiàn)替換目標(biāo)處的函數(shù)。如果想要完全或部分替換現(xiàn)有函數(shù)的實現(xiàn),則通常使用此函數(shù)。示例代碼如下,替換open函數(shù),打印出打開文件的文件描述符和文件路徑。
var openPtr = Module.getExportByName('libc.so', 'open');
var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
Interceptor.replace(openPtr, new NativeCallback(function (pathPtr, flags) {
?var path = pathPtr.readUtf8String();
?log('Opening "' + path + '"');
?var fd = open(pathPtr, flags);
?log('Got fd: ' + fd);
?return fd;
}, 'int', ['pointer', 'int']));
運(yùn)行結(jié)果如下:

Frida-trace

官網(wǎng)首頁就給出了frida-trace的用法,可見其功能強(qiáng)大。這里以windows記事本為例,將記事本打開的文件路徑打印出來。Frida-trace是一個動態(tài)跟蹤函數(shù)調(diào)用的工具,其強(qiáng)大之處在于能夠hook系統(tǒng)函數(shù)。
在Windwos下打開文件函數(shù)使用的是CreateFileW函數(shù),

可以看出CreateFileW是在Kernel32.dll中導(dǎo)出的。

使用 frida-trace-iCreateFileWnotepad.exe
開啟跟蹤,修改相應(yīng)的文件如下:

使用readUtf16String是因為Windwos使用Unicode字符編碼。通過記事本打開Warcraft目錄下的ij115.dll。結(jié)果如下,打開路徑被成功打印出來。

寫在最后
Frida hook系統(tǒng)API是如此的簡單,不得不說,F(xiàn)rida is so great。
公眾號
更多Frida內(nèi)容,歡迎關(guān)注我的微信公眾號。
