1*9f2b1defSdholland /* $NetBSD: binpatch.c,v 1.15 2016/05/30 03:02:58 dholland Exp $ */
222087826Schopps
322087826Schopps /* Author: Markus Wild mw@eunet.ch ??? */
422087826Schopps /* Modified: Rob Leland leland@mitre.org */
5276eff6bSchopps
67dac6517Smw #include <sys/types.h>
77dac6517Smw #include <a.out.h>
822087826Schopps #include <fcntl.h>
97dac6517Smw #include <stdio.h>
1022087826Schopps #include <stdlib.h>
1122087826Schopps #include <string.h>
1222087826Schopps #include <unistd.h>
137dac6517Smw
14f1e1796cSchopps #ifdef __NetBSD__
15f1e1796cSchopps /*
16f1e1796cSchopps * assume NMAGIC files are linked at 0 (for kernel)
17f1e1796cSchopps */
18f1e1796cSchopps #undef N_TXTADDR
19f1e1796cSchopps #define N_TXTADDR(ex) \
20f1e1796cSchopps ((N_GETMAGIC2(ex) == (ZMAGIC|0x10000) || N_GETMAGIC2(ex) == NMAGIC) ? \
21e8cc3884Sthorpej 0 : AOUT_LDPGSZ)
22f1e1796cSchopps #endif
23f1e1796cSchopps
2422087826Schopps
25fca656f5Sthorpej static char synusage[] =
26fca656f5Sthorpej "NAME\n"
27fca656f5Sthorpej "\t%s - Allows the patching of BSD binaries\n"
28fca656f5Sthorpej "SYNOPSIS\n"
29fca656f5Sthorpej "\t%s [-HELP]\n"
30fca656f5Sthorpej "\t%s [-b|-w|-l] -s symbol[[[index]][=value]] binary\n"
31fca656f5Sthorpej "\t%s [-b|-w|-l] [-o offset] -s symbol [-r value] binary\n"
32fca656f5Sthorpej "\t%s [-b|-w|-l] [-o offset] -a address [-r value] binary\n";
33c72886c6Sdholland
34fca656f5Sthorpej static char desusage[] =
35fca656f5Sthorpej "DESCRIPTION\n"
36fca656f5Sthorpej "\tAllows the patching of BSD binaries, for example, a distributed\n"
37fca656f5Sthorpej "\tkernel. Recient additions allows the user to index into an array\n"
38fca656f5Sthorpej "\tand assign a value. Binpatch has internal variables to allow\n"
39fca656f5Sthorpej "\tyou to test it on itself under NetBSD.\n"
40fca656f5Sthorpej "OPTIONS\n"
41fca656f5Sthorpej "\t-a patch variable by specifying address in hex\n"
42fca656f5Sthorpej "\t-b symbol or address to be patched is 1 byte\n"
43fca656f5Sthorpej "\t-l symbol or address to be patched is 4 bytes (default)\n"
44fca656f5Sthorpej "\t-o offset to begin patching value relative to symbol or address\n"
45fca656f5Sthorpej "\t-r replace value, and print out previous value to stdout\n"
46fca656f5Sthorpej "\t-s patch variable by specifying symbol name. Use '[]'\n"
47fca656f5Sthorpej "\t to specify the 'index'. If '-b, -w or -l' not specified\n"
48fca656f5Sthorpej "\t then index value is used like an offset. Also can use '='\n"
49fca656f5Sthorpej "\t to assign value\n"
50fca656f5Sthorpej "\t-w symbol or address to be patched is 2 bytes\n"
51fca656f5Sthorpej "EXAMPLES\n"
52fca656f5Sthorpej "\tThis should print 100 (this is a nice reality check...)\n"
53fca656f5Sthorpej "\t\tbinpatch -l -s _hz netbsd\n"
54fca656f5Sthorpej "\tNow it gets more advanced, replace the value:\n"
55fca656f5Sthorpej "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd\n"
56fca656f5Sthorpej "\tNow patch a variable at a given 'index' not offset,\n"
57fca656f5Sthorpej "\tunder NetBSD you must use '', under AmigaDos CLI '' is optional.:\n"
58fca656f5Sthorpej "\t\tbinpatch -w -s '_vieww[4]' -r 0 a.out\n"
59fca656f5Sthorpej "\tsame as\n"
60fca656f5Sthorpej "\t\tbinpatch -w -o 8 -s _vieww -r 0 a.out\n"
61fca656f5Sthorpej "\tAnother example of using []\n"
62fca656f5Sthorpej "\t\tbinpatch -s '_viewl[4]' -r 0 a.out\n"
63fca656f5Sthorpej "\tsame as\n"
64fca656f5Sthorpej "\t\tbinpatch -o 4 -s _viewl -r 0 a.out\n"
65fca656f5Sthorpej "\tOne last example using '=' and []\n"
66fca656f5Sthorpej "\t\tbinpatch -w -s '_vieww[4]=2' a.out\n"
67fca656f5Sthorpej "\tSo if the kernel is not finding your drives, you could enable\n"
68fca656f5Sthorpej "\tall available debugging options, helping to shed light on that problem.\n"
69fca656f5Sthorpej "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd scsi-level\n"
70fca656f5Sthorpej "\t\tbinpatch -l -s _sddebug -r 1 netbsd sd-level (disk-driver)\n"
71fca656f5Sthorpej "\t\tbinpatch -l -s _acdebug -r 1 netbsd autoconfig-level\n"
72fca656f5Sthorpej "SEE ALSO\n"
73fca656f5Sthorpej "\tbinpatch.c binpatch(1)\n";
7422087826Schopps
757dac6517Smw extern char *optarg;
767dac6517Smw extern int optind;
777dac6517Smw
78d0338842Stsutsui void error(char *) __attribute__((__noreturn__));
7922087826Schopps static void Synopsis(char *program_name);
8022087826Schopps static void Usage(char *program_name);
8122087826Schopps static u_long FindAssign(char *symbol, u_long *rvalue);
8222087826Schopps static void FindOffset(char *symbol, u_long *index);
837dac6517Smw
8422087826Schopps /* The following variables are so binpatch can be tested on itself */
857dac6517Smw int test = 1;
867dac6517Smw int testbss;
877dac6517Smw char foo = 23;
8822087826Schopps char viewb[10] = {0,0,1,0,1,1,0,1,1,1};
8922087826Schopps short vieww[10] = {0,0,1,0,1,1,0,1,1,1};
9022087826Schopps long viewl[10] = {0,0,1,0,1,1,0,1,1,1};
9122087826Schopps /* End of test binpatch variables */
92c72886c6Sdholland
937dac6517Smw int
main(int argc,char * argv[])9422087826Schopps main(int argc, char *argv[])
957dac6517Smw {
967dac6517Smw struct exec e;
977dac6517Smw int c;
9865e7c711Smw u_long addr = 0, offset = 0;
99964a5c8eSdholland /* Related to offset */
100964a5c8eSdholland u_long index = 0;
1017dac6517Smw u_long replace = 0, do_replace = 0;
1027dac6517Smw char *symbol = 0;
103964a5c8eSdholland /* default to long */
104964a5c8eSdholland char size = 4;
105964a5c8eSdholland /* Flag to say size option was set, used with index */
106964a5c8eSdholland char size_opt = 0;
1077dac6517Smw char *fname;
108964a5c8eSdholland /* Program name */
109964a5c8eSdholland char *pgname = argv[0];
1107dac6517Smw int fd;
1117dac6517Smw int type, off;
1127dac6517Smw u_long lval;
1137dac6517Smw u_short sval;
1147dac6517Smw u_char cval;
1157dac6517Smw
1167dac6517Smw
117964a5c8eSdholland while ((c = getopt(argc, argv, "H:a:bwlr:s:o:")) != -1) {
118964a5c8eSdholland switch (c) {
11922087826Schopps case 'H':
12022087826Schopps Usage(argv[0]);
12122087826Schopps break;
1227dac6517Smw case 'a':
123964a5c8eSdholland if (addr || symbol) {
1247dac6517Smw error("only one address/symbol allowed");
125964a5c8eSdholland }
126964a5c8eSdholland if (!strncmp(optarg, "0x", 2)) {
1277dac6517Smw sscanf(optarg, "%x", &addr);
128964a5c8eSdholland } else {
1297dac6517Smw addr = atoi(optarg);
130964a5c8eSdholland }
131964a5c8eSdholland if (!addr) {
1327dac6517Smw error("invalid address");
133964a5c8eSdholland }
1347dac6517Smw break;
1357dac6517Smw
1367dac6517Smw case 'b':
1377dac6517Smw size = 1;
13822087826Schopps size_opt = 1;
1397dac6517Smw break;
1407dac6517Smw
1417dac6517Smw case 'w':
1427dac6517Smw size = 2;
14322087826Schopps size_opt = 1;
1447dac6517Smw break;
1457dac6517Smw
1467dac6517Smw case 'l':
1477dac6517Smw size = 4;
14822087826Schopps size_opt = 1;
1497dac6517Smw break;
1507dac6517Smw
1517dac6517Smw case 'r':
1527dac6517Smw do_replace = 1;
153964a5c8eSdholland if (!strncmp(optarg, "0x", 2)) {
1547dac6517Smw sscanf(optarg, "%x", &replace);
155964a5c8eSdholland } else {
1567dac6517Smw replace = atoi(optarg);
157964a5c8eSdholland }
1587dac6517Smw break;
1597dac6517Smw
1607dac6517Smw case 's':
161964a5c8eSdholland if (addr || symbol) {
1627dac6517Smw error("only one address/symbol allowed");
163964a5c8eSdholland }
1647dac6517Smw symbol = optarg;
1657dac6517Smw break;
16665e7c711Smw
16765e7c711Smw case 'o':
168964a5c8eSdholland if (offset) {
16965e7c711Smw error("only one offset allowed");
170964a5c8eSdholland }
171964a5c8eSdholland if (!strncmp(optarg, "0x", 2)) {
17265e7c711Smw sscanf(optarg, "%x", &offset);
173964a5c8eSdholland } else {
17465e7c711Smw offset = atoi(optarg);
175964a5c8eSdholland }
17665e7c711Smw break;
177964a5c8eSdholland }
178964a5c8eSdholland /* end while switch() */
179964a5c8eSdholland }
1807dac6517Smw
181964a5c8eSdholland if (argc > 1) {
182964a5c8eSdholland if (addr || symbol) {
1837dac6517Smw argv += optind;
1847dac6517Smw argc -= optind;
1857dac6517Smw
186964a5c8eSdholland if (argc < 1) {
1877dac6517Smw error("No file to patch.");
188964a5c8eSdholland }
1897dac6517Smw
1907dac6517Smw fname = argv[0];
191964a5c8eSdholland if ((fd = open(fname, 0)) < 0) {
1927dac6517Smw error("Can't open file");
193964a5c8eSdholland }
1947dac6517Smw
1957dac6517Smw if (read(fd, &e, sizeof(e)) != sizeof(e)
196964a5c8eSdholland || N_BADMAG(e)) {
1977dac6517Smw error("Not a valid executable.");
198964a5c8eSdholland }
1997dac6517Smw
2007dac6517Smw /* fake mid, so the N_ macros work on the amiga.. */
2017dac6517Smw e.a_midmag |= 127 << 16;
2027dac6517Smw
203964a5c8eSdholland if (symbol) {
2047dac6517Smw struct nlist nl[2];
205964a5c8eSdholland
206964a5c8eSdholland if (offset == 0) {
20722087826Schopps u_long new_do_replace = 0;
208c72886c6Sdholland
209964a5c8eSdholland new_do_replace = FindAssign(symbol,
210964a5c8eSdholland &replace);
21122087826Schopps if (new_do_replace && do_replace)
212964a5c8eSdholland error("Cannot use both '=' "
213964a5c8eSdholland "and '-r' option!");
21422087826Schopps FindOffset(symbol, &index);
215964a5c8eSdholland if (size_opt) {
216964a5c8eSdholland /* Treat like an index */
217964a5c8eSdholland offset = index*size;
218964a5c8eSdholland } else {
219964a5c8eSdholland /* Treat like an offset */
220964a5c8eSdholland offset = index;
221964a5c8eSdholland }
22222087826Schopps if (new_do_replace)
22322087826Schopps do_replace = new_do_replace;
22422087826Schopps }
2257dac6517Smw nl[0].n_un.n_name = symbol;
2267dac6517Smw nl[1].n_un.n_name = 0;
227964a5c8eSdholland if (nlist(fname, nl) != 0) {
228*9f2b1defSdholland fprintf(stderr, "Symbol is %s ",
229*9f2b1defSdholland symbol);
2307dac6517Smw error("Symbol not found.");
23122087826Schopps }
2327dac6517Smw addr = nl[0].n_value;
2337dac6517Smw type = nl[0].n_type & N_TYPE;
234964a5c8eSdholland } else {
2357dac6517Smw type = N_UNDF;
236964a5c8eSdholland if (addr >= N_TXTADDR(e) &&
237964a5c8eSdholland addr < N_DATADDR(e)) {
2387dac6517Smw type = N_TEXT;
239964a5c8eSdholland } else if (addr >= N_DATADDR(e) &&
240964a5c8eSdholland addr < N_DATADDR(e) + e.a_data) {
2417dac6517Smw type = N_DATA;
2427dac6517Smw }
243964a5c8eSdholland }
24465e7c711Smw addr += offset;
2457dac6517Smw
246964a5c8eSdholland /*
247964a5c8eSdholland * if replace-mode, have to reopen the file
248964a5c8eSdholland * for writing. Can't do that from the
249964a5c8eSdholland * beginning, or nlist() will not work (at
250964a5c8eSdholland * least not under AmigaDOS)
251964a5c8eSdholland */
252964a5c8eSdholland if (do_replace) {
2537dac6517Smw close(fd);
254964a5c8eSdholland if ((fd = open(fname, 2)) == -1) {
2557dac6517Smw error("Can't reopen file for writing.");
2567dac6517Smw }
257964a5c8eSdholland }
2587dac6517Smw
259964a5c8eSdholland if (type != N_TEXT && type != N_DATA) {
260964a5c8eSdholland error("address/symbol is not in text "
261964a5c8eSdholland "or data section.");
262964a5c8eSdholland }
2637dac6517Smw
264964a5c8eSdholland if (type == N_TEXT) {
2657dac6517Smw off = addr - N_TXTADDR(e) + N_TXTOFF(e);
266964a5c8eSdholland } else {
2677dac6517Smw off = addr - N_DATADDR(e) + N_DATOFF(e);
268964a5c8eSdholland }
2697dac6517Smw
270964a5c8eSdholland if (lseek(fd, off, 0) == -1) {
2717dac6517Smw error("lseek");
272964a5c8eSdholland }
2737dac6517Smw
274964a5c8eSdholland /*
275964a5c8eSdholland * not beautiful, but works on big and little
276964a5c8eSdholland * endian machines
277964a5c8eSdholland */
278964a5c8eSdholland switch (size) {
2797dac6517Smw case 1:
280964a5c8eSdholland if (read(fd, &cval, 1) != 1) {
2817dac6517Smw error("cread");
282964a5c8eSdholland }
2837dac6517Smw lval = cval;
2847dac6517Smw break;
2857dac6517Smw
2867dac6517Smw case 2:
287964a5c8eSdholland if (read(fd, &sval, 2) != 2) {
2887dac6517Smw error("sread");
289964a5c8eSdholland }
2907dac6517Smw lval = sval;
2917dac6517Smw break;
2927dac6517Smw
2937dac6517Smw case 4:
294964a5c8eSdholland if (read(fd, &lval, 4) != 4) {
2957dac6517Smw error("lread");
296964a5c8eSdholland }
2977dac6517Smw break;
29822087826Schopps }/* switch size */
2997dac6517Smw
3007dac6517Smw
301964a5c8eSdholland if (symbol) {
302964a5c8eSdholland printf("%s(0x%x): %d (0x%x)\n", symbol, addr,
303964a5c8eSdholland lval, lval);
304964a5c8eSdholland } else {
3057dac6517Smw printf("0x%x: %d (0x%x)\n", addr, lval, lval);
306964a5c8eSdholland }
3077dac6517Smw
308964a5c8eSdholland if (do_replace) {
309964a5c8eSdholland if (lseek(fd, off, 0) == -1) {
3107dac6517Smw error("write-lseek");
311964a5c8eSdholland }
312964a5c8eSdholland switch (size) {
3137dac6517Smw case 1:
3147dac6517Smw cval = replace;
315964a5c8eSdholland if (cval != replace) {
3167dac6517Smw error("byte-value overflow.");
317964a5c8eSdholland }
318964a5c8eSdholland if (write(fd, &cval, 1) != 1) {
3197dac6517Smw error("cwrite");
320964a5c8eSdholland }
3217dac6517Smw break;
3227dac6517Smw
3237dac6517Smw case 2:
3247dac6517Smw sval = replace;
325964a5c8eSdholland if (sval != replace) {
3267dac6517Smw error("word-value overflow.");
327964a5c8eSdholland }
328964a5c8eSdholland if (write(fd, &sval, 2) != 2) {
3297dac6517Smw error("swrite");
330964a5c8eSdholland }
3317dac6517Smw break;
3327dac6517Smw
3337dac6517Smw case 4:
334964a5c8eSdholland if (write(fd, &replace, 4) != 4) {
3357dac6517Smw error("lwrite");
336964a5c8eSdholland }
3377dac6517Smw break;
338964a5c8eSdholland }
339964a5c8eSdholland /* end switch(size) */
340964a5c8eSdholland }
341964a5c8eSdholland /* end if (do_replace) */
3427dac6517Smw
3437dac6517Smw close(fd);
344964a5c8eSdholland } else {
345964a5c8eSdholland /* not (addr || symbol) */
34622087826Schopps error("Must specify either address or symbol.");
3477dac6517Smw }
348964a5c8eSdholland } else {
349964a5c8eSdholland /* if argc <= 1 */
35022087826Schopps Synopsis(pgname);
35122087826Schopps }
352964a5c8eSdholland
353964a5c8eSdholland return 0;
354964a5c8eSdholland }
355964a5c8eSdholland /* end main () */
3567dac6517Smw
3577dac6517Smw
3587dac6517Smw
359964a5c8eSdholland void
error(char * str)360964a5c8eSdholland error(char *str)
3617dac6517Smw {
3627dac6517Smw fprintf(stderr, "%s\n", str);
3637dac6517Smw exit(1);
3647dac6517Smw }
36522087826Schopps
36622087826Schopps /* Give user very short help to avoid scrolling screen much */
367964a5c8eSdholland static void
Synopsis(char * pgname)368964a5c8eSdholland Synopsis(char *pgname)
36922087826Schopps {
37022087826Schopps fprintf(stdout, synusage, pgname, pgname, pgname, pgname, pgname);
37122087826Schopps }
37222087826Schopps
37322087826Schopps
374964a5c8eSdholland static void
Usage(char * pgname)375964a5c8eSdholland Usage(char *pgname)
37622087826Schopps {
37722087826Schopps Synopsis(pgname);
37822087826Schopps fprintf(stdout, desusage);
37922087826Schopps exit(0);
38022087826Schopps }
38122087826Schopps
38222087826Schopps
383964a5c8eSdholland /*
384964a5c8eSdholland * FindOffset() - Determine if there is an offset, -or- index
385964a5c8eSdholland * embedded in the symbol.
386964a5c8eSdholland *
387964a5c8eSdholland * If there is, return it, and truncate symbol to exclude the [...].
388964a5c8eSdholland *
389964a5c8eSdholland * Example: If view is declared as short view[10],
390964a5c8eSdholland * and we want to index the 3rd. element.
391964a5c8eSdholland * which is offset = (3 -1)*sizeof(short) =4.
392964a5c8eSdholland * we would use view[4], which becomes view,4.
393964a5c8eSdholland *
394964a5c8eSdholland * The way the code is implemented the [value] is
395964a5c8eSdholland * treated as a index if-and-only-if a '-b -w -l' option
396964a5c8eSdholland * was given. Otherwise it is treated like an offset.
397964a5c8eSdholland * See above documentation in for of help!
39822087826Schopps */
399964a5c8eSdholland static void
FindOffset(char * symbol,u_long * index)400964a5c8eSdholland FindOffset(char *symbol, u_long *index)
40122087826Schopps {
402964a5c8eSdholland /* Start of '[', now line must contain matching']' */
403964a5c8eSdholland char *sb = strchr(symbol, '[');
404964a5c8eSdholland
405964a5c8eSdholland /* End of ']' */
406964a5c8eSdholland char *eb = strchr(symbol, ']');
407964a5c8eSdholland
408964a5c8eSdholland /* symbol size */
409964a5c8eSdholland short sz = strlen(symbol);
410964a5c8eSdholland
411964a5c8eSdholland if (sb) {
412964a5c8eSdholland if (eb && (eb > sb)) {
413964a5c8eSdholland if ((eb - symbol) == (sz - 1)) {
414964a5c8eSdholland /* Start of index */
415964a5c8eSdholland char *sindex;
41622087826Schopps u_long newindex = 0;
417964a5c8eSdholland
418964a5c8eSdholland /*
419964a5c8eSdholland * In the future we could get fancy
420964a5c8eSdholland * and parse the sindex string for
421964a5c8eSdholland * mathmatical expressions like: (3 -
422964a5c8eSdholland * 1)*2 = 4 from above example, ugh
423964a5c8eSdholland * forget I mentioned ot :-) !
42422087826Schopps */
42522087826Schopps sindex = sb + 1;
42622087826Schopps *eb = '\0';
42722087826Schopps newindex = (u_long)atoi(sindex);
428964a5c8eSdholland if (*index == 0) {
42922087826Schopps *index = newindex;
430964a5c8eSdholland /* Make _view[3] look like _view */
431964a5c8eSdholland *sb = '\0';
432964a5c8eSdholland } else {
433964a5c8eSdholland fprintf(stderr, "Error index can "
434964a5c8eSdholland "only be specified once!\n");
43522087826Schopps }
436964a5c8eSdholland } else {
437964a5c8eSdholland fprintf(stderr, "Error: Garbage "
438964a5c8eSdholland "trailing ']'\n");
43922087826Schopps }
440964a5c8eSdholland } else {
44122087826Schopps fprintf(stderr, "Error ']' in symbol before '[' !\n");
44222087826Schopps }
443964a5c8eSdholland }
444964a5c8eSdholland /* end if sb != 0 */
445964a5c8eSdholland }
446964a5c8eSdholland /* end FindOffset */
44722087826Schopps
448964a5c8eSdholland /*
449964a5c8eSdholland * FindAssign : Scans symbol name for an '=number' strips it off of
450964a5c8eSdholland * the symbol and proceeds.
45122087826Schopps */
452964a5c8eSdholland static u_long
FindAssign(char * symbol,u_long * rvalue)453964a5c8eSdholland FindAssign(char *symbol, u_long *rvalue)
45422087826Schopps {
455964a5c8eSdholland /* Assign symbol some number */
456964a5c8eSdholland char *ce = rindex(symbol, '=');
457c72886c6Sdholland
458964a5c8eSdholland /* This should point at some number, no spaces allowed */
459964a5c8eSdholland char *cn = ce + 1;
460964a5c8eSdholland
461964a5c8eSdholland /* flag for do_replace */
462964a5c8eSdholland u_long dr = 0;
463964a5c8eSdholland
464964a5c8eSdholland if (ce) {
465964a5c8eSdholland /* number of variaables scanned in */
466964a5c8eSdholland int nscan;
467964a5c8eSdholland
46822087826Schopps /* get the number to assign to symbol and strip off = */
469cb81c2ddSdholland for (cn=ce + 1; *cn==' '; cn++)
47022087826Schopps ;
471964a5c8eSdholland if (!strncmp(cn, "0x", 2)) {
47222087826Schopps nscan = sscanf(cn, "%x", rvalue);
473964a5c8eSdholland } else {
47422087826Schopps nscan = sscanf(cn, "%d", rvalue);
475964a5c8eSdholland }
476964a5c8eSdholland if (nscan != 1) {
47722087826Schopps error("Invalid value following '='");
478964a5c8eSdholland }
47922087826Schopps dr = 1;
480964a5c8eSdholland /* Now were left with just symbol */
481964a5c8eSdholland *ce = '\0';
482964a5c8eSdholland }
483964a5c8eSdholland /* end if (ce) */
48422087826Schopps return(dr);
485964a5c8eSdholland }
486964a5c8eSdholland /* end FindAssign */
487