├── LICENSE ├── README.md ├── assets ├── blackhat_defcon_2021_slides.pdf └── userclients.png └── scripts └── rose_sniff_kernel.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Secure Mobile Networking Lab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Apple Rose U1 2 | ------------- 3 | 4 | U1 is Apple's Ultrawide Band chip. This repository contains some information about it as well as additional scripts. 5 | 6 | 7 | ### Contents 8 | 9 | * [Frida script](scripts/rose_sniff_kernel.js) for sniffing `nearbyd`<->U1 commands 10 | * [Slides](assets/blackhat_defcon_2021_slides.pdf) of our Black Hat + DEF CON talk 11 | 12 | 13 | ### TL;DR version of the talks 14 | 15 | On iOS, watchOS and audioOS, the U1 chip is controlled via the Always-on Processor (AOP). The kernel has two IOKit 16 | `UserClients`, which interact with U1 through the AOP. This indirect communication makes everything a bit more 17 | complicated, but also enhances security---U1 never passes over-the-air packets directly to the kernel, only 18 | abstracted distance measurements. The overall architecture looks as follows: 19 | 20 | ![UserClients + AOP Structure](assets/userclients.png) 21 | 22 | All of this is a bit unreadable until resolving method names in the kernel as well as command and property names. 23 | The [Frida script](scripts/rose_sniff_kernel.js) resolves all of these names, giving one some idea about what 24 | is happening and what to hook. This script was tested on the iPhone 11+12 with iOS versions 13.3, 14.1, 14.2.1 and 14.3. 25 | Note that you will need an iPhone 11 or 12, since older models as well as the iPhone SE 2020 do not have the U1 chip. 26 | 27 | -------------------------------------------------------------------------------- /assets/blackhat_defcon_2021_slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seemoo-lab/apple_u1/6c3da418ea9ae773b8c127618b2558a2f218d588/assets/blackhat_defcon_2021_slides.pdf -------------------------------------------------------------------------------- /assets/userclients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seemoo-lab/apple_u1/6c3da418ea9ae773b8c127618b2558a2f218d588/assets/userclients.png -------------------------------------------------------------------------------- /scripts/rose_sniff_kernel.js: -------------------------------------------------------------------------------- 1 | /* 2 | Add descriptions to interactions between nearbyd and the kernel to see what U1 is doing. 3 | Toggle flight mode to see all mach annotations. 4 | 5 | Attach as follows: 6 | 7 | frida -U nearbyd --no-pause -l rose_sniff_kernel.js 8 | 9 | Change logging settings in the constructor. 10 | 11 | */ 12 | 13 | class CustomRoseController { 14 | 15 | constructor() { 16 | 17 | /*** INITIALIZE SCRIPT ***/ 18 | 19 | // Connection ID changes with every nearbyd restart! 20 | // But we set them automatically on one of the first calls. 21 | this.AppleSPUUserClient_port = 0; 22 | this.AppleSPURoseDriverUserClient_port = 0; 23 | 24 | // TODO disable/enable mach debugging, lots of output! 25 | this.debug_mach = false; 26 | this.mach_truncate_size = 0x40; 27 | this.mach_remove_xpc = true; 28 | 29 | // TODO disable/enable IOKit additional arguments 30 | this.debug_iokit = true; 31 | 32 | this._data_ptr = Memory.alloc(0x1000); // reusable memory pointer 33 | 34 | this.ios_version = "arm64e_14.2.1"; // TODO adjust version here! 35 | } 36 | 37 | 38 | /* 39 | Script preparation, needs to be called in standalone usage. 40 | Separated from constructor for external script usage. 41 | */ 42 | prepare() { 43 | 44 | var self = this; 45 | 46 | // Crash logs will only be written in internal builds. 47 | self._nearbyd_base = Module.getBaseAddress('nearbyd'); 48 | 49 | // Set the correct symbols 50 | self.setSymbols(self.ios_version); 51 | 52 | // gets IOKit ports for better printing 53 | self.getIOKitPorts(); 54 | self.logIOKit(); 55 | if (self.debug_mach) { 56 | self.logMach(); 57 | } 58 | } 59 | 60 | 61 | /* 62 | Somewhat dirty hack to get the correct mach ports but works for me :) 63 | Still more stable than Dave's solution, no idea why that hangs :/ 64 | */ 65 | getIOKitPorts() { 66 | 67 | var self = this; 68 | 69 | /* 70 | Most function calls need a pointer to RoseController later on, save it. 71 | */ 72 | var _RoseController_PerformCommand_addr = Module.getExportByName('RoseControllerLib', '_ZN14RoseController14PerformCommandEhPKhmPvmPmy'); 73 | var _RoseController_ptr = 0; 74 | Interceptor.attach(_RoseController_PerformCommand_addr, { 75 | onEnter: function() { 76 | if (! self._RoseController_ptr) { 77 | console.log(' * LibRoseController: PerformCommand, saving RoseController pointer and IOConnection'); 78 | self._RoseController_ptr = new NativePointer(this.context.x0); 79 | // offset in struct, hope it's generic across versions 80 | self.AppleSPURoseDriverUserClient_port = self._RoseController_ptr.add(480).readU16(); 81 | } 82 | } 83 | }); 84 | 85 | /* 86 | We also want to talk to the generic AppleSPUUserClient via IOKit but there are no exports in 87 | nearbyd, so we need to hook a function by address. 88 | 89 | This function here can be found by looking for the string "com.apple.nearbyd.RoseSupervisorCommandError". 90 | */ 91 | 92 | Interceptor.attach(self._RoseSupervisorCommand_addr, { 93 | onEnter: function() { 94 | if (! self.AppleSPUUserClient_port) { 95 | console.log(' * nearbyd: saving RoseSupervisorCommand IOConnection'); 96 | self.AppleSPUUserClient_port = this.context.x0.add(8).readU16(); 97 | } 98 | } 99 | }); 100 | } 101 | 102 | 103 | 104 | 105 | /* 106 | Log all function calls to the kernel and provide more information. 107 | Both, IOKit and raw mach messages, but IOKit is more readable :) 108 | */ 109 | logIOKit() { 110 | 111 | /* 112 | RoseControllerLib communicates via IOKit, so let's also print that information. 113 | 114 | IOConnectCallMethod(mach_port_t connection, uint32_t selector, const uint64_t *input, uint32_t inputCnt, 115 | const void *inputStruct, size_t inputStructCnt, uint64_t *output, 116 | uint32_t *outputCnt, void *outputStruct, size_t *outputStructCnt) 117 | 118 | TODO double-check that IOConnectCallStructMethod is also covered by this 119 | 120 | */ 121 | 122 | var self = this; 123 | 124 | var _IOConnectCallMethod_addr = Module.getExportByName('IOKit', 'IOConnectCallMethod'); 125 | // might have async issues but works most of the time 126 | this.outputStruct = 0; 127 | this.outputStructCnt = 0; 128 | //this.performCommandInput = Memory.alloc(16); // save this to call performCommand later on 129 | Interceptor.attach(_IOConnectCallMethod_addr, { 130 | onEnter: function(args) { 131 | 132 | var connection = args[0]; 133 | var selector = parseInt(args[1]); 134 | var inputCnt = parseInt(args[3]); 135 | var inputStructCnt = parseInt(args[5]); 136 | this.outputStruct = args[8]; 137 | this.outputStructCnt = args[9]; 138 | 139 | 140 | /* 141 | console.log('Backtrace:\n' + 142 | Thread.backtrace(this.context, Backtracer.ACCURATE) 143 | .map(DebugSymbol.fromAddress).join('\n') + '\n'); 144 | */ 145 | 146 | 147 | connection = parseInt(connection); // convert to int for matching 148 | 149 | // Backtrace would always be via nearbyd!0x1b6c94 (iOS 14.1), which is 150 | // related to com.apple.nearbyd.RoseSupervisorCommand, and it maps to another 151 | // IOKit service! 152 | 153 | if (connection == self.AppleSPUUserClient_port) { 154 | switch(selector) { 155 | case 0: 156 | console.log(" - AppleSPUUserClient::extTestMethod()"); 157 | break; 158 | case 1: 159 | console.log(" - AppleSPUUserClient::extSetPropertyMethod()"); 160 | self.print_property(args[2].readU8()); 161 | console.log(args[4].readByteArray(parseInt(args[5]))); 162 | break; 163 | case 2: 164 | console.log(" - AppleSPUUserClient::extGetPropertyMethod()"); 165 | self.print_property(args[2].readU8()); 166 | // get is only printed on exit 167 | break; 168 | case 3: 169 | console.log(" - AppleSPUUserClient::extPerformCommandMethod()"); 170 | self.print_iokit_args(args); 171 | self.print_supervisor_command(args[2].readU8()); 172 | break; 173 | case 4: 174 | console.log(" - AppleSPUUserClient::extSetNamedPropertyMethod()"); 175 | break; 176 | case 5: 177 | console.log(" - AppleSPUUserClient::extGetNamedPropertyMethod()"); 178 | break; 179 | } 180 | } 181 | // Print mappings to sMethods 182 | else if (connection == self.AppleSPURoseDriverUserClient_port) { 183 | switch(selector) { 184 | case 0: 185 | console.log(" - AppleSPURoseDriverUserClient::extRoseLoadFirmware()"); 186 | // called via RoseController::DownloadCustomFirmware -> RoseController::DownloadCustomFirmwareAsync 187 | break; 188 | case 1: 189 | console.log(" - AppleSPURoseDriverUserClient::extRoseGetInfo()"); 190 | break; 191 | case 2: 192 | console.log(" - AppleSPURoseDriverUserClient::extRoseReset()"); 193 | break; 194 | case 3: 195 | console.log(" - AppleSPURoseDriverUserClient::extRoseEnterCommandMode()"); 196 | break; 197 | case 4: 198 | console.log(" - AppleSPURoseDriverUserClient::extRosePing()"); 199 | break; 200 | case 5: 201 | console.log(" - AppleSPURoseDriverUserClient::extRoseTx()"); 202 | self.print_iokit_args(args); 203 | break; 204 | case 6: 205 | console.log(" - AppleSPURoseDriverUserClient::extRoseTimeSync()"); 206 | break; 207 | case 7: 208 | console.log(" - AppleSPURoseDriverUserClient::extRoseGetSyncedTime()"); 209 | break; 210 | case 8: 211 | console.log(" - AppleSPURoseDriverUserClient::extRoseGetProperty()"); 212 | self.print_rose_property(args[2].readU8()); 213 | break; 214 | case 9: 215 | console.log(" - AppleSPURoseDriverUserClient::extRoseSetProperty()"); 216 | self.print_rose_property(args[2].readU8()); 217 | console.log(args[4].readByteArray(parseInt(args[5]))); 218 | break; 219 | case 10: 220 | console.log(" - AppleSPURoseDriverUserClient::extRosePerformInternalCommand()"); 221 | break; 222 | case 11: 223 | console.log(" - AppleSPURoseDriverUserClient::extRoseCacheFirmwareLogs()"); 224 | break; 225 | case 12: 226 | console.log(" - AppleSPURoseDriverUserClient::extRoseDequeueFirmwareLogs()"); 227 | break; 228 | case 13: 229 | console.log(" - AppleSPURoseDriverUserClient::extRoseTriggerCoredump()"); 230 | break; 231 | case 14: 232 | console.log(" - AppleSPURoseDriverUserClient::extRoseDequeueCoredump()"); 233 | break; 234 | case 15: 235 | console.log(" - AppleSPURoseDriverUserClient::extRoseCoredumpInfo()"); 236 | break; 237 | case 16: 238 | console.log(" - AppleSPURoseDriverUserClient::extRosePowerOn()"); 239 | break; 240 | case 17: 241 | console.log(" - AppleSPURoseDriverUserClient::extRoseReadPowerState()"); 242 | break; 243 | case 18: 244 | console.log(" - AppleSPURoseDriverUserClient::extRoseConfigureFirmwareLogCache()"); 245 | break; 246 | } 247 | } 248 | // Still print sth on unknown calls 249 | // Each time when starting AirDrop we get a new connection (with new ID) 250 | else { 251 | console.log(' * IOConnectCallMethod(connection: ' + connection + ', selector: ' + selector + ', inputCnt: ' + inputCnt + ', ...)'); 252 | } 253 | 254 | // Prints the plain params in hex, but that wasn't very helpful in our case. 255 | //console.log(this.context.x2.readByteArray(inputCnt)); 256 | 257 | // Prints the connection struct, very helpful to figure out extRoseTx Commands! 258 | if (self.debug_iokit && inputStructCnt > 0) { 259 | var inputStruct = new NativePointer(this.context.x4); 260 | console.log(' v---- IOKit input struct ----'); 261 | console.log(inputStruct.readByteArray(inputStructCnt)); 262 | } 263 | 264 | }, 265 | 266 | // also read the response, works now :) 267 | onLeave: function(r) { 268 | 269 | // read count if not null 270 | if (this.outputStructCnt != "0x0") { 271 | this.outputStructCnt = this.outputStructCnt.readU8(); 272 | } 273 | 274 | if (self.debug_iokit && this.outputStructCnt > 0) { 275 | console.log(' v---- IOKit output struct ----'); 276 | console.log(this.outputStruct.readByteArray(this.outputStructCnt)); 277 | //outputStruct = new NativePointer(outputStruct); 278 | //console.log(outputStruct.readByteArray(outputStructCnt)); 279 | } 280 | } 281 | }); 282 | } 283 | 284 | print_iokit_args(args) { 285 | if (this.debug_iokit) { 286 | //console.log(" !!! extra info, command found !!!"); 287 | console.log(" > connection " + args[0]); 288 | console.log(" > selector " + args[1]); 289 | console.log(" > input " + args[2]); 290 | console.log(" > inputCnt " + args[3]); 291 | console.log(" v "); 292 | console.log(args[2].readByteArray(parseInt(args[3]))); 293 | console.log(" > inputStruct " + args[4]); 294 | console.log(" > inputStructCnt " + args[5]); 295 | console.log(" > output " + args[6]); 296 | console.log(" > outputCnt " + args[7]); 297 | console.log(" > outputStruct " + args[8]); 298 | if (args[8] != "0x0") { 299 | console.log(" > outputStructCnt*" + args[9].readU64()); 300 | } 301 | } 302 | } 303 | 304 | 305 | // All of this seems to be handled directly in the AOP 306 | print_property(property_id) { 307 | switch(property_id) { 308 | case 208: 309 | console.log(" ~ SPMISettings"); 310 | break; 311 | case 209: 312 | console.log(" ~ UWBCommsRoute"); 313 | break; 314 | case 210: 315 | console.log(" ~ BeaconWhitelist"); 316 | break; 317 | case 211: 318 | console.log(" ~ R1MacAddress"); 319 | break; 320 | case 212: 321 | console.log(" ~ AllowR1Sleep"); 322 | break; 323 | case 213: 324 | console.log(" ~ CalDataPushed"); 325 | break; 326 | case 214: 327 | console.log(" ~ CmdQueueClearAllowed"); 328 | break; 329 | case 215: 330 | console.log(" ~ LogVerbose"); 331 | break; 332 | case 216: 333 | console.log(" ~ RoseAOPHello"); 334 | break; 335 | default: 336 | console.log(" ~ UNKNOWN " + property_id); 337 | break; 338 | } 339 | } 340 | 341 | // Shown during chip boot, needs `triggerCrashLog();` 342 | print_rose_property(property_id) { 343 | switch(property_id) { 344 | case 208: 345 | console.log(" ! GetBoardID"); 346 | break; 347 | case 209: 348 | console.log(" ! GetChipID"); 349 | break; 350 | case 210: 351 | console.log(" ! GetECID"); 352 | break; 353 | case 211: 354 | console.log(" ! GetBootNonceHash"); 355 | break; 356 | case 214: 357 | console.log(" ! GetBootMode"); 358 | break; 359 | case 215: 360 | console.log(" ! GetHostBootNonce"); 361 | break; 362 | case 216: 363 | console.log(" ! GetProductionMode"); 364 | break; 365 | case 217: 366 | console.log(" ! GetSecureMode"); 367 | break; 368 | case 218: 369 | console.log(" ! GetSecurityDomain"); 370 | break; 371 | case 219: 372 | console.log(" ! GetMinimumEpoch"); 373 | break; 374 | case 220: 375 | console.log(" ! GetDebugInfo::SecureROMStatus"); 376 | break; 377 | case 221: 378 | console.log(" ! GetChipRevision"); 379 | break; 380 | default: 381 | console.log(" ! UNKNOWN"); 382 | break; 383 | } 384 | } 385 | 386 | print_supervisor_command(command_id) { 387 | command_id = String.fromCharCode(command_id); // convert to string representation 388 | 389 | switch(command_id) { 390 | case '0': 391 | console.log(" + PingCommand"); //TODO there's this null case but no idea how to represent... maybe it's \x30, maybe it's \x00 392 | break; 393 | case ' ': 394 | console.log(" + RosePassthrough"); 395 | break; 396 | case '!': 397 | console.log(" + NewServiceRequest"); // starts with 2 bytes ticket ID 398 | break; 399 | case '"': 400 | console.log(" + TriggerRangingStart"); 401 | break; 402 | case '#': 403 | console.log(" + TriggerRangingStop"); 404 | break; 405 | case '$': 406 | console.log(" + CancelServiceRequest"); // 2 bytes ticket ID 407 | break; 408 | case '%': 409 | console.log(" + HelloCommand"); 410 | break; 411 | case '&': 412 | console.log(" + GetPowerStats"); 413 | break; 414 | case '\'': 415 | console.log(" + ResetJobs"); 416 | break; 417 | case '(': 418 | console.log(" + APCheckIn"); 419 | break; 420 | case ')': 421 | console.log(" + APGoodbye"); 422 | break; 423 | case '*': 424 | console.log(" + ActivateTimeSync"); 425 | break; 426 | case '+': 427 | console.log(" + UpdateSessionData"); 428 | break; 429 | case '.': 430 | console.log(" + EmulatedRosePacket"); 431 | break; 432 | case '/': 433 | console.log(" + EmulatedBTData"); 434 | break; 435 | case ',': 436 | case '-': 437 | default: 438 | console.log(" + "); 439 | break; 440 | } 441 | } 442 | 443 | 444 | /* 445 | Even RoseControllerLib sends a raw Mach message :/ So let's also hook this to double-check what we're missing. 446 | */ 447 | logMach() { 448 | /* 449 | 450 | mach_msg_return_t __cdecl mach_msg(mach_msg_header_t *msg, mach_msg_option_t option, mach_msg_size_t send_size, 451 | mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify) 452 | 453 | typedef struct 454 | { 455 | mach_msg_bits_t msgh_bits; 456 | mach_msg_size_t msgh_size; 457 | mach_port_t msgh_remote_port; 458 | mach_port_t msgh_local_port; 459 | mach_msg_size_t msgh_reserved; 460 | mach_msg_id_t msgh_id; (0x14) 461 | } mach_msg_header_t; 462 | 463 | ... I think we need to add 0x18 to be at the body :) 464 | 465 | typedef struct 466 | { 467 | mach_msg_header_t header; 468 | mach_msg_body_t body; 469 | } mach_msg_base_t; 470 | 471 | */ 472 | 473 | 474 | var self = this; 475 | 476 | 477 | var _mach_msg_addr = Module.getExportByName('libSystem.B.dylib', 'mach_msg'); 478 | 479 | // TODO this might again run into runtime issues but also works most of the time 480 | // not sure if there could be vars in the interceptor itself? 481 | this._mach_msg_body_ptr; 482 | this._mach_msg_rcv_size; 483 | this._mach_msg_snd_size; 484 | this._mach_is_xpc = false; 485 | 486 | Interceptor.attach(_mach_msg_addr, { 487 | 488 | // parse what we send 489 | onEnter: function(args) { 490 | 491 | this._mach_is_xpc = false; 492 | this._mach_msg_body_ptr = this.context.x0.add(0x18); 493 | if (self.mach_remove_xpc && this._mach_msg_body_ptr.readU32() == 1079529539) { // Integer corresponding to "CPX@" 494 | console.log(' * mach_msg(XPC, skipping for perf)'); 495 | this._mach_is_xpc = true; 496 | } else { 497 | console.log(' * mach_msg(msg: ' + args[0] + ', option: ' + args[1] + ', send_size: ' + 498 | args[2] + ', rcv_size: ' + args[3] + ', rcv_name port: ' + args[4] + ', timeout: ' + args[5] + 499 | ', notify port: ' + args[6] + ')'); 500 | 501 | // get send_size bytes of body 502 | this._mach_msg_snd_size = parseInt(args[2]); 503 | if (this._mach_msg_snd_size > 0 && this._mach_msg_snd_size < self.mach_truncate_size) { 504 | console.log(' v---- mach_msg input ----'); 505 | console.log(this._mach_msg_body_ptr.readByteArray(this._mach_msg_snd_size)); 506 | } else if (this._mach_msg_snd_size > 0) { 507 | console.log(' v---- mach_msg input (truncated) ----'); 508 | console.log(this._mach_msg_body_ptr.readByteArray(self.mach_truncate_size)); 509 | } 510 | 511 | // keep receive_size info for later 512 | this._mach_msg_rcv_size = parseInt(this.context.x3); 513 | } 514 | }, 515 | 516 | // parse what we receive in response 517 | // as far as I understand the original mach_msg body is overwritten on return 518 | onLeave: function(r) { 519 | 520 | //console.log(r); // it's only 0x0 for success, not interesting to print 521 | 522 | if ( ! (self.mach_remove_xpc && this._mach_is_xpc)) { // skip XPC messages if mach_remove_xpc=true and _mach_is_xpc=true 523 | 524 | if (this._mach_msg_rcv_size > 0 && this._mach_msg_rcv_size < self.mach_truncate_size) { 525 | console.log(' v---- mach_msg output ----'); 526 | console.log(this._mach_msg_body_ptr.readByteArray(this._mach_msg_rcv_size)); 527 | } else if (this._mach_msg_rcv_size > 0) { 528 | console.log(' v---- mach_msg output (truncated) ----'); 529 | console.log(this._mach_msg_body_ptr.readByteArray(self.mach_truncate_size)); 530 | } 531 | } 532 | } 533 | }); 534 | } 535 | 536 | /* 537 | Version-specific symbols, needs to be adjusted for every version. 538 | */ 539 | setSymbols(ios_version) { 540 | 541 | var self = this; 542 | 543 | if (ios_version == "arm64e_13.3") { 544 | console.log(" * Set symbols to A12+ iOS 13.3"); 545 | 546 | self._RoseSupervisorCommand_addr = self._nearbyd_base.add(0x141874); // iOS 13.3, iPhone 11 547 | } 548 | 549 | // tested on an iPhone 11+12 550 | else if (ios_version == "arm64e_14.1") { 551 | console.log(" * Set symbols to A12+ iOS 14.1"); 552 | 553 | self._RoseSupervisorCommand_addr = self._nearbyd_base.add(0x1B6BEC); // iOS 14.1, iPhone 11 + 12 554 | } 555 | 556 | // tested on an iPhone 12 557 | else if (ios_version == "arm64e_14.2.1") { 558 | console.log(" * Set symbols to A12+ iOS 14.2.1"); 559 | 560 | self._RoseSupervisorCommand_addr = self._nearbyd_base.add(0x1D5E58); // iOS 14.1, iPhone 11 + 12 561 | } 562 | 563 | // tested on an iPhone 12 564 | else if (ios_version == "arm64e_14.3") { 565 | console.log(" * Set symbols to A12+ iOS 14.3"); 566 | 567 | self._RoseSupervisorCommand_addr = self._nearbyd_base.add(0x1F32E0); 568 | } 569 | } 570 | 571 | 572 | 573 | // Export class methods for Frida 574 | makeExports() { 575 | var self = this; 576 | return { 577 | setsymbols: (ios_version) => {return self.setSymbols(ios_version)}, 578 | prepare: () => {return self.prepare()}, 579 | } 580 | } 581 | 582 | } 583 | 584 | var r = new CustomRoseController(); 585 | 586 | // Prepare the target function 587 | r.prepare(); //call this when standalone 588 | 589 | // Required to interact with Python ... 590 | rpc.exports = r.makeExports(); 591 | rpc.exports.r = CustomRoseController; --------------------------------------------------------------------------------