Contribute
Register

Patcho, a simple hex binary patcher

SJ_UnderWater

Moderator
Joined
Dec 3, 2010
Messages
707
Motherboard
Gigabyte GA-H55M-S2V
CPU
Intel i3-530
Graphics
HIS HD 6570
Mac
iMac
Classic Mac
Mobile Phone
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
 

SJ_UnderWater

Moderator
Joined
Dec 3, 2010
Messages
707
Motherboard
Gigabyte GA-H55M-S2V
CPU
Intel i3-530
Graphics
HIS HD 6570
Mac
iMac
Classic Mac
Mobile Phone
Android
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

SJ_UnderWater

Moderator
Joined
Dec 3, 2010
Messages
707
Motherboard
Gigabyte GA-H55M-S2V
CPU
Intel i3-530
Graphics
HIS HD 6570
Mac
iMac
Classic Mac
Mobile Phone
Android
created one for NVidia OpenCL patching. As always the embedded syntax is announced before patching.
 

Attachments

SJ_UnderWater

Moderator
Joined
Dec 3, 2010
Messages
707
Motherboard
Gigabyte GA-H55M-S2V
CPU
Intel i3-530
Graphics
HIS HD 6570
Mac
iMac
Classic Mac
Mobile Phone
Android
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.
 
Joined
May 15, 2012
Messages
390
Motherboard
Gigabyte GA-Z170X-UD3\ Define R5 Case\ HP 8200 Elite SFF
CPU
Intel Core i7 6700K \ i7 2600
Graphics
RX580 Sapphire Pulse 8Gb \ MSI GTX750Ti
Mac
MacBook Air
Classic Mac
iMac, Power Mac
Mobile Phone
iOS
When I run rebuild cache it hangs at No supported helper partitions to update.

And when I try to run the Capri06 patch again it said that the file cannot be moddified.

Rick,
 

SJ_UnderWater

Moderator
Joined
Dec 3, 2010
Messages
707
Motherboard
Gigabyte GA-H55M-S2V
CPU
Intel i3-530
Graphics
HIS HD 6570
Mac
iMac
Classic Mac
Mobile Phone
Android
"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?
 

SJ_UnderWater

Moderator
Joined
Dec 3, 2010
Messages
707
Motherboard
Gigabyte GA-H55M-S2V
CPU
Intel i3-530
Graphics
HIS HD 6570
Mac
iMac
Classic Mac
Mobile Phone
Android
yes, that's how it works. automatic elevation with sudo is a recent feature so you only have to enter your password
 
Top