Home | History | Annotate | Line # | Download | only in binpatch
      1 /* $NetBSD: binpatch.c,v 1.7 2016/09/22 17:08:16 christos Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2009 Izumi Tsutsui.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 /*
     28  * Copyright (c) 1996 Christopher G. Demetriou
     29  * All rights reserved.
     30  *
     31  * Redistribution and use in source and binary forms, with or without
     32  * modification, are permitted provided that the following conditions
     33  * are met:
     34  * 1. Redistributions of source code must retain the above copyright
     35  *    notice, this list of conditions and the following disclaimer.
     36  * 2. Redistributions in binary form must reproduce the above copyright
     37  *    notice, this list of conditions and the following disclaimer in the
     38  *    documentation and/or other materials provided with the distribution.
     39  * 3. The name of the author may not be used to endorse or promote products
     40  *    derived from this software without specific prior written permission.
     41  *
     42  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     43  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     44  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     45  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     46  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     47  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     48  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     49  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     50  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     51  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     52  *
     53  * <<Id: LICENSE_GC,v 1.1 2001/10/01 23:24:05 cgd Exp>>
     54  */
     55 
     56 #include <sys/cdefs.h>
     57 #ifndef lint
     58 __COPYRIGHT("@(#) Copyright (c) 1996\
     59  Christopher G. Demetriou.  All rights reserved.");
     60 #endif /* not lint */
     61 
     62 #ifndef lint
     63 __RCSID("$NetBSD: binpatch.c,v 1.7 2016/09/22 17:08:16 christos Exp $");
     64 #endif /* not lint */
     65 
     66 #include <sys/types.h>
     67 #include <sys/mman.h>
     68 #include <sys/stat.h>
     69 #include <sys/inttypes.h>
     70 
     71 #include <err.h>
     72 #include <fcntl.h>
     73 #include <limits.h>
     74 #include <nlist.h>
     75 #include <stdio.h>
     76 #include <stdlib.h>
     77 #include <stdbool.h>
     78 #include <unistd.h>
     79 
     80 #include "extern.h"
     81 
     82 static void	usage(void) __dead;
     83 
     84 bool replace, verbose;
     85 u_long addr, offset;
     86 char *symbol;
     87 size_t size;
     88 uint64_t val;
     89 
     90 static const struct {
     91 	const char *name;
     92 	int	(*check)(const char *, size_t);
     93 	int	(*findoff)(const char *, size_t, u_long, size_t *, u_long);
     94 } exec_formats[] = {
     95 #ifdef NLIST_AOUT
     96 	{	"a.out",	check_aout,	findoff_aout,	},
     97 #endif
     98 #ifdef NLIST_ECOFF
     99 	{	"ECOFF",	check_ecoff,	findoff_ecoff,	},
    100 #endif
    101 #ifdef NLIST_ELF32
    102 	{	"ELF32",	check_elf32,	findoff_elf32,	},
    103 #endif
    104 #ifdef NLIST_ELF64
    105 	{	"ELF64",	check_elf64,	findoff_elf64,	},
    106 #endif
    107 #ifdef NLIST_COFF
    108 	{	"COFF",		check_coff,	findoff_coff,	},
    109 #endif
    110 };
    111 
    112 
    113 int
    114 main(int argc, char *argv[])
    115 {
    116 	const char *fname;
    117 	struct stat sb;
    118 	struct nlist nl[2];
    119 	char *mappedfile;
    120 	size_t valoff;
    121 	void *valp;
    122 	uint8_t uval8;
    123 	int8_t  sval8;
    124 	uint16_t uval16;
    125 	int16_t  sval16;
    126 	uint32_t uval32;
    127 	int32_t  sval32;
    128 	uint64_t uval64;
    129 	int64_t  sval64;
    130 	int ch, fd, rv, i, n;
    131 	u_long	text_start;		/* Start of kernel text (a.out) */
    132 
    133 	setprogname(argv[0]);
    134 	text_start = (unsigned long)~0;
    135 
    136 	while ((ch = getopt(argc, argv, "bwldT:a:s:o:r:v")) != -1)
    137 		switch (ch) {
    138 		case 'b':
    139 			size = sizeof(uint8_t);
    140 			break;
    141 		case 'w':
    142 			size = sizeof(uint16_t);
    143 			break;
    144 		case 'l':
    145 			size = sizeof(uint32_t);
    146 			break;
    147 		case 'd':
    148 			size = sizeof(uint64_t);
    149 			break;
    150 		case 'a':
    151 			if (addr != 0 || symbol != NULL)
    152 				errx(EXIT_FAILURE,
    153 				    "only one address/symbol allowed");
    154 			addr = strtoul(optarg, NULL, 0);
    155 			break;
    156 		case 's':
    157 			if (addr != 0 || symbol != NULL)
    158 				errx(EXIT_FAILURE,
    159 				    "only one address/symbol allowed");
    160 			symbol = optarg;
    161 			break;
    162 		case 'o':
    163 			if (offset != 0)
    164 				err(EXIT_FAILURE,
    165 				    "only one offset allowed");
    166 			offset = strtoul(optarg, NULL, 0);
    167 			break;
    168 		case 'r':
    169 			replace = true;
    170 			val = strtoull(optarg, NULL, 0);
    171 			break;
    172 		case 'v':
    173 			verbose = true;
    174 			break;
    175 		case 'T':
    176 			text_start = strtoul(optarg, NULL, 0);
    177 			break;
    178 		case '?':
    179 		default:
    180 			usage();
    181 	}
    182 	argc -= optind;
    183 	argv += optind;
    184 
    185 	if (argc != 1)
    186 		usage();
    187 
    188 	if (addr == 0 && symbol == NULL) {
    189 		warnx("no address or symbol specified");
    190 		usage();
    191 	}
    192 
    193 	if (size == 0)
    194 		size = sizeof(uint32_t);	/* default to int */
    195 
    196 	fname = argv[0];
    197 
    198 	if ((fd = open(fname, replace ? O_RDWR : O_RDONLY, 0))  == -1)
    199 		err(EXIT_FAILURE, "open %s", fname);
    200 
    201 	if (symbol != NULL) {
    202 		nl[0].n_name = symbol;
    203 		nl[1].n_name = NULL;
    204 		if ((rv = __fdnlist(fd, nl)) != 0)
    205 			errx(EXIT_FAILURE, "could not find symbol %s in %s",
    206 			    symbol, fname);
    207 		addr = nl[0].n_value;
    208 		if (verbose)
    209 			fprintf(stderr, "got symbol address 0x%lx from %s\n",
    210 			    addr, fname);
    211 	}
    212 
    213 	addr += offset * size;
    214 
    215 	if (fstat(fd, &sb) == -1)
    216 		err(EXIT_FAILURE, "fstat %s", fname);
    217 	if (sb.st_size != (ssize_t)sb.st_size)
    218 		errx(EXIT_FAILURE, "%s too big to map", fname);
    219 
    220 	if ((mappedfile = mmap(NULL, sb.st_size,
    221 	    replace ? PROT_READ | PROT_WRITE : PROT_READ,
    222 	    MAP_FILE | MAP_SHARED, fd, 0)) == (char *)-1)
    223 		err(EXIT_FAILURE, "mmap %s", fname);
    224 	if (verbose)
    225 		fprintf(stderr, "mapped %s\n", fname);
    226 
    227 	n = __arraycount(exec_formats);
    228 	for (i = 0; i < n; i++) {
    229 		if ((*exec_formats[i].check)(mappedfile, sb.st_size) == 0)
    230 			break;
    231 	}
    232 	if (i == n)
    233 		errx(EXIT_FAILURE, "%s: unknown executable format", fname);
    234 
    235 	if (verbose) {
    236 		fprintf(stderr, "%s is an %s binary\n", fname,
    237 		    exec_formats[i].name);
    238 		if (text_start != (u_long)~0)
    239 			fprintf(stderr, "kernel text loads at 0x%lx\n",
    240 			    text_start);
    241 	}
    242 
    243 	if ((*exec_formats[i].findoff)(mappedfile, sb.st_size,
    244 	    addr, &valoff, text_start) != 0)
    245 		errx(EXIT_FAILURE, "couldn't find file offset for %s in %s",
    246 		    symbol != NULL ? nl[0].n_name : "address" , fname);
    247 
    248 	valp = mappedfile + valoff;
    249 
    250 	if (symbol)
    251 		printf("%s(0x%lx): ", symbol, addr);
    252 	else
    253 		printf("0x%lx: ", addr);
    254 
    255 	switch (size) {
    256 	case sizeof(uint8_t):
    257 		uval8 = *(uint8_t *)valp;
    258 		sval8 = *(int8_t *)valp;
    259 		printf("0x%02" PRIx8 " (%" PRIu8, uval8, uval8);
    260 		if (sval8 < 0)
    261 			printf("/%" PRId8, sval8);
    262 		printf(")");
    263 		break;
    264 	case sizeof(uint16_t):
    265 		uval16 = *(uint16_t *)valp;
    266 		sval16 = *(int16_t *)valp;
    267 		printf("0x%04" PRIx16 " (%" PRIu16, uval16, uval16);
    268 		if (sval16 < 0)
    269 			printf("/%" PRId16, sval16);
    270 		printf(")");
    271 		break;
    272 	case sizeof(uint32_t):
    273 		uval32 = *(uint32_t *)valp;
    274 		sval32 = *(int32_t *)valp;
    275 		printf("0x%08" PRIx32 " (%" PRIu32, uval32, uval32);
    276 		if (sval32 < 0)
    277 			printf("/%" PRId32, sval32);
    278 		printf(")");
    279 		break;
    280 	case sizeof(uint64_t):
    281 		uval64 = *(uint64_t *)valp;
    282 		sval64 = *(int64_t *)valp;
    283 		printf("0x%016" PRIx64 " (%" PRIu64, uval64, uval64);
    284 		if (sval64 < 0)
    285 			printf("/%" PRId64, sval64);
    286 		printf(")");
    287 		break;
    288 	}
    289 	printf(", at offset %#lx in %s\n", (unsigned long)valoff, fname);
    290 
    291 	if (!replace)
    292 		goto done;
    293 
    294 	printf("new value: ");
    295 
    296 	switch (size) {
    297 	case sizeof(uint8_t):
    298 		uval8 = (uint8_t)val;
    299 		sval8 = (int8_t)val;
    300 		printf("0x%02" PRIx8 " (%" PRIu8, uval8, uval8);
    301 		if (sval8 < 0)
    302 			printf("/%" PRId8, sval8);
    303 		printf(")");
    304 		*(uint8_t *)valp = uval8;
    305 		break;
    306 	case sizeof(uint16_t):
    307 		uval16 = (uint16_t)val;
    308 		sval16 = (int16_t)val;
    309 		printf("0x%04" PRIx16 " (%" PRIu16, uval16, uval16);
    310 		if (sval16 < 0)
    311 			printf("/%" PRId16, sval16);
    312 		printf(")");
    313 		*(uint16_t *)valp = uval16;
    314 		break;
    315 	case sizeof(uint32_t):
    316 		uval32 = (uint32_t)val;
    317 		sval32 = (int32_t)val;
    318 		printf("0x%08" PRIx32 " (%" PRIu32, uval32, uval32);
    319 		if (sval32 < 0)
    320 			printf("/%" PRId32, sval32);
    321 		printf(")");
    322 		*(uint32_t *)valp = uval32;
    323 		break;
    324 	case sizeof(uint64_t):
    325 		uval64 = (uint64_t)val;
    326 		sval64 = (int64_t)val;
    327 		printf("0x%016" PRIx64 " (%" PRIu64, uval64, uval64);
    328 		if (sval64 < 0)
    329 			printf("/%" PRId64, sval64);
    330 		printf(")");
    331 		*(uint64_t *)valp = uval64;
    332 		break;
    333 	}
    334 	printf("\n");
    335 
    336  done:
    337 	munmap(mappedfile, sb.st_size);
    338 	close(fd);
    339 
    340 	if (verbose)
    341 		fprintf(stderr, "exiting\n");
    342 	exit(EXIT_SUCCESS);
    343 }
    344 
    345 static void
    346 usage(void)
    347 {
    348 
    349 	fprintf(stderr,
    350 	    "Usage: %s [-b|-w|-l|-d] [-a address | -s symbol] [-o offset]"
    351 	    " [-r value] [-T text_start] [-v] binary\n", getprogname());
    352 	exit(EXIT_FAILURE);
    353 }
    354