Skip to content

Commit 4fa4941

Browse files
committed
win32: Implement support for Debug USB (KIS) mode
This requires and up-to-date AppleMobileDeviceSupport64.msi package installed that contains the AppleKIS driver.
1 parent 63ea45b commit 4fa4941

1 file changed

Lines changed: 151 additions & 61 deletions

File tree

src/libirecovery.c

Lines changed: 151 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,26 @@ static unsigned int crc32_lookup_t1[256] = {
454454
#define crc32_step(a,b) \
455455
a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
456456

457+
#ifdef WIN32
458+
#pragma pack(1)
459+
typedef struct {
460+
uint16_t vid;
461+
uint16_t pid;
462+
uint32_t unk;
463+
char nonces[255];
464+
char serial[255];
465+
char manufacturer[255];
466+
char product[255];
467+
} KIS_device_info;
468+
469+
typedef struct {
470+
uint8_t data[0x4000];
471+
uint32_t size;
472+
uint32_t unused;
473+
uint64_t address;
474+
} KIS_upload_chunk;
475+
#pragma pack()
476+
#else
457477
#pragma pack(1)
458478
typedef struct {
459479
uint16_t sequence; // A sequence number
@@ -525,6 +545,7 @@ typedef struct {
525545
uint32_t status;
526546
} KIS_generic_reply;
527547
#pragma pack()
548+
#endif
528549

529550
static THREAD_T th_event_handler = THREAD_T_NULL;
530551
struct collection listeners;
@@ -873,6 +894,7 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un
873894
irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf);
874895
}
875896

897+
#ifndef WIN32
876898
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)
877899
{
878900
if (argCount > UINT8_MAX) {
@@ -1001,9 +1023,11 @@ static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, siz
10011023

10021024
return len/2;
10031025
}
1026+
#endif
10041027

10051028
static irecv_error_t irecv_kis_init(irecv_client_t client)
10061029
{
1030+
#ifndef WIN32
10071031
irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL);
10081032
if (err != IRECV_E_SUCCESS) {
10091033
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)
10151039
debug("Failed to write to KIS_INDEX_ENABLE_B, error %d\n", err);
10161040
return err;
10171041
}
1018-
1042+
#endif
10191043
client->isKIS = 1;
10201044

10211045
return IRECV_E_SUCCESS;
@@ -1024,7 +1048,23 @@ static irecv_error_t irecv_kis_init(irecv_client_t client)
10241048
static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
10251049
{
10261050
debug("Loading device info in KIS mode...\n");
1027-
1051+
#ifdef WIN32
1052+
KIS_device_info kisInfo;
1053+
DWORD transferred = 0;
1054+
int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL);
1055+
if (ret) {
1056+
debug("Serial: %s\n", kisInfo.serial);
1057+
irecv_load_device_info_from_iboot_string(client, kisInfo.serial);
1058+
debug("Manufacturer: %s\n", kisInfo.manufacturer);
1059+
debug("Product: %s\n", kisInfo.product);
1060+
debug("Nonces: %s\n", kisInfo.nonces);
1061+
irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, kisInfo.nonces);
1062+
irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, kisInfo.nonces);
1063+
debug("VID: 0x%04x\n", kisInfo.vid);
1064+
debug("PID: 0x%04x\n", kisInfo.pid);
1065+
}
1066+
client->mode = kisInfo.pid;
1067+
#else
10281068
KIS_req_header req = {};
10291069
KIS_device_info di = {};
10301070
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)
10721112
debug("PID: 0x%04x\n", di.deviceDescriptor.idProduct);
10731113

10741114
client->mode = di.deviceDescriptor.idProduct;
1075-
1115+
#endif
10761116
return IRECV_E_SUCCESS;
10771117
}
10781118

10791119
#ifdef WIN32
10801120
static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
10811121
static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
1122+
static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}};
10821123

