From e2ff1128351d75eafd5426af7f96f9719c1af3e6 Mon Sep 17 00:00:00 2001 From: Zach C Date: Tue, 29 Jul 2008 01:11:02 -0700 Subject: First released version, 0.089. --- AFC.c | 279 ++++++++++++++++++++++++++++++++++++++++++++++++ AFC.h | 74 +++++++++++++ buildme.sh | 1 + iphone.c | 156 +++++++++++++++++++++++++++ iphone.h | 29 +++++ lockdown.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lockdown.h | 36 +++++++ main.c | 84 +++++++++++++++ main.h | 1 + plist.c | 91 ++++++++++++++++ plist.h | 17 +++ usbmux.c | 198 +++++++++++++++++++++++++++++++++++ usbmux.h | 39 +++++++ 13 files changed, 1354 insertions(+) create mode 100644 AFC.c create mode 100644 AFC.h create mode 100755 buildme.sh create mode 100644 iphone.c create mode 100644 iphone.h create mode 100644 lockdown.c create mode 100644 lockdown.h create mode 100644 main.c create mode 100644 main.h create mode 100644 plist.c create mode 100644 plist.h create mode 100644 usbmux.c create mode 100644 usbmux.h diff --git a/AFC.c b/AFC.c new file mode 100644 index 0000000..29c0f97 --- /dev/null +++ b/AFC.c @@ -0,0 +1,279 @@ +/* + * AFC.c -- contains functions for the built-in AFC client. + * Written by FxChiP + */ + +#include "AFC.h" + +extern int debug; + +AFClient *afc_connect(iPhone *phone, int s_port, int d_port) { + if (!phone) return NULL; + AFClient *client = (AFClient*)malloc(sizeof(AFClient)); + client->connection = mux_connect(phone, s_port, d_port); + if (!client->connection) { free(client); return NULL; } + else { + client->afc_packet = (AFCPacket*)malloc(sizeof(AFCPacket)); + if (client->afc_packet) { + client->phone = phone; + client->afc_packet->packet_num = 0; + client->afc_packet->unknown1 = client->afc_packet->unknown2 = client->afc_packet->unknown3 = client->afc_packet->unknown4 = 0; + client->afc_packet->header1 = 0x36414643; + client->afc_packet->header2 = 0x4141504C; + client->file_handle = 0; + return client; + } else { + mux_close_connection(client->phone, client->connection); + free(client); + return NULL; + } + } + + return NULL; // should never get to this point +} + +void afc_disconnect(AFClient *client) { + // client and its members should never be NULL is assumed here. + if (!client || !client->connection || !client->phone || !client->afc_packet) return; + mux_close_connection(client->phone, client->connection); + free(client->afc_packet); + free(client); +} + +int count_nullspaces(char *string, int number) { + int i = 0, nulls = 0; + for (i = 0; i < number; i++) { + if (string[i] == '\0') nulls++; + } + return nulls; +} + +int dispatch_AFC_packet(AFClient *client, char *data, int length) { + char *buffer; + int bytes = 0; + if (!client || !client->connection || !client->phone || !client->afc_packet) return 0; + if (!data || !length) length = 0; + + client->afc_packet->packet_num++; + client->afc_packet->entire_length = client->afc_packet->this_length = (length) ? sizeof(AFCPacket) + length + 1 : sizeof(AFCPacket); + + if (!length) { + bytes = mux_send(client->phone, client->connection, (char*)client->afc_packet, client->afc_packet->this_length); + if (bytes <= 0) return 0; + else return bytes; + } else { + buffer = (char*)malloc(sizeof(char) * client->afc_packet->this_length); + memcpy(buffer, client->afc_packet, sizeof(AFCPacket)); + memcpy(buffer+sizeof(AFCPacket), data, length); + buffer[client->afc_packet->this_length-1] = '\0'; + + bytes = mux_send(client->phone, client->connection, buffer, client->afc_packet->this_length); + free(buffer); // don't need it + if (bytes <= 0) return 0; + else return bytes; + } + + return 0; +} + +int receive_AFC_data(AFClient *client, char **dump_here) { + AFCPacket *r_packet; + char *buffer = (char*)malloc(sizeof(AFCPacket) * 4); + int bytes = 0, recv_len = 0; + + bytes = mux_recv(client->phone, client->connection, buffer, sizeof(AFCPacket) * 4); + if (bytes <= 0) { + free(buffer); + printf("Just didn't get enough.\n"); + *dump_here = NULL; + return 0; + } + + r_packet = (AFCPacket*)malloc(sizeof(AFCPacket)); + memcpy(r_packet, buffer, sizeof(AFCPacket)); + + if (r_packet->entire_length == r_packet->this_length && r_packet->entire_length > sizeof(AFCPacket) && r_packet->operation != AFC_ERROR) { + *dump_here = (char*)malloc(sizeof(char) * (r_packet->entire_length-sizeof(AFCPacket))); + memcpy(*dump_here, buffer+sizeof(AFCPacket), r_packet->entire_length-sizeof(AFCPacket)); + free(buffer); + free(r_packet); + return r_packet->entire_length - sizeof(AFCPacket); + } + + uint32 param1 = buffer[sizeof(AFCPacket)]; + free(buffer); + + if (r_packet->operation == 0x01) { + printf("Oops? Bad operation code received.\n"); + if (param1 == 0) printf("... false alarm, but still\n"); + else printf("Errno %i\n", param1); + free(r_packet); + *dump_here = NULL; + return 0; + } else { + printf("Operation code %x\nFull length %i and this length %i\n", r_packet->operation, r_packet->entire_length, r_packet->this_length); + } + + recv_len = r_packet->entire_length - r_packet->this_length; + free(r_packet); + buffer = (char*)malloc(sizeof(char) * recv_len); + bytes = mux_recv(client->phone, client->connection, buffer, recv_len); + if (bytes <= 0) { + free(buffer); + printf("Didn't get it at the second pass.\n"); + *dump_here = NULL; + return 0; + } + + *dump_here = buffer; // what they do beyond this point = not my problem + return bytes; +} + +char **afc_get_dir_list(AFClient *client, char *dir) { + client->afc_packet->operation = AFC_LIST_DIR; + int bytes = 0; + char *blah = NULL, **list = NULL; + bytes = dispatch_AFC_packet(client, dir, strlen(dir)); + if (!bytes) return NULL; + + bytes = receive_AFC_data(client, &blah); + if (!bytes && !blah) return NULL; + + list = make_strings_list(blah, bytes); + free(blah); + return list; +} + +char **make_strings_list(char *tokens, int true_length) { + if (!tokens || !true_length) return NULL; + int nulls = 0, i = 0, j = 0; + char **list = NULL; + + nulls = count_nullspaces(tokens, true_length); + list = (char**)malloc(sizeof(char*) * (nulls + 1)); + for (i = 0; i < nulls; i++) { + list[i] = strdup(tokens+j); + j += strlen(list[i]) + 1; + } + list[i] = strdup(""); + return list; +} + +AFCFile *afc_get_file_info(AFClient *client, char *path) { + client->afc_packet->operation = AFC_GET_INFO; + dispatch_AFC_packet(client, path, strlen(path)); + + char *received, **list; + AFCFile *my_file; + int length, i = 0; + + length = receive_AFC_data(client, &received); + list = make_strings_list(received, length); + free(received); + if (list) { + my_file = (AFCFile *)malloc(sizeof(AFCFile)); + for (i = 0; strcmp(list[i], ""); i++) { + if (!strcmp(list[i], "st_size")) { + my_file->size = atoi(list[i+1]); + } + + + if (!strcmp(list[i], "st_blocks")) { + my_file->blocks = atoi(list[i+1]); + } + + if (!strcmp(list[i], "st_ifmt")) { + if (!strcmp(list[i+1], "S_IFREG")) { + my_file->type = S_IFREG; + } else if (!strcmp(list[i+1], "S_IFDIR")) { + my_file->type = S_IFDIR; + } + } + } + free_dictionary(list); + return my_file; + } else { + return NULL; + } +} + +AFCFile *afc_open_file(AFClient *client, const char *filename, uint32 file_mode) { + if (file_mode != AFC_FILE_READ && file_mode != AFC_FILE_WRITE) return NULL; + if (!client ||!client->connection || !client->phone ||!client->afc_packet) return NULL; + char *further_data = (char*)malloc(sizeof(char) * (8 + strlen(filename) + 1)); + AFCFile *file_infos = NULL; + memcpy(further_data, &file_mode, 4); + uint32 ag = 0; + memcpy(further_data+4, &ag, 4); + memcpy(further_data+8, filename, strlen(filename)); + further_data[8+strlen(filename)] = '\0'; + int bytes = 0, length_thing = 0; + client->afc_packet->operation = AFC_FILE_OPEN; + + bytes = dispatch_AFC_packet(client, further_data, 8+strlen(filename)); + free(further_data); + if (bytes <= 0) { + printf("didn't read enough\n"); + return NULL; + } else { + printf("O HAI\n"); + length_thing = receive_AFC_data(client, &further_data); + if (length_thing && further_data) { + printf("ARA\n"); + file_infos = afc_get_file_info(client, filename); + memcpy(&file_infos->filehandle, further_data, 4); + printf("gr\n"); + return file_infos; + } else { + printf("didn't get further data or something\n"); + return NULL; + } + } + printf("what the fuck\n"); + return NULL; +} + +int afc_read_file(AFClient *client, AFCFile *file, char *data, int length) { + if (!client || !client->afc_packet || !client->phone || !client->connection || !file) return -1; + AFCFilePacket *packet = (AFCFilePacket*)malloc(sizeof(AFCFilePacket)); + char *input = NULL; + packet->unknown1 = packet->unknown2 = 0; + packet->filehandle = file->filehandle; + packet->size = length; + int bytes = 0; + + client->afc_packet->operation = AFC_READ; + bytes = dispatch_AFC_packet(client, packet, sizeof(AFCFilePacket)); + + if (bytes > 0) { + bytes = receive_AFC_data(client, &input); + if (bytes <= 0) { + return -1; + } else { + memcpy(data, input, (bytes > length) ? length : bytes); + return (bytes > length) ? length : bytes; + } + } else { + return -1; + } + return 0; +} + +void afc_close_file(AFClient *client, AFCFile *file) { + char *buffer = malloc(sizeof(char) * 8); + uint32 zero = 0; + if (debug) printf("File handle %i\n", file->filehandle); + memcpy(buffer, &file->filehandle, sizeof(uint32)); + memcpy(buffer, &zero, sizeof(zero)); + client->afc_packet->operation = AFC_FILE_CLOSE; + int bytes = 0; + bytes = dispatch_AFC_packet(client, buffer, sizeof(char) * 8); + free(buffer); + if (!bytes) return; + + bytes = receive_AFC_data(client, &buffer); + if (bytes<=0 && !buffer) printf("closefile: all went as expected\n"); + else { printf("We have a buffer!??!?\nLength %i\n", bytes); fwrite(buffer, 1, bytes, stdout); printf("\n"); } + if (buffer) free(buffer); // we're *SUPPOSED* to get an "error" here. +} + diff --git a/AFC.h b/AFC.h new file mode 100644 index 0000000..281e624 --- /dev/null +++ b/AFC.h @@ -0,0 +1,74 @@ +/* + * AFC.h + * Defines and structs and the like for the built-in AFC client + * Written by FxChiP + */ + +#include "usbmux.h" +#include "iphone.h" + +#include +#include +#include + +typedef struct { + //const uint32 header1 = 0x36414643; // '6AFC' or 'CFA6' when sent ;) + uint32 header1, header2; + //const uint32 header2 = 0x4141504C; // 'AAPL' or 'LPAA' when sent ;) + uint32 entire_length, unknown1, this_length, unknown2, packet_num, unknown3, operation, unknown4; +} AFCPacket; + +typedef struct { + usbmux_tcp_header *connection; + iPhone *phone; + AFCPacket *afc_packet; + int file_handle; +} AFClient; + +typedef struct { + uint32 filehandle, unknown1, size, unknown2; +} AFCFilePacket; + +typedef struct { + uint32 filehandle, blocks, size, type; +} AFCFile; + +typedef struct __AFCToken { + struct __AFCToken *last, *next; + char *token; +} AFCToken; + +enum { + S_IFREG = 0, + S_IFDIR = 1 +}; + +enum { + AFC_FILE_READ = 0x00000002, + AFC_FILE_WRITE = 0x00000003 +}; + +enum { + AFC_ERROR = 0x00000001, + AFC_GET_INFO = 0x0000000a, + AFC_GET_DEVINFO = 0x0000000b, + AFC_LIST_DIR = 0x00000003, + AFC_SUCCESS_RESPONSE = 0x00000002, + AFC_FILE_OPEN = 0x0000000d, + AFC_FILE_CLOSE = 0x00000014, + AFC_FILE_HANDLE = 0x0000000e, + AFC_READ = 0x0000000f +}; + +AFClient *afc_connect(iPhone *phone, int s_port, int d_port); +void afc_disconnect(AFClient *client); +int count_nullspaces(char *string, int number); +char **make_strings_list(char *tokens, int true_length); +int dispatch_AFC_packet(AFClient *client, char *data, int length); +int receive_AFC_data(AFClient *client, char **dump_here); + +char **afc_get_dir_list(AFClient *client, char *dir); +AFCFile *afc_get_file_info(AFClient *client, char *path); +AFCFile *afc_open_file(AFClient *client, const char *filename, uint32 file_mode); +void afc_close_file(AFClient *client, AFCFile *file); +int afc_read_file(AFClient *client, AFCFile *file, char *data, int length); diff --git a/buildme.sh b/buildme.sh new file mode 100755 index 0000000..75549ba --- /dev/null +++ b/buildme.sh @@ -0,0 +1 @@ +gcc `xml2-config --cflags --libs` -o main usbmux.c main.c iphone.c plist.c lockdown.c AFC.c -lusb -lgnutls diff --git a/iphone.c b/iphone.c new file mode 100644 index 0000000..4ddb571 --- /dev/null +++ b/iphone.c @@ -0,0 +1,156 @@ +/* iPhone.c + * Functions for creating and initializing iPhone structures + */ + +#include "usbmux.h" +#include "iphone.h" +#include +#include +#include +#include + +/* get_iPhone() + * + * Returns a structure with data on the first iPhone it finds. + * (Or NULL, on error) + */ +extern int debug; + +iPhone *get_iPhone() { + iPhone *phone = (iPhone*)malloc(sizeof(iPhone)); + usbmux_version_header *version = version_header(); + + // initialize the struct + phone->device = NULL; + phone->__device = NULL; + + // Initialize libusb. + usb_init(); + usb_find_busses(); + usb_find_devices(); + struct usb_bus *busses = usb_get_busses(), *bus; + struct usb_device *dev; + + for (bus = busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == 0x05ac && (dev->descriptor.idProduct == 0x1290 || dev->descriptor.idProduct == 0x1291)) { + phone->__device = dev; + phone->device = usb_open(phone->__device); + usb_reset(phone->device); + } + } + } + + phone->device = NULL; // :( sorry Daniel + phone->__device = NULL; // :( sorry Daniel + + for (bus = busses; bus; bus = bus->next) { // do it again as per libusb documentation + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == 0x05ac && (dev->descriptor.idProduct == 0x1290 || dev->descriptor.idProduct == 0x1291)) { + phone->__device = dev; + phone->device = usb_open(phone->__device); + usb_set_configuration(phone->device, 3); + usb_claim_interface(phone->device, 1); + break; + } + } + if (phone->__device && phone->device) break; + } + + if (!phone->device || !phone->__device) { // nothing connected + free_iPhone(phone); + if (debug) printf("get_iPhone(): iPhone not found\n"); + return NULL; + } + + // Okay, initialize the phone now. + int bytes = 0; + bytes = usb_bulk_write(phone->device, BULKOUT, (char*)version, sizeof(*version), 800); + if (bytes < 20 && debug) { + printf("get_iPhone(): libusb did NOT send enough!\n"); + if (bytes < 0) { + printf("get_iPhone(): libusb gave me the error: %s\n", usb_strerror()); + } + } + bytes = usb_bulk_read(phone->device, BULKIN, (char*)version, sizeof(*version), 800); + if (bytes < 20) { + free_iPhone(phone); + if (debug) printf("get_iPhone(): Invalid version message -- header too short.\n"); + if (debug && bytes < 0) printf("get_iPhone(): libusb error message: %s\n", usb_strerror()); + return NULL; + } else { + if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) { + // We're all ready to roll. + printf("get_iPhone() success\n"); + return phone; + } else { // BAD HEADER + free_iPhone(phone); + if (debug) printf("get_iPhone(): Received a bad header/invalid version number."); + return NULL; + } + } + + if (debug) printf("get_iPhone(): Unknown error.\n"); + return NULL; // if it got to this point it's gotta be bad +} + +/* free_iPhone(victim) + * This is a library-level function; deals directly with the iPhone to tear down relations, + * but otherwise is mostly internal. + * + * victim: a pointer to an iPhone structure + * Cleans up an iPhone structure, then frees the structure itself. + */ + +void free_iPhone(iPhone *victim) { + if (victim->buffer) free(victim->buffer); + if (victim->device) { + usb_release_interface(victim->device, 1); + usb_reset(victim->device); + usb_close(victim->device); + } + free(victim); +} + +/* send_to_phone(phone, data, datalen) + * This is a low-level (i.e. directly to phone) function. + * + * phone: the iPhone to send data to + * data: the data to send to the iPhone + * datalen: the length of the data + * + * Returns the number of bytes sent, or -1 on error or something. + */ +int send_to_phone(iPhone *phone, char *data, int datalen) { + if (!phone) return -1; + int bytes = 0; + // it may die here + if (debug) printf("dying here?\ndatalen = %i\ndata = %x\n", datalen, data); + + bytes = usb_bulk_write(phone->device, BULKOUT, data, datalen, 800); + if (debug) printf("noooo...?\n"); + if (bytes < datalen) { + return -1; + } else { + return bytes; + } + + return -1; +} + +/* recv_from_phone(phone, data, datalen): + * This function is a low-level (i.e. direct to iPhone) function. + * + * phone: the iPhone to receive data from + * data: where to put data read + * datalen: how much data to read in + * + * Returns: how many bytes were read in, or -1 on error. + */ +int recv_from_phone(iPhone *phone, char *data, int datalen) { + if (!phone) return -1; + int bytes = 0; + bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, 3500); + return bytes; +} + diff --git a/iphone.h b/iphone.h new file mode 100644 index 0000000..a49b7ef --- /dev/null +++ b/iphone.h @@ -0,0 +1,29 @@ +/* iphone.h + * iPhone struct + * Written by FxChiP */ + +#ifndef IPHONE_H +#define IPHONE_H + +#ifndef USBMUX_H +#include "usbmux.h" +#warning usbmux not included? +#endif + +#include + +#define BULKIN 0x85 +#define BULKOUT 0x04 + +typedef struct { + char *buffer; + struct usb_dev_handle *device; + struct usb_device *__device; +} iPhone; + +// Function definitions +void free_iPhone(iPhone *victim); +iPhone *get_iPhone(); +int send_to_phone(iPhone *phone, char *data, int datalen); +int recv_from_phone(iPhone *phone, char *data, int datalen); +#endif diff --git a/lockdown.c b/lockdown.c new file mode 100644 index 0000000..5ca6001 --- /dev/null +++ b/lockdown.c @@ -0,0 +1,349 @@ +/* + * lockdown.c -- libiphone built-in lockdownd client + * Written by FxChiP + */ + +#include "usbmux.h" +#include "iphone.h" +#include "lockdown.h" +#include +#include + +extern int debug; + +lockdownd_client *new_lockdownd_client(iPhone *phone) { + if (!phone) return NULL; + lockdownd_client *control = (lockdownd_client*)malloc(sizeof(lockdownd_client)); + control->connection = mux_connect(phone, 0x0a00, 0xf27e); + if (!control->connection) { + free(control); + return NULL; + } + + control->ssl_session = (gnutls_session_t*)malloc(sizeof(gnutls_session_t)); + control->in_SSL = 0; + control->iphone = phone; + control->gtls_buffer_hack_len = 0; + return control; +} + +void lockdown_close(lockdownd_client *control) { + if (!control) return; + if (control->connection) { + mux_close_connection(control->iphone, control->connection); + } + + if (control->ssl_session) free(control->ssl_session); + free(control); +} + + +int lockdownd_recv(lockdownd_client *control, char **dump_data) { + char *receive; + uint32 datalen = 0, bytes = 0; + + if (!control->in_SSL) bytes = mux_recv(control->iphone, control->connection, &datalen, sizeof(datalen)); + else bytes = gnutls_record_recv(*control->ssl_session, &datalen, sizeof(datalen)); + datalen = ntohl(datalen); + + receive = (char*)malloc(sizeof(char) * datalen); + if (!control->in_SSL) bytes = mux_recv(control->iphone, control->connection, receive, datalen); + else bytes = gnutls_record_recv(*control->ssl_session, receive, datalen); + *dump_data = receive; + return bytes; +} + +int lockdownd_send(lockdownd_client *control, char *raw_data, uint32 length) { + char *real_query; + int bytes; + + real_query = (char*)malloc(sizeof(char) * (length+4)); + length = htonl(length); + memcpy(real_query, &length, sizeof(length)); + memcpy(real_query+4, raw_data, ntohl(length)); + if (!control->in_SSL) bytes = mux_send(control->iphone, control->connection, real_query, ntohl(length)+sizeof(length)); + else gnutls_record_send(*control->ssl_session, real_query, ntohl(length)+sizeof(length)); + return bytes; +} + +int lockdownd_hello(lockdownd_client *control) { + xmlDocPtr plist = new_plist(); + xmlNode *dict, *key; + char **dictionary; + int bytes = 0, i = 0; + + dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); + key = add_key_str_dict_element(plist, dict, "Request", "QueryType", 1); + char *XML_content; + uint32 length; + + xmlDocDumpMemory(plist, &XML_content, &length); + + bytes = lockdownd_send(control, XML_content, length); + + xmlFree(XML_content); + xmlFreeDoc(plist); plist = NULL; + + bytes = lockdownd_recv(control, &XML_content); + + plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0); + if (!plist) return 0; + dict = xmlDocGetRootElement(plist); + for (dict = dict->children; dict; dict = dict->next) { + if (!xmlStrcmp(dict->name, "dict")) break; + } + if (!dict) return 0; + + dictionary = read_dict_element_strings(dict); + xmlFreeDoc(plist); + free(XML_content); + + for (i = 0; strcmp(dictionary[i], ""); i+=2) { + if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) { + free_dictionary(dictionary); + return 1; + } + } + + free_dictionary(dictionary); + return 0; +} + +int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID) { + xmlDocPtr plist = new_plist(); + xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); + xmlNode *key; + char *what2send = NULL, **dictionary = NULL; + uint32 len = 0, bytes = 0, return_me = 0, i = 0; + // end variables + + key = add_key_str_dict_element(plist, dict, "HostID", HostID, 1); + if (!key) { + if (debug) printf("Couldn't add a key.\n"); + xmlFreeDoc(plist); + return 0; + } + key = add_key_str_dict_element(plist, dict, "Request", "StartSession", 1); + if (!key) { + if (debug) printf("Couldn't add a key.\n"); + xmlFreeDoc(plist); + return 0; + } + + xmlDocDumpMemory(plist, &what2send, &len); + bytes = lockdownd_send(control, what2send, len); + + xmlFree(what2send); + xmlFreeDoc(plist); + + if (bytes > 0) { + len = lockdownd_recv(control, &what2send); + plist = xmlReadMemory(what2send, len, NULL, NULL, 0); + dict = xmlDocGetRootElement(plist); + for (dict = dict->children; dict; dict = dict->next) { + if (!xmlStrcmp(dict->name, "dict")) break; + } + dictionary = read_dict_element_strings(dict); + xmlFreeDoc(plist); + free(what2send); + for (i = 0; strcmp(dictionary[i], ""); i+=2) { + if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) { + // Set up GnuTLS... + gnutls_certificate_credentials_t xcred; + + if (debug) printf("We started the session OK, now trying GnuTLS\n"); + errno = 0; + gnutls_global_init(); + gnutls_certificate_allocate_credentials(&xcred); + gnutls_certificate_set_x509_trust_file(xcred, "hostcert.pem", GNUTLS_X509_FMT_PEM); + gnutls_init(control->ssl_session, GNUTLS_CLIENT); + if ((return_me = gnutls_priority_set_direct(*control->ssl_session, "NORMAL:+VERS-SSL3.0", NULL)) < 0) { + printf("oops? bad options?\n"); + gnutls_perror(return_me); + return 0; + } + gnutls_credentials_set(*control->ssl_session, GNUTLS_CRD_CERTIFICATE, xcred); // this part is killing me. + + if (debug) printf("GnuTLS step 1...\n"); + gnutls_transport_set_ptr(*control->ssl_session, (gnutls_transport_ptr_t) control); + if (debug) printf("GnuTLS step 2...\n"); + gnutls_transport_set_push_function(*control->ssl_session, (gnutls_push_func)&lockdownd_secuwrite); + if (debug) printf("GnuTLS step 3...\n"); + gnutls_transport_set_pull_function(*control->ssl_session, (gnutls_pull_func)&lockdownd_securead); + if (debug) printf("GnuTLS step 4 -- now handshaking...\n"); + + if (errno && debug) printf("WARN: errno says %s before handshake!\n", strerror(errno)); + return_me = gnutls_handshake(*control->ssl_session); + if (debug) printf("GnuTLS handshake done...\n"); + + free_dictionary(dictionary); + + if (return_me != GNUTLS_E_SUCCESS) { + if (debug) printf("GnuTLS reported something wrong.\n"); + gnutls_perror(return_me); + if (debug) printf("oh.. errno says %s\n", strerror(errno)); + return 0; + } else { + control->in_SSL = 1; + return 1; + } + } + } + + if (debug) { + printf("Apparently failed negotiating with lockdownd.\n"); + printf("Responding dictionary: \n"); + for (i = 0; strcmp(dictionary[i], ""); i+=2) { + printf("\t%s: %s\n", dictionary[i], dictionary[i+1]); + } + } + + free_dictionary(dictionary); + return 0; + } else { + if (debug) printf("Didn't get enough bytes.\n"); + return 0; + } +} + +ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length) { + int bytes = 0; + lockdownd_client *control; + control = (lockdownd_client*)transport; + if (debug) printf("lockdownd_secuwrite() called\n"); + if (debug) printf("pre-send\nlength = %i\n", length); + bytes = mux_send(control->iphone, control->connection, buffer, length); + if (debug) printf("post-send\nsent %i bytes\n", bytes); + return bytes; +} + +ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length) { + int bytes = 0, pos_start_fill = 0; + char *hackhackhack = NULL; + lockdownd_client *control; + control = (lockdownd_client*)transport; + if (debug) printf("lockdownd_securead() called\nlength = %i\n", length); + // Buffering hack! Throw what we've got in our "buffer" into the stream first, then get more. + if (control->gtls_buffer_hack_len > 0) { + if (length > control->gtls_buffer_hack_len) { // If it's asking for more than we got + length -= control->gtls_buffer_hack_len; // Subtract what we have from their requested length + pos_start_fill = control->gtls_buffer_hack_len; // set the pos to start filling at + memcpy(buffer, control->gtls_buffer_hack, control->gtls_buffer_hack_len); // Fill their buffer partially + free(control->gtls_buffer_hack); // free our memory, it's not chained anymore + control->gtls_buffer_hack_len = 0; // we don't have a hack buffer anymore + if (debug) printf("Did a partial fill to help quench thirst for data\n"); + } else if (length < control->gtls_buffer_hack_len) { // If it's asking for less... + control->gtls_buffer_hack_len -= length; // subtract what they're asking for + memcpy(buffer, control->gtls_buffer_hack, length); // fill their buffer + hackhackhack = (char*)malloc(sizeof(char) * control->gtls_buffer_hack_len); // strndup is NOT a good solution -- concatenates \0!!!! Anyway, make a new "hack" buffer. + memcpy(hackhackhack, control->gtls_buffer_hack+length, control->gtls_buffer_hack_len); // Move what's left into the new one + free(control->gtls_buffer_hack); // Free the old one + control->gtls_buffer_hack = hackhackhack; // And make it the new one. + hackhackhack = NULL; + if (debug) printf("Quenched the thirst for data; new hack length is %i\n", control->gtls_buffer_hack_len); + return length; // hand it over. + } else { // length == hack length + memcpy(buffer, control->gtls_buffer_hack, length); // copy our buffer into theirs + free(control->gtls_buffer_hack); // free our "obligation" + control->gtls_buffer_hack_len = 0; // free our "obligation" + if (debug) printf("Satiated the thirst for data; now we have to eventually receive again.\n"); + return length; // hand it over + } + } + // End buffering hack! + char *recv_buffer = (char*)malloc(sizeof(char) * (length * 400)); // ensuring nothing stupid happens + + if (debug) printf("pre-read\nclient wants %i bytes\n", length); + bytes = mux_recv(control->iphone, control->connection, recv_buffer, (length * 400)); + if (debug) printf("post-read\nwe got %i bytes\n", bytes); + if (bytes >= length) { + if (bytes > length) { + if (debug) printf("lockdownd_securead: Client deliberately read less data than was there; resorting to GnuTLS buffering hack.\n"); + if (!control->gtls_buffer_hack_len) { // if there's no hack buffer yet + //control->gtls_buffer_hack = strndup(recv_buffer+length, bytes-length); // strndup is NOT a good solution! + control->gtls_buffer_hack_len += bytes-length; + control->gtls_buffer_hack = (char*)malloc(sizeof(char) * control->gtls_buffer_hack_len); + memcpy(control->gtls_buffer_hack, recv_buffer+length, control->gtls_buffer_hack_len); + } else { // if there is. + control->gtls_buffer_hack = realloc(control->gtls_buffer_hack, control->gtls_buffer_hack_len + (bytes - length)); + memcpy(control->gtls_buffer_hack+control->gtls_buffer_hack_len, recv_buffer+length, bytes-length); + control->gtls_buffer_hack_len += bytes - length; + } + } + memcpy(buffer+pos_start_fill, recv_buffer, length); + free(recv_buffer); + if (bytes == length) { if (debug) printf("Returning how much we received.\n"); return bytes; } + else { if (debug) printf("Returning what they want to hear.\nHack length: %i\n", control->gtls_buffer_hack_len); return length; } + } + return bytes; +} + +int lockdownd_start_service(lockdownd_client *control, const char *service) { + if (!control) return 0; + if (!control->in_SSL && !lockdownd_start_SSL_session(control, "29942970-207913891623273984")) return 0; + + char *XML_query, **dictionary; + uint32 length, i = 0, port = 0; + uint8 result = 0; + + xmlDocPtr plist = new_plist(); + xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0); + xmlNode *key; + key = add_key_str_dict_element(plist, dict, "Request", "StartService", 1); + if (!key) { xmlFreeDoc(plist); return 0; } + key = add_key_str_dict_element(plist, dict, "Service", service, 1); + if (!key) { xmlFreeDoc(plist); return 0; } + + xmlDocDumpMemory(plist, &XML_query, &length); + + lockdownd_send(control, XML_query, length); + free(XML_query); + + length = lockdownd_recv(control, &XML_query); + + xmlFreeDoc(plist); + + if (length <= 0) return 0; + else { + plist = xmlReadMemory(XML_query, length, NULL, NULL, 0); + if (!plist) return 0; + dict = xmlDocGetRootElement(plist); + if (!dict) return 0; + for (dict = dict->children; dict; dict = dict->next) { + if (!xmlStrcmp(dict->name, "dict")) break; + } + + if (!dict) return 0; + dictionary = read_dict_element_strings(dict); + + for (i = 0; strcmp(dictionary[i], ""); i+=2) { + if (debug) printf("lockdownd_start_service() dictionary %s: %s\n", dictionary[i], dictionary[i+1]); + + if (!xmlStrcmp(dictionary[i], "Port")) { + port = atoi(dictionary[i+1]); + if (debug) printf("lockdownd_start_service() atoi'd port: %i\n", port); + } + + if (!xmlStrcmp(dictionary[i], "Result")) { + if (!xmlStrcmp(dictionary[i+1], "Success")) { + result = 1; + } + } + } + + if (debug) { + printf("lockdownd_start_service(): DATA RECEIVED:\n\n"); + fwrite(XML_query, 1, length, stdout); + printf("end data received by lockdownd_start_service()\n"); + } + + free(XML_query); + xmlFreeDoc(plist); + free_dictionary(dictionary); + if (port && result) return port; + else return 0; + } + + return 0; +} + diff --git a/lockdown.h b/lockdown.h new file mode 100644 index 0000000..0acd624 --- /dev/null +++ b/lockdown.h @@ -0,0 +1,36 @@ +/* + * lockdown.h + * Defines lockdown stuff, like the client struct. + */ + +#ifndef LOCKDOWND_H +#define LOCKDOWND_H + +#include "plist.h" + +#include +#include + +typedef struct { + usbmux_tcp_header *connection; + gnutls_session_t *ssl_session; + iPhone *iphone; + int in_SSL; + char *gtls_buffer_hack; + int gtls_buffer_hack_len; +} lockdownd_client; + +lockdownd_client *new_lockdownd_client(iPhone *phone); +int lockdownd_hello(lockdownd_client *control); +int lockdownd_recv(lockdownd_client *control, char **dump_data); +int lockdownd_send(lockdownd_client *control, char *raw_data, uint32 length); +void lockdownd_close(lockdownd_client *control); + +// SSL functions +int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID); +ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length); +ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length); + +// Higher-level lockdownd stuff +int lockdownd_start_service(lockdownd_client *control, const char *service); +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..4cde3d9 --- /dev/null +++ b/main.c @@ -0,0 +1,84 @@ +/* + * libiphone main.c written by FxChiP + * With much help from Daniel Brownlees + */ + +#include +#include +#include +#include + +#include "usbmux.h" +#include "iphone.h" + +#include +#include +#include "plist.h" +#include "lockdown.h" +#include "AFC.h" + +int debug = 1; + +int main(int argc, char *argv[]) { + iPhone *phone = get_iPhone(); + if (argc > 1 && !strcasecmp(argv[1], "--debug")) debug = 1; + else debug = 0; + char *response = (char*)malloc(sizeof(char) * 2048); + int bytes = 0, port = 0, i = 0; + if (phone) printf("I got a phone.\n"); + else { printf("oops\n"); return -1; } + + lockdownd_client *control = new_lockdownd_client(phone); + if (!lockdownd_hello(control)) { + printf("Something went wrong in the lockdownd client, go take a look.\n"); + } else { + printf("We said hello. :)\n"); + } + + printf("Now starting SSL.\n"); + if (!lockdownd_start_SSL_session(control, "29942970-207913891623273984")) { + printf("Error happened in GnuTLS...\n"); + } else { + printf("... we're in SSL with the phone... !?\n"); + } + port = lockdownd_start_service(control, "com.apple.afc"); + if (port) { + printf("Start Service successful -- connect on port %i\n", port); + AFClient *afc = afc_connect(phone, 3432, port); + if (afc) { + char **dirs; + dirs = afc_get_dir_list(afc, "/eafaedf"); + if (!dirs) dirs = afc_get_dir_list(afc, "/"); + printf("Directory time.\n"); + for (i = 0; strcmp(dirs[i], ""); i++) { + printf("/%s\n", dirs[i]); + } + + free_dictionary(dirs); + AFCFile *my_file = afc_open_file(afc, "/iTunesOnTheGoPlaylist.plist", AFC_FILE_READ); + if (my_file) { + printf("A file size: %i\n", my_file->size); + char *file_data = (char*)malloc(sizeof(char) * my_file->size); + bytes = afc_read_file(afc, my_file, file_data, my_file->size); + if (bytes >= 0) { + printf("The file's data:\n"); + fwrite(file_data, 1, bytes, stdout); + } + printf("\nClosing my file.\n"); + afc_close_file(afc, my_file); + free(my_file); + free(file_data); + } else printf("couldn't open a file\n"); + } + afc_disconnect(afc); + } else { + printf("Start service failure.\n"); + } + + printf("All done.\n"); + + free_iPhone(phone); + + return 0; +} + diff --git a/main.h b/main.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/main.h @@ -0,0 +1 @@ + diff --git a/plist.c b/plist.c new file mode 100644 index 0000000..cbd6302 --- /dev/null +++ b/plist.c @@ -0,0 +1,91 @@ +/* + * plist.c + * Builds plist XML structures. + * Written by FxChiP + */ + +#include +#include +#include +#include "plist.h" + +const char *plist_base = "\n\ +\n\ +\n\ +\0"; + +xmlDocPtr new_plist() { + char *plist = strdup(plist_base); + xmlDocPtr plist_xml = xmlReadMemory(plist, strlen(plist), NULL, NULL, 0); + if (!plist_xml) return NULL; + free(plist); + return plist_xml; +} + +void free_plist(xmlDocPtr plist) { + if (!plist) return; + xmlFreeDoc(plist); +} + +xmlNode *add_child_to_plist(xmlDocPtr plist, const char *name, const char *content, xmlNode *to_node, int depth) { + if (!plist) return NULL; + int i = 0; + xmlNode *child; + if (!to_node) to_node = xmlDocGetRootElement(plist); + for (i = 0; i < depth; i++) { + xmlNodeAddContent(to_node, "\t"); + } + child = xmlNewChild(to_node, NULL, name, content); + xmlNodeAddContent(to_node, "\n"); + return child; +} + +xmlNode *add_key_str_dict_element(xmlDocPtr plist, xmlNode *dict, const char *key, const char *value, int depth) { + xmlNode *keyPtr; + keyPtr = add_child_to_plist(plist, "key", key, dict, depth); + add_child_to_plist(plist, "string", value, dict, depth); + return keyPtr; +} + +char **read_dict_element_strings(xmlNode *dict) { + // reads a set of keys and strings into an array where each even number is a key and odd numbers are values. + // if the odd number is \0, that's the end of the list. + char **return_me = NULL, **old = NULL; + int current_length = 0; + int current_pos = 0; + xmlNode *dict_walker; + + for (dict_walker = dict->children; dict_walker; dict_walker = dict_walker->next) { + if (!xmlStrcmp(dict_walker->name, "key")) { + current_length += 2; + old = return_me; + return_me = realloc(return_me, sizeof(char*) * current_length); + if (!return_me) { + free(old); + return NULL; + } + return_me[current_pos++] = xmlNodeGetContent(dict_walker); + return_me[current_pos++] = xmlNodeGetContent(dict_walker->next->next); + } + } + + // one last thing... + old = return_me; + return_me = realloc(return_me, sizeof(char*) * current_length+1); + return_me[current_pos] = strdup(""); + + return return_me; +} + +void free_dictionary(char **dictionary) { + if (!dictionary) return; + int i = 0; + + for (i = 0; strcmp(dictionary[i], ""); i++) { + free(dictionary[i]); + } + + free(dictionary[i]); + free(dictionary); +} + diff --git a/plist.h b/plist.h new file mode 100644 index 0000000..1f18eb4 --- /dev/null +++ b/plist.h @@ -0,0 +1,17 @@ +/* plist.h + * contains structures and the like for plists + * written by fxchip + */ + +#ifndef PLIST_H +#define PLIST_H + +#include +#include + +xmlNode *add_key_str_dict_element(xmlDocPtr plist, xmlNode *dict, const char *key, const char *value, int depth); +xmlNode *add_child_to_plist(xmlDocPtr plist, const char *name, const char *content, xmlNode *to_node, int depth); +void free_plist(xmlDocPtr plist); +xmlDocPtr new_plist(); +void free_dictionary(char **dictionary); +#endif diff --git a/usbmux.c b/usbmux.c new file mode 100644 index 0000000..8c5fc34 --- /dev/null +++ b/usbmux.c @@ -0,0 +1,198 @@ + +#include +#include +#include +#include +#include + +#include "usbmux.h" + +extern int debug; + +usbmux_tcp_header *new_mux_packet(uint16 s_port, uint16 d_port) { + usbmux_tcp_header *conn = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header)); + conn->type = htonl(6); + conn->length = 28; + conn->sport = htons(s_port); + conn->dport = htons(d_port); + conn->scnt = 0; + conn->ocnt = 0; + conn->offset = 0x50; + conn->window = htons(0x0200); + conn->nullnull = 0x0000; + conn->length16 = 28; + return conn; +} + +usbmux_version_header *version_header() { + usbmux_version_header *version = (usbmux_version_header*)malloc(sizeof(usbmux_version_header)); + version->type = 0; + version->length = htonl(20); + version->major = htonl(1); + version->minor = 0; + version->allnull = 0; + return version; +} + +/* mux_connect(phone, s_port, d_port) + * This is a higher-level USBMuxTCP-type function. + * phone: the iPhone to initialize a connection on. + * s_port: the source port + * d_port: the destination port -- 0xf27e for lockdownd. + * Initializes a connection on phone, with source port s_port and destination port d_port + * + * Returns a mux TCP header for the connection which is used for tracking and data transfer. + */ + +usbmux_tcp_header *mux_connect(iPhone *phone, uint16 s_port, uint16 d_port) { + if (!phone || !s_port || !d_port) return NULL; + int bytes = 0; + // Initialize connection stuff + usbmux_tcp_header *new_connection; + new_connection = new_mux_packet(s_port, d_port); + usbmux_tcp_header *response; + response = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header)); + // blargg + if (new_connection) { + new_connection->tcp_flags = 0x02; + new_connection->length = htonl(new_connection->length); + new_connection->length16 = htons(new_connection->length16); + + if (send_to_phone(phone, (char*)new_connection, sizeof(*new_connection)) >= 0) { + bytes = recv_from_phone(phone, (char*)response, sizeof(*response)); + if (response->tcp_flags != 0x12) return NULL; + else { + new_connection->tcp_flags = 0x10; + new_connection->scnt = 1; + new_connection->ocnt = 1; + return new_connection; + } + } else { + return NULL; + } + } + + // if we get to this point it's probably bad + return NULL; +} + +/* mux_close_connection(phone, connection) + * This is a higher-level USBmuxTCP-type function. + * phone: the iPhone to close a connection with. + * connection: the connection to close. + * + * Doesn't return anything; WILL FREE THE CONNECTION'S MEMORY!!! + */ +void mux_close_connection(iPhone *phone, usbmux_tcp_header *connection) { + if (!phone || !connection) return; + + connection->tcp_flags = 0x04; + connection->scnt = htonl(connection->scnt); + connection->ocnt = htonl(connection->ocnt); + int bytes = 0; + + bytes = usb_bulk_write(phone->device, BULKOUT, (char*)connection, sizeof(*connection), 800); + bytes = usb_bulk_read(phone->device, BULKIN, (char*)connection, sizeof(*connection), 800); + + free(connection); +} + +/* mux_send(phone, connection, data, datalen) + * This is a higher-level USBMuxTCP-like function. + * phone: the iPhone to send to. + * connection: the connection we're sending data on. + * data: a pointer to the data to send. + * datalen: how much data we're sending. + * + * Returns number of bytes sent, minus the header (28), or -1 on error. + */ +int mux_send(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen) { + if (!phone || !connection || !data || datalen == 0) return -1; + // connection->scnt and connection->ocnt should already be in host notation... + // we don't need to change them juuuust yet. + int bytes = 0; + if (debug) printf("mux_send(): client wants to send %i bytes\n", datalen); + char *buffer = (char*)malloc(sizeof(*connection) + datalen + 2); // allow 2 bytes of safety padding + // Set the length and pre-emptively htonl/htons it + connection->length = htonl(sizeof(*connection) + datalen); + connection->length16 = htons(sizeof(*connection) + datalen); + + // Put scnt and ocnt into big-endian notation + connection->scnt = htonl(connection->scnt); + connection->ocnt = htonl(connection->ocnt); + // Concatenation of stuff in the buffer. + memcpy(buffer, connection, sizeof(*connection)); + memcpy(buffer+sizeof(*connection)/*+sizeof(datalen)*/, data, datalen); + + // We have a buffer full of data, we should now send it to the phone. + if (debug) printf("actually sending %i bytes of data at %x\n", sizeof(*connection)+datalen, buffer); + + + bytes = send_to_phone(phone, buffer, sizeof(*connection)+datalen); + + // Now that we've sent it off, we can clean up after our sloppy selves. + free(buffer); + + // Re-calculate scnt and ocnt + connection->scnt = ntohl(connection->scnt) + datalen; + connection->ocnt = ntohl(connection->ocnt); + + // Revert lengths + connection->length = ntohl(connection->length); + connection->length16 = ntohs(connection->length16); + + // Now return the bytes. + if (bytes < sizeof(*connection)+datalen) { + return -1; // blah + } else { + return bytes - 28; // actual length sent. :/ + } + + return bytes; // or something +} + +/* mux_recv(phone, connection, data, datalen) + * This is a higher-level USBMuxTCP-like function + * phone: the phone to receive data from. + * connection: the connection to receive data on. + * data: where to put the data we receive. + * datalen: how much data to read. + * + * Returns: how many bytes were read, or -1 if something bad happens. + */ + +int mux_recv(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen) { + char *buffer = (char*)malloc(sizeof(*connection) + sizeof(datalen) + datalen); + int bytes = 0, my_datalen = 0; + if (debug) printf("mux_recv: datalen == %i\n", datalen); + bytes = recv_from_phone(phone, buffer, sizeof(*connection) + datalen); + if (debug) printf("mux_recv: bytes == %i\n", bytes); + if (bytes < datalen) { + if (bytes < 28) { + // if they didn't do that annoying thing, something else mighta happened. + if (debug) printf("mux_recv: bytes too low anyway!\n"); + free(buffer); + return -1; + } else if (bytes == 28) { // no data... + free(buffer); + return 0; + } else { // bytes > 28 + my_datalen = ntohl(buffer[4]) - 28; + connection->ocnt += my_datalen; + memcpy(data, buffer+28, bytes - 28); + free(buffer); + if (debug) printf("mux_recv: bytes received: %i\n", bytes - 28); + return bytes - 28; + } + } else {// all's good, they didn't do anything bonky. + my_datalen = ntohl(buffer[4]) - 28; + connection->ocnt += my_datalen; + memcpy(data, buffer+28, datalen); + free(buffer); + if (debug) printf("mux_recv: bytes received: %i\n", bytes - 28); + return bytes - 28; + } + + return bytes; +} + diff --git a/usbmux.h b/usbmux.h new file mode 100644 index 0000000..921f4b7 --- /dev/null +++ b/usbmux.h @@ -0,0 +1,39 @@ + +#include +#include +#include + + +#ifndef USBMUX_H +#define USBMUX_H + +#ifndef IPHONE_H +#include "iphone.h" +#endif + +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint8_t uint8; + + +typedef struct { + uint32 type, length; + uint16 sport, dport; + uint32 scnt, ocnt; + uint8 offset, tcp_flags; + uint16 window, nullnull, length16; +} usbmux_tcp_header; + +usbmux_tcp_header *new_mux_packet(uint16 s_port, uint16 d_port); + +typedef struct { + uint32 type, length, major, minor, allnull; +} usbmux_version_header; + +usbmux_version_header *version_header(); + +usbmux_tcp_header *mux_connect(iPhone *phone, uint16 s_port, uint16 d_port); +void mux_close_connection(iPhone *phone, usbmux_tcp_header *connection); +int mux_send(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen); +int mux_recv(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen); +#endif -- cgit v1.1-32-gdbae