/* $NetBSD: binpatch.c,v 1.15 2016/05/30 03:02:58 dholland Exp $ */ /* Author: Markus Wild mw@eunet.ch ??? */ /* Modified: Rob Leland leland@mitre.org */ #include #include #include #include #include #include #include #ifdef __NetBSD__ /* * assume NMAGIC files are linked at 0 (for kernel) */ #undef N_TXTADDR #define N_TXTADDR(ex) \ ((N_GETMAGIC2(ex) == (ZMAGIC|0x10000) || N_GETMAGIC2(ex) == NMAGIC) ? \ 0 : AOUT_LDPGSZ) #endif static char synusage[] = "NAME\n" "\t%s - Allows the patching of BSD binaries\n" "SYNOPSIS\n" "\t%s [-HELP]\n" "\t%s [-b|-w|-l] -s symbol[[[index]][=value]] binary\n" "\t%s [-b|-w|-l] [-o offset] -s symbol [-r value] binary\n" "\t%s [-b|-w|-l] [-o offset] -a address [-r value] binary\n"; static char desusage[] = "DESCRIPTION\n" "\tAllows the patching of BSD binaries, for example, a distributed\n" "\tkernel. Recient additions allows the user to index into an array\n" "\tand assign a value. Binpatch has internal variables to allow\n" "\tyou to test it on itself under NetBSD.\n" "OPTIONS\n" "\t-a patch variable by specifying address in hex\n" "\t-b symbol or address to be patched is 1 byte\n" "\t-l symbol or address to be patched is 4 bytes (default)\n" "\t-o offset to begin patching value relative to symbol or address\n" "\t-r replace value, and print out previous value to stdout\n" "\t-s patch variable by specifying symbol name. Use '[]'\n" "\t to specify the 'index'. If '-b, -w or -l' not specified\n" "\t then index value is used like an offset. Also can use '='\n" "\t to assign value\n" "\t-w symbol or address to be patched is 2 bytes\n" "EXAMPLES\n" "\tThis should print 100 (this is a nice reality check...)\n" "\t\tbinpatch -l -s _hz netbsd\n" "\tNow it gets more advanced, replace the value:\n" "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd\n" "\tNow patch a variable at a given 'index' not offset,\n" "\tunder NetBSD you must use '', under AmigaDos CLI '' is optional.:\n" "\t\tbinpatch -w -s '_vieww[4]' -r 0 a.out\n" "\tsame as\n" "\t\tbinpatch -w -o 8 -s _vieww -r 0 a.out\n" "\tAnother example of using []\n" "\t\tbinpatch -s '_viewl[4]' -r 0 a.out\n" "\tsame as\n" "\t\tbinpatch -o 4 -s _viewl -r 0 a.out\n" "\tOne last example using '=' and []\n" "\t\tbinpatch -w -s '_vieww[4]=2' a.out\n" "\tSo if the kernel is not finding your drives, you could enable\n" "\tall available debugging options, helping to shed light on that problem.\n" "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd scsi-level\n" "\t\tbinpatch -l -s _sddebug -r 1 netbsd sd-level (disk-driver)\n" "\t\tbinpatch -l -s _acdebug -r 1 netbsd autoconfig-level\n" "SEE ALSO\n" "\tbinpatch.c binpatch(1)\n"; extern char *optarg; extern int optind; void error(char *) __attribute__((__noreturn__)); static void Synopsis(char *program_name); static void Usage(char *program_name); static u_long FindAssign(char *symbol, u_long *rvalue); static void FindOffset(char *symbol, u_long *index); /* The following variables are so binpatch can be tested on itself */ int test = 1; int testbss; char foo = 23; char viewb[10] = {0,0,1,0,1,1,0,1,1,1}; short vieww[10] = {0,0,1,0,1,1,0,1,1,1}; long viewl[10] = {0,0,1,0,1,1,0,1,1,1}; /* End of test binpatch variables */ int main(int argc, char *argv[]) { struct exec e; int c; u_long addr = 0, offset = 0; /* Related to offset */ u_long index = 0; u_long replace = 0, do_replace = 0; char *symbol = 0; /* default to long */ char size = 4; /* Flag to say size option was set, used with index */ char size_opt = 0; char *fname; /* Program name */ char *pgname = argv[0]; int fd; int type, off; u_long lval; u_short sval; u_char cval; while ((c = getopt(argc, argv, "H:a:bwlr:s:o:")) != -1) { switch (c) { case 'H': Usage(argv[0]); break; case 'a': if (addr || symbol) { error("only one address/symbol allowed"); } if (!strncmp(optarg, "0x", 2)) { sscanf(optarg, "%x", &addr); } else { addr = atoi(optarg); } if (!addr) { error("invalid address"); } break; case 'b': size = 1; size_opt = 1; break; case 'w': size = 2; size_opt = 1; break; case 'l': size = 4; size_opt = 1; break; case 'r': do_replace = 1; if (!strncmp(optarg, "0x", 2)) { sscanf(optarg, "%x", &replace); } else { replace = atoi(optarg); } break; case 's': if (addr || symbol) { error("only one address/symbol allowed"); } symbol = optarg; break; case 'o': if (offset) { error("only one offset allowed"); } if (!strncmp(optarg, "0x", 2)) { sscanf(optarg, "%x", &offset); } else { offset = atoi(optarg); } break; } /* end while switch() */ } if (argc > 1) { if (addr || symbol) { argv += optind; argc -= optind; if (argc < 1) { error("No file to patch."); } fname = argv[0]; if ((fd = open(fname, 0)) < 0) { error("Can't open file"); } if (read(fd, &e, sizeof(e)) != sizeof(e) || N_BADMAG(e)) { error("Not a valid executable."); } /* fake mid, so the N_ macros work on the amiga.. */ e.a_midmag |= 127 << 16; if (symbol) { struct nlist nl[2]; if (offset == 0) { u_long new_do_replace = 0; new_do_replace = FindAssign(symbol, &replace); if (new_do_replace && do_replace) error("Cannot use both '=' " "and '-r' option!"); FindOffset(symbol, &index); if (size_opt) { /* Treat like an index */ offset = index*size; } else { /* Treat like an offset */ offset = index; } if (new_do_replace) do_replace = new_do_replace; } nl[0].n_un.n_name = symbol; nl[1].n_un.n_name = 0; if (nlist(fname, nl) != 0) { fprintf(stderr, "Symbol is %s ", symbol); error("Symbol not found."); } addr = nl[0].n_value; type = nl[0].n_type & N_TYPE; } else { type = N_UNDF; if (addr >= N_TXTADDR(e) && addr < N_DATADDR(e)) { type = N_TEXT; } else if (addr >= N_DATADDR(e) && addr < N_DATADDR(e) + e.a_data) { type = N_DATA; } } addr += offset; /* * if replace-mode, have to reopen the file * for writing. Can't do that from the * beginning, or nlist() will not work (at * least not under AmigaDOS) */ if (do_replace) { close(fd); if ((fd = open(fname, 2)) == -1) { error("Can't reopen file for writing."); } } if (type != N_TEXT && type != N_DATA) { error("address/symbol is not in text " "or data section."); } if (type == N_TEXT) { off = addr - N_TXTADDR(e) + N_TXTOFF(e); } else { off = addr - N_DATADDR(e) + N_DATOFF(e); } if (lseek(fd, off, 0) == -1) { error("lseek"); } /* * not beautiful, but works on big and little * endian machines */ switch (size) { case 1: if (read(fd, &cval, 1) != 1) { error("cread"); } lval = cval; break; case 2: if (read(fd, &sval, 2) != 2) { error("sread"); } lval = sval; break; case 4: if (read(fd, &lval, 4) != 4) { error("lread"); } break; }/* switch size */ if (symbol) { printf("%s(0x%x): %d (0x%x)\n", symbol, addr, lval, lval); } else { printf("0x%x: %d (0x%x)\n", addr, lval, lval); } if (do_replace) { if (lseek(fd, off, 0) == -1) { error("write-lseek"); } switch (size) { case 1: cval = replace; if (cval != replace) { error("byte-value overflow."); } if (write(fd, &cval, 1) != 1) { error("cwrite"); } break; case 2: sval = replace; if (sval != replace) { error("word-value overflow."); } if (write(fd, &sval, 2) != 2) { error("swrite"); } break; case 4: if (write(fd, &replace, 4) != 4) { error("lwrite"); } break; } /* end switch(size) */ } /* end if (do_replace) */ close(fd); } else { /* not (addr || symbol) */ error("Must specify either address or symbol."); } } else { /* if argc <= 1 */ Synopsis(pgname); } return 0; } /* end main () */ void error(char *str) { fprintf(stderr, "%s\n", str); exit(1); } /* Give user very short help to avoid scrolling screen much */ static void Synopsis(char *pgname) { fprintf(stdout, synusage, pgname, pgname, pgname, pgname, pgname); } static void Usage(char *pgname) { Synopsis(pgname); fprintf(stdout, desusage); exit(0); } /* * FindOffset() - Determine if there is an offset, -or- index * embedded in the symbol. * * If there is, return it, and truncate symbol to exclude the [...]. * * Example: If view is declared as short view[10], * and we want to index the 3rd. element. * which is offset = (3 -1)*sizeof(short) =4. * we would use view[4], which becomes view,4. * * The way the code is implemented the [value] is * treated as a index if-and-only-if a '-b -w -l' option * was given. Otherwise it is treated like an offset. * See above documentation in for of help! */ static void FindOffset(char *symbol, u_long *index) { /* Start of '[', now line must contain matching']' */ char *sb = strchr(symbol, '['); /* End of ']' */ char *eb = strchr(symbol, ']'); /* symbol size */ short sz = strlen(symbol); if (sb) { if (eb && (eb > sb)) { if ((eb - symbol) == (sz - 1)) { /* Start of index */ char *sindex; u_long newindex = 0; /* * In the future we could get fancy * and parse the sindex string for * mathmatical expressions like: (3 - * 1)*2 = 4 from above example, ugh * forget I mentioned ot :-) ! */ sindex = sb + 1; *eb = '\0'; newindex = (u_long)atoi(sindex); if (*index == 0) { *index = newindex; /* Make _view[3] look like _view */ *sb = '\0'; } else { fprintf(stderr, "Error index can " "only be specified once!\n"); } } else { fprintf(stderr, "Error: Garbage " "trailing ']'\n"); } } else { fprintf(stderr, "Error ']' in symbol before '[' !\n"); } } /* end if sb != 0 */ } /* end FindOffset */ /* * FindAssign : Scans symbol name for an '=number' strips it off of * the symbol and proceeds. */ static u_long FindAssign(char *symbol, u_long *rvalue) { /* Assign symbol some number */ char *ce = rindex(symbol, '='); /* This should point at some number, no spaces allowed */ char *cn = ce + 1; /* flag for do_replace */ u_long dr = 0; if (ce) { /* number of variaables scanned in */ int nscan; /* get the number to assign to symbol and strip off = */ for (cn=ce + 1; *cn==' '; cn++) ; if (!strncmp(cn, "0x", 2)) { nscan = sscanf(cn, "%x", rvalue); } else { nscan = sscanf(cn, "%d", rvalue); } if (nscan != 1) { error("Invalid value following '='"); } dr = 1; /* Now were left with just symbol */ *ce = '\0'; } /* end if (ce) */ return(dr); } /* end FindAssign */