-
Notifications
You must be signed in to change notification settings - Fork 14
/
libdispatch.js
110 lines (91 loc) · 3.81 KB
/
libdispatch.js
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
Grand Central Dispatch (GCD) is provided by libdispatch. It represents threads within
a process on iOS. For example, `dispatch_async(queue, block)` takes a dispatch queue
with a name and a block that defines execution including a function.
Since even our debug prints are threaded, prints can occur out of order,
so we have to combine them into one logging statement.
libdispatch is open-sourced by Apple: https://opensource.apple.com/tarballs/libdispatch/
Usage:
* Attach to existing daemon
frida -U your_target --no-pause -l libdispatch.js
* Start with new daemon
frida -U -f /bin/your_target --no-pause -l libdispatch.js
*/
/*
libdispatch helper functions
*/
// Print the NSStackBlock function we're going to invoke.
function print_block_invoke(dispatch_block) {
// Is at offset 0x10. Only the least significant are relevant.
return `Callback function: ${DebugSymbol.fromAddress(dispatch_block.add(0x10).readPointer())}\n`;
}
// Get name of a queue
const _dispatch_queue_get_label_addr = Module.getExportByName('libdispatch.dylib', 'dispatch_queue_get_label');
const _dispatch_queue_get_label = new NativeFunction(_dispatch_queue_get_label_addr, "pointer", ["pointer"]);
function print_queue_label(dispatch_queue) {
return `Calling queue: ${_dispatch_queue_get_label(dispatch_queue).readUtf8String()}\n`;
}
function print_backtrace(ctx) {
return 'Backtrace:\n' +
Thread.backtrace(ctx, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n') + '\n';
}
// Hook async dispatching. We do the backtrace in the thread *before* dispatch_async
// was called, so we're off by one.
const _dispatch_async_addr = Module.getExportByName('libdispatch.dylib', 'dispatch_async');
Interceptor.attach(_dispatch_async_addr, {
onEnter: function(args) {
console.log('dispatch_async\n' +
print_queue_label(args[0]) +
print_block_invoke(args[1]) +
print_backtrace(this.context));
},
});
// Dispatching sync. Used a lot during service creation.
const _dispatch_sync_addr = Module.getExportByName('libdispatch.dylib', 'dispatch_sync');
Interceptor.attach(_dispatch_sync_addr, {
onEnter: function(args) {
console.log('dispatch_sync\n' +
print_queue_label(args[0]) +
print_block_invoke(args[1]) +
print_backtrace(this.context));
},
});
// Dispatch queue creation
const _dispatch_queue_create_addr = Module.getExportByName('libdispatch.dylib', 'dispatch_queue_create');
Interceptor.attach(_dispatch_queue_create_addr, {
onEnter: function(args) {
console.log('dispatch_queue_create\n' +
'Label: ' + args[0].readUtf8String() + '\n' +
print_backtrace(this.context));;
},
});
// Hook time dispatching, but this only gives time constraints so it shouldn't be relevant for us.
const _dispatch_time_addr = Module.getExportByName('libdispatch.dylib', 'dispatch_time');
Interceptor.attach(_dispatch_time_addr, {
onEnter: function(args) {
console.log('dispatch_time\n');
},
});
// Delayed dispatching
const _dispatch_after_addr = Module.getExportByName('libdispatch.dylib', 'dispatch_after');
Interceptor.attach(_dispatch_after_addr, {
onEnter: function(args) {
console.log('dispatch_after\n' +
//'in ' + args[0].readDouble() + 'ms' +
print_queue_label(args[1]) +
print_block_invoke(args[2]) +
print_backtrace(this.context));
},
});
/*
// hooking this leads to freezing the target process, hence it's disabled...
const _dispatch_once_addr = Module.getExportByName('libdispatch.dylib', 'dispatch_once');
Interceptor.attach(_dispatch_once_addr, {
onEnter: function(args) {
console.log('dispatch_once\n' +
print_block_invoke(args[1]) +
print_backtrace(this.context));
},
});
*/