Frada研究笔记

官网
github代码

Frida是什么?

它是原生应用程序的 Greasemonkey,或者,用更专业的术语来说,它是一个动态代码检测工具包。它允许您将 JavaScript 片段或您自己的库注入到 Windows、macOS、GNU/Linux、iOS、Android 和 QNX 上的本机应用程序中。Frida 还为您提供了一些基于 Frida API 构建的简单工具。这些可以按原样使用,根据您的需要进行调整,或者作为如何使用 API 的示例。

环境依赖

推荐 python 3.x
brew install python

安装 frida-tools

pip install frida-tools

主语言

Frida 的核心是用 C 语言编写的,并将QuickJS 注入到目标进程中,您的 JS 在执行时可以完全访问内存、挂钩函数,甚至在进程内调用本机函数。有一个双向通信通道,用于在您的应用程序和目标进程内运行的 JS 之间进行通信。

使用 Python 和 JS 可以使用无风险的 API 进行快速开发。Frida 可以帮助您轻松捕获 JS 中的错误,并为您提供异常而不是崩溃。

支持多个语言绑定
例如 Node.js、 Python、 Swift、 .NET、 Qml等。为其他语言和环境构建额外的绑定非常容易.

JavaScript 与 TypeScript 的区别
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。

什么是TypeSctipt?
TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。

Swift版本
其他语言版本

运行模式

注释模式

大部分情况下,我们都是附加到一个已经运行到进程,或者是在程序启动到时候进行劫持,然后再在目标进程中运行我们的代码逻辑。这种方式是Frida最常用的使用方式。注入模式的大致实现思路是这样的,带有GumJS的Frida核心引擎被打包成一个动态连接库,然后把这个动态连接库注入到目标进程中,同时提供了一个双向通信通道,这样控制端就可以和注入的模块进行通信了,在不需要的时候,还可以在目标进程中把这个注入的模块给卸载掉。

嵌入模式

针对没root过的设备Frida提供了一个动态连接库组件 frida-gadget, 可以把这个动态库集成到程序里面来使用Frida的动态执行功能。一旦集成了gadget,就可以和程序使用Frida进行交互。

预加载模式

自动加载Js文件。

主要工具

查看可用的设备列表

frida-ls-devices
用于获取可用的设备列表,在多设备交互的情况下会非常有用

获取设备的进程列表

frida-ps
用于获取进程列表信息

参数 描述
-U 连接到USB设备
-D 如果当前有多台USB设备,可以使用该参数指定设备的UDID(frida-ls-devices 列出的那些 id)
-R/-H 连接到远程 frida-server,主要用于远程调试
-a 仅显示正在运行的应用
-i 显示所有已安装的应用(包括 AppStore安装的应用和系统应用)

举例:
连接到USB设备查看进程列表
frida-ps -U

连接到USB设备查看正在运行的应用
frida-ps -U -a

连接到USB设备查看所有安装的应用
frida-ps -U -a -i

连接到指定的USB设备查看正在运行的应用
frida-ps -D d007dc58edd70caad950ff01b41ebf73cfa49fbe -a

杀死进程

frida-kill 用来结束设备上的指定进程

跟踪函数/方法的调用

frida-trace

参数 描述
-i 包含的C函数
-x 排除某个C函数
-m hook匹配 OC
-M 忽略匹配的OC
-j 跟踪某个java函数
-J 排除某个java函数
-h 其他参考帮助文档

开发中常用的调试方式

跟踪调用栈打印

1
console.log('\tBacktrace:\n\t' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n\t'));

获取目标类

1
var targetClass = ObjC.classes.PDNetworkAccessManager;

获取目标方法

1
2
3
4
var targetMethod =
targetClass[
"- dataAccessFromNetWithFunctionId:param:finishBlock:cancelBlock:"
];

监听目标方法

1
2
3
4
5
6
7
8
9
10
11
12
Interceptor.attach(targetMethod.implementation, {
onEnter: function (args) {
var functionId = ObjC.Object(args[2]);
var param = ObjC.Object(args[3]);
var finishBlock = new ObjC.Block(args[4]);
var cancelBlock = new ObjC.Block(args[5];
},
onLeave: function (retval) {
// 在方法返回后处理返回值
console.log("Return value: " + ObjC.Object(retval));
},
});

接受方法参数的OC对象:

1
var param = ObjC.Object(args[2]);

接受Int类型参数:

1
2
3
4
5
6
var value32 = args[3].toInt32();
console.log("32位整数值: " + value32);

// 示例2:使用 toInt64
var value64 = args[3].toInt64();
console.log("64位整数值: " + value64);

打印对象类型:

1
console.log('Param类型:' + param.$className);

接受Block参数:

1
var finishBlock = new ObjC.Block(args[4]);

打印Block结构签名:

1
console.log('blockTypes:' + finishBlock.types);

修改基本类型参数:

1
args[2] = ptr(123);

nil赋值:

1
args[3] = ptr("0x0");

ObjC.available: 一个布尔值,指定当前进程是否加载了Objective-C运行时。ObjC除非是这种情况,否则不要调用任何其他属性或方法。

ObjC.api: 将函数名称映射到NativeFunction实例的对象,用于直接访问大部分Objective-C运行时 API。

ObjC.classesObjC.Object :为每个当前注册的类映射类名到 JavaScript 绑定的对象。您可以通过使用点表示法并用下划线替换冒号来与对象交互,即: [NSString stringWithString:@"Hello World"] 变成 const { NSString } = ObjC.classes; NSString.stringWithString_("Hello World");. 注意方法名称后面的下划线。

ObjC.protocols: 一个对象映射协议名称到ObjC.Protocol 每个当前注册协议的 JavaScript 绑定。

ObjC.mainQueue:主线程的GCD队列

ObjC.schedule(queue, work)work: 在 .指定的 GCD 队列上调度 JavaScript 函数queue。AnNSAutoreleasePool在调用之前创建work,并在返回时清理。

Demo

Javascript

Frida拦截微信撤回Demo

Python

直接见LinXunFeng大牛写好的代码:Github

更多接口文档

https://frida.re/docs/javascript-api/

注意

Mac测试需要关闭安全模式
iPhone使用Frida一般需要越狱,如果不越狱使用,可以参考文档

END