| /* |
| * 386-specific Win32 dll<->dll snooping functions |
| * |
| * Copyright 1998 Marcus Meissner |
| */ |
| |
| |
| #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 "debug.h" |
| |
| DEFAULT_DEBUG_CHANNEL(snoop) |
| |
| char **debug_snoop_excludelist = NULL, **debug_snoop_includelist = NULL; |
| |
| extern void SNOOP_Entry(); |
| extern void SNOOP_Return(); |
| |
| #ifdef __i386__ |
| |
| #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(snoop,"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 REGS_FUNC(SNOOP_Entry)( CONTEXT *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(snoop,"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 REGS_FUNC(SNOOP_Return)( CONTEXT *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(snoop,"snooping works only on i386 for now.\n"); |
| return; |
| } |
| |
| FARPROC SNOOP_GetProcAddress(HMODULE hmod,LPCSTR name,DWORD ordinal,FARPROC origfun) { |
| return origfun; |
| } |
| |
| REGS_ENTRYPOINT(SNOOP_Entry) { } |
| REGS_ENTRYPOINT(SNOOP_Return) { } |
| |
| #endif /* !__i386__ */ |