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