summaryrefslogtreecommitdiffstats
path: root/rbutil/ipodpatcher/ipodpatcher.c
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2007-09-08 23:27:49 +0000
committerDave Chapman <dave@dchapman.com>2007-09-08 23:27:49 +0000
commit6e7971553e021a7fe72987490439bf9a5475fb44 (patch)
treeaaa6074b94e30cf4e37fed446b30ce059f36dbaa /rbutil/ipodpatcher/ipodpatcher.c
parent6c24189d27f1bdd6f2e0458ada99a8d7e5896689 (diff)
downloadrockbox-6e7971553e021a7fe72987490439bf9a5475fb44.tar.gz
rockbox-6e7971553e021a7fe72987490439bf9a5475fb44.tar.bz2
rockbox-6e7971553e021a7fe72987490439bf9a5475fb44.zip
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
Diffstat (limited to 'rbutil/ipodpatcher/ipodpatcher.c')
-rw-r--r--rbutil/ipodpatcher/ipodpatcher.c349
1 files changed, 347 insertions, 2 deletions
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 <string.h>
#include <stdlib.h>
#include <inttypes.h>
+#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -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