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