frida官方文档

JavaScript API

以下介绍的都是对象。

官方功能非常细致:https://www.frida.re/docs/javascript-api/

这里的回调一般为对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
直接声明对象:
var person=
{
add: function (a, b) {
return a + b;
},
sub: function (a, b) {
return new Promise(resolve => {
setTimeout(() => {
resolve(a - b);
}, 100);
});
}
}
定义类也只是直接定义构造函数即可:
function Car(color,door){
this.color = color;
this.doors = door;
this.showColor = function(){
alert(this.color)
};
}
var car1 = new Car(“red”,4);
var car2 = new Car(“blue”,4);

Global 全局

全局api。
hexdump(target[, options])从提供的ArrayBuffer或NativePointer 生成一个hexdump target(转化byte数组方便显示的)

1
2
3
4
5
6
7
8
ar libc = Module.findBaseAddress('libc.so');
var buf = Memory.readByteArray(libc, 64);
console.log(hexdump(buf, {
offset: 0,
length: 64,
header: true,
ansi: true
}));

int64(v)为new Int64(v)的简写,化为64位的int值,同理还有uint64。
ptr(s)为new NativePointer(s)的简写,表示原生指针,NULL为ptr(“0”),以后的address都是指这个
recv([type, ]callback)异步的用于在js(目标程序中)接收到来自py脚本的type消息后调用callback。只会接收一次,想多次要重复注册,可以用其返回.wait()等待来实现同步。
send(message[, data])将JavaScript对象发送message到基于Frida的应用程序py(它必须可序列化为JSON)。如果你还有一些你想要发送的原始二进制数据,例如你使用了一些内存Memory#readByteArray,那么你可以通过可选data参数传递它。这要求它是一个ArrayBuffer或0到255之间的整数数组。
setTimeout(fn, delay):fn在delay毫秒后调用。返回值给clearTimeout可以取消
clearTimeout(id):取消由呼叫返回的ID setTimeout
setInterval(fn, delay):fn每delay毫秒调用一次。返回值同样用于取消
clearInterval(id):取消由呼叫返回的ID setInterval

console 控制台

console.log(line),console.warn(line),console.error(line)
用于直接从js代码中向frida程序中输出结果(js代码输出在py)ArrayBuffer自动化为hexdump()

RPC 远程执行

rpc.exports可以在js中开启函数供py远程执行

1
2
3
4
5
6
7
8
9
10
11
12
rpc.exports = {
add: function (a, b) {
return a + b;
},
sub: function (a, b) {
return new Promise(resolve => {
setTimeout(() => {
resolve(a - b);
}, 100);
});
}
};

其中promise和reslove都是js的方法,Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。

Frida frida信息

Frida.version:当前Frida版本
Frida.heapSize:包含Frida私有堆的当前大小的动态属性

Process 进程相关

Process.arch:cpu架构,字符串ia32,x64,arm 或arm64
Process.platform:系统,字符串windows, darwin,linux或qnx
Process.pageSize:虚拟内存页面大小(以字节为单位)
Process.pointerSize:指针大小(以字节为单位)
Process.isDebuggerAttached():调试器当前是否连接的布尔值
Process.getCurrentThreadId():线程的id指的是运行脚本的线程,每次运行都不一样
Process.enumerateThreads(callbacks)枚举线程,没有成功。
线程对象含:
id:操作系统特定的ID
state:字符串指定是running,stopped,waiting, uninterruptible或halted
context:与键对象pc和sp,其是指定EIP / RIP / PC分别和ESP / RSP / SP,用于IA32 / 64 /臂NativePointer对象。其他处理器特定键也可以,例如eax,rax,r0,x0,等。

Process.findModuleByAddress(address), Process.getModuleByAddress(address), Process.findModuleByName(name), Process.getModuleByName(name):按地址、名字找或返回module
Process.enumerateModules(callbacks)遍历module

module对象包含:
name:规范模块名称作为字符串
base:基地址为 NativePointer
size:大小以字节为单位
path:完整的文件系统路径作为一个字符串

