/** Projekt PATCHER - File Patcher **/
/* Cop. 2015, 2016 asdala.de. Alle Rechte vorbehalten. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PRGNAME "patcher"
#define PRGVER "1.1"
#define BUFSIZE 2048
#define MAXLN 160
static char fname[128], fbakname[128], buf[BUFSIZE], ln[MAXLN];
static FILE *f, *fbak, *fpatchinfo;
/** Drucke Programmhilfe **/
static int help(void)
{
puts(
PRGNAME " " PRGVER " - Datei-Patcher.\n\n"
"Syntax 1: " PRGNAME " [/?] <Datei> <Offset> <Patch>\n"
"Syntax 2: " PRGNAME " [/?] <Datei> <Patchinfodatei>\n"
"\n"
"Bsp. 1: " PRGNAME " file.exe 18EF 735C0479B537D4C58487\n"
"Bsp. 2: " PRGNAME " file.exe patchinfo.txt\n"
"\n"
"Bsp. Patchinfodatei mit Kommentaren und unterschiedlichen Notationen:\n"
"# Update section headers to add space for code.\n"
"00000118: 98\n"
"00000119: 9B\n"
"\n"
"# Jump to additional code for adding SNI.\n"
"0000EACC: E9 6A 68 01 00 90 90 90 90 90\n"
"\n"
"# Updated OpenSSL function names list.\n"
"00028690:\n"
"65 72 73 69-6F 6E 00 53-53 4C 5F 6C-69 62 72 61\n"
"72 79 5F 69-6E 69 74 00-53 53 4C 5F-43 54 58 5F\n"
);
return 1;
}
/** Programmabbruch **/
static int errout(const char *s, int lnno)
{
fprintf(stderr,PRGNAME "\a abgebrochen");
if (lnno)
fprintf(stderr," bei Zeile %d",lnno);
fprintf(stderr,": %s!\n",s);
if (f)
fclose(f);
if (fbak)
fclose(fbak);
if (fpatchinfo)
fclose(fpatchinfo);
return 2;
}
/** Melde Dateigroesse **/
static unsigned long filesize(FILE *f)
{
unsigned long i, curpos = ftell(f);
fseek(f,0L,SEEK_END);
i = ftell(f);
fseek(f,curpos,SEEK_SET);
return i;
}
/** Setze Dateierweiterung **/
static void setext(char *s, const char *ext)
{
int i;
i = strlen(s);
if (i > 4 && s[i-4] == '.')
i -= 4;
strcpy(s+i,ext);
}
/** Wandle 1 Hexzeichen in Halbbyte -> -1 bei Fehler **/
__inline char x2h(char c)
{
if (c>='0' && c<='9')
return (char)(c-'0');
c &= 0xdf;
if (c>='A' && c<='F')
return (char)(c-'A'+10);
return (char)-1;
}
/** Wandle 2 Hexzeichen in Byte ("41" -> 'A') -> 0 bei Fehler **/
static int x2c(const char *s, char *c)
{
register char hb1, hb2;
if ((hb1=x2h(*s)) < 0 || (hb2=x2h(*(s+1))) < 0)
return 0;
*c = (char)((hb1<<4)+hb2);
return 1;
}
/** Wandle Hexstring in Bytes ("4142" -> "AB") -> -1 bei Fehler, 0 falls leer, Bytezahl **/
static int x2s(const char *s1, char *s2)
{
char *s2start = s2;
while (*s1) {
while (*s1==' ' || *s1=='-' || *s1=='\n')
++s1;
if (*s1 == 0)
break;
if (!x2c(s1,s2))
return -1;
s1 += 2;
++s2;
}
return s2 - s2start;
}
/** Drucke sedezimales Speicherabbild **/
static void printh(const char a[], int alen)
{
register int i;
if (alen) {
for (i=0; i<alen; i++)
printf("%02X%c", (unsigned char)a[i],i%16==15?'\n':' ');
if (i%16)
puts("");
}
}
/** Main **/
int main(int argc, char *argv[])
{
int i, lnno;
unsigned long offset, fsize;
char *p;
/* Lies Programmargumente */
if (argc < 3)
return help();
strcpy(fname,argv[1]);
if ((f=fopen(fname, "r+b")) == NULL)
return errout("Kann zu patchende Datei nicht lesen",0);
fsize = filesize(f);
strcpy(fbakname,fname); setext(fbakname,".bak");
if ((fbak=fopen(fbakname, "wb")) == NULL)
return errout("Kann zu patchende Datei nicht sichern",0);
do {
i = fread(buf,1,BUFSIZE,f);
fwrite(buf,1,i,fbak);
} while (i == BUFSIZE);
fclose(fbak); fbak = NULL;
/* Lese Patch von Kommandozeile oder Patchinfodatei? */
offset = strtoul(argv[2],&p,16);
if (*p == 0) {
/* Lese Kommandozeile, da gueltiges Offset-Argument */
if (argc < 4)
return help();
if (offset >= fsize)
return errout("Offset groesser als Datei",0);
fseek(f,offset,SEEK_SET);
if ((i=x2s(argv[3],buf)) <= 0)
return errout("Nonsedezimaler Patch",0);
printf("# Patche bei %02lX: ",offset);
printh(buf,i);
fwrite(buf,1,i,f);
}
else {
/* Lese Patchinfodatei, da ungueltiges Offset-Argument */
if ((fpatchinfo=fopen(argv[2], "rt")) == NULL)
return errout("Patchinfodatei nicht lesbar",0);
lnno = 0;
while ((p=fgets(ln,MAXLN,fpatchinfo)) != NULL) {
++lnno;
if (strlen(ln) == MAXLN-1)
return errout("Zeile zu lang",lnno);
if (*p == '#' || *p == '\n')
continue;
/* Patch-Offset vorhanden? */
if (strchr(p,':')) {
offset = strtoul(p,&p,16);
if (*p++ != ':')
return errout("Ungueltige Offset-Syntax",lnno);
if (offset >= fsize)
return errout("Offset groesser als Datei",lnno);
fseek(f,offset,SEEK_SET);
printf("\n# Patche bei %02lX:\n",offset);
}
/* Patch-Inhalt vorhanden? */
if ((i=x2s(p,buf)) < 0)
return errout("Nonsedezimaler Patch",lnno);
printh(buf,i);
fwrite(buf,1,i,f);
} /* while */
fclose(fpatchinfo); fpatchinfo = NULL;
}
fclose(f); f = NULL;
return 0;
}