| /* | 
 |  * 386-specific Win16 dll<->dll snooping functions | 
 |  * | 
 |  * Copyright 1998 Marcus Meissner | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
 |  */ | 
 |  | 
 | #include "config.h" | 
 | #include "wine/port.h" | 
 |  | 
 | #include <assert.h> | 
 | #include <string.h> | 
 | #include <stdarg.h> | 
 | #include <stdio.h> | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winnt.h" | 
 | #include "wine/winbase16.h" | 
 | #include "winternl.h" | 
 | #include "wine/library.h" | 
 | #include "kernel_private.h" | 
 | #include "kernel16_private.h" | 
 | #include "toolhelp.h" | 
 | #include "wine/debug.h" | 
 |  | 
 | WINE_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; | 
 | 	struct tagSNOOP16_DLL	*next; | 
 | 	char name[1]; | 
 | } 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(HMODULE16 hModule,LPCSTR name) { | 
 | 	SNOOP16_DLL	**dll = &(firstdll); | 
 | 	char		*s; | 
 |  | 
 | 	if (!TRACE_ON(snoop)) return; | 
 |  | 
 |         TRACE("hmod=%x, name=%s\n", hModule, name); | 
 |  | 
 | 	if (!snr) { | 
 | 		xsnr=GLOBAL_Alloc(GMEM_ZEROINIT,2*sizeof(*snr),0,WINE_LDT_FLAGS_CODE|WINE_LDT_FLAGS_32BIT); | 
 | 		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)__wine_call_from_16_regs; | 
 | 		snr[0].seg      = wine_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)__wine_call_from_16_regs; | 
 | 		snr[1].seg      = wine_get_cs(); | 
 | 		snr[1].lret     = 0xcb66; | 
 | 	} | 
 | 	while (*dll) { | 
 | 		if ((*dll)->hmod == hModule) | 
 |                 { | 
 |                     /* another dll, loaded at the same address */ | 
 |                     GlobalUnlock16((*dll)->funhandle); | 
 |                     GlobalFree16((*dll)->funhandle); | 
 |                     break; | 
 |                 } | 
 | 		dll = &((*dll)->next); | 
 | 	} | 
 |  | 
 | 	if (*dll) | 
 | 		*dll = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *dll, sizeof(SNOOP16_DLL)+strlen(name)); | 
 | 	else | 
 | 		*dll = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SNOOP16_DLL)+strlen(name));	 | 
 |  | 
 | 	(*dll)->next	= NULL; | 
 | 	(*dll)->hmod	= hModule; | 
 | 	if ((s=strrchr(name,'\\'))) | 
 | 		name = s+1; | 
 | 	strcpy( (*dll)->name, name ); | 
 | 	if ((s=strrchr((*dll)->name,'.'))) | 
 | 		*s='\0'; | 
 | 	(*dll)->funhandle = GlobalHandleToSel16(GLOBAL_Alloc(GMEM_ZEROINIT,65535,0,WINE_LDT_FLAGS_CODE)); | 
 | 	(*dll)->funs = GlobalLock16((*dll)->funhandle); | 
 | 	if (!(*dll)->funs) { | 
 | 		HeapFree(GetProcessHeap(),0,*dll); | 
 | 		FIXME("out of memory\n"); | 
 | 		return; | 
 | 	} | 
 | } | 
 |  | 
 | 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)MapSL((SEGPTR)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->ne_restab; | 
 | 	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 = (unsigned 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 = HeapAlloc(GetProcessHeap(),0,strlen(name)+1); | 
 |             strcpy( fun->name, name ); | 
 |         } | 
 | 	else | 
 |             fun->name = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,1); /* empty string */ | 
 |  | 
 | 	if (!SNOOP16_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*)(MapSL( MAKESEGPTR(context->SegSs,LOWORD(context->Esp)+4)))) | 
 | void WINAPI SNOOP16_Entry(FARPROC proc, LPBYTE args, CONTEXT86 *context) { | 
 | 	DWORD		ordinal=0; | 
 | 	DWORD		entry=(DWORD)MapSL( MAKESEGPTR(context->SegCs,LOWORD(context->Eip)) )-5; | 
 | 	WORD		xcs = context->SegCs; | 
 | 	SNOOP16_DLL	*dll = firstdll; | 
 | 	SNOOP16_FUN	*fun = NULL; | 
 | 	SNOOP16_RETURNENTRIES	**rets = &firstrets; | 
 | 	SNOOP16_RETURNENTRY	*ret; | 
 | 	int		i=0, max; | 
 |  | 
 | 	while (dll) { | 
 | 		if (xcs == dll->funhandle) { | 
 | 			fun = (SNOOP16_FUN*)entry; | 
 | 			ordinal = fun-dll->funs; | 
 | 			break; | 
 | 		} | 
 | 		dll=dll->next; | 
 | 	} | 
 | 	if (!dll) { | 
 | 		FIXME("entrypoint 0x%08x 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,WINE_LDT_FLAGS_CODE)); | 
 | 		*rets = GlobalLock16(hand); | 
 | 		(*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(context->Esp); | 
 |  | 
 | 	context->Eip= LOWORD(fun->origfun); | 
 | 	context->SegCs = HIWORD(fun->origfun); | 
 |  | 
 |  | 
 | 	DPRINTF("%04x:CALL %s.%d: %s(",GetCurrentThreadId(), 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 *) MapSL( MAKESEGPTR(context->SegSs,LOWORD(context->Esp)) )+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 *) MapSL( MAKESEGPTR(context->SegSs,LOWORD(context->Esp)) )+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 *) MapSL( MAKESEGPTR(context->SegCs,LOWORD(context->Eip)) )-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(context->Esp)-ret->origSP-4)/2; | 
 | 	} | 
 | 	context->Eip = LOWORD(ret->origreturn); | 
 | 	context->SegCs  = HIWORD(ret->origreturn); | 
 |         DPRINTF("%04x:RET  %s.%d: %s(", | 
 |                 GetCurrentThreadId(),ret->dll->name,ret->ordinal, | 
 |                 ret->dll->funs[ret->ordinal].name); | 
 | 	if (ret->args) { | 
 | 		int	i,max; | 
 |  | 
 | 		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(" ..."); | 
 | 		HeapFree(GetProcessHeap(),0,ret->args); | 
 | 		ret->args = NULL; | 
 | 	} | 
 |         DPRINTF(") retval = %04x:%04x ret=%04x:%04x\n", | 
 |                 (WORD)context->Edx,(WORD)context->Eax, | 
 |                 HIWORD(ret->origreturn),LOWORD(ret->origreturn)); | 
 | 	ret->origreturn = NULL; /* mark as empty */ | 
 | } | 
 | #else	/* !__i386__ */ | 
 | void SNOOP16_RegisterDLL(HMODULE16 hModule,LPCSTR name) { | 
 | 	if (!TRACE_ON(snoop)) return; | 
 | 	FIXME("snooping works only on i386 for now.\n"); | 
 | } | 
 |  | 
 | FARPROC16 SNOOP16_GetProcAddress16(HMODULE16 hmod,DWORD ordinal,FARPROC16 origfun) { | 
 | 	return origfun; | 
 | } | 
 | #endif	/* !__i386__ */ |