Process.enumerateRanges(protection|specifier, callbacks):枚举满足protection给定的内存范围这是什么?
用range对象调用包含:
base:基地址为 NativePointer
size:大小以字节为单位
protection:保护字符串(见上文)
file:(当可用时)文件映射细节作为包含以下内容的对象:
path:完整的文件系统路径作为一个字符串
offset:磁盘上映射文件的偏移量,以字节为单位
size:磁盘上映射文件的大小,以字节为单位

Module 模块相关

name都是字串填模块名
Module.ensureInitialized(name):确保指定模块的初始化器已经运行。
Module.enumerateImports(name, callbacks):枚举模块的进口。
Module.enumerateExports(name, callbacks):枚举模块的出口。
Module.enumerateSymbols(name, callbacks):枚举模块的符号。
Module.enumerateRanges(name, protection, callbacks):枚举满足protection给定的内存范围。
Module.findBaseAddress(name):返回name 模块的基地址
Module.findExportByName(module|null, exp):返回名为exp的导出函数地址。可输入在那个模块,若模块写null就是全部搜索。可用于找知道名称的函数!

module对象包含:
name:规范模块名称作为字符串
base:基地址为 NativePointer
size:大小以字节为单位
path:完整的文件系统路径作为一个字符串

ModuleMap 所有模块

表示所有模块的映射
new ModuleMap([filter]):创建模块映射,可以有条件。
has(address):某地址是否在模块表中。
find(address),get(address):返回一个包含address属于该模块的细节的对象。

Memory 内存相关

Memory.scan(address, size, pattern, callbacks):扫描内存,出现pattern。从address开始,size为大小。callback为回调函数。
Memory.alloc(size):在堆上分配内存。
Memory.copy(dst, src, n):就像memcpy一样。
Memory.protect(address, size, protection):更新内存区域的保护

1
Memory.protect(ptr("0x1234"), 4096, 'rw-');

Memory.patchCode(address, size, apply):好像很强大….怎么用不清楚。
Memory.readPointer(address):把地址内容化为指针返回。(从内存中读取ptr)
Memory.readS8(address),Memory.readU8(address), Memory.readS16(address),Memory.readU16(address), Memory.readS32(address),Memory.readU32(address), Memory.readShort(address),Memory.readUShort(address), Memory.readInt(address),Memory.readUInt(address), Memory.readFloat(address),Memory.readDouble(address):读有符号或无符号的8/16/32 /等。或浮点/双精度值并将其作为数字返回。

Memory.writeS8(address, value),Memory.writeU8(address, value), Memory.writeS16(address, value),Memory.writeU16(address, value), Memory.writeS32(address, value),Memory.writeU32(address, value), Memory.writeShort(address, value),Memory.writeUShort(address, value), Memory.writeInt(address, value),Memory.writeUInt(address, value), Memory.writeFloat(address, value),Memory.writeDouble(address, value):写数value到符号或无符号的8/16/32 /等。或浮动/双值在address。

Memory.readByteArray(address, length):从指定地址读出array
Memory.writeByteArray(address, bytes):写入字节数组

Memory.readUtf8String(address[, size = -1]),Memory.writeUtf16String(address, str),Memory.allocUtf8String(str)等针对特定编码字符串的内存处理。

Thread 线程相关

Thread.backtrace([context, backtracer]):为当前线程生成回溯,作为NativePointer对象数组返回。

NativePointer 原生指针

ptr(s)为new NativePointer(s)的简写
new NativePointer(s):从s 包含内存地址的字符串中创建新的NativePointer,该内存地址为十进制或十六进制(如果前缀为“0x”)
isNull():返回一个布尔值,允许您方便地检查指针是否为NULL。
add(rhs),sub(rhs), and(rhs),or(rhs), xor(rhs):使一个新的NativePointer这个NativePointer正/负/和/或/ XOR rhs,这可以是一个数字或其他NativePointer
equals(rhs):返回一个布尔值,指示是否rhs等于这个值; 即它具有相同的指针值
toInt32():将此NativePointer强制转换为带符号的32位整数
toString([radix = 16]):转换为可选基数的字符串(默认为16)

NativeFunction 原生函数

new NativeFunction(address, returnType, argTypes[, abi]):创建一个新的NativeFunction来调用address(用a指定的 NativePointer)函数,其中returnType指定返回类型, argTypes数组指定参数类型。您也可以选择指定abi是否不是系统默认值。对于可变参数函数,在固定参数和可变参数之间添加一个’…’ 条目argTypes。

