Home | History | Annotate | Line # | Download | only in binpatch
      1 /*	$NetBSD: binpatch.c,v 1.16 2025/08/18 20:59:56 andvar Exp $	*/
      2 
      3 /* Author: Markus Wild mw (at) eunet.ch ???   */
      4 /* Modified: Rob Leland leland (at) mitre.org */
      5 
      6 #include <sys/types.h>
      7 #include <a.out.h>
      8 #include <fcntl.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <unistd.h>
     13 
     14 #ifdef __NetBSD__
     15 /*
     16  * assume NMAGIC files are linked at 0 (for kernel)
     17  */
     18 #undef N_TXTADDR
     19 #define N_TXTADDR(ex) \
     20 	((N_GETMAGIC2(ex) == (ZMAGIC|0x10000) || N_GETMAGIC2(ex) == NMAGIC) ? \
     21 	0 : AOUT_LDPGSZ)
     22 #endif
     23 
     24 
     25 static char synusage[] =
     26 "NAME\n"
     27 "\t%s - Allows the patching of BSD binaries\n"
     28 "SYNOPSIS\n"
     29 "\t%s [-HELP]\n"
     30 "\t%s [-b|-w|-l] -s symbol[[[index]][=value]] binary\n"
     31 "\t%s [-b|-w|-l] [-o offset] -s symbol [-r value] binary\n"
     32 "\t%s [-b|-w|-l] [-o offset] -a address [-r value] binary\n";
     33 
     34 static char desusage[] =
     35 "DESCRIPTION\n"
     36 "\tAllows the patching of BSD binaries, for example, a distributed\n"
     37 "\tkernel. Recent additions allows the user to index into an array\n"
     38 "\tand assign a value. Binpatch has internal variables to allow\n"
     39 "\tyou to test it on itself under NetBSD.\n"
     40 "OPTIONS\n"
     41 "\t-a  patch variable by specifying address in hex\n"
     42 "\t-b  symbol or address to be patched is 1 byte\n"
     43 "\t-l  symbol or address to be patched is 4 bytes  (default)\n"
     44 "\t-o  offset to begin patching value relative to symbol or address\n"
     45 "\t-r  replace value, and print out previous value to stdout\n"
     46 "\t-s  patch variable by specifying symbol name. Use '[]'\n"
     47 "\t    to specify the 'index'. If '-b, -w or -l' not specified\n"
     48 "\t    then index value is used like an offset. Also can use '='\n"
     49 "\t    to assign value\n"
     50 "\t-w  symbol or address to be patched is 2 bytes\n"
     51 "EXAMPLES\n"
     52 "\tThis should print 100 (this is a nice reality check...)\n"
     53 "\t\tbinpatch -l -s _hz netbsd\n"
     54 "\tNow it gets more advanced, replace the value:\n"
     55 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd\n"
     56 "\tNow patch a variable at a given 'index' not offset,\n"
     57 "\tunder NetBSD you must use '', under AmigaDos CLI '' is optional.:\n"
     58 "\t\tbinpatch -w -s '_vieww[4]' -r 0 a.out\n"
     59 "\tsame as\n"
     60 "\t\tbinpatch -w -o 8 -s _vieww -r 0 a.out\n"
     61 "\tAnother example of using []\n"
     62 "\t\tbinpatch -s '_viewl[4]' -r 0 a.out\n"
     63 "\tsame as\n"
     64 "\t\tbinpatch -o 4 -s _viewl -r 0 a.out\n"
     65 "\tOne last example using '=' and []\n"
     66 "\t\tbinpatch -w -s '_vieww[4]=2' a.out\n"
     67 "\tSo if the kernel is not finding your drives, you could enable\n"
     68 "\tall available debugging options, helping to shed light on that problem.\n"
     69 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd	scsi-level\n"
     70 "\t\tbinpatch -l -s _sddebug -r 1 netbsd	sd-level (disk-driver)\n"
     71 "\t\tbinpatch -l -s _acdebug -r 1 netbsd	autoconfig-level\n"
     72 "SEE ALSO\n"
     73 "\tbinpatch.c binpatch(1)\n";
     74 
     75 extern char *optarg;
     76 extern int optind;
     77 
     78 void error(char *) __attribute__((__noreturn__));
     79 static void Synopsis(char *program_name);
     80 static void Usage(char *program_name);
     81 static u_long FindAssign(char *symbol, u_long *rvalue);
     82 static void FindOffset(char *symbol, u_long *index);
     83 
     84 /* The following variables are so binpatch can be tested on itself */
     85 int test = 1;
     86 int testbss;
     87 char foo = 23;
     88 char  viewb[10] = {0,0,1,0,1,1,0,1,1,1};
     89 short vieww[10] = {0,0,1,0,1,1,0,1,1,1};
     90 long  viewl[10] = {0,0,1,0,1,1,0,1,1,1};
     91 /* End of test binpatch variables */
     92 
     93 int
     94 main(int argc, char *argv[])
     95 {
     96 	struct exec e;
     97 	int c;
     98 	u_long addr = 0, offset = 0;
     99 	/* Related to offset */
    100 	u_long index = 0;
    101 	u_long replace = 0, do_replace = 0;
    102 	char *symbol = 0;
    103 	/* default to long */
    104 	char size = 4;
    105 	/* Flag to say size option was set, used with index */
    106 	char size_opt = 0;
    107 	char *fname;
    108 	/* Program name */
    109 	char *pgname = argv[0];
    110 	int fd;
    111 	int type, off;
    112 	u_long  lval;
    113 	u_short sval;
    114 	u_char  cval;
    115 
    116 
    117 	while ((c = getopt(argc, argv, "H:a:bwlr:s:o:")) != -1) {
    118 		switch (c) {
    119 		case 'H':
    120 			Usage(argv[0]);
    121 			break;
    122 		case 'a':
    123 			if (addr || symbol) {
    124 				error("only one address/symbol allowed");
    125 			}
    126 			if (!strncmp(optarg, "0x", 2)) {
    127 				sscanf(optarg, "%x", &addr);
    128 			} else {
    129 				addr = atoi(optarg);
    130 			}
    131 			if (!addr) {
    132 				error("invalid address");
    133 			}
    134 			break;
    135 
    136 		case 'b':
    137 			size = 1;
    138 			size_opt = 1;
    139 			break;
    140 
    141 		case 'w':
    142 			size = 2;
    143 			size_opt = 1;
    144 			break;
    145 
    146 		case 'l':
    147 			size = 4;
    148 			size_opt = 1;
    149 			break;
    150 
    151 		case 'r':
    152 			do_replace = 1;
    153 			if (!strncmp(optarg, "0x", 2)) {
    154 				sscanf(optarg, "%x", &replace);
    155 			} else {
    156 				replace = atoi(optarg);
    157 			}
    158 			break;
    159 
    160 		case 's':
    161 			if (addr || symbol) {
    162 				error("only one address/symbol allowed");
    163 			}
    164 			symbol = optarg;
    165 			break;
    166 
    167 		case 'o':
    168 			if (offset) {
    169 				error("only one offset allowed");
    170 			}
    171 			if (!strncmp(optarg, "0x", 2)) {
    172 				sscanf(optarg, "%x", &offset);
    173 			} else {
    174 				offset = atoi(optarg);
    175 			}
    176 			break;
    177 		}
    178 		/* end while switch() */
    179 	}
    180 
    181 	if (argc > 1) {
    182 		if (addr || symbol) {
    183 			argv += optind;
    184 			argc -= optind;
    185 
    186 			if (argc < 1) {
    187 				error("No file to patch.");
    188 			}
    189 
    190 			fname = argv[0];
    191 			if ((fd = open(fname, 0)) < 0) {
    192 				error("Can't open file");
    193 			}
    194 
    195 			if (read(fd, &e, sizeof(e)) != sizeof(e)
    196 			    || N_BADMAG(e)) {
    197 				error("Not a valid executable.");
    198 			}
    199 
    200 			/* fake mid, so the N_ macros work on the amiga.. */
    201 			e.a_midmag |= 127 << 16;
    202 
    203 			if (symbol) {
    204 				struct nlist nl[2];
    205 
    206 				if (offset == 0) {
    207 					u_long new_do_replace = 0;
    208 
    209 					new_do_replace = FindAssign(symbol,
    210 								&replace);
    211 					if (new_do_replace && do_replace)
    212 						error("Cannot use both '=' "
    213 						      "and '-r' option!");
    214 					FindOffset(symbol, &index);
    215 					if (size_opt) {
    216 						/* Treat like an index */
    217 						offset = index*size;
    218 					} else {
    219 						/* Treat like an offset */
    220 						offset = index;
    221 					}
    222 					if (new_do_replace)
    223 						do_replace = new_do_replace;
    224 				}
    225 				nl[0].n_un.n_name = symbol;
    226 				nl[1].n_un.n_name = 0;
    227 				if (nlist(fname, nl) != 0) {
    228 					fprintf(stderr, "Symbol is %s ",
    229 						symbol);
    230 					error("Symbol not found.");
    231 				}
    232 				addr = nl[0].n_value;
    233 				type = nl[0].n_type & N_TYPE;
    234 			} else {
    235 				type = N_UNDF;
    236 				if (addr >= N_TXTADDR(e) &&
    237 				    addr < N_DATADDR(e)) {
    238 					type = N_TEXT;
    239 				} else if (addr >= N_DATADDR(e) &&
    240 				    addr < N_DATADDR(e) + e.a_data) {
    241 					type = N_DATA;
    242 				}
    243 			}
    244 			addr += offset;
    245 
    246 			/*
    247 			 * if replace-mode, have to reopen the file
    248 			 * for writing. Can't do that from the
    249 			 * beginning, or nlist() will not work (at
    250 			 * least not under AmigaDOS)
    251 			 */
    252 			if (do_replace) {
    253 				close(fd);
    254 				if ((fd = open(fname, 2)) == -1) {
    255 					error("Can't reopen file for writing.");
    256 				}
    257 			}
    258 
    259 			if (type != N_TEXT && type != N_DATA) {
    260 				error("address/symbol is not in text "
    261 				      "or data section.");
    262 			}
    263 
    264 			if (type == N_TEXT) {
    265 				off = addr - N_TXTADDR(e) + N_TXTOFF(e);
    266 			} else {
    267 				off = addr - N_DATADDR(e) + N_DATOFF(e);
    268 			}
    269 
    270 			if (lseek(fd, off, 0) == -1) {
    271 				error("lseek");
    272 			}
    273 
    274 			/*
    275 			 * not beautiful, but works on big and little
    276 			 * endian machines
    277 			 */
    278 			switch (size) {
    279 			case 1:
    280 				if (read(fd, &cval, 1) != 1) {
    281 					error("cread");
    282 				}
    283 				lval = cval;
    284 				break;
    285 
    286 			case 2:
    287 				if (read(fd, &sval, 2) != 2) {
    288 					error("sread");
    289 				}
    290 				lval = sval;
    291 				break;
    292 
    293 			case 4:
    294 				if (read(fd, &lval, 4) != 4) {
    295 					error("lread");
    296 				}
    297 				break;
    298 			}/* switch size */
    299 
    300 
    301 			if (symbol) {
    302 				printf("%s(0x%x): %d (0x%x)\n", symbol, addr,
    303 					lval, lval);
    304 			} else {
    305 				printf("0x%x: %d (0x%x)\n", addr, lval, lval);
    306 			}
    307 
    308 			if (do_replace) {
    309 				if (lseek(fd, off, 0) == -1) {
    310 					error("write-lseek");
    311 				}
    312 				switch (size) {
    313 				case 1:
    314 					cval = replace;
    315 					if (cval != replace) {
    316 						error("byte-value overflow.");
    317 					}
    318 					if (write(fd, &cval, 1) != 1) {
    319 						error("cwrite");
    320 					}
    321 					break;
    322 
    323 				case 2:
    324 					sval = replace;
    325 					if (sval != replace) {
    326 						error("word-value overflow.");
    327 					}
    328 					if (write(fd, &sval, 2) != 2) {
    329 						error("swrite");
    330 					}
    331 					break;
    332 
    333 				case 4:
    334 					if (write(fd, &replace, 4) != 4) {
    335 						error("lwrite");
    336 					}
    337 					break;
    338 				}
    339 				/* end switch(size) */
    340 			}
    341 			/* end if (do_replace) */
    342 
    343 			close(fd);
    344 		} else {
    345 			/* not (addr || symbol) */
    346 			error("Must specify either address or symbol.");
    347 		}
    348 	} else {
    349 		/* if argc <= 1 */
    350 		Synopsis(pgname);
    351 	}
    352 
    353 	return 0;
    354 }
    355 /* end main () */
    356 
    357 
    358 
    359 void
    360 error(char *str)
    361 {
    362 	fprintf(stderr, "%s\n", str);
    363 	exit(1);
    364 }
    365 
    366 /* Give user very short help to avoid scrolling screen much */
    367 static void
    368 Synopsis(char *pgname)
    369 {
    370 	fprintf(stdout, synusage, pgname, pgname, pgname, pgname, pgname);
    371 }
    372 
    373 
    374 static void
    375 Usage(char *pgname)
    376 {
    377 	Synopsis(pgname);
    378 	fprintf(stdout, desusage);
    379 	exit(0);
    380 }
    381 
    382 
    383 /*
    384  * FindOffset() - Determine if there is an offset, -or- index
    385  * embedded in the symbol.
    386  *
    387  * If there is, return it, and truncate symbol to exclude the [...].
    388  *
    389  * Example: If view is declared as short view[10],
    390  *                and we want to index the 3rd. element.
    391  *                which is offset = (3 -1)*sizeof(short) =4.
    392  *          we would use view[4], which becomes view,4.
    393  *
    394  *          The way the code is implemented the [value] is
    395  *          treated as a index if-and-only-if a '-b -w -l' option
    396  *          was given. Otherwise it is treated like an offset.
    397  *           See above documentation in for of help!
    398  */
    399 static void
    400 FindOffset(char *symbol, u_long *index)
    401 {
    402 	/* Start of '[', now line must contain matching']' */
    403 	char *sb = strchr(symbol, '[');
    404 
    405 	/* End of ']' */
    406 	char *eb = strchr(symbol, ']');
    407 
    408 	/* symbol size */
    409 	short sz = strlen(symbol);
    410 
    411 	if (sb) {
    412 		if (eb && (eb > sb)) {
    413 			if ((eb - symbol) == (sz - 1)) {
    414 				/* Start of index */
    415 				char *sindex;
    416 				u_long newindex = 0;
    417 
    418 				/*
    419 				 * In the future we could get fancy
    420 				 * and parse the sindex string for
    421 				 * mathmatical expressions like: (3 -
    422 				 * 1)*2 = 4 from above example, ugh
    423 				 * forget I mentioned ot :-) !
    424 				 */
    425 				sindex = sb + 1;
    426 				*eb = '\0';
    427 				newindex = (u_long)atoi(sindex);
    428 				if (*index == 0) {
    429 					*index = newindex;
    430 					/* Make _view[3] look like _view */
    431 					*sb = '\0';
    432 				} else {
    433 					fprintf(stderr, "Error index can "
    434 						"only be specified once!\n");
    435 				}
    436 			} else {
    437 				fprintf(stderr, "Error: Garbage "
    438 					"trailing ']'\n");
    439 			}
    440 		} else {
    441 			fprintf(stderr, "Error ']' in symbol before '[' !\n");
    442 		}
    443 	}
    444 	/* end if sb != 0 */
    445 }
    446 /* end FindOffset */
    447 
    448 /*
    449  * FindAssign : Scans symbol name for an '=number' strips it off of
    450  * the symbol and proceeds.
    451  */
    452 static u_long
    453 FindAssign(char *symbol, u_long *rvalue)
    454 {
    455 	/* Assign symbol some number */
    456 	char *ce = rindex(symbol, '=');
    457 
    458 	/* This should point at some number, no spaces allowed */
    459 	char *cn = ce + 1;
    460 
    461 	/* flag for do_replace */
    462 	u_long dr = 0;
    463 
    464 	if (ce) {
    465 		/* number of variaables scanned in */
    466 		int nscan;
    467 
    468 		/* get the number to assign to symbol and strip off = */
    469 		for (cn=ce + 1; *cn==' '; cn++)
    470 			;
    471 		if (!strncmp(cn, "0x", 2)) {
    472 			nscan = sscanf(cn, "%x", rvalue);
    473 		} else {
    474 			nscan = sscanf(cn, "%d", rvalue);
    475 		}
    476 		if (nscan != 1) {
    477 			error("Invalid value following '='");
    478 		}
    479 		dr = 1;
    480 		/* Now were left with just symbol */
    481 		*ce = '\0';
    482 	}
    483 	/* end if (ce) */
    484 	return(dr);
    485 }
    486 /* end FindAssign */
    487