Contribute
Register

Patcho, a simple hex binary patcher

Status
Not open for further replies.
Joined
Dec 3, 2010
Messages
460
Motherboard
Gigabyte GA-H55M-S2V
CPU
Intel i3-530
Graphics
HIS HD 6570
Mac
  1. iMac
Mobile Phone
  1. Android
I do hardly any work in C, but I've been asked a few times to add a binary patching function to my apps. While I don't think it belongs in DPCIManager or MaciASL, I did want to release something better than `perl`, so here it is, a simple hexadecimal file patcher, patcho. Takes hexadecimal find and replace arguments, the file, and an optional prefix to make the command cleaner. Returns the offset(s) and length(s) it wrote to, making it easy to parse the output in a script, possibly for validation.
Code:
//
//  main.c
//  patcho
//
//  Created by PHPdev32 on 1/15/13.
//  Licensed under GPLv3, full text at http://www.gnu.org/licenses/gpl-3.0.txt
//

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
char embed[256] = "<--embed starts here";
int hx(char hex) {
    if (hex>='0' && hex<='9')
        return hex-48;
    else if (hex>='A' && hex<='F')
        return hex-55;
    else if (hex>='a' && hex<='f')
        return hex-87;
    printf("Bad hex character '%c'\n", hex);
    exit(6);
}
void hexStr(const char *hex, char *str){
    u_int64_t j = 0, i = strlen(hex)/2;
    if (i < 1) return;
    while (j < i) {
        str[j]=(hx(hex[j*2])<<4)+hx(hex[j*2+1]);
        j++;
    }
}
int main(int argc, char * argv[]) {
    if (embed[0] != '<' && argc == 1) {
        argv[1] = malloc(sizeof(embed));
        argv[2] = malloc(sizeof(embed));
        argv[3] = malloc(sizeof(embed));
        argv[4] = malloc(sizeof(embed));
        argc = sscanf(embed, "%s %s %s %s", argv[1], argv[2], argv[3], argv[4]) + 1;
        printf("Using embed\n%s %s %s %s\n", argv[1], argv[2], argv[3], argv[4]);
    }
    if (argc < 4) {
        printf ("Usage:   %s [hex prefix] <hex find> <hex replace> <file>\nExample: %s CAFE BABE 00AB java.exe\nResult:  CAFEBABE -> CAFE00AB\n", argv[0], argv[0]);
        exit(1);
    }
    if (access(argv[argc-1], F_OK) != 0) {
        printf("File cannot be found\n");
        exit(2);
    }
    if (access(argv[argc-1], W_OK) != 0) {
        if (!getuid())
            printf("File cannot be modified\n");
        else {
            printf("File cannot be modified, trying sudo\n");
            char *argv2[] = {"sudo", argv[0], argv[1], argv[2], argv[3], argv[4][0]?argv[4]:0, 0};
            execvp(argv2[0], argv2);
            printf("Sudo failed, exiting\n");
        }
        exit(3);
    }
    int hasPrefix = (argc > 4);
    if (strlen(argv[hasPrefix+1]) != strlen(argv[hasPrefix+2])) {
        printf("Find and Replace sizes do not match\n");
        exit(4);
    }
    if (strlen(argv[hasPrefix+1]) % 2 != 0 || (hasPrefix && strlen(argv[1]) % 2 != 0)) {
        printf("Find and Replace sizes not in whole bytes\n");
        exit(5);
    }
    u_int64_t i,j,l;
    i=hasPrefix?strlen(argv[1])/2:0;
    j=strlen(argv[hasPrefix+1])/2+i;
    char find[j];
    char replace[j];
    if (hasPrefix) {
        hexStr(argv[1], find);
        memcpy(replace, find, i);
    }
    hexStr(argv[hasPrefix+1], find+i);
    hexStr(argv[hasPrefix+2], replace+i);
    u_int64_t h=j-i,k=i=0;
    FILE *file = fopen(argv[argc-1], "r+");
    fseek(file, 0, SEEK_END);
    l=ftell(file);
    fseek(file, 0, SEEK_SET);
    while (k++ < l) {
        if ((char)fgetc(file) == find[i]) {
            if (++i == j) {
                fseek(file, -h, SEEK_CUR);
                printf("0x%08lX: %zdB\n", ftell(file), fwrite(j-h+replace, 1, h, file));
                if (ferror(file)) {
                    printf("File stream error\n");
                    fclose(file);
                    exit(7);
                }
                i=0;
            }
        }
        else i=0;
    }
    fclose(file);
    return 0;
}
edit: i was asked to make a more portable version, so this is pure C and declares its dependencies.
edit: now uses an optional embedded syntax for single-purpose patching. The string is marked for convenience when hex editing the binary.
edit: announces embedded syntax, and attempts to elevate process automatically
edit: minor embed fixes
 
a sample usage is
Code:
patcho 0A00660100020302000000020000000100000020100700001007000000000000000000000000000000000000000000000205000000040000070100000304000000 0400000701 0800000600 /System/Library/Extensions/AppleIntelFramebufferCapri.kext/Contents/MacOS/AppleIntelFramebufferCapri
for the port 0x6 HDMI audio edit for the Intel DQ77KB

--edit
now in embedded binary form
--edit2
no longer requires Terminal experience, just unzip and double-click the file, then type your password into Terminal to authorize the patch
 

Attachments

  • capri06.zip
    2.8 KB · Views: 749
created one for NVidia OpenCL patching. As always the embedded syntax is announced before patching.
 

Attachments

  • nvidiaopencl.zip
    2.8 KB · Views: 382
if you ever change the contents of a kext, make sure you trigger a rebuild of the kernel cache if it doesn't happen automatically. DPCIManager (http://dpcimanager.sourceforge.net) is just one of many apps which will do it for you.
 
"No supported helper partitions to update" means it finished rebuilding, scroll to read the various messages.
"File cannot be modified" means there was a permissions error, did you enter the administrator password?
 
yes, that's how it works. automatic elevation with sudo is a recent feature so you only have to enter your password
 
Status
Not open for further replies.
Back
Top