From 4fa49416d47076a026d7d2858dbe2002aaa1537f Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Fri, 12 Jan 2024 03:16:24 +0100 Subject: win32: Implement support for Debug USB (KIS) mode This requires and up-to-date AppleMobileDeviceSupport64.msi package installed that contains the AppleKIS driver. --- src/libirecovery.c | 212 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 151 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/libirecovery.c b/src/libirecovery.c index f0acce0..9742cb5 100644 --- a/src/libirecovery.c +++ b/src/libirecovery.c @@ -454,6 +454,26 @@ static unsigned int crc32_lookup_t1[256] = { #define crc32_step(a,b) \ a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8)) +#ifdef WIN32 +#pragma pack(1) +typedef struct { + uint16_t vid; + uint16_t pid; + uint32_t unk; + char nonces[255]; + char serial[255]; + char manufacturer[255]; + char product[255]; +} KIS_device_info; + +typedef struct { + uint8_t data[0x4000]; + uint32_t size; + uint32_t unused; + uint64_t address; +} KIS_upload_chunk; +#pragma pack() +#else #pragma pack(1) typedef struct { uint16_t sequence; // A sequence number @@ -525,6 +545,7 @@ typedef struct { uint32_t status; } KIS_generic_reply; #pragma pack() +#endif static THREAD_T th_event_handler = THREAD_T_NULL; struct collection listeners; @@ -873,6 +894,7 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf); } +#ifndef WIN32 static irecv_error_t irecv_kis_request_init(KIS_req_header *hdr, uint8_t portal, uint16_t index, size_t argCount, size_t payloadSize, size_t rplWords) { if (argCount > UINT8_MAX) { @@ -1001,9 +1023,11 @@ static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, siz return len/2; } +#endif static irecv_error_t irecv_kis_init(irecv_client_t client) { +#ifndef WIN32 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL); if (err != IRECV_E_SUCCESS) { debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err); @@ -1015,7 +1039,7 @@ static irecv_error_t irecv_kis_init(irecv_client_t client) debug("Failed to write to KIS_INDEX_ENABLE_B, error %d\n", err); return err; } - +#endif client->isKIS = 1; return IRECV_E_SUCCESS; @@ -1024,7 +1048,23 @@ static irecv_error_t irecv_kis_init(irecv_client_t client) static irecv_error_t irecv_kis_load_device_info(irecv_client_t client) { debug("Loading device info in KIS mode...\n"); - +#ifdef WIN32 + KIS_device_info kisInfo; + DWORD transferred = 0; + int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL); + if (ret) { + debug("Serial: %s\n", kisInfo.serial); + irecv_load_device_info_from_iboot_string(client, kisInfo.serial); + debug("Manufacturer: %s\n", kisInfo.manufacturer); + debug("Product: %s\n", kisInfo.product); + debug("Nonces: %s\n", kisInfo.nonces); + irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, kisInfo.nonces); + irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, kisInfo.nonces); + debug("VID: 0x%04x\n", kisInfo.vid); + debug("PID: 0x%04x\n", kisInfo.pid); + } + client->mode = kisInfo.pid; +#else KIS_req_header req = {}; KIS_device_info di = {}; irecv_error_t err = irecv_kis_request_init(&req, KIS_PORTAL_RSM, KIS_INDEX_GET_INFO, 0, 0, sizeof(di.deviceInfo)/4); @@ -1072,13 +1112,14 @@ static irecv_error_t irecv_kis_load_device_info(irecv_client_t client) debug("PID: 0x%04x\n", di.deviceDescriptor.idProduct); client->mode = di.deviceDescriptor.idProduct; - +#endif return IRECV_E_SUCCESS; } #ifdef WIN32 static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}}; static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; +static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}}; typedef struct usb_control_request { uint8_t bmRequestType; @@ -1093,7 +1134,7 @@ typedef struct usb_control_request { static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid) { int found = 0; - const GUID *guids[] = { &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL }; + const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL }; irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client_private)); memset(_client, 0, sizeof(struct irecv_client_private)); @@ -1117,21 +1158,32 @@ static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid) } unsigned int pid = 0; - if (sscanf(details->DevicePath, "\\\\?\\usb#vid_05ac&pid_%04x", &pid)!= 1) { - debug("%s: ERROR: failed to parse PID! path: %s\n", __func__, details->DevicePath); + unsigned int vid = 0; + if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid) != 2) { + debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath); free(details); continue; } + if (vid != APPLE_VENDOR_ID) { + free(details); + continue; + } + // make sure the current device is actually in the right mode for the given driver interface if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE) || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4)) + || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1) ) { free(details); continue; } + if (guids[k] == &GUID_DEVINTERFACE_KIS) { + pid = KIS_PRODUCT_ID; + } _client->handle = CreateFileA(details->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (_client->handle == INVALID_HANDLE_VALUE) { + debug("%s: Failed to open device path %s: %d\n", __func__, details->DevicePath, (int)GetLastError()); free(details); continue; } @@ -1158,34 +1210,35 @@ static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid) char serial_str[256]; serial_str[0] = '\0'; - char *p = (char*)details->DevicePath; - while ((p = strstr(p, "\\usb"))) { - if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1) - break; - p += 4; - } - free(details); - - if (serial_str[0] == '\0') { - CloseHandle(_client->handle); - continue; - } + if (_client->mode != KIS_PRODUCT_ID) { + char *p = (char*)details->DevicePath; + while ((p = strstr(p, "\\usb"))) { + if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1) + break; + p += 4; + } + free(details); - p = strchr(serial_str, '#'); - if (p) { - *p = '\0'; - } + if (serial_str[0] == '\0') { + CloseHandle(_client->handle); + continue; + } + p = strchr(serial_str, '#'); + if (p) { + *p = '\0'; + } - unsigned int j; - for (j = 0; j < strlen(serial_str); j++) { - if (serial_str[j] == '_') { - serial_str[j] = ' '; - } else { - serial_str[j] = toupper(serial_str[j]); + unsigned int j; + for (j = 0; j < strlen(serial_str); j++) { + if (serial_str[j] == '_') { + serial_str[j] = ' '; + } else { + serial_str[j] = toupper(serial_str[j]); + } } - } - irecv_load_device_info_from_iboot_string(_client, serial_str); + irecv_load_device_info_from_iboot_string(_client, serial_str); + } if (ecid != 0) { if (_client->device_info.ecid != ecid) { @@ -1434,7 +1487,7 @@ int irecv_usb_bulk_transfer(irecv_client_t client, #endif #else if (endpoint==0x4) { - ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL); + ret = DeviceIoControl(client->handle, 0x2201B6, data, length, data, length, (PDWORD) transferred, NULL); } else { ret = 0; } @@ -2202,40 +2255,51 @@ static void* _irecv_handle_device_add(void *userdata) unsigned int pid = 0; - char *p = result; - while ((p = strstr(p, "\\usb"))) { - if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2) - break; - p += 4; - } - - if (serial_str[0] == '\0') { - debug("%s: ERROR: failed to parse DevicePath?!\n", __func__); - return NULL; - } - - if (!_irecv_is_recovery_device(p)) { - return NULL; - } + if (strncmp(result, "\\\\?\\kis#", 8) == 0) { + pid = KIS_PRODUCT_ID; + } else { + char *p = result; + while ((p = strstr(p, "\\usb"))) { + if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2) + break; + p += 4; + } - p = strchr(serial_str, '#'); - if (p) { - *p = '\0'; - } + if (serial_str[0] == '\0') { + debug("%s: ERROR: failed to parse DevicePath?!\n", __func__); + return NULL; + } - unsigned int j; - for (j = 0; j < strlen(serial_str); j++) { - if (serial_str[j] == '_') { - serial_str[j] = ' '; - } else { - serial_str[j] = toupper(serial_str[j]); + if (!_irecv_is_recovery_device(p)) { + return NULL; } } + product_id = (uint16_t)pid; if (product_id == KIS_PRODUCT_ID) { - debug("%s: ERROR: KIS currently not supported with this backend!\n", __func__); - return NULL; + client = (irecv_client_t)malloc(sizeof(struct irecv_client_private)); + client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (client->handle == INVALID_HANDLE_VALUE) { + debug("%s: Failed to open device path %s\n", __func__, result); + free(client); + return NULL; + } + client->mode = pid; + } else { + char* p = strchr(serial_str, '#'); + if (p) { + *p = '\0'; + } + + unsigned int j; + for (j = 0; j < strlen(serial_str); j++) { + if (serial_str[j] == '_') { + serial_str[j] = ' '; + } else { + serial_str[j] = toupper(serial_str[j]); + } + } } #else /* !WIN32 */ @@ -2523,7 +2587,7 @@ static void *_irecv_event_handler(void* data) struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data; #ifdef WIN32 struct collection newDevices; - const GUID *guids[] = { &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL }; + const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL }; int running = 1; collection_init(&newDevices); @@ -2606,15 +2670,22 @@ static void *_irecv_event_handler(void* data) } ENDFOREACH unsigned int pid = 0; - if (sscanf(details->DevicePath, "\\\\?\\usb#vid_05ac&pid_%04x", &pid)!= 1) { - debug("%s: ERROR: failed to parse PID! path: %s\n", __func__, details->DevicePath); + unsigned int vid = 0; + if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid)!= 2) { + debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath); free(details); continue; } + if (vid != APPLE_VENDOR_ID) { + free(details); + continue; + } + // make sure the current device is actually in the right mode for the given driver interface int skip = 0; if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE) || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4)) + || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1) ) { skip = 1; } @@ -3109,6 +3180,11 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* if (toUpload > 0x4000) toUpload = 0x4000; +#ifdef WIN32 + memcpy(chunk->data, buffer, toUpload); + chunk->size = toUpload; + chunk->address = address; +#else irecv_error_t error = irecv_kis_request_init(&chunk->hdr, KIS_PORTAL_RSM, KIS_INDEX_UPLOAD, 3, toUpload, 0); if (error != IRECV_E_SUCCESS) { free(chunk); @@ -3119,10 +3195,17 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* chunk->address = address; chunk->size = toUpload; memcpy(chunk->data, buffer, toUpload); +#endif +#ifdef WIN32 + DWORD transferred = 0; + int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL); + irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD; +#else KIS_generic_reply reply; size_t rcvSize = sizeof(reply); error = irecv_kis_request(client, &chunk->hdr, sizeof(*chunk) - (0x4000 - toUpload), &reply.hdr, &rcvSize); +#endif if (error != IRECV_E_SUCCESS) { free(chunk); debug("Failed to upload chunk, error %d\n", error); @@ -3147,7 +3230,14 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* free(chunk); if (dfu_notify_finished) { +#ifdef WIN32 + DWORD amount = (DWORD)origLen; + DWORD transferred = 0; + int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL); + irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD; +#else irecv_error_t error = irecv_kis_config_write32(client, KIS_PORTAL_RSM, KIS_INDEX_BOOT_IMG, origLen); +#endif if (error != IRECV_E_SUCCESS) { debug("Failed to boot image, error %d\n", error); return error; -- cgit v1.1-32-gdbae