| /* | |
| * 386-specific Win16 dll<->dll snooping functions | |
| * | |
| * Copyright 1998 Marcus Meissner | |
| */ | |
| #include <assert.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include "winbase.h" | |
| #include "windef.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "winnt.h" | |
| #include "heap.h" | |
| #include "global.h" | |
| #include "selectors.h" | |
| #include "stackframe.h" | |
| #include "builtin16.h" | |
| #include "snoop.h" | |
| #include "debugtools.h" | |
| DEFAULT_DEBUG_CHANNEL(snoop); | |
| #ifdef __i386__ | |
| #include "pshpack1.h" | |
| void WINAPI SNOOP16_Entry(FARPROC proc, LPBYTE args, CONTEXT86 *context); | |
| void WINAPI SNOOP16_Return(FARPROC proc, LPBYTE args, CONTEXT86 *context); | |
| typedef struct tagSNOOP16_FUN { | |
| /* code part */ | |
| BYTE lcall; /* 0x9a call absolute with segment */ | |
| DWORD snr; | |
| /* unreached */ | |
| int nrofargs; | |
| FARPROC16 origfun; | |
| char *name; | |
| } SNOOP16_FUN; | |
| typedef struct tagSNOOP16_DLL { | |
| HMODULE16 hmod; | |
| HANDLE16 funhandle; | |
| SNOOP16_FUN *funs; | |
| LPCSTR name; | |
| struct tagSNOOP16_DLL *next; | |
| } SNOOP16_DLL; | |
| typedef struct tagSNOOP16_RETURNENTRY { | |
| /* code part */ | |
| BYTE lcall; /* 0x9a call absolute with segment */ | |
| DWORD snr; | |
| /* unreached */ | |
| FARPROC16 origreturn; | |
| SNOOP16_DLL *dll; | |
| DWORD ordinal; | |
| WORD origSP; | |
| WORD *args; /* saved args across a stdcall */ | |
| } SNOOP16_RETURNENTRY; | |
| typedef struct tagSNOOP16_RETURNENTRIES { | |
| SNOOP16_RETURNENTRY entry[65500/sizeof(SNOOP16_RETURNENTRY)]; | |
| HANDLE16 rethandle; | |
| struct tagSNOOP16_RETURNENTRIES *next; | |
| } SNOOP16_RETURNENTRIES; | |
| typedef struct tagSNOOP16_RELAY { | |
| WORD pushbp; /* 0x5566 */ | |
| BYTE pusheax; /* 0x50 */ | |
| WORD pushax; /* 0x5066 */ | |
| BYTE pushl; /* 0x68 */ | |
| DWORD realfun; /* SNOOP16_Return */ | |
| BYTE lcall; /* 0x9a call absolute with segment */ | |
| DWORD callfromregs; | |
| WORD seg; | |
| WORD lret; /* 0xcb66 */ | |
| } SNOOP16_RELAY; | |
| #include "poppack.h" | |
| static SNOOP16_DLL *firstdll = NULL; | |
| static SNOOP16_RETURNENTRIES *firstrets = NULL; | |
| static SNOOP16_RELAY *snr; | |
| static HANDLE16 xsnr = 0; | |
| void | |
| SNOOP16_RegisterDLL(NE_MODULE *pModule,LPCSTR name) { | |
| SNOOP16_DLL **dll = &(firstdll); | |
| char *s; | |
| if (!TRACE_ON(snoop)) return; | |
| if (!snr) { | |
| xsnr=GLOBAL_Alloc(GMEM_ZEROINIT,2*sizeof(*snr),0,TRUE,TRUE,FALSE); | |
| snr = GlobalLock16(xsnr); | |
| snr[0].pushbp = 0x5566; | |
| snr[0].pusheax = 0x50; | |
| snr[0].pushax = 0x5066; | |
| snr[0].pushl = 0x68; | |
| snr[0].realfun = (DWORD)SNOOP16_Entry; | |
| snr[0].lcall = 0x9a; | |
| snr[0].callfromregs = (DWORD)CallFrom16Register; | |
| snr[0].seg = __get_cs(); | |
| snr[0].lret = 0xcb66; | |
| snr[1].pushbp = 0x5566; | |
| snr[1].pusheax = 0x50; | |
| snr[1].pushax = 0x5066; | |
| snr[1].pushl = 0x68; | |
| snr[1].realfun = (DWORD)SNOOP16_Return; | |
| snr[1].lcall = 0x9a; | |
| snr[1].callfromregs = (DWORD)CallFrom16Register; | |
| snr[1].seg = __get_cs(); | |
| snr[1].lret = 0xcb66; | |
| } | |
| while (*dll) { | |
| if ((*dll)->hmod == pModule->self) | |
| return; /* already registered */ | |
| dll = &((*dll)->next); | |
| } | |
| *dll = (SNOOP16_DLL*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SNOOP16_DLL)); | |
| (*dll)->next = NULL; | |
| (*dll)->hmod = pModule->self; | |
| if ((s=strrchr(name,'\\'))) | |
| name = s+1; | |
| (*dll)->name = HEAP_strdupA(GetProcessHeap(),0,name); | |
| if ((s=strrchr((*dll)->name,'.'))) | |
| *s='\0'; | |
| (*dll)->funhandle = GlobalHandleToSel16(GLOBAL_Alloc(GMEM_ZEROINIT,65535,0,TRUE,FALSE,FALSE)); | |
| (*dll)->funs = GlobalLock16((*dll)->funhandle); | |
| if (!(*dll)->funs) { | |
| HeapFree(GetProcessHeap(),0,*dll); | |
| FIXME("out of memory\n"); | |
| return; | |
| } | |
| memset((*dll)->funs,0,65535); | |
| } | |
| FARPROC16 | |
| SNOOP16_GetProcAddress16(HMODULE16 hmod,DWORD ordinal,FARPROC16 origfun) { | |
| SNOOP16_DLL *dll = firstdll; | |
| SNOOP16_FUN *fun; | |
| NE_MODULE *pModule = NE_GetPtr(hmod); | |
| unsigned char *cpnt; | |
| char name[200]; | |
| if (!TRACE_ON(snoop) || !pModule || !HIWORD(origfun)) | |
| return origfun; | |
| if (!*(LPBYTE)PTR_SEG_TO_LIN(origfun)) /* 0x00 is an imposs. opcode, poss. dataref. */ | |
| return origfun; | |
| while (dll) { | |
| if (hmod == dll->hmod) | |
| break; | |
| dll=dll->next; | |
| } | |
| if (!dll) /* probably internal */ | |
| return origfun; | |
| if (ordinal>65535/sizeof(SNOOP16_FUN)) | |
| return origfun; | |
| fun = dll->funs+ordinal; | |
| /* already done? */ | |
| fun->lcall = 0x9a; | |
| fun->snr = MAKELONG(0,xsnr); | |
| fun->origfun = origfun; | |
| if (fun->name) | |
| return (FARPROC16)(SEGPTR)MAKELONG(((char*)fun-(char*)dll->funs),dll->funhandle); | |
| cpnt = (unsigned char *)pModule + pModule->name_table; | |
| while (*cpnt) { | |
| cpnt += *cpnt + 1 + sizeof(WORD); | |
| if (*(WORD*)(cpnt+*cpnt+1) == ordinal) { | |
| sprintf(name,"%.*s",*cpnt,cpnt+1); | |
| break; | |
| } | |
| } | |
| /* Now search the non-resident names table */ | |
| if (!*cpnt && pModule->nrname_handle) { | |
| cpnt = (char *)GlobalLock16( pModule->nrname_handle ); | |
| while (*cpnt) { | |
| cpnt += *cpnt + 1 + sizeof(WORD); | |
| if (*(WORD*)(cpnt+*cpnt+1) == ordinal) { | |
| sprintf(name,"%.*s",*cpnt,cpnt+1); | |
| break; | |
| } | |
| } | |
| } | |
| if (*cpnt) | |
| fun->name = HEAP_strdupA(GetProcessHeap(),0,name); | |
| else | |
| fun->name = HEAP_strdupA(GetProcessHeap(),0,""); | |
| if (!SNOOP_ShowDebugmsgSnoop(dll->name, ordinal, fun->name)) | |
| return origfun; | |
| /* more magic. do not try to snoop thunk data entries (MMSYSTEM) */ | |
| if (strchr(fun->name,'_')) { | |
| char *s=strchr(fun->name,'_'); | |
| if (!strncasecmp(s,"_thunkdata",10)) { | |
| HeapFree(GetProcessHeap(),0,fun->name); | |
| fun->name = NULL; | |
| return origfun; | |
| } | |
| } | |
| fun->lcall = 0x9a; | |
| fun->snr = MAKELONG(0,xsnr); | |
| fun->origfun = origfun; | |
| fun->nrofargs = -1; | |
| return (FARPROC16)(SEGPTR)MAKELONG(((char*)fun-(char*)dll->funs),dll->funhandle); | |
| } | |
| #define CALLER1REF (*(DWORD*)(PTR_SEG_OFF_TO_LIN(SS_reg(context),LOWORD(ESP_reg(context))+4))) | |
| void WINAPI SNOOP16_Entry(FARPROC proc, LPBYTE args, CONTEXT86 *context) { | |
| DWORD ordinal=0; | |
| DWORD entry=(DWORD)PTR_SEG_OFF_TO_LIN(CS_reg(context),LOWORD(EIP_reg(context)))-5; | |
| WORD xcs = CS_reg(context); | |
| SNOOP16_DLL *dll = firstdll; | |
| SNOOP16_FUN *fun = NULL; | |
| SNOOP16_RETURNENTRIES **rets = &firstrets; | |
| SNOOP16_RETURNENTRY *ret; | |
| int i,max; | |
| while (dll) { | |
| if (xcs == dll->funhandle) { | |
| fun = (SNOOP16_FUN*)entry; | |
| ordinal = fun-dll->funs; | |
| break; | |
| } | |
| dll=dll->next; | |
| } | |
| if (!dll) { | |
| FIXME("entrypoint 0x%08lx not found\n",entry); | |
| return; /* oops */ | |
| } | |
| while (*rets) { | |
| for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++) | |
| if (!(*rets)->entry[i].origreturn) | |
| break; | |
| if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0])) | |
| break; | |
| rets = &((*rets)->next); | |
| } | |
| if (!*rets) { | |
| HANDLE16 hand = GlobalHandleToSel16(GLOBAL_Alloc(GMEM_ZEROINIT,65535,0,TRUE,FALSE,FALSE)); | |
| *rets = GlobalLock16(hand); | |
| memset(*rets,0,65535); | |
| (*rets)->rethandle = hand; | |
| i = 0; /* entry 0 is free */ | |
| } | |
| ret = &((*rets)->entry[i]); | |
| ret->lcall = 0x9a; | |
| ret->snr = MAKELONG(sizeof(SNOOP16_RELAY),xsnr); | |
| ret->origreturn = (FARPROC16)CALLER1REF; | |
| CALLER1REF = MAKELONG((char*)&(ret->lcall)-(char*)((*rets)->entry),(*rets)->rethandle); | |
| ret->dll = dll; | |
| ret->args = NULL; | |
| ret->ordinal = ordinal; | |
| ret->origSP = LOWORD(ESP_reg(context)); | |
| EIP_reg(context)= LOWORD(fun->origfun); | |
| CS_reg(context) = HIWORD(fun->origfun); | |
| DPRINTF("Call %s.%ld: %s(",dll->name,ordinal,fun->name); | |
| if (fun->nrofargs>0) { | |
| max = fun->nrofargs; | |
| if (max>16) max=16; | |
| for (i=max;i--;) | |
| DPRINTF("%04x%s",*(WORD*)((char *) PTR_SEG_OFF_TO_LIN(SS_reg(context),LOWORD(ESP_reg(context)))+8+sizeof(WORD)*i),i?",":""); | |
| if (max!=fun->nrofargs) | |
| DPRINTF(" ..."); | |
| } else if (fun->nrofargs<0) { | |
| DPRINTF("<unknown, check return>"); | |
| ret->args = HeapAlloc(GetProcessHeap(),0,16*sizeof(WORD)); | |
| memcpy(ret->args,(LPBYTE)((char *) PTR_SEG_OFF_TO_LIN(SS_reg(context),LOWORD(ESP_reg(context)))+8),sizeof(WORD)*16); | |
| } | |
| DPRINTF(") ret=%04x:%04x\n",HIWORD(ret->origreturn),LOWORD(ret->origreturn)); | |
| } | |
| void WINAPI SNOOP16_Return(FARPROC proc, LPBYTE args, CONTEXT86 *context) { | |
| SNOOP16_RETURNENTRY *ret = (SNOOP16_RETURNENTRY*)((char *) PTR_SEG_OFF_TO_LIN(CS_reg(context),LOWORD(EIP_reg(context)))-5); | |
| /* We haven't found out the nrofargs yet. If we called a cdecl | |
| * function it is too late anyway and we can just set '0' (which | |
| * will be the difference between orig and current SP | |
| * If pascal -> everything ok. | |
| */ | |
| if (ret->dll->funs[ret->ordinal].nrofargs<0) { | |
| ret->dll->funs[ret->ordinal].nrofargs=(LOWORD(ESP_reg(context))-ret->origSP-4)/2; | |
| } | |
| EIP_reg(context) = LOWORD(ret->origreturn); | |
| CS_reg(context) = HIWORD(ret->origreturn); | |
| if (ret->args) { | |
| int i,max; | |
| DPRINTF("Ret %s.%ld: %s(",ret->dll->name,ret->ordinal,ret->dll->funs[ret->ordinal].name); | |
| max = ret->dll->funs[ret->ordinal].nrofargs; | |
| if (max>16) | |
| max=16; | |
| if (max<0) | |
| max=0; | |
| for (i=max;i--;) | |
| DPRINTF("%04x%s",ret->args[i],i?",":""); | |
| if (max!=ret->dll->funs[ret->ordinal].nrofargs) | |
| DPRINTF(" ..."); | |
| DPRINTF(") retval = %04x:%04x ret=%04x:%04x\n", | |
| DX_reg(context),AX_reg(context),HIWORD(ret->origreturn),LOWORD(ret->origreturn) | |
| ); | |
| HeapFree(GetProcessHeap(),0,ret->args); | |
| ret->args = NULL; | |
| } else | |
| DPRINTF("Ret %s.%ld: %s() retval = %04x:%04x ret=%04x:%04x\n", | |
| ret->dll->name,ret->ordinal,ret->dll->funs[ret->ordinal].name, | |
| DX_reg(context),AX_reg(context),HIWORD(ret->origreturn),LOWORD(ret->origreturn) | |
| ); | |
| ret->origreturn = NULL; /* mark as empty */ | |
| } | |
| #else /* !__i386__ */ | |
| void SNOOP16_RegisterDLL(NE_MODULE *pModule,LPCSTR name) { | |
| FIXME("snooping works only on i386 for now.\n"); | |
| return; | |
| } | |
| FARPROC16 SNOOP16_GetProcAddress16(HMODULE16 hmod,DWORD ordinal,FARPROC16 origfun) { | |
| return origfun; | |
| } | |
| #endif /* !__i386__ */ | |