Home | History | Annotate | Line # | Download | only in binpatch
binpatch.c revision 1.8
      1 /*	$NetBSD: binpatch.c,v 1.8 2002/01/26 13:16:06 aymeric 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 : __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