home *** CD-ROM | disk | FTP | other *** search
- /*
- CALLFUNC.C -- Windows Dynlink Function-Call Interpreter
-
- Copyright (c) Andrew Schulman & Dave Maxey, 1992. All Rights Reserved.
-
- Contact: Andrew Schulman (CompuServe 76320,302)
-
- From Chapter 4 of "Undocumented Windows" (Addison-Wesley 1992)
- by Andrew Schulman, Dave Maxey and Matt Pietrek
-
- Build using: WINIOBC CALLFUNC HANDLES (for Borland C++ v3.00)
- WINIOMS CALLFUNC HANDLES (for Microsoft C/SDK)
- Note: Ensure that HANDLES.H and HANDLES.C are present
- */
-
- #include <windows.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <dos.h>
- #include "toolhelp.h"
- #include "winio.h"
- #include "handles.h"
-
- typedef WORD (far *FN)();
- typedef LPSTR (far *STRFN)();
- typedef BYTE (far *BYTEFN)();
- typedef WORD (far *WORDFN)();
- typedef DWORD (far *LONGFN)();
- typedef double (far pascal *FLOATFN)();
-
- #ifdef __BORLANDC__
- #define _asm asm
- #endif
-
- #ifndef FALSE
- #define FALSE 0
- #define TRUE 1
- #endif
-
- #define ID_HELP 1
-
-
- HANDLE modhandle(LPSTR modname)
- {
- HANDLE handle;
- if ((handle = GetModuleHandle(modname)) != NULL)
- return handle;
- else
- return LoadLibrary(modname);
- }
-
- DWORD axtol(char *s)
- {
- DWORD x, xx;
- int radix;
- int c;
- BOOL neg = FALSE;
-
- if (! s)
- return 0;
-
- if (*s == '-')
- {
- neg = TRUE;
- s++;
- }
-
- if (s[0] == '0' && s[1] == 'x')
- {
- radix = 16;
- s += 2;
- }
- else if (strchr(s, ':')) // seg:ofs
- radix = 16;
- else
- radix = 10;
-
- x = xx = 0;
- while (c = *s++)
- {
- if (c == ':')
- {
- xx = x << 16;
- x = 0;
- }
- else
- x *= radix;
- if (c >= '0' && c <= '9')
- x += (c - '0');
- else if (radix == 16)
- {
- if (c >= 'A' && c <= 'F')
- x += (10 + (c - 'A'));
- else if (c >= 'a' && c <= 'f')
- x += (10 + (c - 'a'));
- else if (c != ':')
- break;
- }
- else
- break;
- }
-
- return (x | xx) * (neg ? -1L : 1L);
- }
-
- typedef enum { typ_string, typ_byte, typ_word, typ_long, typ_float,
- typ_buffer } TYPE;
-
- TYPE type(char *arg);
- TYPE retval_type(char *s);
-
- /* push(): a trick that relies on pascal calling convention */
- void pascal push() { }
-
- // push args on stack, count # of words
- #define PUSH_ARG(arg) \
- { \
- switch (type(arg)) \
- { \
- case typ_string: push((LPSTR) arg); c += 2; break; \
- case typ_byte: push(arg[1]); c += 1; break; \
- case typ_word: push((WORD) axtol(arg)); c += 1; break; \
- case typ_long: push(axtol(arg)); c += 2; break; \
- case typ_float: push(atof(arg)); c += 4; break; \
- } \
- }
-
- int split(char *s, char *arr[], int max, char *splitchars)
- {
- char *tok;
- char *eos;
- int argc = 1; // argv[0] unused
- int inQuotes = 0;
-
- if (! s)
- goto done;
- eos = s + strlen(s);
- while (tok = strtok(s, splitchars))
- {
- s = 0;
- if (! inQuotes)
- {
- if (*tok == '\"')
- {
- inQuotes = 1;
- tok++;
- if ((s = tok + strlen(tok)) != eos)
- *s = ' ';
- }
- arr[argc++] = tok;
- if (argc == max)
- break;
- }
- if (inQuotes)
- if ((s = strchr(tok, '\"')) != NULL)
- {
- *s++ = 0;
- inQuotes = 0;
- }
- else
- if ((s = tok + strlen(tok)) != eos)
- *s++ = ' ';
- }
- done:
- return argc;
- }
-
-
- #define ARG_MAX 24
-
- static CATCHBUF catchbuf = {0} ;
- static char *err_msg = 0;
- static BOOL in_dynlink = 0;
- static unsigned callfunc_ss = 0;
-
- #if 1
- #define error(s) do { err_msg = s; Throw(catchbuf, 1); } while (0)
- #else
- #define error(s) do { puts(s); return; } while (0)
- #endif
-
- #define SAVE_ES() _asm push es
- #define RESTORE_ES() _asm pop es
-
- void dynlink(int argc, char *argv[])
- {
- FN f;
- TYPE retval_typ = typ_word;
- char *mask = "0x%04x";
- HANDLE module;
- WORD save_ax, save_bx, save_cx, save_dx;
- WORD save_si, save_di, save_ds, save_es;
- BOOL is_cdecl = FALSE;
- BOOL is_reg_based = FALSE;
- BOOL show_regs = FALSE;
- int start_arg = 3;
- int i, c;
-
- if (argc < 3)
- error("usage: <dllname> <func name> [args...] [%mask] [!]");
-
- /* see if cdecl */
- if (argv[argc-1][0] == '!')
- {
- is_cdecl = TRUE;
- argc--;
- }
- else if (argv[argc-1][0] == '?')
- {
- show_regs = TRUE;
- argc--;
- }
- else if ((argc > 2) && (strcmp(argv[3], "regs") == 0))
- {
- is_reg_based = TRUE;
- start_arg++;
- }
-
- /* handle optional printf mask */
- if (strchr(argv[argc-1], '%'))
- retval_typ = retval_type(mask = argv[--argc]);
-
- if ((module = modhandle(argv[1])) == 0)
- error("can't load dll");
-
- /* pass ASCIIZ string or ordinal number */
- if (isdigit(argv[2][0]))
- f = (FN) GetProcAddress(module, (LPSTR) atol(argv[2])); // ord#
- else if (strchr(argv[2], ':'))
- f = (FN) axtol(argv[2]);
- else
- f = (FN) GetProcAddress(module, argv[2]);
- if (! f)
- error("can't find function");
-
- if (show_regs)
- {
- _asm mov save_ax, ax;
- _asm mov save_bx, bx;
- _asm mov save_cx, cx;
- _asm mov save_dx, dx;
- _asm mov save_si, si;
- _asm mov save_di, di;
- _asm mov save_ds, ds;
- _asm mov save_es, es;
-
- printf("AX=%04x BX=%04x CX=%04x DX=%04x SI=%04x DI=%04x "
- "DS=%04x ES=%04x\n", save_ax, save_bx, save_cx, save_dx,
- save_si, save_di, save_ds, save_es);
- }
- else if (is_cdecl)
- {
- /* push in reverse order for cdecl */
- for (i=argc-1, c=0; i>=start_arg; i--)
- PUSH_ARG(argv[i]);
- }
- else
- {
- for (i=start_arg, c=0; i<argc; i++)
- PUSH_ARG(argv[i]);
- }
-
- if (is_reg_based)
- {
- save_dx = (WORD) axtol(argv[7]);
- save_cx = (WORD) axtol(argv[6]);
- save_bx = (WORD) axtol(argv[5]);
- save_ax = (WORD) axtol(argv[4]);
- _asm mov ax, save_ax;
- _asm mov bx, save_bx;
- _asm mov cx, save_cx;
- _asm mov dx, save_dx;
- }
-
- in_dynlink++;
-
- /* args are on the stack : call (*f)() and print retval */
- switch (retval_typ)
- {
- case typ_string: printf(mask, ((STRFN) f)()); break;
- case typ_byte: printf(mask, ((BYTEFN) f)()); break;
- case typ_word: printf(mask, f()); break;
- case typ_long: printf(mask, ((LONGFN) f)()); break;
- case typ_float: printf(mask, ((FLOATFN) f)()); break;
- }
-
- in_dynlink--;
-
- if (is_cdecl)
- {
- c <<= 1;
- _asm add sp, word ptr c;
- }
-
- if (show_regs)
- {
- _asm mov save_ax, ax;
- _asm mov save_bx, bx;
- _asm mov save_cx, cx;
- _asm mov save_dx, dx;
- _asm mov save_si, si;
- _asm mov save_di, di;
- _asm mov save_ds, ds;
- _asm mov save_es, es;
-
- printf("AX=%04x BX=%04x CX=%04x DX=%04x SI=%04x DI=%04x "
- "DS=%04x ES=%04x\n", save_ax, save_bx, save_cx, save_dx,
- save_si, save_di, save_ds, save_es);
- }
-
- printf("\n");
- }
-
- void do_help(HWND hwnd, WORD wID)
- {
- winio_warn(FALSE, "CallFunc HELP",
- "At the > prompt, enter:\n"
- "\tdll-name func-name [REGS] [params] [%mask] [!]\n"
- "\t\tOR\n"
- "\tdll-name ordinal [REGS] [params] [%mask] [!]\n"
- "\t\tOR\n"
- "\tDUMP xxxx:xxxx [bytes]\n"
- "\t\tOR\n"
- "\tSEL xxxx\n"
- "\t\tOR\n"
- "\tINFO or ?\n"
- "\t\tOR\n"
- "\t!exe-name [params]\n"
- "\t\tOR\n"
- "\tEXIT");
- }
-
- void info(void)
- {
- extern HANDLE FAR PASCAL GetTaskQueue(HANDLE hTask);
- static WORD hCS;
- HANDLE hTask = GetCurrentTask();
-
- _asm mov word ptr hCS, cs;
-
- printf("hwnd = 0x%04x\n", __hMainWnd);
- printf("task = 0x%04x\n", hTask);
- printf("module = 0x%04x\n", HMODULE_FROM_HTASK(hTask));
- printf("instance (DS) = 0x%04x\n", HINSTANCE_FROM_HTASK(hTask));
- printf("CS = 0x%04x\n", hCS);
- printf("PSP (PDB) = 0x%04x\n", PSP_FROM_HTASK(hTask));
- printf("task q = 0x%04x\n", GetTaskQueue(hTask));
- printf("local heap = 0x%04x\n", *((WORD near *) 6));
- }
-
- #define WIDTH 16
-
- void dump(LPSTR fp, WORD bytes, char *mask, DWORD addr)
- {
- HWND hwndNew, hwndSav = NULL;
- LPSTR p;
- WORD i, j, c, limit;
- char buf[120];
-
- if (! verr(FP_SEG(fp)))
- {
- printf("%04x not valid for reading\n", FP_SEG(fp));
- return;
- }
- // if (lar(FP_SEG(fp)) & 8)
- // {
- // printf("%04x is a code segment\n", FP_SEG(fp));
- // return;
- // }
- if ((limit = lsl(FP_SEG(fp))) < FP_OFF(fp))
- {
- printf("Start offs %04X is outside selector %04X limit of %04X\n",
- FP_OFF(fp), FP_SEG(fp), limit);
- return;
- }
- if ((limit + 1) < (bytes + FP_OFF(fp)))
- {
- printf("Selector %04X limit is %04X, length of dump adjusted\n",
- FP_SEG(fp), limit);
- bytes = limit - FP_OFF(fp) + 1;
- }
- FP_SEG(fp) &= ~3;
- FP_SEG(fp) |= 1;
-
- if (limit > 63)
- {
- sprintf(buf, "Dump: %Fp for %d bytes", fp, bytes);
- if (! (hwndNew = winio_window(buf, bytes * 5, WW_HASMENU)))
- winio_warn(FALSE, "Dump", "Insufficient resources!");
- hwndSav = winio_setcurrent(hwndNew);
- }
- else
- hwndNew = __hMainWnd;
-
- winio_setpaint(hwndNew, FALSE);
-
- for (i=0; i<bytes; i += WIDTH)
- {
- c = ((bytes-i) > WIDTH) ? WIDTH : bytes-i;
- printf(mask, addr+i);
- putchar(' '); putchar('|'); putchar(' ');
- for (j=c, p=fp+i; j--; p++)
- printf("%02X ", (unsigned char) *p);
- for (j=WIDTH-c; j--; ) // pad out on last line
- printf(" ");
- putchar('|'); putchar(' ');
- for (j=c, p=fp+i; j--; p++)
- putchar( isprint(*p) ? *p : '.' );
- putchar('\n');
- }
-
- winio_setpaint(hwndNew, TRUE);
- if (hwndSav)
- {
- winio_home(hwndNew);
- winio_setcurrent(hwndSav);
- }
- }
-
- BOOL builtin(int argc, char *argv[])
- {
- if (argv[1][0] == '?')
- info();
- else if (stricmp(argv[1], "info") == 0)
- info();
- else if (stricmp(argv[1], "cls") == 0)
- winio_clear(__hMainWnd);
- else if (stricmp(argv[1], "dump") == 0)
- {
- static unsigned char far *base = 0;
- static unsigned bytes = 64;
- if (argc < 3)
- {
- if (base)
- base += bytes;
- else
- return 1;
- }
- else
- base = (unsigned char far *) axtol(argv[2]);
- if (argc >= 4)
- bytes = (unsigned) axtol(argv[3]);
- dump(base, bytes, "%Fp", (DWORD) base);
- }
- else if (stricmp(argv[1], "sel") == 0)
- {
- WORD sel = (WORD) axtol(argv[2]);
- BOOL r = verr(sel);
- BOOL w = verw(sel);
- if (r || w)
- printf("sel=0x%04X size=%d %s %s%s%s\n",
- sel, lsl(sel) + 1, lar(sel) & 8 ? "code" : "data",
- r ? "read" : "", r && w ? "/" : "", w ? "write" : "");
- else
- printf("Invalid selector\n");
- }
- else
- return 0; // no match
-
- return 1;
- }
-
- void _export far FaultHandler(void)
- {
- static unsigned intnum;
- static unsigned fault_ss;
-
- _asm mov ax, word ptr [bp+8]
- _asm mov intnum, ax
- _asm mov ax, word ptr [bp+14h]
- _asm mov fault_ss, ax
-
- if ((in_dynlink) && (intnum == 13) && (fault_ss == callfunc_ss))
- error("GP fault!");
- else
- return;
- }
-
- main()
- {
- FARPROC procinst_faulthandler;
- char buf[256];
- char *argv[ARG_MAX];
- int argc;
-
- winio_settitle(__hMainWnd, "CallFunc");
- winio_about(
- "CALLFUNC"
- "\nWindows DynaLink Function Caller"
- "\n\nCopyright (c) Andrew Schulman & Dave Maxey, 1991-1992"
- "\n\nFrom Chapter 4 of"
- "\n\"Undocumented Windows\" (Addison-Wesley, 1992)"
- "\nby Andrew Schulman, David Maxey and Matt Pietrek"
- );
- InsertMenu(winio_hmenuhelp(__hMainWnd),
- 0, MF_STRING | MF_ENABLED | MF_BYPOSITION,
- ID_HELP, "&Usage");
-
- DrawMenuBar(__hMainWnd);
-
- winio_setmenufunc(__hMainWnd, ID_HELP, (MENU_FUNC) do_help);
-
- _asm mov callfunc_ss, ss
- procinst_faulthandler =
- MakeProcInstance((FARPROC) FaultHandler, __hInst);
- if (! InterruptRegister(0, procinst_faulthandler))
- puts("Can't register GP fault handler!");
-
- if (Catch(catchbuf) != 0)
- puts(err_msg);
-
- for (;;)
- {
- putchar('>'); putchar(' '); // prompt
- gets(buf); // read
- if (buf[0] == '!') // do before split!
- {
- WinExec(&buf[1], SW_SHOWNORMAL);
- continue;
- }
- if (strcmp(buf, "exit") == 0)
- break;
- argc = split(buf, argv, ARG_MAX, " \t");
- if (! builtin(argc, argv))
- dynlink(argc, argv); // eval-print
- }
-
- FreeProcInstance(procinst_faulthandler);
- puts("done");
- return 0;
- }
-
- /*
- type() uses some dumb rules to determine the type of an argument:
- if first character of arg is a digit or '-'
- and if arg contains '.' then it's a floating-point number
- else if last character is an 'L' then it's a long
- else it's a unsigned word
- else if first character is an apostrophe
- it's a single-byte character
- otherwise
- it's a string
- if the first char
- */
- TYPE type(char *arg)
- {
- if (isdigit(arg[0]) || (arg[0] == '-' && isdigit(arg[1])))
- {
- char *p = arg;
- while (*p)
- if (*p++ == '.')
- return typ_float;
- return (*--p == 'L') ? typ_long : typ_word;
- }
- else if (stricmp(arg, "@buf") == 0)
- return typ_buffer;
- else
- return (arg[0] == '\'') ? typ_byte : typ_string;
- }
-
- /*
- retval_type() uses a printf() mask (e.g., %s or %lX) to determine
- type of return value
- */
- TYPE retval_type(char *s)
- {
- while (*s)
- {
- switch (*s)
- {
- case 's' : return typ_string; break;
- case 'c' : return typ_byte; break;
- case 'p' : case 'l' : case 'I' : case 'O' : case 'U' :
- return typ_long; break;
- case 'e' : case 'E' : case 'f' : case 'g' : case 'G' :
- return typ_float; break;
- }
- s++;
- }
-
- /* still here */
- return typ_word;
- }
-
-