diff --git a/docs/NOTES.md b/docs/NOTES.md new file mode 100644 index 00000000..520cf32c --- /dev/null +++ b/docs/NOTES.md @@ -0,0 +1,50 @@ +# Apple iDevice Restore/Upgrade/Revive Protocol + +# DFU Mode + +DFU is a USB standard protocol that Apple uses from `SecureROM` state to bring up the SEP and AP into a `recovery` +state. This requires a multi-stage process whereby the device is interrogated for it's version, chip, board etc. +(this is done by querying the SerialNumber using an alternate language string property of the USB device, which +is part of a USB device standard descriptors). This breaks the device into CPID (chip ID) and BDID (board ID). +Further the device is queried for the value of `APNonce` and `SEPNonce` which are transmitted to TSS (tatsu signing server) +at tss.apple.com to get a "personalized" manifest. This manifest is a ASN1 signed IMG4 (specifically an IM4M or IMG4 manifest) +that contains the SHA fingerprints of all the later component stages. Because the APNonce and SEPNonce are unique to each +boot, this means that the values must be submitted to tss on every recovery and cannot be reused (a mistake in NVRAM allowed +reuse of such things in the "FutureRestore" exploit). SecureROM is very low level assembly that has the responsibility +of interacting with USB, generation of the APNonce, interaction with the SEP for the SEP's SecureROM and associated SEPNonce +and the transfer, and validation of the payloads received over USB. This means SecureROM has a base USB stack, the SEP +mailbox protocol, SHA implementation, base bring-up of essential controllers such as Hydra/Tristar/ACE/PMP-PMGR, +a hardened ASN1/IMG4 implementation, and a base public/private algorithm implementation as well as compiled (or in the +case of silicon, masked-in) root-of-trust keys. + +After personalizing the manifest it is transmitted to the device, which validates it against the nonce set, and then DFU +begins to receive the payloads specified in the BuildManifest.plist file. Each of these is copied to a volatile memory +region where it is hashed and compared to the manifest. (Because of the DFU specification this is handled using bulk +transfers to an output endpoint of the interface of 512 byes and completion is signaled by a final 0 byte transfer) +If everything lines up, the payload (iBoot recovery) is handed off too. + +Modern versions of DFU / SecureROM include an arbitrary iBoot data payload which per never_released contains DDR4 training +data to bring online LPDDR4 memory early. + +# Recovery Mode + +Recovery mode is entered either by a failure of the iBoot process (in which case it is the version held in emulated NOR +which is truly NAND backed in any modern iDevice) or by a bring-up from DFU. In the case of a boot failure, the version +of iBoot / recovery could in fact be out of date, in the case of DFU (excepting AppleInternal access to the sign-anything TSS) +this should always be the most recent version of iBoot. iBoot is a slightly more sophisticated package as it can be patched +unlike the SecureROM variant, though they come from the exact same codebase. Because of it's patch-ability all other loading +behaviour happens here. This stage will bring up additional firmware devices, and bring the SEP out of spin-lock to SEPOS. +The iBoot protocol is also able to handle transports of larger size and to fully access NVRAM and SysCfg which is why +their serial number is available at this stage but not at DFU (ECID is available because its fused or masked in to the AP). +In the final stage, iBoot will receive the DeviceTree and XNU kernel, which should it passes IMG4 verification from personalization +will boot the XNU kernel as the next stage. (iBoot will set a few flags to alter the XNU behaviour for a recovery boot vs +a normal boot before the jump, for more info use siguza's `dt` to explore the DeviceTree and research OpenFirmware) + +## M1 macOS Note Related to Local Security Policy + +The M1 macOS restore process leverages the same recovery / restore process as the iDevice, but the iBoot system has +an additional payload as the mac, unlike a iPhone or iPad can boot code not signed by Apple. This is managed by the +modification of the "local security policy" using the `bptuil` command. This is a SEP entangled signed policy that +iBoot will verify. Because this policy must be reset to a secure state, the recovery process will provide a `lpol` +payload, which is the initial secure local security policy. It should NEVER be the case that this policy is different +than the version in the IPSW in the case of a restore as it could otherwise allow a SecureBoot policy violation \ No newline at end of file diff --git a/src/common.h b/src/common.h index 1828476d..abb4ceea 100644 --- a/src/common.h +++ b/src/common.h @@ -89,7 +89,6 @@ struct idevicerestore_entry_t { struct idevicerestore_client_t { int flags; plist_t tss; - plist_t tss_localpolicy; plist_t tss_recoveryos_root_ticket; char* tss_url; plist_t version_data; diff --git a/src/dfu.c b/src/dfu.c index a675c53e..b7d189b3 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -129,40 +129,31 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide // Use a specific TSS ticket for the Ap,LocalPolicy component plist_t tss = client->tss; - if (strcmp(component, "Ap,LocalPolicy") == 0) { - tss = client->tss_localpolicy; - } unsigned char* component_data = NULL; unsigned int component_size = 0; - if (strcmp(component, "Ap,LocalPolicy") == 0) { - // If Ap,LocalPolicy => Inject an empty policy - component_data = malloc(sizeof(lpol_file)); - component_size = sizeof(lpol_file); - memcpy(component_data, lpol_file, component_size); - } else { - if (tss) { - if (tss_response_get_path_by_entry(tss, component, &path) < 0) { - debug("NOTE: No path for component %s in TSS, will fetch from build_identity\n", component); - } - } - if (!path) { - if (build_identity_get_component_path(build_identity, component, &path) < 0) { - error("ERROR: Unable to get path for component '%s'\n", component); - free(path); - return -1; - } - } + if (tss) { + if (tss_response_get_path_by_entry(tss, component, &path) < 0) { + debug("NOTE: No path for component %s in TSS, will fetch from build_identity\n", component); + } + } + if (!path) { + if (build_identity_get_component_path(build_identity, component, &path) < 0) { + error("ERROR: Unable to get path for component '%s'\n", component); + free(path); + return -1; + } + } + + if (extract_component(client->ipsw, path, &component_data, &component_size) < 0) { + error("ERROR: Unable to extract component: %s\n", component); + free(path); + return -1; + } + free(path); + path = NULL; - if (extract_component(client->ipsw, path, &component_data, &component_size) < 0) { - error("ERROR: Unable to extract component: %s\n", component); - free(path); - return -1; - } - free(path); - path = NULL; - } unsigned char* data = NULL; uint32_t size = 0; diff --git a/src/idevicerestore.c b/src/idevicerestore.c index 192faef6..a0953569 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -151,13 +151,6 @@ static void usage(int argc, char* argv[], int err) } #endif -const uint8_t lpol_file[22] = { - 0x30, 0x14, 0x16, 0x04, 0x49, 0x4d, 0x34, 0x50, - 0x16, 0x04, 0x6c, 0x70, 0x6f, 0x6c, 0x16, 0x03, - 0x31, 0x2e, 0x30, 0x04, 0x01, 0x00 -}; -const uint32_t lpol_file_length = 22; - static int idevicerestore_keep_pers = 0; static int load_version_data(struct idevicerestore_client_t* client) @@ -1096,7 +1089,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return -1; } if (client->build_major >= 20) { - if (get_local_policy_tss_response(client, build_identity, &client->tss_localpolicy) < 0) { + if (get_local_policy_tss_response(client, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device (local policy)\n"); return -1; } diff --git a/src/idevicerestore.h b/src/idevicerestore.h index 880f9ee2..aab74b03 100644 --- a/src/idevicerestore.h +++ b/src/idevicerestore.h @@ -60,11 +60,6 @@ enum { RESTORE_NUM_STEPS }; -// lpol_file has been extracted from the IMG4 dump of the ac2 usb protocol. It is not present in the .ipsw and -// represents and empty "local policy". See https://support.apple.com/guide/security/contents-a-localpolicy-file-mac-apple-silicon-secc745a0845/web. -extern const uint8_t lpol_file[22]; -extern const uint32_t lpol_file_length; - typedef void (*idevicerestore_progress_cb_t)(int step, double step_progress, void* userdata); struct idevicerestore_client_t* idevicerestore_client_new(void); diff --git a/src/restore.c b/src/restore.c index 0593f2dd..e5d59cf0 100644 --- a/src/restore.c +++ b/src/restore.c @@ -3298,13 +3298,13 @@ int restore_send_restore_local_policy(restored_client_t restore, struct idevicer // The Update mode does not have a specific build identity for the recovery os. plist_t build_identity = restore_get_build_identity(client, client->flags & FLAG_ERASE ? 1 : 0); - int ret = get_recovery_os_local_policy_tss_response(client, build_identity, &client->tss_localpolicy, plist_dict_get_item(msg, "Arguments")); + int ret = get_recovery_os_local_policy_tss_response(client, build_identity, &client->tss, plist_dict_get_item(msg, "Arguments")); if (ret < 0) { error("ERROR: Unable to get recovery os local policy tss response\n"); return -1; } - ret = personalize_component(component, component_data, component_size, client->tss_localpolicy, &data, &size); + ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); free(component_data); component_data = NULL; if (ret < 0) {