From 61e9c92033270017e5d9ced4eb917adbc4511595 Mon Sep 17 00:00:00 2001 From: atatat Date: Fri, 23 Jul 2004 03:37:26 +0000 Subject: A small package to arbitrary tiny patches to binaries (where the source cannot otherwise be patched). --- pkgtools/binpatch/DESCR | 2 + pkgtools/binpatch/Makefile | 31 +++++++ pkgtools/binpatch/PLIST | 3 + pkgtools/binpatch/files/binpatch.1 | 116 +++++++++++++++++++++++++ pkgtools/binpatch/files/binpatch.c | 174 +++++++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+) create mode 100644 pkgtools/binpatch/DESCR create mode 100644 pkgtools/binpatch/Makefile create mode 100644 pkgtools/binpatch/PLIST create mode 100644 pkgtools/binpatch/files/binpatch.1 create mode 100644 pkgtools/binpatch/files/binpatch.c (limited to 'pkgtools') diff --git a/pkgtools/binpatch/DESCR b/pkgtools/binpatch/DESCR new file mode 100644 index 00000000000..64ce39d7776 --- /dev/null +++ b/pkgtools/binpatch/DESCR @@ -0,0 +1,2 @@ +Apply small, arbitrary binary patches using an arcane command line +syntax. diff --git a/pkgtools/binpatch/Makefile b/pkgtools/binpatch/Makefile new file mode 100644 index 00000000000..fd20af1eada --- /dev/null +++ b/pkgtools/binpatch/Makefile @@ -0,0 +1,31 @@ +# $NetBSD: Makefile,v 1.1.1.1 2004/07/23 03:37:26 atatat Exp $ +# + +DISTNAME= binpatch-1.0 +CATEGORIES= pkgtools +MASTER_SITES= # empty +DISTFILES= # empty + +MAINTAINER= atatat@NetBSD.org +HOMEPAGE= ftp://ftp.NetBSD.org/pub/NetBSD/packages/pkgsrc/Packages.txt +COMMENT= Trivial binary patch applicator + +USE_BUILDLINK3= yes + +NO_CHECKSUM= # defined + +.include "../../mk/bsd.prefs.mk" + +do-extract: + @${CP} -Rp ${FILESDIR} ${WRKSRC} + +do-build: + @(cd ${WRKSRC}; \ + ${ECHO} "${CC} -o binpatch binpatch.c"; \ + ${CC} -o binpatch binpatch.c ) + +do-install: + ${INSTALL_PROGRAM} ${WRKSRC}/binpatch ${PREFIX}/bin/binpatch + ${INSTALL_MAN} ${WRKSRC}/binpatch.1 ${PREFIX}/man/man1 + +.include "../../mk/bsd.pkg.mk" diff --git a/pkgtools/binpatch/PLIST b/pkgtools/binpatch/PLIST new file mode 100644 index 00000000000..13f0c0b99f8 --- /dev/null +++ b/pkgtools/binpatch/PLIST @@ -0,0 +1,3 @@ +@comment $NetBSD: PLIST,v 1.1.1.1 2004/07/23 03:37:26 atatat Exp $ +bin/binpatch +man/man1/binpatch.1 diff --git a/pkgtools/binpatch/files/binpatch.1 b/pkgtools/binpatch/files/binpatch.1 new file mode 100644 index 00000000000..8e7ef0930e5 --- /dev/null +++ b/pkgtools/binpatch/files/binpatch.1 @@ -0,0 +1,116 @@ +.\" $NetBSD: binpatch.1,v 1.1.1.1 2004/07/23 03:37:26 atatat Exp $ +.\" +.\" Copyright (c) 2004 by Andrew Brown +.\" Absolutely no warranty. +.\" +.Dd July 20, 2004 +.Dt BINPATCH 1 +.Sh NAME +.Nm binpatch +.Nd trivial binary patch applicator +.Sh SYNOPSIS +.Nm +.Pa file=... +.Pa size=... +.Pa offset=... +.Pa compare=... +.Pa skip=... +.Pa replace=... +.Sh DESCRIPTION +The +.Nm +utility can read and replace a small section of a given file. +It is designed for use in those instances where a problem exists with +a given binary that cannot be reconstructed from source code, but the +required change can be implemented by replacing a few bytes in the +existing binary. +All arguments must be given. +.Sh EXAMPLES +Given a binary called +.Dq a.out +of 10713 bytes in size with the following text segment: +.Bd -literal -offset indent +% objdump -h a.out +.sp +a.out: file format elf32-i386 +.sp +Sections: +Idx Name Size VMA LMA File off Algn +[...] + 9 .text 00000be4 08048968 08048968 00000968 2**2 + CONTENTS, ALLOC, LOAD, READONLY, CODE +[...] +% objdump -d -j .text a.out +[...] + 8048b0f: 83 ef 04 sub $0x4,%edi + 8048b12: ff d0 call *%eax + 8048b14: 83 fe ff cmp $0xffffffff,%esi +[...] +.sp +.Ed +where we wish to elide the call through +.Ar %eax +by replacing it with a series of +.Ar nop +(or +.Dq no operation ) +instructions (the machine code for this on the i386 platform is 0x90), +we first calculate the offset into the file of the previous +.Ar sub +instruction. To do this, we take the address of the +.Ar sub +instruction as given by the dissassembly output, subtract the +.Dq LMA +and add the +.Dq File off +values from the objdump output (note that +.Xr bc 1 +expects hexadecimal values to be given using upper case): +.Bd -literal -offset indent +% bc +ibase=16 +8048B0F-08048968+00000968 +2831 +.sp +.Ed +The region of the binary we want to compare to before applying the +patch is the concatenation of the relevant machine codes from the +dissassembly dump (\c +.Ar 83ef04ffd083feff ) +and the replacement is simply two +.Ar nop +instructions (\c +.Ar 9090 ) , +that will replace the +.Ar ffd0 +of the original call. +The offset of the replacement is 3, since that is the number of bytes +in the +.Ar sub +instruction. +From this we have our patch: +.Bd -literal -offset indent +% binpatch file=a.out size=10713 offset=2831 \\ + compare=83ef04ffd083feff skip=3 replace=9090 +% objdump -d -j .text a.out +[...] + 8048b0f: 83 ef 04 sub $0x4,%edi + 8048b12: 90 nop + 8048b13: 90 nop + 8048b14: 83 fe ff cmp $0xffffffff,%esi +[...] +.sp +.Ed +And thus the call is removed. +.Sh DIAGNOSTICS +The diagnostics are terse and almost unhelpful, but are more verbose +than users of +.Xr ed 1 +might be used to. +They typically mention the command line argument that was in error. +.Sh SEE ALSO +.Xr bc 1 , +.Xr objdump 1 , +.Xr patch 1 +.Sh AUTHORS +.An Andrew Brown Aq atatat@netbsd.org diff --git a/pkgtools/binpatch/files/binpatch.c b/pkgtools/binpatch/files/binpatch.c new file mode 100644 index 00000000000..a80667f0bd4 --- /dev/null +++ b/pkgtools/binpatch/files/binpatch.c @@ -0,0 +1,174 @@ +/* $NetBSD: binpatch.c,v 1.1.1.1 2004/07/23 03:37:26 atatat Exp $ */ + +/* + * ------------------------------------------------------------------------ + * "THE BEER-WARE LICENSE" (Revision 42): + * Andrew Brown wrote this file. As long as you + * retain this notice you can do whatever you want with this stuff. + * If we meet some day, and you think this stuff is worth it, you can + * buy me a beer in return. + * ------------------------------------------------------------------------ + */ + +#include +#include + +#include +#include +#include +#include +#include + +static int +die(int rc, const char *msg) +{ + if (rc) + perror(msg); + else + fprintf(stderr, "%s\n", msg); + exit(1); +} + +static void +cvt(const char *s, const char *i, unsigned char *o, size_t l) +{ + int t, x; + + for (t = 0; t < l; t++) { + x = i[2 * t]; + if (x >= '0' && x <= '9') + x -= '0'; + else if (x >= 'a' && x <= 'f') + x -= 'a' - 10; + else if (x >= 'A' && x <= 'F') + x -= 'A' - 10; + else + die(0, s); + o[t] = x * 16; + + x = i[2 * t + 1]; + if (x >= '0' && x <= '9') + x -= '0'; + else if (x >= 'a' && x <= 'f') + x -= 'a' - 10; + else if (x >= 'A' && x <= 'F') + x -= 'A' - 10; + else + die(0, s); + o[t] += x; + } +} + +int +main(int argc, char *argv[]) +{ + struct stat st; + char *key, *value; + int f, i; + unsigned char *buf, *get, *put; + size_t lget, lput; + off_t sz, cmp_off, skip_off; + + f = -1; + get = put = NULL; + lget = lput = 0; + sz = cmp_off = skip_off = -1; + + while (--argc > 0) { + key = *++argv; + if ((value = strchr(key, '=')) == NULL) + die(0, "value required"); + else + *value++ = '\0'; + + if (strcmp(key, "file") == 0) { + f = open(value, O_RDWR); + if (f == -1) + die(1, "file"); + } + else if (strcmp(key, "size") == 0) { + char *t; + errno = 0; + sz = strtol(value, &t, 0); + if (errno != 0) + die(1, "size"); + } + else if (strcmp(key, "offset") == 0) { + char *t; + errno = 0; + cmp_off = strtol(value, &t, 0); + if (errno != 0) + die(1, "offset"); + } + else if (strcmp(key, "compare") == 0) { + lget = strlen(value); + if (lget % 2 != 0) + die(0, "compare"); + lget /= 2; + get = malloc(lget); + buf = malloc(lget); + cvt("compare", value, get, lget); + } + else if (strcmp(key, "skip") == 0) { + char *t; + errno = 0; + skip_off = strtol(value, &t, 0); + if (errno != 0) + die(1, "offset"); + } + else if (strcmp(key, "replace") == 0) { + lput = strlen(value); + if (lput % 2 != 0) + die(0, "replace"); + lput /= 2; + put = malloc(lput); + cvt("replace", value, put, lput); + } + } + + /* + * ./binpatch + * file=${MOZILLA_HOME}/netscape + * size=13823336 + * offset=0x008073e9 + * compare=6a00e82406a3ffe81f0ca3ff + * skip=2 + * replace=9090909090 + */ + + if (f == -1) + die(0, "file missing"); + if (get == NULL || lget == 0) + die(0, "compare missing"); + if (put == NULL || lput == 0) + die(0, "replace missing"); + if (sz == -1) + die(0, "size missing"); + if (cmp_off == -1) + die(0, "offset missing"); + if (skip_off == -1) + die(0, "skip missing"); + if (skip_off < 0 || + (skip_off == 0 && lput >= lget) || + (skip_off > 0 && skip_off + lput > lget)) + die(0, "illegal skip"); + + if (fstat(f, &st) == -1) + die(1, "fstat"); + if (st.st_size != sz) + die(0, "wrong size"); + if (lseek(f, cmp_off, SEEK_SET) == -1) + die(1, "lseek"); + if (read(f, buf, lget) != lget) + die(1, "read"); + if (memcmp(buf, get, lget) != 0) + die(0, "instructions not found"); + if (lseek(f, cmp_off + skip_off, SEEK_SET) == -1) + die(1, "lseek"); + if (write(f, put, lput) != lput) + die(1, "write"); + if (close(f) != 0) + die(1, "close"); + + return (0); +} -- cgit v1.2.3