binpatch.c revision 1.14 1 1.14 dholland /* $NetBSD: binpatch.c,v 1.14 2016/05/30 02:57:32 dholland 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.9 thorpej "\tAllows the patching of BSD binaries, for example,a distributed\n"
37 1.9 thorpej "\tkernel. Recient 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.11 tsutsui 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.14 dholland while ((c = getopt (argc, argv, "H:a:bwlr:s:o:")) != -1) {
118 1.14 dholland switch (c) {
119 1.13 dholland case 'H':
120 1.13 dholland Usage(argv[0]);
121 1.13 dholland break;
122 1.13 dholland case 'a':
123 1.14 dholland if (addr || symbol) {
124 1.13 dholland error ("only one address/symbol allowed");
125 1.14 dholland }
126 1.14 dholland if (!strncmp(optarg, "0x", 2)) {
127 1.13 dholland sscanf (optarg, "%x", &addr);
128 1.14 dholland } else {
129 1.13 dholland addr = atoi (optarg);
130 1.14 dholland }
131 1.14 dholland if (!addr) {
132 1.13 dholland error ("invalid address");
133 1.14 dholland }
134 1.13 dholland break;
135 1.13 dholland
136 1.13 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.13 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.13 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.13 dholland case 'r':
152 1.13 dholland do_replace = 1;
153 1.14 dholland if (!strncmp(optarg, "0x", 2)) {
154 1.13 dholland sscanf (optarg, "%x", &replace);
155 1.14 dholland } else {
156 1.13 dholland replace = atoi (optarg);
157 1.14 dholland }
158 1.13 dholland break;
159 1.13 dholland
160 1.13 dholland case 's':
161 1.14 dholland if (addr || symbol) {
162 1.13 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.13 dholland case 'o':
168 1.14 dholland if (offset) {
169 1.13 dholland error ("only one offset allowed");
170 1.14 dholland }
171 1.14 dholland if (! strncmp (optarg, "0x", 2)) {
172 1.13 dholland sscanf (optarg, "%x", &offset);
173 1.14 dholland } else {
174 1.13 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.13 dholland error ("No file to patch.");
188 1.14 dholland }
189 1.13 dholland
190 1.13 dholland fname = argv[0];
191 1.14 dholland if ((fd = open (fname, 0)) < 0) {
192 1.13 dholland error ("Can't open file");
193 1.14 dholland }
194 1.13 dholland
195 1.13 dholland if (read (fd, &e, sizeof (e)) != sizeof (e)
196 1.14 dholland || N_BADMAG (e)) {
197 1.13 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.13 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.14 dholland if (nlist (fname, nl) != 0) {
228 1.13 dholland fprintf(stderr,"Symbol is %s ",symbol);
229 1.13 dholland error ("Symbol not found.");
230 1.13 dholland }
231 1.13 dholland addr = nl[0].n_value;
232 1.13 dholland type = nl[0].n_type & N_TYPE;
233 1.14 dholland } else {
234 1.13 dholland type = N_UNDF;
235 1.14 dholland if (addr >= N_TXTADDR(e) &&
236 1.14 dholland addr < N_DATADDR(e)) {
237 1.13 dholland type = N_TEXT;
238 1.14 dholland } else if (addr >= N_DATADDR(e) &&
239 1.14 dholland addr < N_DATADDR(e) + e.a_data) {
240 1.13 dholland type = N_DATA;
241 1.14 dholland }
242 1.13 dholland }
243 1.13 dholland addr += offset;
244 1.13 dholland
245 1.14 dholland /*
246 1.14 dholland * if replace-mode, have to reopen the file
247 1.14 dholland * for writing. Can't do that from the
248 1.14 dholland * beginning, or nlist() will not work (at
249 1.14 dholland * least not under AmigaDOS)
250 1.14 dholland */
251 1.14 dholland if (do_replace) {
252 1.13 dholland close (fd);
253 1.14 dholland if ((fd = open (fname, 2)) == -1) {
254 1.14 dholland error("Can't reopen file for writing.");
255 1.14 dholland }
256 1.13 dholland }
257 1.13 dholland
258 1.14 dholland if (type != N_TEXT && type != N_DATA) {
259 1.14 dholland error("address/symbol is not in text "
260 1.14 dholland "or data section.");
261 1.14 dholland }
262 1.13 dholland
263 1.14 dholland if (type == N_TEXT) {
264 1.13 dholland off = addr - N_TXTADDR(e) + N_TXTOFF(e);
265 1.14 dholland } else {
266 1.13 dholland off = addr - N_DATADDR(e) + N_DATOFF(e);
267 1.14 dholland }
268 1.13 dholland
269 1.14 dholland if (lseek(fd, off, 0) == -1) {
270 1.13 dholland error ("lseek");
271 1.14 dholland }
272 1.13 dholland
273 1.14 dholland /*
274 1.14 dholland * not beautiful, but works on big and little
275 1.14 dholland * endian machines
276 1.14 dholland */
277 1.14 dholland switch (size) {
278 1.13 dholland case 1:
279 1.14 dholland if (read(fd, &cval, 1) != 1) {
280 1.13 dholland error ("cread");
281 1.14 dholland }
282 1.13 dholland lval = cval;
283 1.13 dholland break;
284 1.13 dholland
285 1.13 dholland case 2:
286 1.14 dholland if (read(fd, &sval, 2) != 2) {
287 1.13 dholland error ("sread");
288 1.14 dholland }
289 1.13 dholland lval = sval;
290 1.13 dholland break;
291 1.13 dholland
292 1.13 dholland case 4:
293 1.14 dholland if (read(fd, &lval, 4) != 4) {
294 1.13 dholland error ("lread");
295 1.14 dholland }
296 1.13 dholland break;
297 1.13 dholland }/* switch size */
298 1.13 dholland
299 1.13 dholland
300 1.14 dholland if (symbol) {
301 1.14 dholland printf("%s(0x%x): %d (0x%x)\n", symbol, addr,
302 1.14 dholland lval, lval);
303 1.14 dholland } else {
304 1.14 dholland printf("0x%x: %d (0x%x)\n", addr, lval, lval);
305 1.14 dholland }
306 1.14 dholland
307 1.14 dholland if (do_replace) {
308 1.14 dholland if (lseek (fd, off, 0) == -1) {
309 1.13 dholland error ("write-lseek");
310 1.14 dholland }
311 1.14 dholland switch (size) {
312 1.13 dholland case 1:
313 1.13 dholland cval = replace;
314 1.14 dholland if (cval != replace) {
315 1.13 dholland error ("byte-value overflow.");
316 1.14 dholland }
317 1.14 dholland if (write(fd, &cval, 1) != 1) {
318 1.13 dholland error ("cwrite");
319 1.14 dholland }
320 1.13 dholland break;
321 1.13 dholland
322 1.13 dholland case 2:
323 1.13 dholland sval = replace;
324 1.14 dholland if (sval != replace) {
325 1.13 dholland error ("word-value overflow.");
326 1.14 dholland }
327 1.14 dholland if (write(fd, &sval, 2) != 2) {
328 1.13 dholland error ("swrite");
329 1.14 dholland }
330 1.13 dholland break;
331 1.13 dholland
332 1.13 dholland case 4:
333 1.14 dholland if (write(fd, &replace, 4) != 4) {
334 1.13 dholland error ("lwrite");
335 1.14 dholland }
336 1.13 dholland break;
337 1.14 dholland }
338 1.14 dholland /* end switch(size) */
339 1.14 dholland }
340 1.14 dholland /* end if (do_replace) */
341 1.13 dholland
342 1.13 dholland close (fd);
343 1.14 dholland } else {
344 1.14 dholland /* not (addr || symbol) */
345 1.13 dholland error("Must specify either address or symbol.");
346 1.13 dholland }
347 1.14 dholland } else {
348 1.14 dholland /* if argc <= 1 */
349 1.13 dholland Synopsis(pgname);
350 1.6 chopps }
351 1.14 dholland
352 1.14 dholland return 0;
353 1.14 dholland }
354 1.14 dholland /* end main () */
355 1.1 mw
356 1.1 mw
357 1.1 mw
358 1.14 dholland void
359 1.14 dholland error(char *str)
360 1.6 chopps {
361 1.13 dholland fprintf (stderr, "%s\n", str);
362 1.13 dholland exit (1);
363 1.1 mw }
364 1.1 mw
365 1.6 chopps /* Give user very short help to avoid scrolling screen much */
366 1.14 dholland static void
367 1.14 dholland Synopsis(char *pgname)
368 1.6 chopps {
369 1.13 dholland fprintf(stdout, synusage, pgname, pgname, pgname, pgname, pgname);
370 1.6 chopps }
371 1.1 mw
372 1.1 mw
373 1.14 dholland static void
374 1.14 dholland Usage(char *pgname)
375 1.1 mw {
376 1.13 dholland Synopsis(pgname);
377 1.13 dholland fprintf(stdout, desusage);
378 1.13 dholland exit(0);
379 1.1 mw }
380 1.6 chopps
381 1.6 chopps
382 1.14 dholland /*
383 1.14 dholland * FindOffset() - Determine if there is an offset, -or- index
384 1.14 dholland * embedded in the symbol.
385 1.14 dholland *
386 1.14 dholland * If there is, return it, and truncate symbol to exclude the [...].
387 1.14 dholland *
388 1.14 dholland * Example: If view is declared as short view[10],
389 1.14 dholland * and we want to index the 3rd. element.
390 1.14 dholland * which is offset = (3 -1)*sizeof(short) =4.
391 1.14 dholland * we would use view[4], which becomes view,4.
392 1.14 dholland *
393 1.14 dholland * The way the code is implemented the [value] is
394 1.14 dholland * treated as a index if-and-only-if a '-b -w -l' option
395 1.14 dholland * was given. Otherwise it is treated like an offset.
396 1.14 dholland * See above documentation in for of help!
397 1.14 dholland */
398 1.14 dholland static void
399 1.14 dholland FindOffset(char *symbol, u_long *index)
400 1.6 chopps {
401 1.14 dholland /* Start of '[', now line must contain matching']' */
402 1.14 dholland char *sb = strchr(symbol,'[');
403 1.14 dholland
404 1.14 dholland /* End of ']' */
405 1.14 dholland char *eb = strchr(symbol,']');
406 1.14 dholland
407 1.14 dholland /* symbol size */
408 1.14 dholland short sz = strlen(symbol);
409 1.14 dholland
410 1.14 dholland if (sb) {
411 1.14 dholland if (eb && (eb > sb)) {
412 1.14 dholland if ((eb - symbol) == (sz - 1)) {
413 1.14 dholland /* Start of index */
414 1.14 dholland char *sindex;
415 1.13 dholland u_long newindex = 0;
416 1.14 dholland
417 1.14 dholland /*
418 1.14 dholland * In the future we could get fancy
419 1.14 dholland * and parse the sindex string for
420 1.14 dholland * mathmatical expressions like: (3 -
421 1.14 dholland * 1)*2 = 4 from above example, ugh
422 1.14 dholland * forget I mentioned ot :-) !
423 1.14 dholland */
424 1.13 dholland sindex = sb + 1;
425 1.13 dholland *eb = '\0';
426 1.13 dholland newindex = (u_long)atoi(sindex);
427 1.14 dholland if (*index == 0) {
428 1.13 dholland *index = newindex;
429 1.14 dholland /* Make _view[3] look like _view */
430 1.14 dholland *sb = '\0';
431 1.14 dholland } else {
432 1.14 dholland fprintf(stderr, "Error index can "
433 1.14 dholland "only be specified once!\n");
434 1.13 dholland }
435 1.14 dholland } else {
436 1.14 dholland fprintf(stderr, "Error: Garbage "
437 1.14 dholland "trailing ']'\n");
438 1.13 dholland }
439 1.14 dholland } else {
440 1.14 dholland fprintf(stderr, "Error ']' in symbol before '[' !\n");
441 1.13 dholland }
442 1.14 dholland }
443 1.14 dholland /* end if sb != 0 */
444 1.14 dholland }
445 1.14 dholland /* end FindOffset */
446 1.6 chopps
447 1.14 dholland /*
448 1.14 dholland * FindAssign : Scans symbol name for an '=number' strips it off of
449 1.14 dholland * the symbol and proceeds.
450 1.14 dholland */
451 1.14 dholland static u_long
452 1.14 dholland FindAssign(char *symbol, u_long *rvalue)
453 1.6 chopps {
454 1.14 dholland /* Assign symbol some number */
455 1.14 dholland char *ce = rindex(symbol,'=');
456 1.14 dholland
457 1.14 dholland /* This should point at some number, no spaces allowed */
458 1.14 dholland char *cn = ce + 1;
459 1.14 dholland
460 1.14 dholland /* flag for do_replace */
461 1.14 dholland u_long dr = 0;
462 1.14 dholland
463 1.14 dholland if (ce) {
464 1.14 dholland /* number of variaables scanned in */
465 1.14 dholland int nscan;
466 1.14 dholland
467 1.13 dholland /* get the number to assign to symbol and strip off = */
468 1.13 dholland for (cn=ce + 1; *cn==' '; cn++)
469 1.13 dholland ;
470 1.14 dholland if (!strncmp(cn, "0x", 2)) {
471 1.14 dholland nscan = sscanf(cn, "%x", rvalue);
472 1.14 dholland } else {
473 1.14 dholland nscan = sscanf(cn, "%d", rvalue);
474 1.14 dholland }
475 1.14 dholland if (nscan != 1) {
476 1.13 dholland error("Invalid value following '='");
477 1.14 dholland }
478 1.13 dholland dr = 1;
479 1.14 dholland /* Now were left with just symbol */
480 1.14 dholland *ce = '\0';
481 1.14 dholland }
482 1.14 dholland /* end if (ce) */
483 1.13 dholland return(dr);
484 1.14 dholland }
485 1.14 dholland /* end FindAssign */
486