From 6e7971553e021a7fe72987490439bf9a5475fb44 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Sat, 8 Sep 2007 23:27:49 +0000 Subject: Add functions to read and write the AUPD (flash update) image. "--read-aupd aupd.bin" will read (and decrypt) the AUPD image, and "--write-aupd aupd.bin" will write (and encrypt) an image. Also fix a bug in the "diskmove" function which corrupted the AUPD image when a bootloader was installed. So in order to manipulate the aupd image, you need to restore a clean firmware partition, and install the bootloader with this version of ipodpatcher. Decryption functions based on the description and sample code at http://ipodlinux.org/Flash_Decryption git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14644 a1c6a512-1295-4272-9138-f99709370657 --- rbutil/ipodpatcher/Makefile | 2 +- rbutil/ipodpatcher/arc4.c | 108 ++++++++++++ rbutil/ipodpatcher/arc4.h | 47 ++++++ rbutil/ipodpatcher/ipodpatcher.c | 349 ++++++++++++++++++++++++++++++++++++++- rbutil/ipodpatcher/ipodpatcher.h | 2 + rbutil/ipodpatcher/main.c | 32 ++++ 6 files changed, 537 insertions(+), 3 deletions(-) create mode 100644 rbutil/ipodpatcher/arc4.c create mode 100644 rbutil/ipodpatcher/arc4.h diff --git a/rbutil/ipodpatcher/Makefile b/rbutil/ipodpatcher/Makefile index f65234a505..f76715b9f6 100644 --- a/rbutil/ipodpatcher/Makefile +++ b/rbutil/ipodpatcher/Makefile @@ -22,7 +22,7 @@ endif NATIVECC = gcc CC = $(CROSS)gcc -SRC = main.c ipodpatcher.c fat32format.c parttypes.h +SRC = main.c ipodpatcher.c fat32format.c parttypes.h arc4.c all: $(OUTPUT) diff --git a/rbutil/ipodpatcher/arc4.c b/rbutil/ipodpatcher/arc4.c new file mode 100644 index 0000000000..75b1862b89 --- /dev/null +++ b/rbutil/ipodpatcher/arc4.c @@ -0,0 +1,108 @@ +/* + * arc4.c + * Release $Name: MATRIXSSL_1_8_3_OPEN $ + * + * ARC4 stream cipher implementation + */ +/* + * Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved. + * The latest version of this code is available at http://www.matrixssl.org + * + * This software is open source; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This General Public License does NOT permit incorporating this software + * into proprietary programs. If you are unable to comply with the GPL, a + * commercial license for this software may be purchased from PeerSec Networks + * at http://www.peersec.com + * + * This program is distributed in WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * http://www.gnu.org/copyleft/gpl.html + */ +/******************************************************************************/ + +#include "arc4.h" + +/* + Some accounts, such as O'Reilly's Secure Programming Cookbook say that no + more than 2^30 bytes should be processed without rekeying, so we + enforce that limit here. FYI, this is equal to 1GB of data transferred. +*/ +#define ARC4_MAX_BYTES 0x40000000 + +/******************************************************************************/ +/* + SSL_RSA_WITH_RC4_* cipher callbacks + */ +void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen) +{ + unsigned char index1, index2, tmp, *state; + int16_t counter; + + ctx->byteCount = 0; + state = &ctx->state[0]; + + for (counter = 0; counter < 256; counter++) { + state[counter] = (unsigned char)counter; + } + ctx->x = 0; + ctx->y = 0; + index1 = 0; + index2 = 0; + + for (counter = 0; counter < 256; counter++) { + index2 = (key[index1] + state[counter] + index2) & 0xff; + + tmp = state[counter]; + state[counter] = state[index2]; + state[index2] = tmp; + + index1 = (index1 + 1) % keylen; + } +} + +int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in, + unsigned char *out, int32_t len) +{ + unsigned char x, y, *state, xorIndex, tmp; + int counter; /* NOTE BY DAVE CHAPMAN: This was a short in + the original code, which caused a segfault + when attempting to process data > 32767 + bytes. */ + + ctx->byteCount += len; + if (ctx->byteCount > ARC4_MAX_BYTES) { + return -1; + } + + x = ctx->x; + y = ctx->y; + state = &ctx->state[0]; + for (counter = 0; counter < len; counter++) { + x = (x + 1) & 0xff; + y = (state[x] + y) & 0xff; + + tmp = state[x]; + state[x] = state[y]; + state[y] = tmp; + + xorIndex = (state[x] + state[y]) & 0xff; + + tmp = in[counter]; + tmp ^= state[xorIndex]; + out[counter] = tmp; + } + ctx->x = x; + ctx->y = y; + return len; +} + +/*****************************************************************************/ diff --git a/rbutil/ipodpatcher/arc4.h b/rbutil/ipodpatcher/arc4.h new file mode 100644 index 0000000000..8bff0e2dc1 --- /dev/null +++ b/rbutil/ipodpatcher/arc4.h @@ -0,0 +1,47 @@ +/* + arc4.h - based on matrixssl-1-8-3-open + +*/ + +/* + * Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved. + * The latest version of this code is available at http://www.matrixssl.org + * + * This software is open source; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This General Public License does NOT permit incorporating this software + * into proprietary programs. If you are unable to comply with the GPL, a + * commercial license for this software may be purchased from PeerSec Networks + * at http://www.peersec.com + * + * This program is distributed in WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * http://www.gnu.org/copyleft/gpl.html + */ +/*****************************************************************************/ + +#ifndef _ARC4_H + +#include + +struct rc4_key_t +{ + unsigned char state[256]; + uint32_t byteCount; + unsigned char x; + unsigned char y; +}; + +void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen); +int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in, + unsigned char *out, int32_t len); + +#endif diff --git a/rbutil/ipodpatcher/ipodpatcher.c b/rbutil/ipodpatcher/ipodpatcher.c index 2655c57113..08ba9263d2 100644 --- a/rbutil/ipodpatcher/ipodpatcher.c +++ b/rbutil/ipodpatcher/ipodpatcher.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,10 @@ #include "ipodvideo.h" #endif +#ifndef RBUTIL +#include "arc4.h" +#endif + extern int verbose; unsigned char* sectorbuf; @@ -392,7 +397,7 @@ int diskmove(struct ipod_t* ipod, int delta) int chunksize; int n; - src_start = ipod->ipod_directory[1].devOffset + ipod->sector_size; + src_start = ipod->ipod_directory[1].devOffset; src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size + ipod->ipod_directory[ipod->nimages-1].len + (ipod->sector_size-1)) & ~(ipod->sector_size-1); @@ -575,7 +580,7 @@ int add_bootloader(struct ipod_t* ipod, char* filename, int type) ipod->ipod_directory[1].devOffset) { fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength - - ipod->ipod_directory[1].devOffset; + - ipod->ipod_directory[1].devOffset + ipod->sector_size; if (diskmove(ipod, delta) < 0) { fprintf(stderr,"[ERR] Image movement failed.\n"); @@ -1373,3 +1378,343 @@ int write_dos_partition_table(struct ipod_t* ipod) return 0; } + +#ifndef RBUTIL + +static inline uint32_t getuint32le(unsigned char* buf) +{ + int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return res; +} + +/* testMarker and GetSecurityBlockKey based on code from BadBlocks and + Kingstone, posted at http://ipodlinux.org/Flash_Decryption + +*/ + +static bool testMarker(int marker) +{ + int mask, decrypt, temp1, temp2; + + mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24); + decrypt = marker ^ mask; + temp1=(int)((unsigned int)decrypt>>24); + temp2=decrypt<<8; + + if (temp1==0) + return false; + + temp2=(int)((unsigned int)temp2>>24); + decrypt=decrypt<<16; + decrypt=(int)((unsigned int)decrypt>>24); + + if ((temp1 < temp2) && (temp2 < decrypt)) + { + temp1 = temp1 & 0xf; + temp2 = temp2 & 0xf; + decrypt = decrypt & 0xf; + + if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0)) + { + return true; + } + } + return false; +} + +static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key) +{ + int constant = 0x54c3a298; + int key=0; + int nkeys = 0; + int aMarker=0; + int pos=0; + int c, count; + int temp1; + static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34}; + + for (c = 0; c < 8; c++) + { + pos = offset[c]*4; + aMarker = getuint32le(data + pos); + + if (testMarker(aMarker)) + { + if (c<7) + pos =(offset[c+1]*4)+4; + else + pos =(offset[0]*4)+4; + + key=0; + + temp1=aMarker; + + for (count=0;count<2;count++){ + int word = getuint32le(data + pos); + temp1 = aMarker; + temp1 = temp1^word; + temp1 = temp1^constant; + key = temp1; + pos = pos+4; + } + int r1=0x6f; + int r2=0; + int r12; + int r14; + unsigned int r_tmp; + + for (count=2;count<128;count=count+2){ + r2=getuint32le(data+count*4); + r12=getuint32le(data+(count*4)+4); + r_tmp=(unsigned int)r12>>16; + r14=r2 | ((int)r_tmp); + r2=r2&0xffff; + r2=r2 | r12; + r1=r1^r14; + r1=r1+r2; + } + key=key^r1; + + // Invert key, little endian + this_key[0] = key & 0xff; + this_key[1] = (key >> 8) & 0xff; + this_key[2] = (key >> 16) & 0xff; + this_key[3] = (key >> 24) & 0xff; + nkeys++; + } + } + return nkeys; +} + +static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key) +{ + int n; + + /* Firstly read the security block and find the RC4 key. This is + in the sector preceeding the AUPD image. */ + + fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size); + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) { + return -1; + } + + if ((n = ipod_read(ipod, sectorbuf, 512)) < 0) { + return -1; + } + + n = GetSecurityBlockKey(sectorbuf, key); + + if (n != 1) + { + fprintf(stderr, "[ERR] %d keys found in security block, can not continue\n",n); + return -1; + } + + return 0; +} + +int read_aupd(struct ipod_t* ipod, char* filename) +{ + int length; + int i; + int outfile; + int n; + int aupd; + struct rc4_key_t rc4; + unsigned char key[4]; + unsigned long chksum=0; + + aupd = 0; + while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) + { + aupd++; + } + + if (aupd == ipod->nimages) + { + fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); + return -1; + } + + length = ipod->ipod_directory[aupd].len; + + fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); + + if (find_key(ipod, aupd, key) < 0) + { + return -1; + } + + fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); + + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { + return -1; + } + + i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); + + if ((n = ipod_read(ipod,sectorbuf,i)) < 0) { + return -1; + } + + if (n < i) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + i,n); + return -1; + } + + /* Perform the decryption - this is standard (A)RC4 */ + matrixArc4Init(&rc4, key, 4); + matrixArc4(&rc4, sectorbuf, sectorbuf, length); + + chksum = 0; + for (i = 0; i < (int)length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += sectorbuf[i]; + } + + if (chksum != ipod->ipod_directory[aupd].chksum) + { + fprintf(stderr,"[ERR] Decryption failed - checksum error\n"); + return -1; + } + fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n"); + + outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); + if (outfile < 0) { + fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); + return -1; + } + + n = write(outfile,sectorbuf,length); + if (n != length) { + fprintf(stderr,"[ERR] Write error - %d\n",n); + } + close(outfile); + + return 0; +} + +int write_aupd(struct ipod_t* ipod, char* filename) +{ + unsigned int length; + int i; + int x; + int n; + int infile; + int newsize; + int aupd; + unsigned long chksum=0; + struct rc4_key_t rc4; + unsigned char key[4]; + + /* First check that the input file is the correct type for this ipod. */ + infile=open(filename,O_RDONLY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; + } + + length = filesize(infile); + newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); + + fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", + length,newsize); + + if (newsize > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + if (infile >= 0) close(infile); + return -1; + } + + /* Find aupd image number */ + aupd = 0; + while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) + { + aupd++; + } + + if (aupd == ipod->nimages) + { + fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); + return -1; + } + + if (length != ipod->ipod_directory[aupd].len) + { + fprintf(stderr,"[ERR] AUPD image (%d bytes) differs in size to %s (%d bytes).\n", + ipod->ipod_directory[aupd].len, filename, length); + return -1; + } + + if (find_key(ipod, aupd, key) < 0) + { + return -1; + } + + fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); + + /* We now know we have enough space, so write it. */ + + fprintf(stderr,"[INFO] Reading input file...\n"); + n = read(infile,sectorbuf,length); + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + close(infile); + return -1; + } + close(infile); + + /* Pad the data with zeros */ + memset(sectorbuf+length,0,newsize-length); + + /* Calculate the new checksum (before we encrypt) */ + chksum = 0; + for (i = 0; i < (int)length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += sectorbuf[i]; + } + + /* Perform the encryption - this is standard (A)RC4 */ + matrixArc4Init(&rc4, key, 4); + matrixArc4(&rc4, sectorbuf, sectorbuf, length); + + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,sectorbuf,newsize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < newsize) { + fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" + ,newsize,n); + return -1; + } + fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + + n=ipod_read(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { return -1; } + + /* Update checksum */ + fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(sectorbuf + x + aupd*40 + 28)); + int2le(chksum,sectorbuf+x+aupd*40+28); + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + +#endif diff --git a/rbutil/ipodpatcher/ipodpatcher.h b/rbutil/ipodpatcher/ipodpatcher.h index d816c68724..0d9222268f 100644 --- a/rbutil/ipodpatcher/ipodpatcher.h +++ b/rbutil/ipodpatcher/ipodpatcher.h @@ -47,6 +47,8 @@ int list_images(struct ipod_t* ipod); int getmodel(struct ipod_t* ipod, int ipod_version); int ipod_scan(struct ipod_t* ipod); int write_dos_partition_table(struct ipod_t* ipod); +int read_aupd(struct ipod_t* ipod, char* filename); +int write_aupd(struct ipod_t* ipod, char* filename); off_t filesize(int fd); #endif diff --git a/rbutil/ipodpatcher/main.c b/rbutil/ipodpatcher/main.c index c47063cba8..f113c8aff4 100644 --- a/rbutil/ipodpatcher/main.c +++ b/rbutil/ipodpatcher/main.c @@ -45,6 +45,8 @@ enum { ADD_BOOTLOADER, READ_FIRMWARE, WRITE_FIRMWARE, + READ_AUPD, + WRITE_AUPD, READ_PARTITION, WRITE_PARTITION, FORMAT_PARTITION, @@ -89,6 +91,8 @@ void print_usage(void) fprintf(stderr," -d, --delete-bootloader\n"); fprintf(stderr," -f, --format\n"); fprintf(stderr," -c, --convert\n"); + fprintf(stderr," --read-aupd filename.bin\n"); + fprintf(stderr," --write-aupd filename.bin\n"); fprintf(stderr,"\n"); #ifdef __WIN32__ @@ -299,6 +303,18 @@ int main(int argc, char* argv[]) (strcmp(argv[i],"--format")==0)) { action = FORMAT_PARTITION; i++; + } else if (strcmp(argv[i],"--read-aupd")==0) { + action = READ_AUPD; + i++; + if (i == argc) { print_usage(); return 1; } + filename=argv[i]; + i++; + } else if (strcmp(argv[i],"--write-aupd")==0) { + action = WRITE_AUPD; + i++; + if (i == argc) { print_usage(); return 1; } + filename=argv[i]; + i++; } else if ((strcmp(argv[i],"-c")==0) || (strcmp(argv[i],"--convert")==0)) { action = CONVERT_TO_FAT32; @@ -444,6 +460,22 @@ int main(int argc, char* argv[]) } else { fprintf(stderr,"[ERR] --read-firmware failed.\n"); } + } else if (action==READ_AUPD) { + if (read_aupd(&ipod, filename)==0) { + fprintf(stderr,"[INFO] AUPD image read to file %s.\n",filename); + } else { + fprintf(stderr,"[ERR] --read-aupd failed.\n"); + } + } else if (action==WRITE_AUPD) { + if (ipod_reopen_rw(&ipod) < 0) { + return 5; + } + + if (write_aupd(&ipod, filename)==0) { + fprintf(stderr,"[INFO] AUPD image %s written to device.\n",filename); + } else { + fprintf(stderr,"[ERR] --write-aupd failed.\n"); + } } else if (action==READ_PARTITION) { outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); if (outfile < 0) { -- cgit v1.2.3