tanner@ki4pv.UUCP (07/03/87)
: "un-pack me with /bin/sh" # This shar file contains a file patcher known around here as "fddt". # The "Makefile" is incredibly simple. The man page explains # everything (hihi). I hand-shar'ed the thing, as I don't have a # "shar" prog running here. It might unpack anyway. Try it, after # inspecting for trojan horse droppings. echo "unshar: x-fddt.1" sed s/\^X-// > fddt.1 <<END_OF_FILE X-.TH FDDT 1 X-.SH NAME X-fddt \- display and patch files X-.SH SYNOPSIS X-.B fddt X-.I "[-rw]" X-.I "file" X-.I "[-patch addr old1=new1 [old2=new2 ...] ]" X-.SH DESCRIPTION X-The X-.I fddt X-program allows viewing and patching of files, including special X-files, program binaries, and binary data files. This is especially X-helpful on those systems where "adb" won't touch your target file, X-or where "adb" isn't available. X-.PP X-If the X-.I "-r" X-argument is given, the file is opened for reading only. X-If the X-.I "-w" X-argument is given, the file is opened for writing only. X-The default is to open for both read and write. It is advised that X-you use the "-r" option on directories and "-w" option on line X-printers. X-.PP X-If the X-.I "-p" X-option is not given, the program is interactive and reads commands X-from the standard input. X-All of the commands take one or two addresses, which are X-offsets from the beginning of the file. X-The addresses are expressed in hex. X-The first address is the starting address, X-and the second address is (if given) the ending address. X-If the second address is omitted, EOF (actually a number greater than X-the max unix filesize) is assumed. X-If the first address is omitted, no work is done. X-Addresses may be separated by a space, comma, or dash. X-.\" X-.SH "INTERACTIVE COMMANDS" X-.IP "\fBd\fP" X-Display data from the file, starting at the address X-given. X-Data are shown as hex numbers and as characters, a la \fIddt\fP. X-.\" X-.IP "\fBe\fP" X-Enter data into the file, starting at the address X-given. It prompts with the address, and accepts bytes to be written X-into the file. The bytes are written upon receipt of a newline. X-Bytes must be entered as one or two hex digits. An EOF (normally X-ctrl/D or ctrl/Z) will end input, as will an interrupt. X-.\" X-.IP "\fBf\fP" X-Find data in the file, starting at the address X-given and continuing to the end of file, interrupt, or the second X-address if given. It prompts for a series of bytes to be searched X-for; they must be entered as one or two hex digits each. One line of X-bytes may be entered; the search starts upon receipt of the newline. X-.\" X-.IP "\fBq,x\fP" X-Quit. Also known as exit. X-.SH "OPTIONS" X-.IP "\fB-r\fP" X-The file is opened for reading only. The "e" command to enter data X-will not work. Non-interactive patching will not work. X-.IP "\fB-w\fP" X-The file is opened for writing only. The "d" command to display X-data and the "f" command to find data will not work. If patching, X-the old values must be given as "XX". X-.IP "\fB-p\fP" X-The file is to be patched non-interactively. X-The next argument is the starting address for the patching, X-and the following arguments are the old and new value substitutions. X-If the old value given does not match the value found in the file, an X-error is indicated; use "XX" if the old value is unknown or if the X-file is open for writing only. X-If the new value is given as "XX", no value is written. Allows X-skipping of bytes in the file. X-.SH BUGS X-This prog was originally written to fill a need under ms-dos, where X-the local debugger won't patch executable files! X-.PP X-Most versions of unix won't let you patch a running program. X-This means, of course, that the prog can't patch itself. X-.PP X-There should probably be a way to supress the lseek(2) attempts for X-non-seekable files. Displaying or patching your own tty can be X-amusing. X-.SH "SEE ALSO" X-adb(1), debug(ms-dos) X-.SH "AUTHOR" X-This program and document were written by Tanner Andrews at X-CompuData, Inc. in support of the Brother John\(rg project. X-CompuData, by making them available to you, X-neither gives up any rights nor assumes any responsibility X-for damages caused by them. END_OF_FILE echo "unshar: x-Makefile" sed s/\^X-// > Makefile <<END_OF_FILE X- X-# Makefile for "fddt" X- X-# set this as proper (use second one for xenix) X-CFLAGS = -O X-#CFLAGS = -O -K -Mm2 X- X-fddt: fddt.o X- $(CC) $(CFLAGS) -o fddt /lib/cdate.o fddt.o X- END_OF_FILE echo "unshar: x-fddt.c" sed s/\^X-// > fddt.c <<END_OF_FILE X- X- /*************************************************************** X- * * X- * disk file patch prog * X- * * X- ***************************************************************/ X- X- /* 19-Nov-85 [ki4pv!tanner] */ X- /* ed 20-Dec-85 [ki4pv!tanner] -- add find command */ X- /* ed 03-Jan-86 [ki4pv!tanner] -- add exit command */ X- /* ed 01-Jul-86 [ki4pv!tanner] -- add -p flag */ X- /* ed 22-Oct-86 [ki4pv!tanner] -- turn off stdout buffer */ X- X- X- X-#include <stdio.h> X-#include <signal.h> X- X-#define SZ 0x0100 /* size of things */ X- X- /* this program allows someone to apply patches to files X- * of almost any type. it helps a lot if lseek() will work X- * on the file, but it is not required if the file to be X- * "patched" is a serial device which is really being fed X- * a stream of bytes. X- * X- * usage: X- * fddt [-rw] <file> X- * or: X- * fddt [-w] <file> -p <addr> old1=new1 old2=new2 ... X- * where X- * -r read-only; patches may not be applied X- * -w write-only; old contents may not be displayed X- * -p "patch" mode, starts at addr and applies patch X- * X- * once a file is selected for editing, the following commands X- * will apply for interactive mode within the prog: X- * d display data X- * e enter new values X- * f find data; will prompt for values X- * q,x exit X- * all commands take either one or two addresses (if no addr X- * given, no work is done). the first addr is a starting addr; X- * the second, if given, is an ending addr. default end is EOF. X- * note that all addresses and data values are to be expressed X- * in hex. new values being entered are entered as 1-byte hex X- * values. X- * X- * patch mode is a way to non-interactively change values in the X- * file being edited. see the man page. X- * X- * warning: if your unix does not have rdchk(), you may either X- * provide a substitute with ioctl() or eliminate the line of X- * code using it with a return(0) and just use ^C to interrupt X- * commands. rdchk() appears new with SYS-3, replacing a V7 X- * ioctl() call. X- * X- * note for cd_txt[] -- this is supplied by a little daemon X- * which creates /lib/cdate.o containing today's date. the X- * daemon to do this is available from "ki4pv!tanner" upon X- * application. you could also eliminate the reference. X- * X- * This prog and documentation (C) 1987, CompuData, Inc. X- * Permission is granted to use it, so long as this X- * copyright notice remains intact, and so long as you X- * don't try to sell it or claim that you wrote it. X- * By making this program available, CompuData neither gives X- * up any rights in it, nor assumes any responsibility for X- * any damages caused by it. X- */ X- X- /* important vars */ X- X- X-static char X- tf_blt[] = "FDDT vers 1.2a built %s\n\n", X- X- tf_usg[] = "? usage: %s [-rw] <file> [-p <addr> old=new ...]\n", X- X- tf_sup[] = "%% %s suppressed\n", X- tx_rd[] = "reading", X- tx_wr[] = "writing", X- X- tf_bad[] = "? %s: bad %s: %s\n", X- tf_bdr[] = "? bad range: %08lX > %08lX\n", X- tf_nos[] = "? no search values\n", X- tf_sek[] = "? can't seek: %08lX\n", X- tf_wre[] = "? write error: file %s, off 0x%08X, n 0x%X\n", X- X- tf_fnd[] = "found @ 0x%08lX\n", X- tf_nfd[] = "not found, end @ 0x%08lX\n", X- tf_8x[] = "%08lX: ", X- tf_2x[] = "%02X ", X- X- tf_pat[] = "patching 0x%08lX: ", X- tf_pr1[] = "0x%02X", X- tx_prx[] = " xx ", X- tf_per[] = "read mismatch: exp 0x%02X, got 0x%02X\n", X- tx_pex[] = "patch expr", X- X- tx_int[] = "** intr **\n"; X- X- X-unsigned int _fmode = 0x8000; /* for ms-dos */ X- X- X- /* - - - - */ X- X-short int X- stoppt, /* "action stopped" flag */ X- rw_sup, /* -r or -w flag */ X- patch, /* set true if patching */ X- ed_fd; /* fd of file to be patched */ X- X-char X- *prog_name, X- *inbuf, /* user input buffer */ X- *ed_fn, /* name of file being patched */ X- *ed_buf, /* edit buffer */ X- *match; /* file match buffer */ X- X- /* - - - - - */ X- X-extern char X- cd_txt[]; /* /lib/cdate.o, kept by daemon */ X- X- /* = = = = = */ X- X-short int X- do_hdr(), /* things to do before any work */ X- do_lev(), /* evaluate a long addr */ X- get_ln(), /* get series of numbers */ X- get_st(), /* get "stopped" flag */ X- pat_ev(), /* eval expression for patch */ X- sig_st(); /* signal handler */ X- X-void X- do_opn(), /* open the file */ X- do_pat(), /* patch the file */ X- do_edt(), /* edit the file */ X- do_dmp(), /* dump part of file */ X- do_inp(), /* input new values to file */ X- do_src(), /* find values in file */ X- usage(); X- X- /* - - - - - */ X- X-extern long int X- lseek(); X- X-extern int X- chupper(), /* $LOCLIB */ X- dehex(); /* $LOCLIB */ X- isprint(), iswhite(), isxdigit(), X- rdchk(); /* $SYSDEP */ /* pending input check */ X- X-extern char X- *malloc(), X- *strbrk(), /* $LOCLIB */ X- *strchr(); X- X- /* main block of prog */ X- X-main(ac, av) X-short int ac; X-char **av; X- { X- register char X- *p; X- X- X- fprintf(stderr, tf_blt, cd_txt); X- X- prog_name = **av ? *av : "FDDT"; X- X- if (ac < 2) X- usage(); X- X- if (*(p = *++av) == '-') { /* have flag */ X- if (((rw_sup = chupper(*++p)) != 'R') && X- (rw_sup != 'W')) X- usage(); X- ++av; X- } X- else X- rw_sup = 0; X- X- ed_fn = *av; /* get working filename */ X- X- setbuf(stdout, (char *)0); X- X- if ((!(ed_buf = malloc(SZ))) || X- (!(match = malloc(2*SZ))) || X- (!(inbuf = malloc(SZ)))) { X- fprintf(stderr, "? %s: can't allocate\n", prog_name); X- exit(1); X- } X- X- signal(SIGINT, sig_st); /* catch ^C to abort command */ X- X- do_opn(); /* open the file */ X- X- if (*++av) X- do_pat(av); /* patch the file */ X- else X- do_edt(); /* edit the file */ X- X- if (close(ed_fd)) /* close the file */ X- no_clos(ed_fn); X- X- exit(0); X- X- } X- X- X-static void usage() X- { X- X- fprintf(stderr, tf_usg, prog_name); X- exit(1); X- X- } X- X- X- /* open file to be edited */ X- X- X-void do_opn() X- { X- register short int X- mode; X- X- X- switch (rw_sup) { /* select file open mode */ X- case 'R': X- mode = 0; X- break; X- case 'W': X- mode = 1; X- break; X- default: X- mode = 2; X- } X- X- if ((ed_fd = open(ed_fn, mode)) < 0) X- no_open(ed_fn); X- X- } X- X- /* patch the file */ X- X-void do_pat(ap) X-char **ap; X- { X- register char X- *p; X- short int X- old, new; X- long int X- p_addr, /* patch addr */ X- tmp; /* tmp value from eval */ X- unsigned char X- c; /* in/out data item */ X- X- X- if ((*(p = *ap) != '-') || X- (chupper(*++p) != 'P')) X- usage(); X- X- if (do_lev(*++ap, &p_addr)) { /* eval addr */ X- fprintf(stderr, tf_bad, prog_name, "addr", *ap); X- exit(1); X- } X- X- if (lseek(ed_fd, p_addr, 0) != p_addr) { /* just test */ X- fprintf(stderr, tf_sek, p_addr); X- exit(2); X- } X- X- for ( ; p = *++ap ; ++p_addr ) { X- p += pat_ev(p, &old, "old"); X- if (*p++ != '=') { X- fprintf(stderr, tf_bad, prog_name, tx_pex, *ap); X- exit(3); X- } X- p += pat_ev(p, &new, "new"); X- if (*p) { X- fprintf(stderr, tf_bad, prog_name, tx_pex, *ap); X- exit(4); X- } X- printf(tf_pat, p_addr); X- if (old >= 0) { /* match old */ X- if (rw_sup == 'R') { X- fprintf(stderr, tf_sup, tx_rd); X- exit(5); X- } X- if ((lseek(ed_fd, p_addr, 0) != p_addr) || X- (read(ed_fd, &c, 1) != 1) || X- (c != old)) { X- fprintf(stderr, tf_per, old, c); X- exit(6); X- } X- printf(tf_pr1, old); X- } X- else X- printf(tx_prx); X- printf(" -> "); X- if (new >= 0) { X- if (rw_sup == 'W') { X- fprintf(stderr, tf_sup, tx_wr); X- exit(7); X- } X- c = new; X- if ((lseek(ed_fd, p_addr, 0) != p_addr) || X- (write(ed_fd, &c, 1) != 1)) { X- fprintf(stderr, tf_wre, ed_fn, p_addr, 1); X- exit(8); X- } X- printf(tf_pr1, new); X- } X- else X- printf(tx_prx); X- printf(" ok\n"); X- } X- X- } X- X- X- /* patch support: evaluate old/new value */ X- X- X-static short int pat_ev(sp, dp, err) X-char *sp, *err; X-short int *dp; /* result goes here */ X- { X- register char X- *q; X- long int X- tmp; X- X- X- if (chupper(*sp) == 'X') { X- *dp = -1; X- return(1); X- } X- if (do_lev(sp, &tmp) || (tmp < 0) || (tmp > 0xFF)) { X- fprintf(stderr, tf_bad, prog_name, err, sp); X- exit(3); X- } X- *dp = tmp; X- X- for ( q=sp ; isxdigit(*q) ; ++q ); X- return((short int)(q - sp)); X- X- } X- X- /* edit the file (top-level interaction) */ X- X- X-void do_edt() X- { X- register char X- *p; X- register short int X- nrd; X- X- X- while (((nrd=read(0, inbuf, 100)) > 0) || get_st()) { X- if (nrd < 0) /* ^C caused I/O err */ X- nrd = 0; X- *(inbuf+nrd) = 0; /* null at end-of-input */ X- if (p = strchr(inbuf, '\n')) X- *p = 0; X- p = inbuf; X- while (iswhite(*p)) X- ++p; X- switch (chupper(*p)) { X- case 'F': X- do_src(p+1); X- break; X- case 'E': X- do_inp(p+1); X- break; X- case 'D': X- do_dmp(p+1); X- break; X- case 'Q': X- case 'X': X- return; X- case '?': X- printf("D-display E-enter F-find Q-exit\n"); X- break; X- default: X- printf("unknown: %c\n", *p); X- case 0: X- break; X- } X- } X- X- } X- X- /* dump values */ X- X- X-void do_dmp(p) X-char *p; X- { X- register short int X- sz, nd; X- long int X- ed_adr, X- ed_cur, X- ed_end, X- lnend; X- X- X- if (do_hdr('R', p, &ed_adr, &ed_end)) /* input bad */ X- return; X- X- ed_cur = ed_adr; /* back to start */ X- X- do { X- lnend = ed_cur - (ed_cur % 16) + 15; X- if (lnend > ed_end) X- lnend = ed_end; X- sz = lnend - ed_cur + 1; X- if ((nd = read(ed_fd, ed_buf, sz)) < sz) X- sz = nd; X- if (sz < 1) { X- printf("e-o-f\n"); X- break; X- } X- printf(tf_8x, ed_cur); X- for ( nd=0 ; nd<sz ; ++nd ) { X- printf(tf_2x, *(ed_buf+nd) & 0x00FF); X- if (nd == 7) X- fputc(' ', stdout); X- } X- fputs(" ", stdout); X- for ( nd=0 ; nd<sz ; ++nd ) { X- fputc(isprint(*(ed_buf+nd)) ? *(ed_buf+nd) : '.', X- stdout); X- if (nd == 7) X- fputc(' ', stdout); X- } X- fputc('\n', stdout); X- } while ((! get_st()) && ((ed_cur += sz) <= ed_end)); X- X- fputc('\n', stdout); X- X- } X- X- /* enter new values into file */ X- X-void do_inp(p) X-register char *p; X- { X- register short int X- n; X- long int X- ed_adr, X- ed_cur, X- ed_end; X- X- X- if (do_hdr('W', p, &ed_adr, &ed_end)) /* command bad */ X- return; X- X- ed_cur = ed_adr; X- for ( ; (n = get_ln(ed_cur)) >= 0 ; ) { X- if ((n > 0) && (write(ed_fd, ed_buf, n) < n)) { X- fprintf(stderr, tf_wre, ed_fn, ed_cur, n); X- break; X- } X- if ((ed_cur += n) > ed_end) /* gave exit point */ X- break; X- } X- X- } X- X- X- /* find values in file */ X- X-#define MATCH (*(ed_buf+bufm) == *(match+bufo+bufm)) X- X-void do_src(p) X-char *p; X- { X- register short int X- nsrc, /* # to search for */ X- nbuf, /* # chars in buff */ X- bufo, /* buffer offset */ X- bufm, /* buffer # matched */ X- nrd; X- long int X- ed_adr, X- ed_cur, X- ed_end, X- val; X- X- X- if (do_hdr('R', p, &ed_adr, &ed_end)) /* input bad */ X- return; X- X- ed_cur = ed_adr; /* back to start */ X- X- if ((nsrc = get_ln(0L)) <= 0) { X- printf(tf_nos); X- return; X- } X- X- printf("searching..."); X- nrd = nbuf = read(ed_fd, match, 2*SZ); X- for ( bufo=0 ;; ) { X- for ( bufm=0 ; (bufm < nsrc) && MATCH ; ++bufm ); X- if (bufm == nsrc) { X- printf(tf_fnd, ed_cur); X- return; X- } X- ++ed_cur; /* start at next char */ X- if (++bufo > SZ) { /* must read more */ X- move(match, match+SZ, SZ); X- bufo -= SZ; X- nbuf -= SZ; X- if (get_st()) /* input, stop search */ X- nrd = 0; X- if (nrd && ((nrd=read(ed_fd, match+SZ, SZ)) > 0)) X- nbuf += nrd; X- } X- if ((bufo >= nbuf) || (ed_cur > ed_end)) { X- printf(tf_nfd, ed_cur); X- return; X- } X- } X- X- } X- X- /* common support routines */ X- X- X- /* handle the command understanding for both display and edit X- * expects X- * sup 'R', 'W' if reading, writing required X- * p user's input X- * sad, ead ptrs to display/edit start/end addr X- * returns X- * 0 good input X- * -1 didn't X- */ X-static short int do_hdr(sup, p, sad, ead) X-char sup, *p; X-long int *sad, *ead; X- { X- X- if (rw_sup && (rw_sup != sup)) { /* make sure it's OK */ X- printf(tf_sup, (sup=='R') ? tx_rd : tx_wr); X- return; X- } X- X- if (do_lev(p, sad)) /* get starting addr */ X- return(-1); X- X- *ead = 0x40000000; /* dflt end addr */ X- X- while (iswhite(*p)) /* skip white space */ X- ++p; X- X- if ((p = strbrk(p, "-, ")) && do_lev(++p, ead)) { X- printf(tf_bad, prog_name, "end", p); X- return(-1); X- } X- X- if (*ead < *sad) { X- printf(tf_bdr, *sad, *ead); X- return(-1); X- } X- X- if (lseek(ed_fd, *sad, 0) != *sad) { /* do the seek */ X- printf(tf_sek, *sad); X- return(-1); X- } X- X- return(0); X- X- } X- X- X- /* evaluate an address X- * expects X- * p ptr to addr to scan X- * adr addr to receive value if we found number X- * returns X- * 0 worked X- * -1 no addr found X- * note X- * if it works, *adr will be filled in. X- */ X- X-static short int do_lev(p, adr) X-char *p; X-long int *adr; X- { X- register long int X- val; X- X- X- while (iswhite(*p)) /* skip */ X- ++p; X- X- if (! isxdigit(*p)) X- return(-1); X- X- for ( val=0 ; isxdigit(*p) ; ++p ) X- val = (val << 4) + dehex(*p); X- X- if (*p && (! iswhite(*p)) && (*p != '=') && (*p != '-')) X- return(-1); X- X- *adr = val; X- return(0); X- X- } X- X- X- /* get a list of numbers into the edit buffer X- * expects X- * ed_cur address to be printed in prompt X- * returns X- * -1 trouble X- * n # bytes specified on line by user X- * fills in ed_buf with the values given X- */ X- X-static short int get_ln(ed_cur) X-long int ed_cur; X- { X- register char X- *p; X- register short int X- n; X- long int X- val; X- X- X- printf(tf_8x, ed_cur); /* prompt */ X- zero(inbuf, SZ); X- if (read(0, p=inbuf, 200) < 1) { /* hit EOF */ X- fputs(" ** OK\n", stdout); X- return(-1); X- } X- for ( n=0 ; *p ; ) { /* scan & convert */ X- if (do_lev(p, &val) || X- (val > 0x00FF) || (val < 0)) { X- printf("bad value: %s\n", p); X- n = 0; X- break; X- } X- *(ed_buf+(n++)) = val; X- while (isxdigit(*p)) X- ++p; X- while (isxdigit(*p)) X- ++p; X- if (*p == ',') X- ++p; X- while (iswhite(*p)) X- ++p; X- } X- X- return(n); X- X- } X- X- /* user-attention handlers */ X- X- /* user hits ^C, set flag to indicate same X- * note: under ms-dos, signal() only works for ^C X- * but that's OK. use MS-C; lattice doesn't have X- * signal(2). X- */ X-static short int sig_st() X- { X- X- signal(SIGINT, sig_st); /* continued protection */ X- printf(tx_int); /* ack for abuser */ X- stoppt = 1; /* set flag */ X- X- } X- X- X- /* routine to see if user wants to stop command X- * checks/resets "stoppt" flag set by ^C X- * if machine has rdchk() uses that to see if pending input X- */ X- /* $SYSDEP */ X-static short int get_st() X- { X- X- if (stoppt) { /* hit ^C */ X- stoppt = 0; /* clear it */ X- return(1); X- } X- X- return(rdchk(0) > 0); /* pending input */ X- X- } X- X- /* local library routines */ X- X- /* these routines normally live in a local library. the names X- * are actually left-overs from CP/M days. X- */ X-#ifndef DONT_NEED X- /* convert a hex digit */ X-int dehex(c) X-int c; X- { X- if ((c >= '0') && (c <= '9')) X- return(c - '0'); X- if ((c >= 'A') && (c <= 'F')) X- return(c + 10 - 'A'); X- if ((c >= 'a') && (c <= 'f')) X- return(c + 10 - 'a'); X- return(-1); X- } X- X- /* find character in "breakset" */ X-char *strbrk(s1, s2) X-register char *s1, *s2; X- { X- X- if (s1 && s2) /* have strings */ X- for ( ; *s1 ; ++s1 ) /* scan string */ X- if (strchr(s2, *s1)) /* match any? */ X- return(s1); /* yup, winner */ X- X- return((char *)0); X- X- } X- X- /* case conversion -- don't trust "toupper" or "tolower */ X-int chupper(c) X-int c; X- { X- return(((c >= 'a') && (c <= 'z')) ? (c - ('a'-'A')) : c); X- } X- X-#endif X- END_OF_FILE