Home | History | Annotate | Line # | Download | only in binpatch
binpatch.c revision 1.10
      1 /*	$NetBSD: binpatch.c,v 1.10 2002/12/10 17:14:05 thorpej Exp $	*/
      2 
      3 /* Author: Markus Wild mw (at) eunet.ch ???   */
      4 /* Modified: Rob Leland leland (at) 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 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