Skip to content

Commit

Permalink
win32: Implement support for Debug USB (KIS) mode
Browse files Browse the repository at this point in the history
This requires and up-to-date AppleMobileDeviceSupport64.msi package
installed that contains the AppleKIS driver.
  • Loading branch information
nikias committed Jan 12, 2024
1 parent 63ea45b commit 4fa4941
Showing 1 changed file with 151 additions and 61 deletions.
212 changes: 151 additions & 61 deletions src/libirecovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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));

Expand All @@ -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;
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Expand Down

0 comments on commit 4fa4941

Please sign in to comment.