欢迎光临散文网 会员登陆 & 注册

Frida API高级

2020-08-30 14:20 作者:无情剑客Burning  | 我要投稿

在前面的文章中介绍了Frida基本API的使用,在这篇文章中介绍一些更加强大的API。同时简单介绍下HOOK 系统函数的利器frida-trace。

内存,内存还是内存。

Java对象

Java对象

Java是极其重要的API。无论想对so层亦或java层进行拦截,通常都须编Java.perform。

  • Java.available: 该函数一般用来判断当前进程是否加载了JavaVM,Dalvik或ART虚拟机

  • Java.androidVersion: 显示Android系统版本号

  • Java.enumerateLoadedClasses(callbacks):   枚举当前加载的所有类信息,它有一个回调函数分别是onMatch、onComplete函数。这是一个异步的方法,有一个同步的API是Java.enumerateLoadedClassesSync。

  • Java.enumerateClassLoaders(callbacks):  枚举Java VM中存在的类加载器,其有一个回调函数,分别是onMatch: function (loader)与onComplete:  function ()。这是一个异步的方法,有一个同步的API是Java.enumerateClassLoadersSync()。

  • Java.array(type, elements): 根据指定的元素类型创建一个Java数组。

还有一些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");      }      //枚举当前加载的所有类      Java.enumerateLoadedClasses({          //每一次回调此函数时其参数className就是类的信息          onMatch: function (className)          {              //输出类字符串              console.log("",className);          },          //枚举完毕所有类之后的回调函数          onComplete: function (){              //输出类字符串              console.log("输出完毕");          }      });      //枚举当前加载的Java VM类加载器      Java.enumerateClassLoaders({        //回调函数,参数loader是类加载的信息        onMatch: function (loader)        {            console.log("",loader);        },        //枚举完毕所有类加载器之后的回调函数        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);

部分运行结果如下:

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);

部分运行结果:

Intercepter对象

该对象功能十分强大,函数原型是Interceptor.attach(target, callbacks):参数target是需要拦截的位置的函数地址,也就是填某个so层函数的地址即可对其拦截,target是一个NativePointer参数,用来指定你想要拦截的函数的地址,NativePointer是一个指针。需要注意的是对于Thumb函数需要对函数地址+1,callbacks则是它的回调函数,分别是以下两个回调函数:

  • 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系统函数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);

运行结果如下:

关于this比较重要的属性在表格中列了出来:

属性含义returnAddress返回地址,类型是NativePointercontext上下文:具有键pc和sp的对象,它们是分别为ia32/x64/arm指定EIP/RIP/PC和ESP/RSP/SP的NativePointer对象。其他处理器特定的键也可用,例如eax、rax、r0、x0等。也可以通过分配给这些键来更新寄存器值errno当前errno值lastError当前操作系统错误值threadId操作系统线程IDdepth相对于其他调用的调用深度

Interceptor.replace

相当于替换掉原本的函数,用替换时的实现替换目标处的函数。如果想要完全或部分替换现有函数的实现,则通常使用此函数。示例代码如下,替换open函数,打印出打开文件的文件描述符和文件路径。










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']));

运行结果如下:

Frida-trace

官网首页就给出了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内容,欢迎关注我的微信公众号。


Frida API高级的评论 (共 条)

分享到微博请遵守国家法律