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