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
main(int argc,char * argv[])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
error(char * str)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
Synopsis(char * pgname)368 Synopsis(char *pgname)
369 {
370           fprintf(stdout, synusage, pgname, pgname, pgname, pgname, pgname);
371 }
372 
373 
374 static void
Usage(char * pgname)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
FindOffset(char * symbol,u_long * index)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
FindAssign(char * symbol,u_long * rvalue)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