/*
 * This patches CoreGraphics to fix the libtiff buffer overflow.
 *
 * If run with no arguments, it will look for CoreGraphics in its default location. When
 * run with an argument, it will patch that file. (For use outside of the touch.)
 *
 * This assumes a little endian platform - it won't work on the PPC, sorry.
 *
 * The C code was adapted from a springboard patch I found on pastebin.
 *
 * The code changes are summarized below.cp
 *
 < 307ea47c      e1d430b2        ldrh r3, [r4, #2]
 < 307ea480      e2433001        sub r3, r3, #0x1
 < 307ea484      e3530007        cmp r3, #0x7
 < 307ea488      908ff103        addls pc, pc, r3, lsl #2
 < 307ea48c      eafffe52        b 0x307e9ddc
 < 307ea490      ea000006        b 0x307ea4b0
 < 307ea494      eafffe50        b 0x307e9ddc
 < 307ea498      ea000050        b 0x307ea5e0
 < 307ea49c      eafffe4e        b 0x307e9ddc
 < 307ea4a0      eafffe4d        b 0x307e9ddc
 < 307ea4a4      ea000001        b 0x307ea4b0
 < 307ea4a8      eafffe4b        b 0x307e9ddc
 < 307ea4ac      ea00004b        b 0x307ea5e0
 ---
 > 307ea47c      e5943004        ldr r3, [r4, #4]
 > 307ea480      e3530002        cmp r3, #0x2
 > 307ea484      cafffdf8        bgt 0x307e9c6c
 > 307ea488      e1d430b2        ldrh r3, [r4, #2]
 > 307ea48c      e3530001        cmp r3, #0x1
 > 307ea490      0a000006        beq 0x307ea4b0
 > 307ea494      e3530006        cmp r3, #0x6
 > 307ea498      0a000004        beq 0x307ea4b0
 > 307ea49c      e3530003        cmp r3, #0x3
 > 307ea4a0      0a00004e        beq 0x307ea5e0
 > 307ea4a4      e3530008        cmp r3, #0x8
 > 307ea4a8      0a00004c        beq 0x307ea5e0
 > 307ea4ac      eafffe4a        b 0x307e9ddc
 *
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>

#define MS_SIZE   3291960
#define MS_FILE "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics"



struct Patch {
        unsigned int offset;
        unsigned int old;
        unsigned int new;
};

struct Patch patches[] = {
        {0x001f747c,        0xe1d430b2,        0xe5943004},
        {0x001f7480,        0xe2433001,        0xe3530002},
        {0x001f7484,        0xe3530007,        0xcafffdf8},
        {0x001f7488,        0x908ff103,        0xe1d430b2},
        {0x001f748c,        0xeafffe52,        0xe3530001},
        {0x001f7490,        0xea000006,        0x0a000006},
        {0x001f7494,        0xeafffe50,        0xe3530006},
        {0x001f7498,        0xea000050,        0x0a000004},
        {0x001f749c,        0xeafffe4e,        0xe3530003},
        {0x001f74a0,        0xeafffe4d,        0x0a00004e},
        {0x001f74a4,        0xea000001,        0xe3530008},
        {0x001f74a8,        0xeafffe4b,        0x0a00004c},
        {0x001f74ac,        0xea00004b,        0xeafffe4a},
    { 0, 0, 0 }
};

void checkError(int rval, const char* format, ...) {
        if (rval)
            return;
        va_list args;
        va_start(args, format);    
        vfprintf(stderr, format, args);
        va_end(args);
        exit(-1);
}

int main(int argc, char **argv) {
    unsigned int *DATA;
    FILE *fd;
    struct stat s;

    char *filename = MS_FILE;
    if (argc > 1) {
            filename = argv[1];
    }
    
    int len = strlen(filename);
    char *backupName = malloc(len+5);
    strcpy(backupName, filename);
    strcpy(backupName+len,".bak");
    
    char *newName = malloc(len+5);
    strcpy(newName, filename);
    strcpy(newName+len,".new");
    fprintf(stdout,"Patching %s to %s\n", filename, newName);
    
    checkError(stat(filename, &s)==0, "Could not stat file '%s': %s\n", filename, strerror(errno));
    checkError(s.st_size == MS_SIZE, 
        "%s: file size mismatch: %d; expecting %d\n", filename, (int)s.st_size, (int)MS_SIZE);
       
    fd = fopen(filename, "rb");
    checkError(fd != NULL, "Could not open '%s' for reading: %s\n", filename, strerror(errno));
    DATA = malloc(MS_SIZE+1);
    checkError(DATA != NULL,"Couldn't allocation %d bytes of memory\n", (int)MS_SIZE);
    fread(DATA, MS_SIZE, 1, fd);
    fclose(fd);

    struct Patch *patch = patches;
    while (patch->offset) {
            fprintf(stderr,"Patch %08x: %08x -> %08x\n", (int)patch->offset, (int)patch->old, (int)patch->new);
            int offset = patch->offset / 4;
            unsigned int old = DATA[offset];
            if (old != patch->old) {
                    fprintf(stderr, "Mismatch at %08x: got %08x instead of %08x\n", 
                            (int)patch->offset, (int)DATA[offset], (int)patch->old);
                    exit(-1);
            }
            DATA[offset] = patch->new;
            patch++;
    }
    fprintf(stderr, "Writing %s\n",newName);

    fd = fopen(newName, "wb");
    checkError(fd != NULL, "Could not open '%s' for writing: %s\n", newName, strerror(errno));
    checkError(fwrite(DATA, MS_SIZE, 1, fd) == 1, "Write to %s failed\n", newName);
    fclose(fd);
    
    fprintf(stderr, "Renaming files\n");
    //checkError(link(filename,backupName), "Can't link %s to %s\n", filename, backupName);
    checkError(unlink(filename) == 0, "Can't unlink %s\n",filename);
    checkError(link(newName,filename) == 0, "Can't link %s to %s\n", newName, filename);
    checkError(unlink(newName) == 0, "Can't unlink %s\n", newName);
    
    return 0;
}
