Chinaunix首页 | 论坛 | 博客
  • 博客访问: 522333
  • 博文数量: 184
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1172
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-21 13:40
个人简介

技术改变命运

文章分类

全部博文(184)

文章存档

2020年(16)

2017年(12)

2016年(156)

我的朋友

分类: Android平台

2020-08-30 14:22:08

在中介绍了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 返回地址,类型是NativePointer
context 上下文:具有键pc和sp的对象,它们是分别为ia32/x64/arm指定EIP/RIP/PC和ESP/RSP/SP的NativePointer对象。其他处理器特定的键也可用,例如eax、rax、r0、x0等。也可以通过分配给这些键来更新寄存器值
errno 当前errno值
lastError 当前操作系统错误值
threadId 操作系统线程ID
depth 相对于其他调用的调用深度

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内容,欢迎关注我的微信公众号:无情剑客。

阅读(3561) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~