1 /* $NetBSD: binpatch.c,v 1.15 2016/05/30 03:02:58 dholland Exp $ */ 2 3 /* Author: Markus Wild mw@eunet.ch ??? */ 4 /* Modified: Rob Leland leland@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. Recient 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