准备Frida环境
- 越狱设备
1.在你的iOS设备上打开Cydia应用程序。
2.添加一个源,URL为:https://build.frida.re
3.打开Source或搜索Frida,单击Modify,然后单击Install。 - 非越狱设备
需要将frida-garget打包到app中,参考之前的文章《iOS应用安全- 非越狱下使用Frida》
frida用法
1.注入到进程
注入进程有两种方式:
通过frida-tools的REPL
1
frida -U Safari -l NSURL_openURL_hook.js
通过frida-bindings
以python为例:1
2
3
4
5
6
7
8jscode='''
console.log("add js code here");
'''
process = frida.get_usb_device().attach("Safari")
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] dump all class into obclass.txt')
script.load()
2.列出所有运行的进程
1 | frida-ps -U |
frida-ps -Uai 以pid、名称、进程名称的格式输出
3.打印调用栈
在hook到的函数中加入以下代码:1
2
3console.log('\tBacktrace:\n\t' + Thread.backtrace(this.context,
Backtracer.ACCURATE).map(DebugSymbol.fromAddress)
.join('\n\t'));
4.调用native函数
1 | var address = Module.findExportByName('libsqlite3.dylib', 'sqlite3_sql'); |
5.数据类型和转换
如果对一个变量的类型不确定,可以使用如下代码确定其类型:1
console.log("Type of args[2] -> " + new ObjC.Object(args[2]).$className)
一些常用的数据类型转换:
- NSData转String
1
2var data = new ObjC.Object(args[2]);
Memory.readUtf8String(data.bytes(), data.length());
如果为null的不需要第二个参数
NSData转二进制数据
1
2var data = new ObjC.Object(args[2]);
Memory.readByteArray(data.bytes(), data.length());遍历NSArray
1
2
3
4
5
6
7
8
9
10var array = new ObjC.Object(args[2]);
/*
* Be sure to use valueOf() as NSUInteger is a Number in
* 32-bit processes, and UInt64 in 64-bit processes. This
* coerces it into a Number in the latter case.
*/
var count = array.count().valueOf();
for (var i = 0; i !== count; i++) {
var element = array.objectAtIndex_(i);
}遍历NSDictionary
1
2
3
4
5
6var dict = new ObjC.Object(args[2]);
var enumerator = dict.keyEnumerator();
var key;
while ((key = enumerator.nextObject()) !== null) {
var value = dict.objectForKey_(key);
}NSKeyedArchiver
1
var parsedValue = ObjC.classes.NSKeyedUnarchiver.unarchiveObjectWithData_(value);
读一个结构体
1
Memory.readU32(args[0].add(4));
常用脚本
1.枚举所有的类
1 | for (var className in ObjC.classes) |
2.枚举一个类的所有method
1 | if (ObjC.available) |
3. Hook一个method
打印参数时需要注意:
- args[0]:self
- args[1]:The selector (openURL:)
- args[2]:The first param
1 | if (ObjC.available) |
4.调用函数
- 函数名以”+”开头的,如:“+ URLWithString:”,可以直接通过类名调用方法,相当于java中的static函数
函数名以“-”开头的需要找到一个实例化的对象,然后再调用方法
- 如果内存中没有这样的对象
这种情况需要手动生成一个实例,用法为ObjC.classes.类名.alloc() 如果内存中存在实例化后的对象
这种情况需要先找出一个类的实例,使用var tmp=ObjC.chooseSync(ObjC.classes.类名),例如:1
ObjC.chooseSync(ObjC.classes.PARSHealthPedometer10thHomeViewController)[0]
其中[0]表示取找到的实例中的第一个实例,可根据实际情况换成其他的实例。
调用函数时,以my_obj“- requestUploadWithSure”的函数,如果有参数直接附在括号中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17if (ObjC.available)
{
try
{
//var my_obj=ObjC.chooseSync(ObjC.classes.PARSHealthPedometer10thHomeViewController)[0]
var my_obj=ObjC.classes.PARSHealthPedometer10thHomeViewController.alloc()
my_obj["- requestUploadWithSure:"](1)
}
catch(err)
{
console.log("[!] Exception2: " + err.message);
}
}
else
{
console.log("Objective-C Runtime is not available!");
}
- 如果内存中没有这样的对象