/* 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 #include #include #include #include #include #include #include #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); }