Kjetil's Information Center: A Blog About My Projects

Binary Patching Tools

Here is a couple of tools I created for patching binary files. The diffing tool will compare two binary files and print a list on standard out with differences. The differences is displayed with an offset and hex values of changed bytes. This output can be used together with the patching tool to patch existing files, since it uses the same format on standard in to determine where to make changes.

Here is the differ source code:

#include <stdio.h>
#include <stdbool.h>

int main(int argc, char *argv[])
{
  FILE *fh_old, *fh_new;
  int c_old, c_new;
  unsigned int address;
  bool prev_different;
  
  if (argc != 3) {
    fprintf(stderr, "Usage: %s <old file> <new file>\n", argv[0]);
    return 1;
  }

  fh_old = fopen(argv[1], "rb");
  if (fh_old == NULL) {
    fprintf(stderr, "Error: Unable to open file for reading: %s\n", argv[1]);
    return 1;
  }

  fh_new = fopen(argv[2], "rb");
  if (fh_new == NULL) {
    fprintf(stderr, "Error: Unable to open file for reading: %s\n", argv[2]);
    fclose(fh_old);
    return 1;
  }

  prev_different = false;
  address = 0;
  while ((c_old = fgetc(fh_old)) != EOF) {
    c_new = fgetc(fh_new);
    if (c_new == EOF) {
      fprintf(stderr, "Warning: New file shorter than old file.\n");
      break;
    }

    if (c_old != c_new) {
      if (prev_different) {
        printf(" %02x", c_new);
      } else {
        printf("%08x: %02x", address, c_new);
      }
      prev_different = true;
    } else {
      if (prev_different) {
        printf("\n");
      }
      prev_different = false;
    }

    address++;
  }

  c_new = fgetc(fh_new);
  if (c_new != EOF) {
    fprintf(stderr, "Warning: Old file shorter than new file.\n");
  }

  fclose(fh_old);
  fclose(fh_new);

  return 0;
}
          


Here is the patcher source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define CODE_MAX 256

typedef struct subst_s {
  unsigned int address;
  size_t code_len;
  unsigned char *code;
  struct subst_s *next;
} subst_t;

static subst_t *patch_read(FILE *fh)
{
  subst_t *current, *first;
  char line[CODE_MAX];
  unsigned char code[CODE_MAX];
  unsigned int address, byte;
  int consumed, start;
  size_t code_len;

  current = (subst_t *)malloc(sizeof(subst_t));
  if (current == NULL) {
    fprintf(stderr, "Error: malloc() failed.\n");
    return NULL;
  }
  current->address = 0xffffffff;
  current->code_len = 0;
  current->code = NULL;
  current->next = NULL;

  first = current;

  while (fgets(line, CODE_MAX, fh) != NULL) {
    if (strlen(line) <= 0)
      continue;
    if (line[0] == '#')
      continue;
    if (sscanf(line, "%x:%n", &address, &consumed) <= 0)
      continue;

    code_len = 0;
    start = consumed;
    while (sscanf(&line[start], "%2x%n", &byte, &consumed) > 0) {
      code[code_len] = byte;
      start += consumed;
      code_len++;
      if (code_len >= CODE_MAX)
        break;
    }

    if (code_len > 0) {
      current->address = address;
      current->code_len = code_len;
      current->code = (unsigned char *)malloc(code_len * sizeof(unsigned char));
      if (current->code == NULL) {
        fprintf(stderr, "Error: malloc() failed.\n");
        return NULL;
      }
      memcpy(current->code, code, code_len);

      current->next = (subst_t *)malloc(sizeof(subst_t));
      if (current->next == NULL) {
        fprintf(stderr, "Error: malloc() failed.\n");
        return NULL;
      }
      current->next->address = 0xffffffff;
      current->next->code_len = 0;
      current->next->code = NULL;
      current->next->next = NULL;

      current = current->next;
    }
  }

  return first;
}

static void patch_free(subst_t *patch)
{
  subst_t *prev;
  prev = NULL;
  while (patch->next != NULL) {
    if (prev != NULL)
      free(prev);
    if (patch->code != NULL)
      free(patch->code);
    prev = patch;
    patch = patch->next;
  }
  free(prev);
  free(patch);
}

int patch_lookup(subst_t *patch, int address)
{
  int i;
  while (patch->next != NULL) {
    for (i = 0; i < patch->code_len; i++) {
      if (patch->address + i == address)
        return (int)patch->code[i];
    }
    patch = patch->next;
  }
  return EOF;
}

int main(int argc, char *argv[])
{
  subst_t *patch;
  FILE *fh_old, *fh_new;
  int c_old, c_new;
  unsigned int address;
  
  if (argc != 3) {
    fprintf(stderr, "Usage: %s <old file> <new file>\n", argv[0]);
    return 1;
  }

  fh_old = fopen(argv[1], "rb");
  if (fh_old == NULL) {
    fprintf(stderr, "Error: Unable to open file for reading: %s\n", argv[1]);
    return 1;
  }

  fh_new = fopen(argv[2], "wb");
  if (fh_new == NULL) {
    fprintf(stderr, "Error: Unable to open file for writing: %s\n", argv[2]);
    fclose(fh_old);
    return 1;
  }

  patch = patch_read(stdin);

  address = 0;
  while ((c_old = fgetc(fh_old)) != EOF) {
    c_new = patch_lookup(patch, address);
    if (c_new != EOF) {
      fputc(c_new, fh_new);
    } else {
      fputc(c_old, fh_new);
    }
    address++;
  }

  patch_free(patch);

  fclose(fh_old);
  fclose(fh_new);

  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 17/06-2013, Article Link