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