10831124
typedef struct usb_control_request {
10841125
uint8_t bmRequestType;
@@ -1093,7 +1134,7 @@ typedef struct usb_control_request {
10931134
static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid)
10941135
{
10951136
int found = 0;
1096-
const GUID *guids[] = { &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
1137+
const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
10971138
irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
10981139
memset(_client, 0, sizeof(struct irecv_client_private));
10991140

@@ -1117,21 +1158,32 @@ static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid)
11171158
}
11181159

11191160
unsigned int pid = 0;
1120-
if (sscanf(details->DevicePath, "\\\\?\\usb#vid_05ac&pid_%04x", &pid)!= 1) {
1121-
debug("%s: ERROR: failed to parse PID! path: %s\n", __func__, details->DevicePath);
1161+
unsigned int vid = 0;
1162+
if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid) != 2) {
1163+
debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
11221164
free(details);
11231165
continue;
11241166
}
1167+
if (vid != APPLE_VENDOR_ID) {
1168+
free(details);
1169+
continue;
1170+
}
1171+
11251172
// make sure the current device is actually in the right mode for the given driver interface
11261173
if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
11271174
|| (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
1175+
|| (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
11281176
) {
11291177
free(details);
11301178
continue;
11311179
}
1180+
if (guids[k] == &GUID_DEVINTERFACE_KIS) {
1181+
pid = KIS_PRODUCT_ID;
1182+
}
11321183

11331184
_client->handle = CreateFileA(details->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
11341185
if (_client->handle == INVALID_HANDLE_VALUE) {
1186+
debug("%s: Failed to open device path %s: %d\n", __func__, details->DevicePath, (int)GetLastError());
11351187
free(details);
11361188
continue;
11371189
}
@@ -1158,34 +1210,35 @@ static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid)
11581210
char serial_str[256];
11591211
serial_str[0] = '\0';
11601212

1161-
char *p = (char*)details->DevicePath;
1162-
while ((p = strstr(p, "\\usb"))) {
1163-
if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1)
1164-
break;
1165-
p += 4;
1166-
}
1167-
free(details);
1168-
1169-
if (serial_str[0] == '\0') {
1170-
CloseHandle(_client->handle);
1171-
continue;
1172-
}
1213+
if (_client->mode != KIS_PRODUCT_ID) {
1214+
char *p = (char*)details->DevicePath;
1215+
while ((p = strstr(p, "\\usb"))) {
1216+
if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1)
1217+
break;
1218+
p += 4;
1219+
}
1220+
free(details);
11731221

1174-
p = strchr(serial_str, '#');
1175-
if (p) {
1176-
*p = '\0';
1177-
}
1222+
if (serial_str[0] == '\0') {
1223+
CloseHandle(_client->handle);
1224+
continue;
1225+
}
1226+
p = strchr(serial_str, '#');
1227+
if (p) {
1228+
*p = '\0';
1229+
}
11781230

1179-
unsigned int j;
1180-
for (j = 0; j < strlen(serial_str); j++) {
1181-
if (serial_str[j] == '_') {
1182-
serial_str[j] = ' ';
1183-
} else {
1184-
serial_str[j] = toupper(serial_str[j]);
1231+
unsigned int j;
1232+
for (j = 0; j < strlen(serial_str); j++) {
1233+
if (serial_str[j] == '_') {
1234+
serial_str[j] = ' ';
1235+
} else {
1236+
serial_str[j] = toupper(serial_str[j]);
1237+
}
11851238
}
1186-
}
11871239

1188-
irecv_load_device_info_from_iboot_string(_client, serial_str);
1240+
irecv_load_device_info_from_iboot_string(_client, serial_str);
1241+
}
11891242

