int mkdir_with_parents(const char *dir, int mode) { if (!dir) return -1; if (__mkdir(dir, mode) == 0) { return 0; } else { if (errno == EEXIST) { return 0; } else if (errno == ENOENT) { // ignore } else { return -1; } } int res; char *parent = strdup(dir); char *parentdir = dirname(parent); if (parentdir && (strcmp(parentdir, ".") != 0) && (strcmp(parentdir, dir) != 0)) { res = mkdir_with_parents(parentdir, mode); } else { res = -1; } free(parent); if (res == 0) { mkdir_with_parents(dir, mode); } return res; }
/* Trap attempts to create directories outside the sandbox. */ int mkdir(const char* path, mode_t mode) { #define __mkdir(x,y) syscall(SYS_mkdir, (x), (y)) int result = 0; int isInSandbox = __darwintrace_is_in_sandbox(path, 0); if (isInSandbox == 1) { debug_printf("darwintrace: mkdir was allowed at %s\n", path); } else if (isInSandbox == 0) { /* outside sandbox, but sandbox is defined: forbid */ /* only consider directories that do not exist. */ struct stat theInfo; int err; err = lstat(path, &theInfo); if ((err == -1) && (errno == ENOENT)) { debug_printf("darwintrace: mkdir was forbidden at %s\n", path); errno = EACCES; result = -1; } /* otherwise, mkdir will do nothing (directory exists) or fail (another error) */ } if (result == 0) { result = __mkdir(path, mode); } return result; }
static uint8_t __mkbasedir(const char *file_name) { char *dir_name; uint8_t ret; dir_name = strdup(file_name); dir_name = dirname(dir_name); ret = __mkdir(dir_name); free(dir_name); return ret; }
static int load_version_data(struct idevicerestore_client_t* client) { if (!client) { return -1; } struct stat fst; int cached = 0; if ((stat(VERSION_XML, &fst) < 0) || ((time(NULL)-86400) > fst.st_mtime)) { char tmpf[256]; tmpf[0] = '\0'; if (!tmpnam(tmpf) || (tmpf[0] == '\0')) { error("ERROR: Could not get temporary filename\n"); return -1; } if (download_to_file("http://ax.itunes.apple.com/check/version", tmpf) == 0) { __mkdir("cache", 0755); remove(VERSION_XML); if (rename(tmpf, VERSION_XML) < 0) { error("ERROR: Could not update '" VERSION_XML "'\n"); } else { info("NOTE: Updated version data.\n"); } } } else { cached = 1; } char *verbuf = NULL; size_t verlen = 0; read_file(VERSION_XML, (void**)&verbuf, &verlen); if (!verbuf) { error("ERROR: Could not load '" VERSION_XML "'.\n"); return -1; } client->version_data = NULL; plist_from_xml(verbuf, verlen, &client->version_data); if (!client->version_data) { error("ERROR: Cannot parse plist data from '" VERSION_XML "'.\n"); return -1; } if (cached) { info("NOTE: using cached version data\n"); } return 0; }
MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr) { #ifdef _WIN_ALL BOOL RetCode; if (WinNT() && NameW!=NULL && *NameW!=0) RetCode=CreateDirectoryW(NameW,NULL); else if (Name!=NULL) RetCode=CreateDirectoryA(Name,NULL); else return(MKDIR_BADPATH); if (RetCode!=0) // Non-zero return code means success for CreateDirectory. { if (SetAttr) SetFileAttr(Name,NameW,Attr); return(MKDIR_SUCCESS); } int ErrCode=GetLastError(); if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) return(MKDIR_BADPATH); return(MKDIR_ERROR); #else // No Unicode in the rest of function, so Name must be not NULL. if (Name==NULL) return(MKDIR_BADPATH); #endif #ifdef _EMX #ifdef _DJGPP if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0) #else if (__mkdir(Name)==0) #endif { if (SetAttr) SetFileAttr(Name,NameW,Attr); return(MKDIR_SUCCESS); } return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); #endif #ifdef _UNIX mode_t uattr=SetAttr ? (mode_t)Attr:0777; int ErrCode=mkdir(Name,uattr); if (ErrCode==-1) return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); return(MKDIR_SUCCESS); #endif }
/* We need to use both the URL and the client_addr for checking, if two clients retrieve the same file, it would clash. However, this is pretty inefficent - FIXME (i.e. make me deal with multiple downloads of the same URL). */ const char * const update_file(hash_table *hash, const char *modname,const char * const tempdir,const char * const url,const char * const client_addr,const char * const buf,int len,long int **lastscannedsize) { FILE* f; char *key, *tmpfile; tmpfile_hash_link* lp; int keylen; // first create the key keylen=strnlen(url, 1024) + strnlen(client_addr, 1024); if((key=(char*) xmalloc (sizeof(char) * (keylen + 1)))==NULL) return NULL; strncpy(key, url, keylen); strncat(key, client_addr, keylen); // do we already know that combination? if((lp=hash_lookup(hash, key))==NULL) { // nope, create a new temp file if(__is_dir(tempdir)==0) __mkdir(tempdir); tmpfile=tempnam(tempdir, "file"); if(tmpfile==NULL) { debug(93,3) ("%s: error: could not create temporary file name\n", modname); xfree(key); return NULL; } // insert into the hash lp=xmalloc(sizeof(tmpfile_hash_link)); lp->hash.key=xstrdup(key); lp->filename=xstrdup(tmpfile); lp->lastscannedsize=0; xfree(tmpfile); hash_join(hash, &lp->hash); } xfree(key); // here we are sure that the file name has been set (in lp->filename) // care about file permissions! umask(0077); f=fopen(lp->filename, "ab"); if(f==NULL) return NULL; if(buf[0]!=' ') fwrite(buf, 1, len, f); fclose(f); if(lastscannedsize !=NULL) *lastscannedsize=&lp->lastscannedsize; return lp->filename; }
MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,uint Attr) { #ifdef _WIN_32 int Success; #if !defined(_XBOX) && !defined(_LINUX) if (WinNT() && NameW!=NULL && *NameW!=0) Success=CreateDirectoryW(NameW,NULL); else #endif Success=CreateDirectory(Name,NULL); if (Success) { SetFileAttr(Name,NameW,Attr); return(MKDIR_SUCCESS); } int ErrCode=GetLastError(); if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) return(MKDIR_BADPATH); return(MKDIR_ERROR); #endif #ifdef _EMX #ifdef _DJGPP if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0) #else if (__mkdir(Name)==0) #endif { SetFileAttr(Name,NameW,Attr); return(MKDIR_SUCCESS); } return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); #endif #ifdef _UNIX int prevmask=umask(0); int ErrCode=Name==NULL ? -1:mkdir(Name,(mode_t)Attr); umask(prevmask); if (ErrCode==-1) return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); return(MKDIR_SUCCESS); #endif }
MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr) { #ifdef _WIN_32 int Success; if (WinNT() && NameW!=NULL && *NameW!=0) Success=CreateDirectoryW(NameW,NULL); else Success=CreateDirectory(Name,NULL); if (Success) { if (SetAttr) SetFileAttr(Name,NameW,Attr); return(MKDIR_SUCCESS); } int ErrCode=GetLastError(); if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) return(MKDIR_BADPATH); return(MKDIR_ERROR); #endif #ifdef _EMX #ifdef _DJGPP if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0) #else if (__mkdir(Name)==0) #endif { if (SetAttr) SetFileAttr(Name,NameW,Attr); return(MKDIR_SUCCESS); } return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); #endif #ifdef _UNIX mode_t uattr=SetAttr ? (mode_t)Attr:0777; int ErrCode=Name==NULL ? -1:mkdir(Name,uattr); if (ErrCode==-1) return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); return(MKDIR_SUCCESS); #endif }
int mkdir_with_parents(const char *dir, int mode) { if (!dir) return -1; if (__mkdir(dir, mode) == 0) { return 0; } else { if (errno == EEXIST) return 0; } int res; char *parent = strdup(dir); parent = dirname(parent); if (parent) { res = mkdir_with_parents(parent, mode); } else { res = -1; } free(parent); if (res == 0) { mkdir_with_parents(dir, mode); } return res; }
/* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in at least X_SUFFIX_LEN "X"s, possibly with a suffix). The name constructed does not exist at the time of the call to this function. TMPL is overwritten with the result. KIND may be one of: __GT_NOCREATE: simply verify that the name does not exist at the time of the call. __GT_FILE: create the file using open(O_CREAT|O_EXCL) and return a read-write fd. The file is mode 0600. __GT_DIR: create a directory, which will be mode 0700. We use a clever algorithm to get hard-to-predict names. */ int gen_tempname_len (char *tmpl, int suffixlen, int flags, int kind, size_t x_suffix_len) { size_t len; char *XXXXXX; unsigned int count; int fd = -1; int save_errno = errno; struct_stat64 st; struct randint_source *rand_src; /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6. It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3) fail to give the system administrator the chance to remove the problems. This value requires that X_SUFFIX_LEN be at least 3. */ #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX unsigned int attempts = TMP_MAX; #else unsigned int attempts = ATTEMPTS_MIN; #endif len = strlen (tmpl); if (len < x_suffix_len + suffixlen || ! check_x_suffix (&tmpl[len - x_suffix_len - suffixlen], x_suffix_len)) { __set_errno (EINVAL); return -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - x_suffix_len - suffixlen]; /* Get some more or less random data. */ rand_src = randint_all_new (NULL, x_suffix_len); if (! rand_src) return -1; for (count = 0; count < attempts; ++count) { size_t i; for (i = 0; i < x_suffix_len; i++) XXXXXX[i] = letters[randint_genmax (rand_src, sizeof letters - 2)]; switch (kind) { case __GT_FILE: fd = __open (tmpl, (flags & ~O_ACCMODE) | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); break; case __GT_DIR: fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); break; case __GT_NOCREATE: /* This case is backward from the other three. This function succeeds if __xstat fails because the name does not exist. Note the continue to bypass the common logic at the bottom of the loop. */ if (__lxstat64 (_STAT_VER, tmpl, &st) < 0) { if (errno == ENOENT) { __set_errno (save_errno); fd = 0; goto done; } else { /* Give up now. */ fd = -1; goto done; } } continue; default: assert (! "invalid KIND in __gen_tempname"); abort (); } if (fd >= 0) { __set_errno (save_errno); goto done; } else if (errno != EEXIST) { fd = -1; goto done; } } randint_all_free (rand_src); /* We got out of the loop because we ran out of combinations to try. */ __set_errno (EEXIST); return -1; done: { int saved_errno = errno; randint_all_free (rand_src); __set_errno (saved_errno); } return fd; }
/* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed does not exist at the time of the call to __gen_tempname. TMPL is overwritten with the result. KIND may be one of: __GT_NOCREATE: simply verify that the name does not exist at the time of the call. __GT_FILE: create the file using open(O_CREAT|O_EXCL) and return a read-write fd. The file is mode 0600. __GT_BIGFILE: same as __GT_FILE but use open64(). __GT_DIR: create a directory, which will be mode 0700. We use a clever algorithm to get hard-to-predict names. */ int __gen_tempname (char *tmpl, int kind) { int len; char *XXXXXX; static uint64_t value; uint64_t random_time_bits; unsigned int count; int fd = -1; int save_errno = errno; struct_stat64 st; /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6. It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3) fail to give the system administrator the chance to remove the problems. */ unsigned int attempts_min = 62 * 62 * 62; /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ unsigned int attempts = attempts_min < TMP_MAX ? TMP_MAX : attempts_min; len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { __set_errno (EINVAL); return -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get some more or less random data. */ #ifdef RANDOM_BITS RANDOM_BITS (random_time_bits); #else # if HAVE_GETTIMEOFDAY || _LIBC { struct timeval tv; __gettimeofday (&tv, NULL); random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; } # else random_time_bits = time (NULL); # endif #endif value += random_time_bits ^ __getpid (); for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; switch (kind) { case __GT_FILE: fd = __open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); break; case __GT_BIGFILE: fd = __open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); break; case __GT_DIR: fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); break; case __GT_NOCREATE: /* This case is backward from the other three. __gen_tempname succeeds if __xstat fails because the name does not exist. Note the continue to bypass the common logic at the bottom of the loop. */ if (__lxstat64 (_STAT_VER, tmpl, &st) < 0) { if (errno == ENOENT) { __set_errno (save_errno); return 0; } else /* Give up now. */ return -1; } continue; default: assert (! "invalid KIND in __gen_tempname"); } if (fd >= 0) { __set_errno (save_errno); return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ __set_errno (EEXIST); return -1; }
/* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed does not exist at the time of the call to __gen_tempname. TMPL is overwritten with the result. KIND may be one of: __GT_NOCREATE: simply verify that the name does not exist at the time of the call. __GT_FILE: create the file using open(O_CREAT|O_EXCL) and return a read-write fd. The file is mode 0600. __GT_BIGFILE: same as __GT_FILE but use open64(). __GT_DIR: create a directory, which will be mode 0700. We use a clever algorithm to get hard-to-predict names. */ int __gen_tempname (char *tmpl, int kind) { int len; char *XXXXXX; static uint64_t value; uint64_t random_time_bits; int count, fd = -1; int save_errno = errno; struct_stat64 st; len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { __set_errno (EINVAL); return -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get some more or less random data. */ #ifdef RANDOM_BITS RANDOM_BITS (random_time_bits); #else # if HAVE_GETTIMEOFDAY || _LIBC { struct timeval tv; __gettimeofday (&tv, NULL); random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; } # else random_time_bits = time (NULL); # endif #endif value += random_time_bits ^ __getpid (); for (count = 0; count < TMP_MAX; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; switch (kind) { case __GT_FILE: fd = __open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); break; case __GT_BIGFILE: fd = __open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); break; case __GT_DIR: fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); break; case __GT_NOCREATE: /* This case is backward from the other three. __gen_tempname succeeds if __xstat fails because the name does not exist. Note the continue to bypass the common logic at the bottom of the loop. */ if (__lxstat64 (_STAT_VER, tmpl, &st) < 0) { if (errno == ENOENT) { __set_errno (save_errno); return 0; } else /* Give up now. */ return -1; } continue; default: assert (! "invalid KIND in __gen_tempname"); } if (fd >= 0) { __set_errno (save_errno); return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ __set_errno (EEXIST); return -1; }
static int try_dir (char *tmpl, void *flags) { return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); }
/* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed does not exist at the time of the call to __gen_tempname. TMPL is overwritten with the result. KIND is: __GT_DIR: create a directory, which will be mode 0700. We use a clever algorithm to get hard-to-predict names. */ static int gen_tempname (char *tmpl) { int len; char *XXXXXX; static uint64_t value; uint64_t random_time_bits; unsigned int count; int fd = -1; int save_errno = errno; /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6. It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3) fail to give the system administrator the chance to remove the problems. */ #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX unsigned int attempts = TMP_MAX; #else unsigned int attempts = ATTEMPTS_MIN; #endif len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { __set_errno (EINVAL); return -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get some more or less random data. */ #ifdef RANDOM_BITS RANDOM_BITS (random_time_bits); #else # if HAVE_GETTIMEOFDAY || _LIBC { struct timeval tv; __gettimeofday (&tv, NULL); random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; } # else random_time_bits = time (NULL); # endif #endif value += random_time_bits ^ __getpid (); for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); if (fd >= 0) { __set_errno (save_errno); return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ __set_errno (EEXIST); return -1; }
int main(int argc, char* argv[]) { int opt = 0; int optindex = 0; char* ipsw = NULL; char* uuid = NULL; int tss_enabled = 0; int shsh_only = 0; char* shsh_dir = NULL; use_apple_server=1; // create an instance of our context struct idevicerestore_client_t* client = (struct idevicerestore_client_t*) malloc(sizeof(struct idevicerestore_client_t)); if (client == NULL) { error("ERROR: Out of memory\n"); return -1; } memset(client, '\0', sizeof(struct idevicerestore_client_t)); while ((opt = getopt_long(argc, argv, "dhcesxtpi:u:", longopts, &optindex)) > 0) { switch (opt) { case 'h': usage(argc, argv); return 0; case 'd': client->flags |= FLAG_DEBUG; idevicerestore_debug = 1; break; case 'e': client->flags |= FLAG_ERASE; break; case 'c': client->flags |= FLAG_CUSTOM; break; case 's': use_apple_server=0; break; case 'x': client->flags |= FLAG_EXCLUDE; break; case 'i': if (optarg) { char* tail = NULL; client->ecid = strtoull(optarg, &tail, 16); if (tail && (tail[0] != '\0')) { client->ecid = 0; } if (client->ecid == 0) { error("ERROR: Could not parse ECID from '%s'\n", optarg); return -1; } } break; case 'u': uuid = optarg; break; case 't': shsh_only = 1; break; case 'p': client->flags |= FLAG_PWN; break; default: usage(argc, argv); return -1; } } if (((argc-optind) == 1) || (client->flags & FLAG_PWN)) { argc -= optind; argv += optind; ipsw = argv[0]; } else { usage(argc, argv); return -1; } if (client->flags & FLAG_DEBUG) { idevice_set_debug_level(1); irecv_set_debug_level(1); } client->uuid = uuid; client->ipsw = ipsw; // update version data (from cache, or apple if too old) load_version_data(client); // check which mode the device is currently in so we know where to start if (check_mode(client) < 0 || client->mode->index == MODE_UNKNOWN) { error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n"); return -1; } info("Found device in %s mode\n", client->mode->string); if (client->mode->index == MODE_WTF) { int cpid = 0; if (dfu_client_new(client) != 0) { error("ERROR: Could not open device in WTF mode\n"); return -1; } if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { error("ERROR: Could not get CPID for WTF mode device\n"); dfu_client_free(client); return -1; } char* s_wtfurl = NULL; plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL"); if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) { plist_get_string_val(wtfurl, &s_wtfurl); } if (!s_wtfurl) { info("Using hardcoded x12220000_5_Recovery.ipsw URL\n"); s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw"); } // make a local file name char* fnpart = strrchr(s_wtfurl, '/'); if (!fnpart) { fnpart = "x12220000_5_Recovery.ipsw"; } else { fnpart++; } struct stat fst; char wtfipsw[256]; sprintf(wtfipsw, "cache/%s", fnpart); if (stat(wtfipsw, &fst) != 0) { __mkdir("cache", 0755); download_to_file(s_wtfurl, wtfipsw); } char wtfname[256]; sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid); char* wtftmp = NULL; uint32_t wtfsize = 0; ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { error("ERROR: Could not extract WTF\n"); } else { if (dfu_send_buffer(client, wtftmp, wtfsize) != 0) { error("ERROR: Could not send WTF...\n"); } } dfu_client_free(client); sleep(1); free(wtftmp); client->mode = &idevicerestore_modes[MODE_DFU]; } // discover the device type if (check_device(client) < 0 || client->device->index == DEVICE_UNKNOWN) { error("ERROR: Unable to discover device type\n"); return -1; } info("Identified device as %s\n", client->device->product); if ((client->flags & FLAG_PWN) && (client->mode->index != MODE_DFU)) { error("ERROR: you need to put your device into DFU mode to pwn it.\n"); return -1; } if (client->flags & FLAG_PWN) { recovery_client_free(client); info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { return -1; } info("exploiting with limera1n...\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); return -1; } dfu_client_free(client); info("Device should be in pwned DFU state now.\n"); return 0; } if (client->mode->index == MODE_RESTORE) { if (restore_reboot(client) < 0) { error("ERROR: Unable to exit restore mode\n"); return -1; } } // extract buildmanifest plist_t buildmanifest = NULL; if (client->flags & FLAG_CUSTOM) { info("Extracting Restore.plist from IPSW\n"); if (ipsw_extract_restore_plist(ipsw, &buildmanifest) < 0) { error("ERROR: Unable to extract Restore.plist from %s\n", ipsw); return -1; } } else { info("Extracting BuildManifest from IPSW\n"); if (ipsw_extract_build_manifest(ipsw, &buildmanifest, &tss_enabled) < 0) { error("ERROR: Unable to extract BuildManifest from %s\n", ipsw); return -1; } } /* check if device type is supported by the given build manifest */ if (build_manifest_check_compatibility(buildmanifest, client->device->product) < 0) { error("ERROR: could not make sure this firmware is suitable for the current device. refusing to continue.\n"); return -1; } /* print iOS information from the manifest */ build_manifest_get_version_information(buildmanifest, &client->version, &client->build); info("Product Version: %s\n", client->version); info("Product Build: %s\n", client->build); if (client->flags & FLAG_CUSTOM) { /* prevent signing custom firmware */ tss_enabled = 0; info("Custom firmware requested. Disabled TSS request.\n"); } // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; if (client->flags & FLAG_CUSTOM) { build_identity = plist_new_dict(); { plist_t node; plist_t comp; plist_t info; plist_t manifest; info = plist_new_dict(); plist_dict_insert_item(info, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update")); plist_dict_insert_item(info, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)")); plist_dict_insert_item(build_identity, "Info", info); manifest = plist_new_dict(); char tmpstr[256]; char p_all_flash[128]; char lcmodel[8]; strcpy(lcmodel, client->device->model); int x = 0; while (lcmodel[x]) { lcmodel[x] = tolower(lcmodel[x]); x++; } sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production"); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/manifest"); // get all_flash file manifest char *files[16]; char *fmanifest = NULL; uint32_t msize = 0; if (ipsw_extract_to_memory(ipsw, tmpstr, &fmanifest, &msize) < 0) { error("ERROR: could not extract %s from IPSW\n", tmpstr); return -1; } char *tok = strtok(fmanifest, "\r\n"); int fc = 0; while (tok) { files[fc++] = strdup(tok); if (fc >= 16) { break; } tok = strtok(NULL, "\r\n"); } free(fmanifest); for (x = 0; x < fc; x++) { info = plist_new_dict(); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/"); strcat(tmpstr, files[x]); plist_dict_insert_item(info, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); const char* compname = get_component_name(files[x]); if (compname) { plist_dict_insert_item(manifest, compname, comp); if (!strncmp(files[x], "DeviceTree", 10)) { plist_dict_insert_item(manifest, "RestoreDeviceTree", plist_copy(comp)); } } else { error("WARNING: unhandled component %s\n", files[x]); plist_free(comp); } free(files[x]); files[x] = NULL; } // add iBSS sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE"); info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "iBSS", comp); // add iBEC sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE"); info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "iBEC", comp); // add kernel cache node = plist_dict_get_item(buildmanifest, "KernelCachesByTarget"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { char tt[4]; strncpy(tt, lcmodel, 3); tt[3] = 0; plist_t kdict = plist_dict_get_item(node, tt); if (kdict && (plist_get_node_type(kdict) == PLIST_DICT)) { plist_t kc = plist_dict_get_item(kdict, "Release"); if (kc && (plist_get_node_type(kc) == PLIST_STRING)) { info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_copy(kc)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "KernelCache", comp); plist_dict_insert_item(manifest, "RestoreKernelCache", plist_copy(comp)); } } } // add ramdisk node = plist_dict_get_item(buildmanifest, "RestoreRamDisks"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { plist_t rd = plist_dict_get_item(node, (client->flags & FLAG_ERASE) ? "User" : "Update"); if (rd && (plist_get_node_type(rd) == PLIST_STRING)) { info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_copy(rd)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "RestoreRamDisk", comp); } } // add OS filesystem node = plist_dict_get_item(buildmanifest, "SystemRestoreImages"); if (!node) { error("ERROR: missing SystemRestoreImages in Restore.plist\n"); } plist_t os = plist_dict_get_item(node, "User"); if (!os) { error("ERROR: missing filesystem in Restore.plist\n"); } else { info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_copy(os)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "OS", comp); } // finally add manifest plist_dict_insert_item(build_identity, "Manifest", manifest); } } else if (client->flags & FLAG_ERASE) { build_identity = build_manifest_get_build_identity(buildmanifest, 0); if (build_identity == NULL) { error("ERROR: Unable to find any build identities\n"); plist_free(buildmanifest); return -1; } } else { // loop through all build identities in the build manifest // and list the valid ones int i = 0; int valid_builds = 0; int build_count = build_manifest_get_identity_count(buildmanifest); for (i = 0; i < build_count; i++) { build_identity = build_manifest_get_build_identity(buildmanifest, i); valid_builds++; } } /* print information about current build identity */ build_identity_print_information(build_identity); /* retrieve shsh blobs if required */ if (tss_enabled) { debug("Getting device's ECID for TSS request\n"); /* fetch the device's ECID for the TSS request */ if (get_ecid(client, &client->ecid) < 0) { error("ERROR: Unable to find device ECID\n"); return -1; } info("Found ECID " FMT_qu "\n", (long long unsigned int)client->ecid); if (get_shsh_blobs(client, client->ecid, NULL, 0, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } } if (shsh_only) { if (!tss_enabled) { info("This device does not require a TSS record"); return 0; } if (!client->tss) { error("ERROR: could not fetch TSS record"); plist_free(buildmanifest); return -1; } else { char *bin = NULL; uint32_t blen = 0; plist_to_bin(client->tss, &bin, &blen); if (bin) { char zfn[512]; sprintf(zfn, "shsh/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product, client->version); __mkdir("shsh", 0755); struct stat fst; if (stat(zfn, &fst) != 0) { gzFile zf = gzopen(zfn, "wb"); gzwrite(zf, bin, blen); gzclose(zf); info("SHSH saved to '%s'\n", zfn); } else { info("SHSH '%s' already present.\n", zfn); } free(bin); } else { error("ERROR: could not get TSS record data\n"); } plist_free(client->tss); plist_free(buildmanifest); return 0; } } /* verify if we have tss records if required */ if ((tss_enabled) && (client->tss == NULL)) { error("ERROR: Unable to proceed without a TSS record.\n"); plist_free(buildmanifest); return -1; } if ((tss_enabled) && client->tss) { /* fix empty dicts */ fixup_tss(client->tss); } // Extract filesystem from IPSW and return its name char* filesystem = NULL; if (ipsw_extract_filesystem(client->ipsw, build_identity, &filesystem) < 0) { error("ERROR: Unable to extract filesystem from IPSW\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -1; } // if the device is in normal mode, place device into recovery mode if (client->mode->index == MODE_NORMAL) { info("Entering recovery mode...\n"); if (normal_enter_recovery(client) < 0) { error("ERROR: Unable to place device into recovery mode\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -1; } } // if the device is in DFU mode, place device into recovery mode if (client->mode->index == MODE_DFU) { recovery_client_free(client); if (client->flags & FLAG_CUSTOM) { info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { return -1; } info("exploiting with limera1n\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); return -1; } dfu_client_free(client); info("exploited\n"); } if (dfu_enter_recovery(client, build_identity) < 0) { error("ERROR: Unable to place device into recovery mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); return -1; } } if (client->mode->index == MODE_DFU) { client->mode = &idevicerestore_modes[MODE_RECOVERY]; } else { /* now we load the iBEC */ if (recovery_send_ibec(client, build_identity) < 0) { error("ERROR: Unable to send iBEC\n"); return -1; } recovery_client_free(client); /* this must be long enough to allow the device to run the iBEC */ /* FIXME: Probably better to detect if the device is back then */ sleep(7); } if (client->build[0] > '8') { // we need another tss request with nonce. unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (get_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); recovery_send_reset(client); return -1; } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } info("Nonce: "); int i; for (i = 0; i < client->nonce_size; i++) { info("%02x ", client->nonce[i]); } info("\n"); if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { // Welcome iOS5. We have to re-request the TSS with our nonce. plist_free(client->tss); if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); return -1; } fixup_tss(client->tss); } } // now finally do the magic to put the device into restore mode if (client->mode->index == MODE_RECOVERY) { if (client->srnm == NULL) { error("ERROR: could not retrieve device serial number. Can't continue.\n"); return -1; } if (recovery_enter_restore(client, build_identity) < 0) { error("ERROR: Unable to place device into restore mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); return -1; } } // device is finally in restore mode, let's do this if (client->mode->index == MODE_RESTORE) { info("About to restore device... \n"); if (restore_device(client, build_identity, filesystem) < 0) { error("ERROR: Unable to restore device\n"); return -1; } } info("Cleaning up...\n"); if (filesystem) unlink(filesystem); info("DONE\n"); return 0; }
int mkdir(const char *path, mode_t mode) { _gfs_hook_debug_v(fputs("Hooking mkdir\n", stderr)); return (__mkdir(path, mode)); }
int idevicerestore_start(struct idevicerestore_client_t* client) { int tss_enabled = 0; int result = 0; if (!client) { return -1; } if ((client->flags & FLAG_LATEST) && (client->flags & FLAG_CUSTOM)) { error("ERROR: FLAG_LATEST cannot be used with FLAG_CUSTOM.\n"); return -1; } if (!client->ipsw && !(client->flags & FLAG_PWN) && !(client->flags & FLAG_LATEST)) { error("ERROR: no ipsw file given\n"); return -1; } if (client->flags & FLAG_DEBUG) { idevice_set_debug_level(1); irecv_set_debug_level(1); idevicerestore_debug = 1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.0); // update version data (from cache, or apple if too old) load_version_data(client); // check which mode the device is currently in so we know where to start if (check_mode(client) < 0 || client->mode->index == MODE_UNKNOWN) { error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n"); return -1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.1); info("Found device in %s mode\n", client->mode->string); if (client->mode->index == MODE_WTF) { unsigned int cpid = 0; if (dfu_client_new(client) != 0) { error("ERROR: Could not open device in WTF mode\n"); return -1; } if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { error("ERROR: Could not get CPID for WTF mode device\n"); dfu_client_free(client); return -1; } char wtfname[256]; sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid); unsigned char* wtftmp = NULL; unsigned int wtfsize = 0; // Prefer to get WTF file from the restore IPSW ipsw_extract_to_memory(client->ipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { // Download WTF IPSW char* s_wtfurl = NULL; plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL"); if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) { plist_get_string_val(wtfurl, &s_wtfurl); } if (!s_wtfurl) { info("Using hardcoded x12220000_5_Recovery.ipsw URL\n"); s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw"); } // make a local file name char* fnpart = strrchr(s_wtfurl, '/'); if (!fnpart) { fnpart = "x12220000_5_Recovery.ipsw"; } else { fnpart++; } struct stat fst; char wtfipsw[1024]; if (client->cache_dir) { if (stat(client->cache_dir, &fst) < 0) { mkdir_with_parents(client->cache_dir, 0755); } strcpy(wtfipsw, client->cache_dir); strcat(wtfipsw, "/"); strcat(wtfipsw, fnpart); } else { strcpy(wtfipsw, fnpart); } if (stat(wtfipsw, &fst) != 0) { download_to_file(s_wtfurl, wtfipsw, 0); } ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { error("ERROR: Could not extract WTF\n"); } } if (wtftmp) { if (dfu_send_buffer(client, wtftmp, wtfsize) != 0) { error("ERROR: Could not send WTF...\n"); } } dfu_client_free(client); sleep(1); free(wtftmp); client->mode = &idevicerestore_modes[MODE_DFU]; } // discover the device type if (check_product_type(client) == NULL || client->device == NULL) { error("ERROR: Unable to discover device type\n"); return -1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.2); info("Identified device as %s\n", client->device->product_type); if ((client->flags & FLAG_PWN) && (client->mode->index != MODE_DFU)) { error("ERROR: you need to put your device into DFU mode to pwn it.\n"); return -1; } if (client->flags & FLAG_PWN) { recovery_client_free(client); info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { return -1; } info("exploiting with limera1n...\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); return -1; } dfu_client_free(client); info("Device should be in pwned DFU state now.\n"); return 0; } if (client->flags & FLAG_LATEST) { char* ipsw = NULL; int res = ipsw_download_latest_fw(client->version_data, client->device->product_type, "cache", &ipsw); if (res != 0) { if (ipsw) { free(ipsw); } return res; } else { client->ipsw = ipsw; } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.6); if (client->flags & FLAG_NOACTION) { return 0; } if (client->mode->index == MODE_RESTORE) { if (restore_reboot(client) < 0) { error("ERROR: Unable to exit restore mode\n"); return -2; } // we need to refresh the current mode again check_mode(client); info("Found device in %s mode\n", client->mode->string); } // verify if ipsw file exists if (access(client->ipsw, F_OK) < 0) { error("ERROR: Firmware file %s does not exist.\n", client->ipsw); return -1; } // extract buildmanifest plist_t buildmanifest = NULL; if (client->flags & FLAG_CUSTOM) { info("Extracting Restore.plist from IPSW\n"); if (ipsw_extract_restore_plist(client->ipsw, &buildmanifest) < 0) { error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw); return -1; } } else { info("Extracting BuildManifest from IPSW\n"); if (ipsw_extract_build_manifest(client->ipsw, &buildmanifest, &tss_enabled) < 0) { error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw); return -1; } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8); /* check if device type is supported by the given build manifest */ if (build_manifest_check_compatibility(buildmanifest, client->device->product_type) < 0) { error("ERROR: Could not make sure this firmware is suitable for the current device. Refusing to continue.\n"); return -1; } /* print iOS information from the manifest */ build_manifest_get_version_information(buildmanifest, client); info("Product Version: %s\n", client->version); info("Product Build: %s Major: %d\n", client->build, client->build_major); if (client->flags & FLAG_CUSTOM) { /* prevent signing custom firmware */ tss_enabled = 0; info("Custom firmware requested. Disabled TSS request.\n"); } // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; if (client->flags & FLAG_CUSTOM) { build_identity = plist_new_dict(); { plist_t node; plist_t comp; plist_t inf; plist_t manifest; char tmpstr[256]; char p_all_flash[128]; char lcmodel[8]; strcpy(lcmodel, client->device->hardware_model); int x = 0; while (lcmodel[x]) { lcmodel[x] = tolower(lcmodel[x]); x++; } sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production"); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/manifest"); // get all_flash file manifest char *files[16]; char *fmanifest = NULL; uint32_t msize = 0; if (ipsw_extract_to_memory(client->ipsw, tmpstr, (unsigned char**)&fmanifest, &msize) < 0) { error("ERROR: could not extract %s from IPSW\n", tmpstr); return -1; } char *tok = strtok(fmanifest, "\r\n"); int fc = 0; while (tok) { files[fc++] = strdup(tok); if (fc >= 16) { break; } tok = strtok(NULL, "\r\n"); } free(fmanifest); manifest = plist_new_dict(); for (x = 0; x < fc; x++) { inf = plist_new_dict(); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/"); strcat(tmpstr, files[x]); plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); const char* compname = get_component_name(files[x]); if (compname) { plist_dict_insert_item(manifest, compname, comp); if (!strncmp(files[x], "DeviceTree", 10)) { plist_dict_insert_item(manifest, "RestoreDeviceTree", plist_copy(comp)); } } else { error("WARNING: unhandled component %s\n", files[x]); plist_free(comp); } free(files[x]); files[x] = NULL; } // add iBSS sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "iBSS", comp); // add iBEC sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "iBEC", comp); // add kernel cache plist_t kdict = NULL; node = plist_dict_get_item(buildmanifest, "KernelCachesByTarget"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { char tt[4]; strncpy(tt, lcmodel, 3); tt[3] = 0; kdict = plist_dict_get_item(node, tt); } else { // Populated in older iOS IPSWs kdict = plist_dict_get_item(buildmanifest, "RestoreKernelCaches"); } if (kdict && (plist_get_node_type(kdict) == PLIST_DICT)) { plist_t kc = plist_dict_get_item(kdict, "Release"); if (kc && (plist_get_node_type(kc) == PLIST_STRING)) { inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_copy(kc)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "KernelCache", comp); plist_dict_insert_item(manifest, "RestoreKernelCache", plist_copy(comp)); } } // add ramdisk node = plist_dict_get_item(buildmanifest, "RestoreRamDisks"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { plist_t rd = plist_dict_get_item(node, (client->flags & FLAG_ERASE) ? "User" : "Update"); // if no "Update" ram disk entry is found try "User" ram disk instead if (!rd && !(client->flags & FLAG_ERASE)) { rd = plist_dict_get_item(node, "User"); // also, set the ERASE flag since we actually change the restore variant client->flags |= FLAG_ERASE; } if (rd && (plist_get_node_type(rd) == PLIST_STRING)) { inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_copy(rd)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "RestoreRamDisk", comp); } } // add OS filesystem node = plist_dict_get_item(buildmanifest, "SystemRestoreImages"); if (!node) { error("ERROR: missing SystemRestoreImages in Restore.plist\n"); } plist_t os = plist_dict_get_item(node, "User"); if (!os) { error("ERROR: missing filesystem in Restore.plist\n"); } else { inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_copy(os)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "OS", comp); } // add info inf = plist_new_dict(); plist_dict_insert_item(inf, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update")); plist_dict_insert_item(inf, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)")); plist_dict_insert_item(build_identity, "Info", inf); // finally add manifest plist_dict_insert_item(build_identity, "Manifest", manifest); } } else if (client->flags & FLAG_ERASE) { build_identity = build_manifest_get_build_identity(buildmanifest, 0); if (build_identity == NULL) { error("ERROR: Unable to find any build identities\n"); plist_free(buildmanifest); return -1; } } else { // loop through all build identities in the build manifest // and list the valid ones int i = 0; int valid_builds = 0; int build_count = build_manifest_get_identity_count(buildmanifest); for (i = 0; i < build_count; i++) { build_identity = build_manifest_get_build_identity(buildmanifest, i); valid_builds++; } } /* print information about current build identity */ build_identity_print_information(build_identity); idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.0); /* retrieve shsh blobs if required */ if (tss_enabled) { debug("Getting device's ECID for TSS request\n"); /* fetch the device's ECID for the TSS request */ if (get_ecid(client, &client->ecid) < 0) { error("ERROR: Unable to find device ECID\n"); return -1; } info("Found ECID " FMT_qu "\n", (long long unsigned int)client->ecid); if (client->build_major > 8) { unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (get_nonce(client, &nonce, &nonce_size) < 0) { /* the first nonce request with older firmware releases can fail and it's OK */ info("NOTE: Unable to get nonce from device\n"); } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } } if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } } if (client->flags & FLAG_SHSHONLY) { if (!tss_enabled) { info("This device does not require a TSS record"); return 0; } if (!client->tss) { error("ERROR: could not fetch TSS record"); plist_free(buildmanifest); return -1; } else { char *bin = NULL; uint32_t blen = 0; plist_to_bin(client->tss, &bin, &blen); if (bin) { char zfn[1024]; if (client->cache_dir) { strcpy(zfn, client->cache_dir); strcat(zfn, "/shsh"); } else { strcpy(zfn, "shsh"); } mkdir_with_parents(zfn, 0755); sprintf(zfn+strlen(zfn), "/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product_type, client->version); struct stat fst; if (stat(zfn, &fst) != 0) { gzFile zf = gzopen(zfn, "wb"); gzwrite(zf, bin, blen); gzclose(zf); info("SHSH saved to '%s'\n", zfn); } else { info("SHSH '%s' already present.\n", zfn); } free(bin); } else { error("ERROR: could not get TSS record data\n"); } plist_free(client->tss); plist_free(buildmanifest); return 0; } } /* verify if we have tss records if required */ if ((tss_enabled) && (client->tss == NULL)) { error("ERROR: Unable to proceed without a TSS record.\n"); plist_free(buildmanifest); return -1; } if ((tss_enabled) && client->tss) { /* fix empty dicts */ fixup_tss(client->tss); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.1); // if the device is in normal mode, place device into recovery mode if (client->mode->index == MODE_NORMAL) { info("Entering recovery mode...\n"); if (normal_enter_recovery(client) < 0) { error("ERROR: Unable to place device into recovery mode from %s mode\n", client->mode->string); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -5; } } // Get filesystem name from build identity char* fsname = NULL; if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { error("ERROR: Unable get path for filesystem component\n"); return -1; } // check if we already have an extracted filesystem int delete_fs = 0; char* filesystem = NULL; struct stat st; memset(&st, '\0', sizeof(struct stat)); char tmpf[1024]; if (client->cache_dir) { if (stat(client->cache_dir, &st) < 0) { mkdir_with_parents(client->cache_dir, 0755); } strcpy(tmpf, client->cache_dir); strcat(tmpf, "/"); char *ipswtmp = strdup(client->ipsw); strcat(tmpf, basename(ipswtmp)); free(ipswtmp); } else { strcpy(tmpf, client->ipsw); } char* p = strrchr((const char*)tmpf, '.'); if (p) { *p = '\0'; } if (stat(tmpf, &st) < 0) { __mkdir(tmpf, 0755); } strcat(tmpf, "/"); strcat(tmpf, fsname); memset(&st, '\0', sizeof(struct stat)); if (stat(tmpf, &st) == 0) { off_t fssize = 0; ipsw_get_file_size(client->ipsw, fsname, &fssize); if ((fssize > 0) && (st.st_size == fssize)) { info("Using cached filesystem from '%s'\n", tmpf); filesystem = strdup(tmpf); } } if (!filesystem) { char extfn[1024]; strcpy(extfn, tmpf); strcat(extfn, ".extract"); char lockfn[1024]; strcpy(lockfn, tmpf); strcat(lockfn, ".lock"); lock_info_t li; lock_file(lockfn, &li); FILE* extf = NULL; if (access(extfn, F_OK) != 0) { extf = fopen(extfn, "w"); } unlock_file(&li); if (!extf) { // use temp filename filesystem = tempnam(NULL, "ipsw_"); if (!filesystem) { error("WARNING: Could not get temporary filename, using '%s' in current directory\n", fsname); filesystem = strdup(fsname); } delete_fs = 1; } else { // use <fsname>.extract as filename filesystem = strdup(extfn); fclose(extf); } remove(lockfn); // Extract filesystem from IPSW info("Extracting filesystem from IPSW\n"); if (ipsw_extract_to_file(client->ipsw, fsname, filesystem) < 0) { error("ERROR: Unable to extract filesystem from IPSW\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -1; } if (strstr(filesystem, ".extract")) { // rename <fsname>.extract to <fsname> rename(filesystem, tmpf); free(filesystem); filesystem = strdup(tmpf); } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.3); // if the device is in DFU mode, place device into recovery mode if (client->mode->index == MODE_DFU) { recovery_client_free(client); if ((client->flags & FLAG_CUSTOM) && limera1n_is_supported(client->device)) { info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { if (delete_fs && filesystem) unlink(filesystem); return -1; } info("exploiting with limera1n\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); if (delete_fs && filesystem) unlink(filesystem); return -1; } dfu_client_free(client); info("exploited\n"); } if (dfu_enter_recovery(client, build_identity) < 0) { error("ERROR: Unable to place device into recovery mode from %s mode\n", client->mode->string); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); if (delete_fs && filesystem) unlink(filesystem); return -2; } } if (client->mode->index == MODE_DFU) { client->mode = &idevicerestore_modes[MODE_RECOVERY]; } else { if ((client->build_major > 8) && !(client->flags & FLAG_CUSTOM)) { /* send ApTicket */ if (recovery_send_ticket(client) < 0) { error("WARNING: Unable to send APTicket\n"); } } /* now we load the iBEC */ if (recovery_send_ibec(client, build_identity) < 0) { error("ERROR: Unable to send iBEC\n"); if (delete_fs && filesystem) unlink(filesystem); return -2; } recovery_client_free(client); /* this must be long enough to allow the device to run the iBEC */ /* FIXME: Probably better to detect if the device is back then */ sleep(7); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.5); if (client->build_major > 8) { // we need another tss request with nonce. unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (get_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); recovery_send_reset(client); if (delete_fs && filesystem) unlink(filesystem); return -2; } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { // Welcome iOS5. We have to re-request the TSS with our nonce. plist_free(client->tss); if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } fixup_tss(client->tss); } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.7); // now finally do the magic to put the device into restore mode if (client->mode->index == MODE_RECOVERY) { if (client->srnm == NULL) { error("ERROR: could not retrieve device serial number. Can't continue.\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } if (recovery_enter_restore(client, build_identity) < 0) { error("ERROR: Unable to place device into restore mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); if (delete_fs && filesystem) unlink(filesystem); return -2; } recovery_client_free(client); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.9); // device is finally in restore mode, let's do this if (client->mode->index == MODE_RESTORE) { info("About to restore device... \n"); result = restore_device(client, build_identity, filesystem); if (result < 0) { error("ERROR: Unable to restore device\n"); if (delete_fs && filesystem) unlink(filesystem); return result; } } info("Cleaning up...\n"); if (delete_fs && filesystem) unlink(filesystem); /* special handling of AppleTVs */ if (strncmp(client->device->product_type, "AppleTV", 7) == 0) { if (recovery_client_new(client) == 0) { if (recovery_set_autoboot(client, 1) == 0) { recovery_send_reset(client); } else { error("Setting auto-boot failed?!\n"); } } else { error("Could not connect to device in recovery mode.\n"); } } info("DONE\n"); if (result == 0) { idevicerestore_progress(client, RESTORE_NUM_STEPS-1, 1.0); } return result; }
int __gen_tempname (char *tmpl, int kind) { int len; char *XXXXXX; static uint64_t value; uint64_t random_time_bits; unsigned long count; int fd = -1; int save_errno = __libc_errno; struct stat64 st; unsigned long attempts_min = 62L * 62L * 62L; unsigned long attempts = attempts_min < 238328 ? 238328 : attempts_min; len = strlen (tmpl); if (len < 6 || strcmp(&tmpl[len - 6], "XXXXXX")) { (__libc_errno = (22)); return -1; } XXXXXX = &tmpl[len - 6]; for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; switch (kind) { case 0: fd = __open (tmpl, 02 | 01000 | 04000, 0400 | 0200); break; case 1: fd = __open64 (tmpl, 02 | 01000 | 04000, 0400 | 0200); break; case 2: fd = __mkdir (tmpl, 0400 | 0200 | 0100); break; case 3: if (__lxstat64 (2, tmpl, &st) < 0) { if (__libc_errno == 2) { (__libc_errno = (save_errno)); return 0; } else return -1; } continue; } if (fd >= 0) { (__libc_errno = (save_errno)); return fd; } else if (__libc_errno != 17) return -1; } (__libc_errno = (17)); return -1; }