1
2
原函数int fun(char* a):
var f = new NativeFunction(ptr("%s"), 'int', ['pointer']);

详细:https://www.frida.re/docs/javascript-api/#nativefunction

NativeCallback

new NativeCallback(func, returnType, argTypes[, abi]):创建一个由JavaScript函数实现的新的NativeCallback func,返回的对象也是a NativePointer,因此可以传递给它Interceptor#replace。

io,socket,file

支持js下的io、socket、file等操作

Interceptor

Interceptor.attach(target, callbacks):拦截target处的函数。target可以是NativePointer指定的地址。Module.findExportByName()等api获得的地址可以由frida解决arm一位为1时是thumb指令等问题。callbacks是一个对象,可以有多个包含:
onEnter: function (args):回调函数给出了一个参数 args,可以用来读取或写入参数作为一个NativePointer对象数组 。
onLeave: function (retval):给定一个参数的回调函数,该参数 retval是一个NativePointer包含原始返回值的衍生对象。您可以调用retval.replace(1337)以用整数替换返回值1337,或者retval.replace(ptr(“0x1234”))用指针替换。请注意,该对象在onLeave调用中被回收,因此不要在回调之外存储和使用它。如果需要存储包含的值,请进行深层复制,例如:ptr(retval.toString())。

1
2
3
4
5
6
7
8
9
10
nterceptor.attach(Module.findExportByName("libc.so", "read"), {
onEnter: function (args) {
this.fileDescriptor = args[0].toInt32();
},
onLeave: function (retval) {
if (retval.toInt32() > 0) {
/* do something with this.fileDescriptor */
}
}
});

Interceptor.replace(target, replacement):在target 执行时替换函数at replacement。这通常用于完全或部分替换现有函数的实现。用于 在JavaScript中NativeCallback实现一个replacement。请注意, replacement直到Interceptor#revert被调用时才会保持活动状态。如果你想链接到原始的实现,你可以target通过NativeFunction内部的实现同步调用,这将绕过并直接进入原始实现。

1
2
3
4
5
6
7
8
9
var openPtr = Module.findExportByName("libc.so", "open");
var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
Interceptor.replace(openPtr, new NativeCallback(function (pathPtr, flags) {
var path = Memory.readUtf8String(pathPtr);
log("Opening '" + path + "'");
var fd = open(pathPtr, flags);
log("Got fd: " + fd);
return fd;
}, 'int', ['pointer', 'int']));

Interceptor.revert(target):将函数恢复target到之前的实现。

Stalker

Stalker.follow([threadId, options]):开始跟踪threadId(或省略当前线程),可选地options启用事件。
Stalker.unfollow([threadId]):停止跟踪threadId(或省略当前线程)。

arm mip

有各种针对不同平台的原生读写与结构类型

Java

Java.available:一个布尔值,指定当前进程是否加载了Java虚拟机。
Java.enumerateLoadedClasses(callbacks):枚举现在加载的类,其中callbacks是一个对象:
onMatch: function (className):为每个加载的类调用 className可以传递给use()JavaScript封装器的类。
onComplete: function ():当所有类都被枚举时调用。

Java.perform(fn):确保当前线程连接到虚拟机并呼叫fn。

1
2
3
4
5
6
7
Java.perform(function () {
var Activity = Java.use("android.app.Activity");
Activity.onResume.implementation = function () {
send("onResume() got called! Let's call the original implementation");
this.onResume();
};
});

Java.use(className):动态获得一个JavaScript包装程序 className,您可以通过调用$new()它来调用构造函数来实例化对象。调用$dispose()一个实例来显式清理它(或等待JavaScript对象获取垃圾回收,或脚本被卸载)。静态和非静态方法是可用的,你甚至可以替换一个方法实现并抛出一个异常:

1
2
3
4
5
6
7
Java.perform(function () {
var Activity = Java.use("android.app.Activity");
var Exception = Java.use("java.lang.Exception");
Activity.onResume.implementation = function () {
throw Exception.$new("Oh noes!");
};
});

Java.choose(className, callbacks):className通过扫描Java堆枚举类的活动实例。用来获得某类java对象。

任务:使用frida hook android系统调用。