The iPhone Wiki is no longer updated. Visit this article on The Apple Wiki for current information. |
Gunlock
It was the first implementation of the Minus 0x20000 with Back Extend Erase exploit. In the first version of this unlock, Airplane Mode had to be switched on. If it was not, your phone would enter a boot loop upon updating to 1.1.3.
Credit
Instructions
- Download gunlock and the secpack from http://iphonejtag.blogspot.com/, and the 4.02.13 fls from http://george.zjlotto.com/index.php/baseband/
- Downgrade your phone to 1.0.2. See all the great tutorials online to do this. Your baseband won't be downgraded, this is normal. This will probably work on other versions too, but 1.0.2 doesn't lose wifi on bb access.
- Kill CommCenter and run "gunlock secpack ICE04.02.13_G.fls"
- Reload CommCenter. For some reason my phone was in brick mode. Use the elite team bricktool to get out.
- Enjoy your 1.1.2 OTB unlocked iPhone
if you dont want to downgrade to 1.0.2 try
- unlock and the secpack from http://iphonejtag.blogspot.com/ or the blog and the 4.02.13 fls from http://george.zjlotto.com/index.php/baseband/
- Upgrade to 1.1.2 and jailbreak ( must not be on 1.1.3 )
- install ssh
- upload all downloaded files to /usr/bin and set all permissions to 755
- chmod +x gunlock
- chmod +x gunlock.c
- Log to your phone trough terminal and follow this commands one by one:
- ssh root@iPhone ip address
- password: alpine
- launchctl unload /System/Library/LaunchDaemons/com.apple.CommCenter.plist
- cd /usr/bin
- /gunlock secpack ICE04.02.13_G.fls
- launchctl load /System/Library/LaunchDaemons/com.apple.CommCenter.plist
version info
1.1.2 [1]
1.1.3 [2]
the exploit was updated for 1.1.4 ( removed the need for a new secpack ) [3] or [4]
Road blocks
- Send the 1.1.3 secpack to erase 1.1.2
- Second exploit, the fake secpack erase range
- If a valid secpack is present in 0x3C0000, the phone won't boot. And since endpack doesn't work, I needed to find another way.
source code
//geohot's 112 otb unlocker //this code is GPLed #include <stdio.h> #include <stdlib.h> #include <termios.h> #include <unistd.h> #include <fcntl.h> #include <IOKit/IOKitLib.h> #include <sys/ioctl.h> #include <strings.h> #include <errno.h> #include <mach/mach_time.h> struct termios term; int hlen,t,u,fp; unsigned char *data, *secpack; FILE *f; int adrcount; int openport(int speed) { int fd = open("/dev/tty.baseband", O_RDWR | 0x20000 | O_NOCTTY); unsigned int blahnull = 0; unsigned int handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR; if(fd == -1) { fprintf(stderr, "%i(%s)\n", errno, strerror(errno)); exit(1); } ioctl(fd, 0x2000740D); fcntl(fd, 4, 0); tcgetattr(fd, &term); ioctl(fd, 0x8004540A, &blahnull); cfsetspeed(&term, speed); cfmakeraw(&term); term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 5; term.c_iflag = (term.c_iflag & 0xFFFFF0CD) | 5; term.c_oflag = term.c_oflag & 0xFFFFFFFE; term.c_cflag = (term.c_cflag & 0xFFFC6CFF) | 0x3CB00; term.c_lflag = term.c_lflag & 0xFFFFFA77; term.c_cflag = (term.c_cflag & ~CSIZE) | CS8; term.c_cflag &= ~PARENB; term.c_lflag &= ~ECHO; tcsetattr(fd, TCSANOW, &term); ioctl(fd, TIOCSDTR); ioctl(fd, TIOCCDTR); ioctl(fd, TIOCMSET, &handshake); return fd; } void resetbaseband() { kern_return_t result; mach_port_t masterPort; result = IOMasterPort(MACH_PORT_NULL, &masterPort); CFMutableDictionaryRef matchingDict = IOServiceMatching("AppleBaseband"); io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict); io_connect_t conn; result = IOServiceOpen(service, mach_task_self(), 0, &conn); result = IOConnectCallScalarMethod(conn, 0, 0, 0, 0, 0); IOServiceClose(conn); } void getheader(unsigned int timeout) { fd_set nfp; FD_ZERO(&nfp); FD_SET(fp, &nfp); struct timeval tv; tv.tv_sec=0; tv.tv_usec=timeout*1000; hlen=0; while(select(fp+1,&nfp,0,0,&tv)>0) { hlen+=read(fp,data+hlen, 0x10064-hlen); //printf("Attempting to read[%d]...%x %x\n",hlen,data[0],data[1]); } } void getcommand() //will return when done { int maxlength=6; hlen=0; while(hlen<maxlength) { hlen+=read(fp,data+hlen, 6); } maxlength+=data[5]*0x100+data[4]+4; //2 for checksum and 2 for end while(hlen<maxlength) { hlen+=read(fp,data+hlen, 0x10064-hlen); } } struct termios options; void openbaseband() { int t1=0; int t2=0x126; fp=open("/dev/tty.baseband",0x20002); ioctl(fp,0x2000740D); fcntl(fp,4,0); tcgetattr(fp,&options); ioctl(fp,0x8004540A,&t1); cfsetspeed(&options,115200); cfmakeraw(&options); options.c_cc[16]=0; options.c_cc[17]=5; options.c_iflag=(options.c_iflag | 0x5) & 0xFFFFF0CD; options.c_oflag=options.c_oflag & 0xFFFFFFFE; options.c_cflag=(options.c_cflag | 0x3CB00) & 0xFFFFEFFF; options.c_lflag=options.c_lflag & 0xFFFFFA77; tcsetattr(fp,0,&options); ioctl(fp,0x20007479); ioctl(fp,0x20007478); ioctl(fp,0x8004746D,&t2); printf("Opened: /dev/tty.baseband\n"); } void printbuffer() { for(t=0;t<hlen;t++) { if(t!=0&&t%16==0) printf("\n"); printf("%2.2X ", data[t]); } if(hlen>0) printf("\n"); } struct cmd_pkt{ unsigned short int w02; unsigned short int cmd; unsigned short int data_size; }; struct cmd_pkt_end{ unsigned short int checksum; unsigned short int w03; }; struct cmd_pkt mycmdpkt; struct cmd_pkt_end mycmdpktend; void cmd_write() { mycmdpkt.w02=2; mycmdpktend.w03=3; mycmdpktend.checksum=0; for(t=0;t<mycmdpkt.data_size;t++) { mycmdpktend.checksum+=data[t]; } mycmdpktend.checksum+=mycmdpkt.cmd+mycmdpkt.data_size; write(fp,&mycmdpkt,6); write(fp,data,mycmdpkt.data_size); write(fp,&mycmdpktend,4); } void usage() { printf("geohot's 112 otb unlocker...\n"); } int enterinteractive() { tcgetattr(fp,&options); //baud rate upped cfsetspeed(&options,115200); tcsetattr(fp,0,&options); printf("Waiting for data...\n"); do { data[0]=0x60; data[1]=0x0D; if(write(fp,data,2)==-1) { printf("Can't write\n"); return -1; } printf("Attempt...\n"); getheader(500); } while(hlen==0||data[0]!=0xb); printf("Got Header: %d %2.2x %2.2x\n",hlen, data[0], data[1]); return 0; } void increasebaudrate() { printf("Increasing baud rate...\n"); mycmdpkt.cmd=0x82; mycmdpkt.data_size=4; data[0]=0x00; data[1]=0x10; data[2]=0x0E; data[3]=0x00; //115200 bps cmd_write(); getcommand(); printbuffer(); tcgetattr(fp,&options); //baud rate upped cfsetspeed(&options,921600); tcsetattr(fp,0,&options); } void getflashid() { printf("Get flash ID\n"); mycmdpkt.cmd=0x801; mycmdpkt.data_size=0; cmd_write(); getcommand(); //printbuffer(); } void cfistage1() { printf("CFI Stage 1\n"); mycmdpkt.cmd=0x84; mycmdpkt.data_size=2; data[0]=0; data[1]=0; cmd_write(); getcommand(); //printbuffer(); } void cfistage2() { printf("CFI Stage 2\n"); mycmdpkt.cmd=0x85; mycmdpkt.data_size=0; cmd_write(); getcommand(); //printbuffer(); } void address(unsigned int addr, int print) { adrcount=addr; if(print==0) printf("Address to 0x%X ",addr); mycmdpkt.cmd=0x802; mycmdpkt.data_size=4; memcpy(data,&addr,4); cmd_write(); getcommand(); if(print==0) printbuffer(); } void sendsecpack(char *secpack) { printf("Sending secpack... "); mycmdpkt.cmd=0x204; mycmdpkt.data_size=0x800; memcpy(data,secpack,0x800); cmd_write(); getcommand(); printbuffer(); } void bbread(short int len) { mycmdpkt.cmd=0x803; mycmdpkt.data_size=2; memcpy(data,&len,2); cmd_write(); getcommand(); printbuffer(); } void bbwrite(unsigned int size, int print) //put crap in data already { if(print==0) printf("Writing: 0x%X ",adrcount); mycmdpkt.cmd=0x804; mycmdpkt.data_size=size; cmd_write(); getcommand(); if(print==0) printbuffer(); adrcount+=size; } int erase(unsigned int start, unsigned int end, int debug) { printf("Erasing: 0x%X-0x%X ",start, end); mycmdpkt.cmd=0x805; mycmdpkt.data_size=8; memcpy(data,&start,0x4); memcpy(&data[4],&end,0x4); cmd_write(); getcommand(); printbuffer(); printf("Waiting for erase to finish...\n"); do{ mycmdpkt.cmd=0x806; mycmdpkt.data_size=2; data[0]=0; data[1]=0; cmd_write(); getcommand(); if(debug==0) printbuffer(); usleep(100000); }while(data[6]==0); if(debug!=0) printbuffer(); if(data[9]!=0x31) { //printf("Erase failed!\n"); return -1; } return 0; } void endsecpack() { printf("End Secpack "); mycmdpkt.cmd=0x205; mycmdpkt.data_size=2; data[0]=0; data[1]=0; cmd_write(); getcommand(); printbuffer(); } void readmem(unsigned int addr) //you need a patched bootloader :) { //printf("procx102\n"); unsigned int memdata; mycmdpkt.cmd=0x102; mycmdpkt.data_size=4; memcpy(data,&addr,0x4); cmd_write(); getcommand(); memcpy(&memdata,&data[6],0x4); printf("[0x%X]=0x%X\n",addr,memdata); //printbuffer(); } #define patchloc 0x2359d4 //this is for 4.02.13 int main(int argc, char *argv[]) { usage(); if(argc<3) { printf("usage: %s <113secpack> <112fls>\n",argv[0]); return -1;} resetbaseband(); fp = openport(115200); //FILE *secpack=fopen(argv[1],"rb"); data=(unsigned char *)malloc(70000); if(enterinteractive()==-1) return -1; printf("Bootloader version: %s\n",&data[0xD]); if(data[5]!=4) { printf("Incorrect bootloader version\n"); return -1; } increasebaudrate(); cfistage1(); cfistage2(); char *rsecpack=(char *)malloc(0x800); FILE *secpack=fopen(argv[1],"rb"); fread(rsecpack,1,0x800,secpack); fclose(secpack); //Send the 1.1.3 secpack to erase 1.1.2 sendsecpack(rsecpack); if(erase(0xA0020000, 0xA03BFFFE,1)==-1) { printf("Erase failed\n"); printf("Hang on...we can fix that\n"); const char efakesec[]={0x00,0x00,0x02,0xA0,0x00,0x00,0x3D,0x00,0x00,0x00,0x3D,0x00,0x00,0x00,0x00,0x00}; //full range including main fw... //2nd exploit variant for >=1.1.3 memcpy(&rsecpack[0x780],efakesec,0x10); sendsecpack(rsecpack); endsecpack(); erase(0xA03D0000,0xA03F0000,1); //the only secpack free allowed erase :) printf("Okay, lets try that again...\n"); secpack=fopen(argv[1],"rb"); //reread fread(rsecpack,1,0x800,secpack); fclose(secpack); sendsecpack(rsecpack); if(erase(0xA0020000, 0xA03BFFFE,1)==-1) { printf("Hmm...what did you do?"); return -1; } } //First exploit, the -0x20000 exploit //This writes the firmware, in all its unsigned glory //I guess Apple figured -0x400 was simple, -0x20000 is *much* harder address(0xA0000000,0); //-0x20000, like i said :) FILE *bb=fopen(argv[2],"rb"); fseek(bb,0x9a4,SEEK_SET); //skip bbupdater data and secpack int a,rc=0; do{ a=fread(data,1,0x800,bb); if(rc<patchloc&&patchloc<(rc+a)) //patch the firmware { printf("Patching...\n"); data[patchloc-rc+3] = 0xe3; data[patchloc-rc+2] = 0xa0; data[patchloc-rc+1] = 0x00; data[patchloc-rc] = 0x01; } if(rc%0x10000==0||a!=0x800) printf("Wrote: 0x%x 0x%x\n",a,rc); if(a>0) bbwrite(a,1); //write like hell rc+=a; }while(a>0); //Second exploit, the fake secpack erase range //If a valid secpack is present in 0x3C0000, the phone won't boot //And since endpack doesn't work, I needed to find another way const char fakesec[]={0x00,0x00,0x3C,0xA0,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00}; //not 0xA03D0000 memcpy(&rsecpack[0x780],fakesec,0x10); sendsecpack(rsecpack); endsecpack(); erase(0xA03D0000,0xA03F0000,1); //the only secpack free allowed erase :) close(fp); resetbaseband(); printf("Enjoy your unlocked iPhone...\n"); return 0; }