From 748b00a7fcd0e9d820fd04fdf13422eaad4c2dd4 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Fri, 11 Mar 2022 20:34:50 +0100 Subject: ipodpatcher: Split executable only parts out. Allow building both as library and executable at the same time. Change-Id: Idc40354fdedaeace727043936352fc17232bf16e --- utils/ipodpatcher/ipodpatcher-aupd.c | 398 +++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 utils/ipodpatcher/ipodpatcher-aupd.c (limited to 'utils/ipodpatcher/ipodpatcher-aupd.c') diff --git a/utils/ipodpatcher/ipodpatcher-aupd.c b/utils/ipodpatcher/ipodpatcher-aupd.c new file mode 100644 index 0000000000..69b027284c --- /dev/null +++ b/utils/ipodpatcher/ipodpatcher-aupd.c @@ -0,0 +1,398 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Dave Chapman + * + * This program is free software; 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipodpatcher.h" + +#include "arc4.h" + +static inline int le2int(unsigned char* buf) +{ + int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return res; +} + +static inline void int2le(unsigned int val, unsigned char* addr) +{ + addr[0] = val & 0xFF; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +} + +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. */ + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + 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, 512)) < 0) { + return -1; + } + + n = GetSecurityBlockKey(ipod->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; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + 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,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, ipod->sectorbuf, ipod->sectorbuf, length); + + chksum = 0; + for (i = 0; i < (int)length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->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,ipod->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]; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + /* 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,ipod->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(ipod->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 += ipod->sectorbuf[i]; + } + + /* Perform the encryption - this is standard (A)RC4 */ + matrixArc4Init(&rc4, key, 4); + matrixArc4(&rc4, ipod->sectorbuf, ipod->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,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, 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(ipod->sectorbuf + x + aupd*40 + 28)); + int2le(chksum,ipod->sectorbuf+x+aupd*40+28); + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + -- cgit v1.2.3