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