11901243
if (ecid != 0) {
11911244
if (_client->device_info.ecid != ecid) {
@@ -1434,7 +1487,7 @@ int irecv_usb_bulk_transfer(irecv_client_t client,
14341487
#endif
14351488
#else
14361489
if (endpoint==0x4) {
1437-
ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL);
1490+
ret = DeviceIoControl(client->handle, 0x2201B6, data, length, data, length, (PDWORD) transferred, NULL);
14381491
} else {
14391492
ret = 0;
14401493
}
@@ -2202,40 +2255,51 @@ static void* _irecv_handle_device_add(void *userdata)
22022255

22032256
unsigned int pid = 0;
22042257

2205-
char *p = result;
2206-
while ((p = strstr(p, "\\usb"))) {
2207-
if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2)
2208-
break;
2209-
p += 4;
2210-
}
2211-
2212-
if (serial_str[0] == '\0') {
2213-
debug("%s: ERROR: failed to parse DevicePath?!\n", __func__);
2214-
return NULL;
2215-
}
2216-
2217-
if (!_irecv_is_recovery_device(p)) {
2218-
return NULL;
2219-
}
2258+
if (strncmp(result, "\\\\?\\kis#", 8) == 0) {
2259+
pid = KIS_PRODUCT_ID;
2260+
} else {
2261+
char *p = result;
2262+
while ((p = strstr(p, "\\usb"))) {
2263+
if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2)
2264+
break;
2265+
p += 4;
2266+
}
22202267

2221-
p = strchr(serial_str, '#');
2222-
if (p) {
2223-
*p = '\0';
2224-
}
2268+
if (serial_str[0] == '\0') {
2269+
debug("%s: ERROR: failed to parse DevicePath?!\n", __func__);
2270+
return NULL;
2271+
}
22252272

2226-
unsigned int j;
2227-
for (j = 0; j < strlen(serial_str); j++) {
2228-
if (serial_str[j] == '_') {
2229-
serial_str[j] = ' ';
2230-
} else {
2231-
serial_str[j] = toupper(serial_str[j]);
2273+
if (!_irecv_is_recovery_device(p)) {
2274+
return NULL;
22322275
}
22332276
}
2277+
22342278
product_id = (uint16_t)pid;
22352279

22362280
if (product_id == KIS_PRODUCT_ID) {
2237-
debug("%s: ERROR: KIS currently not supported with this backend!\n", __func__);
2238-
return NULL;
2281+
client = (irecv_client_t)malloc(sizeof(struct irecv_client_private));
2282+
client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
2283+
if (client->handle == INVALID_HANDLE_VALUE) {
2284+
debug("%s: Failed to open device path %s\n", __func__, result);
2285+
free(client);
2286+
return NULL;
2287+
}
2288+
client->mode = pid;
2289+
} else {
2290+
char* p = strchr(serial_str, '#');
2291+
if (p) {
2292+
*p = '\0';
2293+
}
2294+
2295+
unsigned int j;
2296+
for (j = 0; j < strlen(serial_str); j++) {
2297+
if (serial_str[j] == '_') {
2298+
serial_str[j] = ' ';
2299+
} else {
2300+
serial_str[j] = toupper(serial_str[j]);
2301+
}
2302+
}
22392303
}
22402304

22412305
#else /* !WIN32 */
@@ -2523,7 +2587,7 @@ static void *_irecv_event_handler(void* data)
25232587
struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data;
25242588
#ifdef WIN32
25252589
struct collection newDevices;
2526-
const GUID *guids[] = { &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
2590+
const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
25272591
int running = 1;
25282592

25292593
collection_init(&newDevices);
@@ -2606,15 +2670,22 @@ static void *_irecv_event_handler(void* data)
26062670
} ENDFOREACH
26072671
26082672
unsigned int pid = 0;
2609-
if (sscanf(details->DevicePath, "\\\\?\\usb#vid_05ac&pid_%04x", &pid)!= 1) {
2610-
debug("%s: ERROR: failed to parse PID! path: %s\n", __func__, details->DevicePath);
2673+
unsigned int vid = 0;
2674+
if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid)!= 2) {
2675+
debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
26112676
free(details);
26122677
continue;
26132678
}
2679+
if (vid != APPLE_VENDOR_ID) {
2680+
free(details);
2681+
continue;
2682+
}
2683+
26142684
// make sure the current device is actually in the right mode for the given driver interface
26152685
int skip = 0;
26162686
if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
26172687
|| (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
2688+
|| (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
26182689
) {
26192690
skip = 1;
26202691
}
@@ -3109,6 +3180,11 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char*
31093180
if (toUpload > 0x4000)
31103181
toUpload = 0x4000;
31113182

3183+
#ifdef WIN32
3184+
memcpy(chunk->data, buffer, toUpload);
3185+
chunk->size = toUpload;
3186+
chunk->address = address;
3187+
#else
31123188
irecv_error_t error = irecv_kis_request_init(&chunk->hdr, KIS_PORTAL_RSM, KIS_INDEX_UPLOAD, 3, toUpload, 0);
31133189
if (error != IRECV_E_SUCCESS) {
31143190
free(chunk);
@@ -3119,10 +3195,17 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char*
31193195
chunk->address = address;
31203196
chunk->size = toUpload;
31213197
memcpy(chunk->data, buffer, toUpload);
3198+
#endif
31223199

3200+
#ifdef WIN32
3201+
DWORD transferred = 0;
3202+
int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL);
3203+
irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3204+
#else
31233205
KIS_generic_reply reply;
31243206
size_t rcvSize = sizeof(reply);
31253207
error = irecv_kis_request(client, &chunk->hdr, sizeof(*chunk) - (0x4000 - toUpload), &reply.hdr, &rcvSize);
3208+
#endif
31263209
if (error != IRECV_E_SUCCESS) {
31273210
free(chunk);
31283211
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*
31473230
free(chunk);
31483231

31493232
if (dfu_notify_finished) {
3233+
#ifdef WIN32
3234+
DWORD amount = (DWORD)origLen;
3235+
DWORD transferred = 0;
3236+
int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL);
3237+
irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3238+
#else
31503239
irecv_error_t error = irecv_kis_config_write32(client, KIS_PORTAL_RSM, KIS_INDEX_BOOT_IMG, origLen);
3240+
#endif
31513241
if (error != IRECV_E_SUCCESS) {
31523242
debug("Failed to boot image, error %d\n", error);
31533243
return error;

0 commit comments

Comments
 (0)