|  | /* | 
|  | * 386-specific Win32 dll<->dll snooping functions | 
|  | * | 
|  | * Copyright 1998 Marcus Meissner | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <string.h> | 
|  | #include "winbase.h" | 
|  | #include "winnt.h" | 
|  | #include "heap.h" | 
|  | #include "builtin32.h" | 
|  | #include "snoop.h" | 
|  | #include "neexe.h" | 
|  | #include "peexe.h" | 
|  | #include "selectors.h" | 
|  | #include "stackframe.h" | 
|  | #include "debugstr.h" | 
|  | #include "debugtools.h" | 
|  |  | 
|  | DEFAULT_DEBUG_CHANNEL(snoop) | 
|  |  | 
|  | char **debug_snoop_excludelist = NULL, **debug_snoop_includelist = NULL; | 
|  |  | 
|  | #ifdef __i386__ | 
|  |  | 
|  | extern void WINAPI SNOOP_Entry(); | 
|  | extern void WINAPI SNOOP_Return(); | 
|  |  | 
|  | #ifdef NEED_UNDERSCORE_PREFIX | 
|  | # define PREFIX "_" | 
|  | #else | 
|  | # define PREFIX | 
|  | #endif | 
|  |  | 
|  | #include "pshpack1.h" | 
|  |  | 
|  | typedef	struct tagSNOOP_FUN { | 
|  | /* code part */ | 
|  | BYTE		lcall;		/* 0xe8 call snoopentry (relative) */ | 
|  | /* NOTE: If you move snoopentry OR nrofargs fix the relative offset | 
|  | * calculation! | 
|  | */ | 
|  | DWORD		snoopentry;	/* SNOOP_Entry relative */ | 
|  | /* unreached */ | 
|  | int		nrofargs; | 
|  | FARPROC	origfun; | 
|  | char		*name; | 
|  | } SNOOP_FUN; | 
|  |  | 
|  | typedef struct tagSNOOP_DLL { | 
|  | HMODULE	hmod; | 
|  | SNOOP_FUN	*funs; | 
|  | LPCSTR		name; | 
|  | int		nrofordinals; | 
|  | struct tagSNOOP_DLL	*next; | 
|  | } SNOOP_DLL; | 
|  | typedef struct tagSNOOP_RETURNENTRY { | 
|  | /* code part */ | 
|  | BYTE		lcall;		/* 0xe8 call snoopret relative*/ | 
|  | /* NOTE: If you move snoopret OR origreturn fix the relative offset | 
|  | * calculation! | 
|  | */ | 
|  | DWORD		snoopret;	/* SNOOP_Ret relative */ | 
|  | /* unreached */ | 
|  | FARPROC	origreturn; | 
|  | SNOOP_DLL	*dll; | 
|  | DWORD		ordinal; | 
|  | DWORD		origESP; | 
|  | DWORD		*args;		/* saved args across a stdcall */ | 
|  | } SNOOP_RETURNENTRY; | 
|  |  | 
|  | typedef struct tagSNOOP_RETURNENTRIES { | 
|  | SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)]; | 
|  | struct tagSNOOP_RETURNENTRIES	*next; | 
|  | } SNOOP_RETURNENTRIES; | 
|  |  | 
|  | #include "poppack.h" | 
|  |  | 
|  | static	SNOOP_DLL		*firstdll = NULL; | 
|  | static	SNOOP_RETURNENTRIES 	*firstrets = NULL; | 
|  |  | 
|  | /*********************************************************************** | 
|  | *          SNOOP_ShowDebugmsgSnoop | 
|  | * | 
|  | * Simple function to decide if a particular debugging message is | 
|  | * wanted. | 
|  | */ | 
|  | int SNOOP_ShowDebugmsgSnoop(const char *dll, int ord, const char *fname) { | 
|  |  | 
|  | if(debug_snoop_excludelist || debug_snoop_includelist) { | 
|  | char **listitem; | 
|  | char buf[80]; | 
|  | int len, len2, itemlen, show; | 
|  |  | 
|  | if(debug_snoop_excludelist) { | 
|  | show = 1; | 
|  | listitem = debug_snoop_excludelist; | 
|  | } else { | 
|  | show = 0; | 
|  | listitem = debug_snoop_includelist; | 
|  | } | 
|  | len = strlen(dll); | 
|  | assert(len < 64); | 
|  | sprintf(buf, "%s.%d", dll, ord); | 
|  | len2 = strlen(buf); | 
|  | for(; *listitem; listitem++) { | 
|  | itemlen = strlen(*listitem); | 
|  | if((itemlen == len && !strncmp(*listitem, buf, len)) || | 
|  | (itemlen == len2 && !strncmp(*listitem, buf, len2)) || | 
|  | !strcmp(*listitem, fname)) { | 
|  | show = !show; | 
|  | break; | 
|  | } | 
|  | } | 
|  | return show; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | void | 
|  | SNOOP_RegisterDLL(HMODULE hmod,LPCSTR name,DWORD nrofordinals) { | 
|  | SNOOP_DLL	**dll = &(firstdll); | 
|  | char		*s; | 
|  |  | 
|  | if (!TRACE_ON(snoop)) return; | 
|  | while (*dll) { | 
|  | if ((*dll)->hmod == hmod) | 
|  | return; /* already registered */ | 
|  | dll = &((*dll)->next); | 
|  | } | 
|  | *dll = (SNOOP_DLL*)HeapAlloc(SystemHeap,HEAP_ZERO_MEMORY,sizeof(SNOOP_DLL)); | 
|  | (*dll)->next	= NULL; | 
|  | (*dll)->hmod	= hmod; | 
|  | (*dll)->nrofordinals = nrofordinals; | 
|  | (*dll)->name	= HEAP_strdupA(SystemHeap,0,name); | 
|  | if ((s=strrchr((*dll)->name,'.'))) | 
|  | *s='\0'; | 
|  | (*dll)->funs = VirtualAlloc(NULL,nrofordinals*sizeof(SNOOP_FUN),MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); | 
|  | memset((*dll)->funs,0,nrofordinals*sizeof(SNOOP_FUN)); | 
|  | if (!(*dll)->funs) { | 
|  | HeapFree(SystemHeap,0,*dll); | 
|  | FIXME("out of memory\n"); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | FARPROC | 
|  | SNOOP_GetProcAddress(HMODULE hmod,LPCSTR name,DWORD ordinal,FARPROC origfun) { | 
|  | SNOOP_DLL			*dll = firstdll; | 
|  | SNOOP_FUN			*fun; | 
|  | int				j; | 
|  | IMAGE_SECTION_HEADER		*pe_seg = PE_SECTIONS(hmod); | 
|  |  | 
|  | if (!TRACE_ON(snoop)) return origfun; | 
|  | if (!*(LPBYTE)origfun) /* 0x00 is an imposs. opcode, poss. dataref. */ | 
|  | return origfun; | 
|  | for (j=0;j<PE_HEADER(hmod)->FileHeader.NumberOfSections;j++) | 
|  | /* 0x42 is special ELF loader tag */ | 
|  | if ((pe_seg[j].VirtualAddress==0x42) || | 
|  | (((DWORD)origfun-hmod>=pe_seg[j].VirtualAddress)&& | 
|  | ((DWORD)origfun-hmod <pe_seg[j].VirtualAddress+ | 
|  | pe_seg[j].SizeOfRawData | 
|  | )) | 
|  | ) | 
|  | break; | 
|  | /* If we looked through all sections (and didn't find one) | 
|  | * or if the sectionname contains "data", we return the | 
|  | * original function since it is most likely a datareference. | 
|  | */ | 
|  | if (	(j==PE_HEADER(hmod)->FileHeader.NumberOfSections)	|| | 
|  | (strstr(pe_seg[j].Name,"data"))				|| | 
|  | !(pe_seg[j].Characteristics & IMAGE_SCN_CNT_CODE) | 
|  | ) | 
|  | return origfun; | 
|  |  | 
|  | while (dll) { | 
|  | if (hmod == dll->hmod) | 
|  | break; | 
|  | dll=dll->next; | 
|  | } | 
|  | if (!dll)	/* probably internal */ | 
|  | return origfun; | 
|  | if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,name)) | 
|  | return origfun; | 
|  | assert(ordinal<dll->nrofordinals); | 
|  | fun = dll->funs+ordinal; | 
|  | if (!fun->name) fun->name = HEAP_strdupA(SystemHeap,0,name); | 
|  | fun->lcall	= 0xe8; | 
|  | /* NOTE: origreturn struct member MUST come directly after snoopentry */ | 
|  | fun->snoopentry	= (char*)SNOOP_Entry-((char*)(&fun->nrofargs)); | 
|  | fun->origfun	= origfun; | 
|  | fun->nrofargs	= -1; | 
|  | return (FARPROC)&(fun->lcall); | 
|  | } | 
|  |  | 
|  | static char* | 
|  | SNOOP_PrintArg(DWORD x) { | 
|  | static char	buf[200]; | 
|  | int		i,nostring; | 
|  | MEMORY_BASIC_INFORMATION	mbi; | 
|  |  | 
|  | if (	!HIWORD(x)					|| | 
|  | !VirtualQuery((LPVOID)x,&mbi,sizeof(mbi))	|| | 
|  | !mbi.Type | 
|  | ) { | 
|  | sprintf(buf,"%08lx",x); | 
|  | return buf; | 
|  | } | 
|  | i=0;nostring=0; | 
|  | if (!IsBadStringPtrA((LPSTR)x,80)) { | 
|  | while (i<80) { | 
|  | LPBYTE	s=(LPBYTE)x; | 
|  |  | 
|  | if (s[i]==0) break; | 
|  | if (s[i]<0x20) {nostring=1;break;} | 
|  | if (s[i]>=0x80) {nostring=1;break;} | 
|  | i++; | 
|  | } | 
|  | if (!nostring) { | 
|  | if (i>5) { | 
|  | sprintf(buf,"%08lx \"",x); | 
|  | strncat(buf,(LPSTR)x,198-strlen(buf)-2); | 
|  | strcat(buf,"\""); | 
|  | return buf; | 
|  | } | 
|  | } | 
|  | } | 
|  | i=0;nostring=0; | 
|  | if (!IsBadStringPtrW((LPWSTR)x,80)) { | 
|  | while (i<80) { | 
|  | LPWSTR	s=(LPWSTR)x; | 
|  |  | 
|  | if (s[i]==0) break; | 
|  | if (s[i]<0x20) {nostring=1;break;} | 
|  | if (s[i]>0x100) {nostring=1;break;} | 
|  | i++; | 
|  | } | 
|  | if (!nostring) { | 
|  | if (i>5) { | 
|  | sprintf(buf,"%08lx L",x); | 
|  | strcat(buf,debugstr_wn((LPWSTR)x,198-strlen(buf)-2)); | 
|  | return buf; | 
|  | } | 
|  | } | 
|  | } | 
|  | sprintf(buf,"%08lx",x); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | #define CALLER1REF (*(DWORD*)ESP_reg(context)) | 
|  |  | 
|  | void WINAPI SNOOP_DoEntry( CONTEXT86 *context ); | 
|  | DEFINE_REGS_ENTRYPOINT_0( SNOOP_Entry, SNOOP_DoEntry ); | 
|  | void WINAPI SNOOP_DoEntry( CONTEXT86 *context ) | 
|  | { | 
|  | DWORD		ordinal=0,entry = EIP_reg(context)-5; | 
|  | SNOOP_DLL	*dll = firstdll; | 
|  | SNOOP_FUN	*fun = NULL; | 
|  | SNOOP_RETURNENTRIES	**rets = &firstrets; | 
|  | SNOOP_RETURNENTRY	*ret; | 
|  | int		i,max; | 
|  |  | 
|  | while (dll) { | 
|  | if (	((char*)entry>=(char*)dll->funs)	&& | 
|  | ((char*)entry<=(char*)(dll->funs+dll->nrofordinals)) | 
|  | ) { | 
|  | fun = (SNOOP_FUN*)entry; | 
|  | ordinal = fun-dll->funs; | 
|  | break; | 
|  | } | 
|  | dll=dll->next; | 
|  | } | 
|  | if (!dll) { | 
|  | FIXME("entrypoint 0x%08lx not found\n",entry); | 
|  | return; /* oops */ | 
|  | } | 
|  | /* guess cdecl ... */ | 
|  | if (fun->nrofargs<0) { | 
|  | /* Typical cdecl return frame is: | 
|  | * 	add esp, xxxxxxxx | 
|  | * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx". | 
|  | * (after that 81 C2 xx xx xx xx) | 
|  | */ | 
|  | LPBYTE	reteip = (LPBYTE)CALLER1REF; | 
|  |  | 
|  | if (reteip) { | 
|  | if ((reteip[0]==0x83)&&(reteip[1]==0xc4)) | 
|  | fun->nrofargs=reteip[2]/4; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | 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) { | 
|  | *rets = VirtualAlloc(NULL,4096,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); | 
|  | memset(*rets,0,4096); | 
|  | i = 0;	/* entry 0 is free */ | 
|  | } | 
|  | ret = &((*rets)->entry[i]); | 
|  | ret->lcall	= 0xe8; | 
|  | /* NOTE: origreturn struct member MUST come directly after snoopret */ | 
|  | ret->snoopret	= ((char*)SNOOP_Return)-(char*)(&ret->origreturn); | 
|  | ret->origreturn	= (FARPROC)CALLER1REF; | 
|  | CALLER1REF	= (DWORD)&ret->lcall; | 
|  | ret->dll	= dll; | 
|  | ret->args	= NULL; | 
|  | ret->ordinal	= ordinal; | 
|  | ret->origESP	= ESP_reg(context); | 
|  |  | 
|  | EIP_reg(context)= (DWORD)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=0;i<max;i++) | 
|  | DPRINTF("%s%s",SNOOP_PrintArg(*(DWORD*)(ESP_reg(context)+4+sizeof(DWORD)*i)),(i<fun->nrofargs-1)?",":""); | 
|  | if (max!=fun->nrofargs) | 
|  | DPRINTF(" ..."); | 
|  | } else if (fun->nrofargs<0) { | 
|  | DPRINTF("<unknown, check return>"); | 
|  | ret->args = HeapAlloc(SystemHeap,0,16*sizeof(DWORD)); | 
|  | memcpy(ret->args,(LPBYTE)(ESP_reg(context)+4),sizeof(DWORD)*16); | 
|  | } | 
|  | DPRINTF(") ret=%08lx fs=%04lx\n",(DWORD)ret->origreturn,FS_reg(context)); | 
|  | } | 
|  |  | 
|  | void WINAPI SNOOP_DoReturn( CONTEXT86 *context ); | 
|  | DEFINE_REGS_ENTRYPOINT_0( SNOOP_Return, SNOOP_DoReturn ); | 
|  | void WINAPI SNOOP_DoReturn( CONTEXT86 *context ) | 
|  | { | 
|  | SNOOP_RETURNENTRY	*ret = (SNOOP_RETURNENTRY*)(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 ESP | 
|  | * If stdcall -> everything ok. | 
|  | */ | 
|  | if (ret->dll->funs[ret->ordinal].nrofargs<0) | 
|  | ret->dll->funs[ret->ordinal].nrofargs=(ESP_reg(context)-ret->origESP-4)/4; | 
|  | EIP_reg(context) = (DWORD)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; | 
|  |  | 
|  | for (i=0;i<max;i++) | 
|  | DPRINTF("%s%s",SNOOP_PrintArg(ret->args[i]),(i<max-1)?",":""); | 
|  | DPRINTF(") retval = %08lx ret=%08lx fs=%04lx\n", | 
|  | EAX_reg(context),(DWORD)ret->origreturn,FS_reg(context) | 
|  | ); | 
|  | HeapFree(SystemHeap,0,ret->args); | 
|  | ret->args = NULL; | 
|  | } else | 
|  | DPRINTF("Ret  %s.%ld: %s() retval = %08lx ret=%08lx fs=%04lx\n", | 
|  | ret->dll->name,ret->ordinal,ret->dll->funs[ret->ordinal].name, | 
|  | EAX_reg(context),(DWORD)ret->origreturn,FS_reg(context) | 
|  | ); | 
|  | ret->origreturn = NULL; /* mark as empty */ | 
|  | } | 
|  | #else	/* !__i386__ */ | 
|  | void SNOOP_RegisterDLL(HMODULE hmod,LPCSTR name,DWORD nrofordinals) { | 
|  | FIXME("snooping works only on i386 for now.\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | FARPROC SNOOP_GetProcAddress(HMODULE hmod,LPCSTR name,DWORD ordinal,FARPROC origfun) { | 
|  | return origfun; | 
|  | } | 
|  | #endif	/* !__i386__ */ |