home *** CD-ROM | disk | FTP | other *** search
- /* intercept.c -- spawn a process and intercept BIOS and DOS
- * SWI (software interrupt) calls, recording register data to
- * a file.
- * Demonstrates chaining interrupt handlers.
- *************************************************************
- * Solely for compilation under Borland's Turbo C.
- *************************************************************
- * Written 7/31/1987 by:
- * Ned Konz
- * 210 Oleeta St.
- * Ormond Bch, FL 32074 (904)672-2431
- * BIX:nkonz CIS:76046,223
- *
- * Released into the public domain by the author.
- *
- * Modified 4/11/1989 by:
- * Russell Nelson
- * 11 Grant St.
- * Potsdam, NY 13676 (215)265-5655
- * CIS: 70441,205 GEnie: BH01 Internet: nelson@clutx.clarkson.edu
- * BITNET: nelson@clutx UUCP: uunet!clutx.clarkson.edu!nelson
- *
- * No copyright is claimed by Russell Nelson
- *
- **************************************************************/
-
- /*************************************************************
- * This program was written so I could find out quickly what
- * an unknown program's interface with the outside world was.
- * It does not currently handle hardware interrupts.
- * What it does:
- *
- * 1. Runs the specified program, intercepting certain interrupts
- * and recording data in memory.
- * 2. Writes an intermediate file filled with Swi_info structures (binary)
- * 3. Runs a program called "INTERPRE.EXE" to interpret the
- * intermediate file (It's a separate program to keep
- * this one small and so you can write your own.)
- * It's called like this:
- * interpre.exe [-l] infilename outfilename progname [args...]
- * where -l denotes long output format.
- **************************************************************/
-
- #include <dos.h>
- #include <stdio.h>
- #include <mem.h>
- #include <alloc.h>
- #include <process.h>
- #include <io.h>
- #include <string.h>
- #include <dir.h>
- #include "intercept.h"
-
- char *usage =
- "[-l] [-T tmpdir] [-s maxcalls] [-o outfile] [-d datafile]\n"
- "\tprogram [args...]\n"
- " -l sets long format output: explanation AND register values\n"
- " -T sets temporary directory for intermediate file to \"tmpdir\"\n"
- " (will use TMP or TMPDIR environment vars. if found otherwise)\n"
- " -s sets the maximum number of SWI records to \"maxcalls\"\n"
- " -o names the output filename to \"outfile\" rather than\n"
- " the default name (\"intercep.out\")\n"
- " -d sets the interrupt data file to use, defaults to INTERPRE.DAT\n"
- " program is the name of the program to monitor\n"
- " args are any command-line arguments to be passed\n"
- " to the monitored program.\n";
-
- char *logo = "INTERCEPT -- monitor DOS and BIOS calls. By:\n"
- " Ned Konz\n"
- " 210 Oleeta St.\n"
- " Ormond Bch, FL 32074\n"
- " BIX:nkonz CIS:76046,223 (904)672-2431\n"
- " 08/02/1987\n"
- " Datafile option and parsing added by Russell Nelson\n";
-
- char switchar, /* DOS parameter switch char (from int 0x21, fn 0x3700) */
- sepchar = '\\'; /* DOS filename separator */
-
- void interrupt inthandler ( Regpack r, Intpack i );
- int get_swi_list(unsigned);
- void install(void);
- void uninstall(void);
-
- /* the 8086/8088 INT instruction */
- #define SWI_INSTRUCTION 0xCD
-
- /* int_table[] --
- * table containg numbers of interrupts we're catching
- * and their old handlers
- * should be sorted by most common interrupts first.
- * NO HARDWARE INTERRUPTS!!!
- * Note: some of these may be commented out because they tend to
- * quickly fill up the output file. Uncomment and re-compile
- * if you want them too.
- */
- Intblock
- int_table[MAX_INTS + 1]; /* leave room for a terminator */
-
- /* swi_list[] --
- * area in memory into which we store data about each SWI
- */
- Swi_info huge *swi_list = NULL; /* beginning of swi_list */
- Swi_info huge *swi_list_end = NULL; /* just past end of swi_list */
- volatile Swi_info huge *swi_next = NULL; /* pointer to next block */
-
- /* our single interrupt handler
- * which merely records our registers and interrupt number
- * in swi_list[]
- * and chains to old handler.
- */
- void interrupt
- inthandler ( Regpack r, Intpack i )
- {
- /* the following variables are declared as static to get them
- * off the caller's stack and to ensure that t[] is where
- * we want it to be: from [BP-01] through [BP-06]
- */
- static unsigned char far * caller;
- static unsigned char which_int;
- static IFP oldhandler;
- static Intblock *ibp;
- volatile unsigned t[3]; /* to move stack values down by 3 words into */
-
- /* point to next instruction */
- caller = (char far *)i.ipcs;
-
- if (caller[-2] != SWI_INSTRUCTION) { /* was this a non-SWI? (uh-oh!) */
- uninstall();
- exit(-1);
- }
-
- which_int = caller[-1]; /* which SWI is this? */
-
- if (FP_SEG(i.ipcs) > _CS && FP_SEG(i.ipcs) < 0xA000
- && swi_next < swi_list_end) {
- swi_next->regs = r;
- swi_next->caller = i;
- swi_next->intnum = which_int;
- swi_next++;
- }
-
- /* get old handler value */
- for (ibp=int_table; ibp->intnum>=0 && ibp->intnum!=which_int; ibp++)
- ;
-
- if (ibp->intnum < 0) /* can't happen... */
- return; /* but if it does, just return "safely" */
-
- oldhandler = ibp->oldint;
-
- /* move all our registers down by 3 words on the stack */
- movedata(_SS, FP_OFF(&r), _SS, FP_OFF(t), sizeof(Regpack));
-
- /* supply a mock flag value with interrupts masked OFF */
- r.ovl.new.flags = i.flags & ~0x0200;
-
- /* get the address of the routine to chain to */
- r.ovl.new.ipcs = oldhandler;
-
- /* bump our frame pointer value down by 3 words --
- * the stack pointer will be loaded from this new value next.
- */
- _BP -= 6;
-
- /* unstack all registers and do an IRET */
- return;
- }
-
- int
- get_swi_list(unsigned n)
- {
- if (!(swi_list = farcalloc(n, sizeof(Swi_info))))
- return 0;
-
- swi_next = swi_list;
- swi_list_end = swi_list + n;
- return n;
- }
-
-
- /* install our handler for all the named interrupts
- */
- void
- install()
- {
- Intblock *ibp;
- IFPP vp;
-
- for (ibp = int_table; ibp->intnum >= 0; ibp++) {
- vp = (IFPP) MK_FP(0, ibp->intnum*4);
- ibp->oldint = *vp;
- disable();
- *vp = inthandler;
- enable();
- }
- }
-
- /* un-install our handler for all the named interrupts
- */
- void
- uninstall()
- {
- Intblock *ibp;
- IFPP vp;
-
- for (ibp = int_table; ibp->intnum >= 0; ibp++) {
- if (ibp->oldint != NULL) {
- vp = (IFPP) MK_FP(0, ibp->intnum*4);
- disable();
- *vp = ibp->oldint;
- enable();
- }
- }
- }
-
-
- /* Read template file into buffer, setting pointers to
- * beginning of lines.
- * Sets template_text, templates and ntemplates.
- * Pads out interrupt IDs to 6 characters.
- * Returns number of lines.
- */
- int
- read_template_file(char *filename)
- {
- FILE *ifile;
- char inline[ 100 ];
- Intblock *ibp = int_table;
- int i;
-
- if (! (ifile = fopen(filename, "rt")))
- return 0;
- while (fgets(inline, sizeof(inline), ifile)) {
- if (ibp - int_table >= MAX_INTS)
- return 0;
- if (sscanf(inline, "%2x", &i) != 1)
- continue;
- if (ibp > int_table && i == (ibp-1)->intnum)
- continue; /* same as the previous one */
- ibp->intnum = i;
- ibp->oldint = NULL;
- ibp++;
- }
- ibp->intnum = -1; /* terminate the list */
- fclose(ifile);
- return 1;
- }
-
-
- /* output structures to intermediate file, call filter program
- * return child return code (zero if OK)
- */
- int
- output_file(char *tmpdir, char *outfilename, int longmode,
- char *progname, char *tfilename)
- {
- Swi_info huge *swip;
- Swi_info outrec;
- FILE *ofp;
- char tempname[ 80 ];
- int rval = 0;
-
- sprintf(tempname, "%s%c%s", tmpdir, sepchar, "intercXXXXXX");
-
- if (! mktemp(tempname)) {
- fprintf(stderr, "%s: Bad temp file: %s\n", progname, tempname);
- return -1;
- }
- if (! (ofp = fopen(tempname, "wb"))) {
- fprintf(stderr, "%s: Can't open intermediate file %s\n",
- progname, tempname);
- return -1;
- }
- #ifdef DEBUG
- fprintf(stderr, "%s: Using \"%s\" as intermediate file\n",
- progname, tempname);
- #endif
- for (swip = swi_list; swip < (Swi_info huge *)swi_next; swip++) {
- outrec = *swip;
- if (fwrite(&outrec, sizeof(outrec), 1, ofp) != 1) {
- fprintf(stderr, "%s: Write error on file %s\n", progname, tempname);
- fclose(ofp);
- unlink(tempname);
- return -1;
- }
- }
- fclose(ofp);
- #ifdef DEBUG
- fprintf(stderr, "Running: " OFILTER " %s %s %s %s %s\n",
- (longmode ? "-l" : " "), "-t", tfilename, tempname, outfilename
- );
- #endif
- rval = spawnlp(P_WAIT, OFILTER, OFILTER,
- (longmode ? "-l" : " "), "-t", tfilename, tempname, outfilename
- , NULL);
- #ifndef DEBUG
- unlink(tempname);
- #endif
- return rval;
- }
-
- void
- main(int argc, char *argv[])
- {
- static unsigned nswi = MAX_INTERRUPTS; /* how many to record? */
- static char *ofilename; /* output file name */
- static char *tfilename; /* template file name */
- static int childret;
- static int longmode = 0;
- static char tmpdir[ 64 ] = ".";
- char *progname;
-
- /* spit out logo */
- fprintf(stderr, "%s", logo);
-
- if ((switchar = getswitchar()) != '/')
- sepchar = '/';
- progname = strrchr(argv[0], sepchar) + 1;
- *strchr(progname, '.') = '\0';
-
- /* process cmdline arguments */
- if (argc < 2) {
- fprintf(stderr, "Usage: %s %s", progname, usage);
- exit(1);
- }
-
- /* get TMP or TMPDIR (use ofilename as tmp var) */
- if ((ofilename = getenv("TMPDIR")) || (ofilename = getenv("TMP")))
- strncpy(tmpdir, ofilename, sizeof(tmpdir));
-
- ofilename = OFILENAME;
- tfilename = TFILENAME;
-
- while (argv[1][0] == '-') {
- char *dummy; /* is this needed? */
- switch (argv[1][1]) {
- case 's': /* specify max swi calls */
- case 'S':
- nswi = (unsigned)strtol(argv[2], &dummy, 0);
- argv++;
- break;
-
- case 'o': /* specify output filename */
- case 'O':
- ofilename = argv[2];
- argv++;
- break;
-
- case 'd': /* specify data filename */
- case 'D':
- tfilename = argv[2];
- argv++;
- break;
-
- case 'l': /* set long-mode output */
- case 'L':
- longmode++;
- break;
-
- case 't':
- case 'T':
- strncpy(tmpdir, argv[2], sizeof(tmpdir));
- argv++;
- break;
-
- default:
- fprintf(stderr, "%s: unknown option \"%s\"\n",progname,argv[1]);
- fprintf(stderr, "Usage: %s\t%s", progname, usage);
- exit(2);
- break;
- }
- argv++;
- }
-
- if (! read_template_file( searchpath(tfilename) )) {
- fprintf(stderr, "%s: error during read of \"%s\"\n",
- progname, tfilename);
- exit(5);
- }
-
- /* obtain far segment for recording calls */
- if (! get_swi_list(nswi)) {
- fprintf(stderr, "%s: Can't get enough memory for %u swi records\n",
- progname, nswi);
- exit(3);
- }
- else
- fprintf(stderr, "%s: Recording up to %u SWI records to file \"%s\"\n",
- progname, nswi, ofilename);
-
- /* install our interrupt handler for each interrupt in the list */
- install();
-
- /* now run the process */
- childret = spawnvp(P_WAIT, argv[1], argv+1);
-
- /* and restore our captured interrupts */
- uninstall();
-
- /* report on child return value */
- if (childret == -1) {
- fprintf(stderr, "%s: Spawn of \"%s\" failed: %s\n",
- progname, argv[1], strerror(NULL));
- exit(4);
- }
- if (childret != 0)
- fprintf(stderr, "%s: Child process \"%s\" exit value: %d\n",
- progname, argv[1], childret);
-
- /* output intermediate file and run output filter program */
- if (output_file(tmpdir, ofilename, longmode, progname, tfilename))
- fprintf(stderr, "%s: couldn't run output filter program " OFILTER "\n",
- progname);
-
- farfree(swi_list);
-
- exit(0);
- }
-