-
Notifications
You must be signed in to change notification settings - Fork 21
/
fastpaths.js
259 lines (212 loc) · 7.3 KB
/
fastpaths.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
const code = `#include <glib.h>
#include <ptrauth.h>
#define KERN_SUCCESS 0
#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
#if defined (HAVE_I386) && GLIB_SIZEOF_VOID_P == 8
# define OBJC_ISA_MASK 0x7ffffffffff8ULL
#elif defined (HAVE_ARM64)
# define OBJC_ISA_MASK 0xffffffff8ULL
#endif
typedef struct _ChooseContext ChooseContext;
typedef struct _malloc_zone_t malloc_zone_t;
typedef struct _malloc_introspection_t malloc_introspection_t;
typedef struct _vm_range_t vm_range_t;
typedef gpointer Class;
typedef int kern_return_t;
typedef guint mach_port_t;
typedef mach_port_t task_t;
typedef guintptr vm_offset_t;
typedef guintptr vm_size_t;
typedef vm_offset_t vm_address_t;
struct _ChooseContext
{
GHashTable * classes;
GArray * matches;
};
struct _malloc_zone_t
{
void * reserved1;
void * reserved2;
size_t (* size) (struct _malloc_zone_t * zone, const void * ptr);
void * (* malloc) (struct _malloc_zone_t * zone, size_t size);
void * (* calloc) (struct _malloc_zone_t * zone, size_t num_items, size_t size);
void * (* valloc) (struct _malloc_zone_t * zone, size_t size);
void (* free) (struct _malloc_zone_t * zone, void * ptr);
void * (* realloc) (struct _malloc_zone_t * zone, void * ptr, size_t size);
void (* destroy) (struct _malloc_zone_t * zone);
const char * zone_name;
unsigned (* batch_malloc) (struct _malloc_zone_t * zone, size_t size, void ** results, unsigned num_requested);
void (* batch_free) (struct _malloc_zone_t * zone, void ** to_be_freed, unsigned num_to_be_freed);
malloc_introspection_t * introspect;
};
typedef kern_return_t (* memory_reader_t) (task_t remote_task, vm_address_t remote_address, vm_size_t size, void ** local_memory);
typedef void (* vm_range_recorder_t) (task_t task, void * user_data, unsigned type, vm_range_t * ranges, unsigned count);
typedef kern_return_t (* enumerator_func) (task_t task, void * user_data, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader,
vm_range_recorder_t recorder);
struct _malloc_introspection_t
{
enumerator_func enumerator;
};
struct _vm_range_t
{
vm_address_t address;
vm_size_t size;
};
extern int objc_getClassList (Class * buffer, int buffer_count);
extern Class class_getSuperclass (Class cls);
extern size_t class_getInstanceSize (Class cls);
extern kern_return_t malloc_get_all_zones (task_t task, memory_reader_t reader, vm_address_t ** addresses, unsigned * count);
static void collect_subclasses (Class klass, GHashTable * result);
static void collect_matches_in_ranges (task_t task, void * user_data, unsigned type, vm_range_t * ranges, unsigned count);
static kern_return_t read_local_memory (task_t remote_task, vm_address_t remote_address, vm_size_t size, void ** local_memory);
extern mach_port_t selfTask;
gpointer *
choose (Class * klass,
gboolean consider_subclasses,
guint * count)
{
ChooseContext ctx;
GHashTable * classes;
vm_address_t * malloc_zone_addresses;
unsigned malloc_zone_count, i;
classes = g_hash_table_new_full (NULL, NULL, NULL, NULL);
ctx.classes = classes;
ctx.matches = g_array_new (FALSE, FALSE, sizeof (gpointer));
if (consider_subclasses)
collect_subclasses (klass, classes);
else
g_hash_table_insert (classes, klass, GSIZE_TO_POINTER (class_getInstanceSize (klass)));
malloc_zone_count = 0;
malloc_get_all_zones (selfTask, read_local_memory, &malloc_zone_addresses, &malloc_zone_count);
for (i = 0; i != malloc_zone_count; i++)
{
vm_address_t zone_address = malloc_zone_addresses[i];
malloc_zone_t * zone = (malloc_zone_t *) zone_address;
enumerator_func enumerator;
if (zone != NULL && zone->introspect != NULL &&
(enumerator = (ptrauth_strip (zone->introspect, ptrauth_key_asda))->enumerator) != NULL)
{
enumerator = ptrauth_sign_unauthenticated (
ptrauth_strip (enumerator, ptrauth_key_asia),
ptrauth_key_asia, 0);
enumerator (selfTask, &ctx, MALLOC_PTR_IN_USE_RANGE_TYPE, zone_address, read_local_memory,
collect_matches_in_ranges);
}
}
g_hash_table_unref (classes);
*count = ctx.matches->len;
return (gpointer *) g_array_free (ctx.matches, FALSE);
}
void
destroy (gpointer mem)
{
g_free (mem);
}
static void
collect_subclasses (Class klass,
GHashTable * result)
{
Class * classes;
int count, i;
count = objc_getClassList (NULL, 0);
classes = g_malloc (count * sizeof (gpointer));
count = objc_getClassList (classes, count);
for (i = 0; i != count; i++)
{
Class candidate = classes[i];
Class c;
c = candidate;
do
{
if (c == klass)
{
g_hash_table_insert (result, candidate, GSIZE_TO_POINTER (class_getInstanceSize (candidate)));
break;
}
c = class_getSuperclass (c);
}
while (c != NULL);
}
g_free (classes);
}
static void
collect_matches_in_ranges (task_t task,
void * user_data,
unsigned type,
vm_range_t * ranges,
unsigned count)
{
ChooseContext * ctx = user_data;
GHashTable * classes = ctx->classes;
unsigned i;
for (i = 0; i != count; i++)
{
const vm_range_t * range = &ranges[i];
gconstpointer candidate = GSIZE_TO_POINTER (range->address);
gconstpointer isa;
guint instance_size;
isa = *(gconstpointer *) candidate;
#ifdef OBJC_ISA_MASK
isa = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (isa) & OBJC_ISA_MASK);
#endif
instance_size = GPOINTER_TO_UINT (g_hash_table_lookup (classes, isa));
if (instance_size != 0 && range->size >= instance_size)
{
g_array_append_val (ctx->matches, candidate);
}
}
}
static kern_return_t
read_local_memory (task_t remote_task,
vm_address_t remote_address,
vm_size_t size,
void ** local_memory)
{
*local_memory = (void *) remote_address;
return KERN_SUCCESS;
}
`;
const {getApi} = require('./api');
const {pointerSize} = Process;
let cachedModule = null;
module.exports = {
get() {
if (cachedModule === null)
cachedModule = compileModule();
return cachedModule;
},
};
function compileModule() {
const {
objc_getClassList,
class_getSuperclass,
class_getInstanceSize,
} = getApi();
const selfTask = Memory.alloc(4);
selfTask.writeU32(Module.getExportByName(null, 'mach_task_self_').readU32());
const cm = new CModule(code, {
objc_getClassList,
class_getSuperclass,
class_getInstanceSize,
malloc_get_all_zones: Module.getExportByName('/usr/lib/system/libsystem_malloc.dylib', 'malloc_get_all_zones'),
selfTask,
});
const _choose = new NativeFunction(cm.choose, 'pointer', ['pointer', 'bool', 'pointer']);
const _destroy = new NativeFunction(cm.destroy, 'void', ['pointer']);
return {
handle: cm,
choose(klass, considerSubclasses) {
const result = [];
const countPtr = Memory.alloc(4);
const matches = _choose(klass, considerSubclasses ? 1 : 0, countPtr);
try {
const count = countPtr.readU32();
for (let i = 0; i !== count; i++)
result.push(matches.add(i * pointerSize).readPointer());
} finally {
_destroy(matches);
}
return result;
},
};
}