1 /* $NetBSD: binpatch.c,v 1.9 2002/05/31 20:52:14 thorpej 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 : __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 static char desusage[] = 34 "DESCRIPTION\n" 35 "\tAllows the patching of BSD binaries, for example,a distributed\n" 36 "\tkernel. Recient additions allows the user to index into an array\n" 37 "\tand assign a value. Binpatch has internal variables to allow\n" 38 "\tyou to test it on itself under NetBSD.\n" 39 "OPTIONS\n" 40 "\t-a patch variable by specifying address in hex\n" 41 "\t-b symbol or address to be patched is 1 byte\n" 42 "\t-l symbol or address to be patched is 4 bytes (default)\n" 43 "\t-o offset to begin patching value relative to symbol or address\n" 44 "\t-r replace value, and print out previous value to stdout\n" 45 "\t-s patch variable by specifying symbol name. Use '[]'\n" 46 "\t to specify the 'index'. If '-b, -w or -l' not specified\n" 47 "\t then index value is used like an offset. Also can use '='\n" 48 "\t to assign value\n" 49 "\t-w symbol or address to be patched is 2 bytes\n" 50 "EXAMPLES\n" 51 "\tThis should print 100 (this is a nice reality check...)\n" 52 "\t\tbinpatch -l -s _hz netbsd\n" 53 "\tNow it gets more advanced, replace the value:\n" 54 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd\n" 55 "\tNow patch a variable at a given 'index' not offset,\n" 56 "\tunder NetBSD you must use '', under AmigaDos CLI '' is optional.:\n" 57 "\t\tbinpatch -w -s '_vieww[4]' -r 0 a.out\n" 58 "\tsame as\n" 59 "\t\tbinpatch -w -o 8 -s _vieww -r 0 a.out\n" 60 "\tAnother example of using []\n" 61 "\t\tbinpatch -s '_viewl[4]' -r 0 a.out\n" 62 "\tsame as\n" 63 "\t\tbinpatch -o 4 -s _viewl -r 0 a.out\n" 64 "\tOne last example using '=' and []\n" 65 "\t\tbinpatch -w -s '_vieww[4]=2' a.out\n" 66 "\tSo if the kernel is not finding your drives, you could enable\n" 67 "\tall available debugging options, helping to shed light on that problem.\n" 68 "\t\tbinpatch -l -s _sbic_debug -r 1 netbsd scsi-level\n" 69 "\t\tbinpatch -l -s _sddebug -r 1 netbsd sd-level (disk-driver)\n" 70 "\t\tbinpatch -l -s _acdebug -r 1 netbsd autoconfig-level\n" 71 "SEE ALSO\n" 72 "\tbinpatch.c binpatch(1)\n"; 73 74 extern char *optarg; 75 extern int optind; 76 77 volatile void error (char *); 78 static void Synopsis(char *program_name); 79 static void Usage(char *program_name); 80 static u_long FindAssign(char *symbol,u_long *rvalue); 81 static void FindOffset(char *symbol,u_long *index); 82 83 /* The following variables are so binpatch can be tested on itself */ 84 int test = 1; 85 int testbss; 86 char foo = 23; 87 char viewb[10] = {0,0,1,0,1,1,0,1,1,1}; 88 short vieww[10] = {0,0,1,0,1,1,0,1,1,1}; 89 long viewl[10] = {0,0,1,0,1,1,0,1,1,1}; 90 /* End of test binpatch variables */ 91 int 92 main(int argc, char *argv[]) 93 { 94 struct exec e; 95 int c; 96 u_long addr = 0, offset = 0; 97 u_long index = 0;/* Related to offset */ 98 u_long replace = 0, do_replace = 0; 99 char *symbol = 0; 100 char size = 4; /* default to long */ 101 char size_opt = 0; /* Flag to say size option was set, used with index */ 102 char *fname; 103 char *pgname = argv[0]; /* Program name */ 104 int fd; 105 int type, off; 106 u_long lval; 107 u_short sval; 108 u_char cval; 109 110 111 while ((c = getopt (argc, argv, "H:a:bwlr:s:o:")) != -1) 112 switch (c) 113 { 114 case 'H': 115 Usage(argv[0]); 116 break; 117 case 'a': 118 if (addr || symbol) 119 error ("only one address/symbol allowed"); 120 if (! strncmp (optarg, "0x", 2)) 121 sscanf (optarg, "%x", &addr); 122 else 123 addr = atoi (optarg); 124 if (! addr) 125 error ("invalid address"); 126 break; 127 128 case 'b': 129 size = 1; 130 size_opt = 1; 131 break; 132 133 case 'w': 134 size = 2; 135 size_opt = 1; 136 break; 137 138 case 'l': 139 size = 4; 140 size_opt = 1; 141 break; 142 143 case 'r': 144 do_replace = 1; 145 if (! strncmp (optarg, "0x", 2)) 146 sscanf (optarg, "%x", &replace); 147 else 148 replace = atoi (optarg); 149 break; 150 151 case 's': 152 if (addr || symbol) 153 error ("only one address/symbol allowed"); 154 symbol = optarg; 155 break; 156 157 case 'o': 158 if (offset) 159 error ("only one offset allowed"); 160 if (! strncmp (optarg, "0x", 2)) 161 sscanf (optarg, "%x", &offset); 162 else 163 offset = atoi (optarg); 164 break; 165 }/* while switch() */ 166 167 if (argc > 1) 168 { 169 if (addr || symbol) 170 { 171 argv += optind; 172 argc -= optind; 173 174 if (argc < 1) 175 error ("No file to patch."); 176 177 fname = argv[0]; 178 if ((fd = open (fname, 0)) < 0) 179 error ("Can't open file"); 180 181 if (read (fd, &e, sizeof (e)) != sizeof (e) 182 || N_BADMAG (e)) 183 error ("Not a valid executable."); 184 185 /* fake mid, so the N_ macros work on the amiga.. */ 186 e.a_midmag |= 127 << 16; 187 188 if (symbol) 189 { 190 struct nlist nl[2]; 191 if (offset == 0) 192 { 193 u_long new_do_replace = 0; 194 new_do_replace = FindAssign(symbol,&replace); 195 if (new_do_replace && do_replace) 196 error("Cannot use both '=' and '-r' option!"); 197 FindOffset(symbol,&index); 198 if (size_opt) 199 offset = index*size; /* Treat like an index */ 200 else 201 offset = index; /* Treat index like an offset */ 202 if (new_do_replace) 203 do_replace = new_do_replace; 204 } 205 nl[0].n_un.n_name = symbol; 206 nl[1].n_un.n_name = 0; 207 if (nlist (fname, nl) != 0) 208 { 209 fprintf(stderr,"Symbol is %s ",symbol); 210 error ("Symbol not found."); 211 } 212 addr = nl[0].n_value; 213 type = nl[0].n_type & N_TYPE; 214 } 215 else 216 { 217 type = N_UNDF; 218 if (addr >= N_TXTADDR(e) && addr < N_DATADDR(e)) 219 type = N_TEXT; 220 else if (addr >= N_DATADDR(e) && addr < N_DATADDR(e) + e.a_data) 221 type = N_DATA; 222 } 223 addr += offset; 224 225 /* if replace-mode, have to reopen the file for writing. 226 Can't do that from the beginning, or nlist() will not 227 work (at least not under AmigaDOS) */ 228 if (do_replace) 229 { 230 close (fd); 231 if ((fd = open (fname, 2)) == -1) 232 error ("Can't reopen file for writing."); 233 } 234 235 if (type != N_TEXT && type != N_DATA) 236 error ("address/symbol is not in text or data section."); 237 238 if (type == N_TEXT) 239 off = addr - N_TXTADDR(e) + N_TXTOFF(e); 240 else 241 off = addr - N_DATADDR(e) + N_DATOFF(e); 242 243 if (lseek (fd, off, 0) == -1) 244 error ("lseek"); 245 246 /* not beautiful, but works on big and little endian machines */ 247 switch (size) 248 { 249 case 1: 250 if (read (fd, &cval, 1) != 1) 251 error ("cread"); 252 lval = cval; 253 break; 254 255 case 2: 256 if (read (fd, &sval, 2) != 2) 257 error ("sread"); 258 lval = sval; 259 break; 260 261 case 4: 262 if (read (fd, &lval, 4) != 4) 263 error ("lread"); 264 break; 265 }/* switch size */ 266 267 268 if (symbol) 269 printf ("%s(0x%x): %d (0x%x)\n", symbol, addr, lval, lval); 270 else 271 printf ("0x%x: %d (0x%x)\n", addr, lval, lval); 272 273 if (do_replace) 274 { 275 if (lseek (fd, off, 0) == -1) 276 error ("write-lseek"); 277 switch (size) 278 { 279 case 1: 280 cval = replace; 281 if (cval != replace) 282 error ("byte-value overflow."); 283 if (write (fd, &cval, 1) != 1) 284 error ("cwrite"); 285 break; 286 287 case 2: 288 sval = replace; 289 if (sval != replace) 290 error ("word-value overflow."); 291 if (write (fd, &sval, 2) != 2) 292 error ("swrite"); 293 break; 294 295 case 4: 296 if (write (fd, &replace, 4) != 4) 297 error ("lwrite"); 298 break; 299 }/* switch(size) */ 300 }/* if (do_replace) */ 301 302 close (fd); 303 }/* if(addr || symbol ) */ 304 else 305 { 306 error("Must specify either address or symbol."); 307 } 308 }/* if argc < 1 */ 309 else 310 { 311 Synopsis(pgname); 312 } 313 return(0); 314 }/* main () */ 315 316 317 318 volatile void error (char *str) 319 { 320 fprintf (stderr, "%s\n", str); 321 exit (1); 322 } 323 324 /* Give user very short help to avoid scrolling screen much */ 325 static void Synopsis(char *pgname) 326 { 327 fprintf(stdout,synusage,pgname,pgname,pgname,pgname,pgname); 328 } 329 330 331 static void Usage(char *pgname) 332 { 333 Synopsis(pgname); 334 fprintf(stdout,desusage); 335 exit(0); 336 } 337 338 339 /* FindOffset() - Determine if there is an offset, -or- index 340 embedded in the symbol. 341 If there is, return it, and truncate symbol to 342 exclude the [...]. 343 Example: If view is declared as short view[10], 344 and we want to index the 3rd. element. 345 which is offset = (3 -1)*sizeof(short) =4. 346 we would use view[4], which becomes view,4. 347 The was the code is implemented the [value] is 348 treated as a index if-and-only-if a '-b -w -l' option 349 was given. Otherwise it is treated like an offset. 350 See above documentation in for of help! 351 */ 352 static void FindOffset(char *symbol,u_long *index) 353 { 354 char *sb=strchr(symbol,'['); /* Start of '[', now line must 355 contain matching']' */ 356 char *eb=strchr(symbol,']'); /* End of ']' */ 357 short sz=strlen(symbol); /* symbol size */ 358 if (sb) 359 { 360 if (eb && (eb > sb)) 361 { 362 if ((eb - symbol) == (sz - 1)) 363 { 364 char *sindex; /* Start of index */ 365 u_long newindex = 0; 366 /* In the future we could get fancy and parse the 367 sindex string for mathmatical expressions like: 368 (3 - 1)*2 = 4 from above example, 369 ugh forget I mentioned ot :-) ! 370 */ 371 sindex = sb + 1; 372 *eb = '\0'; 373 newindex = (u_long)atoi(sindex); 374 if (*index == 0) 375 { 376 *index = newindex; 377 *sb = '\0'; /* Make _view[3] look like _view */ 378 } 379 else 380 fprintf(stderr,"Error index can only be specified once!\n"); 381 } 382 else 383 { 384 fprintf(stderr,"Error: Garbage trailing ']'\n"); 385 } 386 } 387 else 388 { 389 fprintf(stderr,"Error ']' in symbol before '[' !\n"); 390 } 391 }/* if sb != 0 */ 392 }/* FindOffset */ 393 394 /* FindAssign : Scans symbol name for an '=number' strips it off 395 of the symbol and proceeds. 396 */ 397 static u_long FindAssign(char *symbol,u_long *rvalue) 398 { 399 char *ce = rindex(symbol,'='); /* Assign symbol some number */ 400 char *cn = ce + 1; /* This should point at some number, no spaces allowed */ 401 u_long dr = 0; /* flag for do_replace */ 402 if (ce) 403 { 404 int nscan; /* number of variaables scanned in */ 405 /* get the number to assign to symbol and strip off = */ 406 for (cn=ce + 1;((*cn==' ')&&(*cn!='\0'));cn++) 407 ; 408 if (! strncmp (cn, "0x", 2)) 409 nscan = sscanf (cn, "%x",rvalue); 410 else 411 nscan = sscanf(cn,"%d",rvalue); 412 if (nscan != 1) 413 error("Invalid value following '='"); 414 dr = 1; 415 *ce = '\0';/* Now were left with just symbol */ 416 }/* if (ce) */ 417 return(dr); 418 }/* FindAssign */ 419