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