From f7e482e4333df76b198b16c7685c2304007fbe79 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Mon, 16 Jul 2012 18:28:49 +0000 Subject: dynamic dll checker git-svn-id: http://svn.miranda-ng.org/main/trunk@989 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- src/modules/plugins/dll_sniffer.cpp | 150 +++++++++++++++++++++++++++++++++++ src/modules/plugins/newplugins.cpp | 151 ++++++++++++++++-------------------- src/modules/plugins/pluginopts.cpp | 2 +- src/modules/plugins/plugins.h | 7 +- 4 files changed, 220 insertions(+), 90 deletions(-) create mode 100644 src/modules/plugins/dll_sniffer.cpp (limited to 'src/modules') diff --git a/src/modules/plugins/dll_sniffer.cpp b/src/modules/plugins/dll_sniffer.cpp new file mode 100644 index 0000000000..469cebf931 --- /dev/null +++ b/src/modules/plugins/dll_sniffer.cpp @@ -0,0 +1,150 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2010 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "..\..\core\commonheaders.h" +#include "plugins.h" + +static IMAGE_SECTION_HEADER *getSectionByRVA(IMAGE_SECTION_HEADER *pISH, int nSections, IMAGE_DATA_DIRECTORY *pIDD) +{ + for (int i=0; i < nSections; i++, pISH++) + if (pIDD->VirtualAddress >= pISH->VirtualAddress && pIDD->VirtualAddress + pIDD->Size <= pISH->VirtualAddress + pISH->SizeOfRawData ) + return pISH; + + return NULL; +} + +MUUID* GetPluginInterfaces(const TCHAR* ptszFileName, bool& bIsPlugin) +{ + bIsPlugin = false; + + HANDLE hFile = CreateFile( ptszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + if (hFile == INVALID_HANDLE_VALUE) + return NULL; + + MUUID* pResult = NULL; + BYTE* ptr = NULL; + HANDLE hMap = CreateFileMapping( hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL ); + + __try + { + if ( !hMap ) + __leave; + + DWORD dwHSize = 0, filesize = GetFileSize( hFile, &dwHSize ); + if ( !filesize || filesize == INVALID_FILE_SIZE || dwHSize) + __leave; + + if ( filesize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) ) + __leave; + + ptr = (BYTE*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + if (ptr == NULL) + __leave; + + PIMAGE_NT_HEADERS pINTH; + PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)ptr; + if ( pIDH->e_magic == IMAGE_DOS_SIGNATURE ) + pINTH = (PIMAGE_NT_HEADERS)(ptr + pIDH->e_lfanew); + else + __leave; + + if ((PBYTE)pINTH + sizeof(IMAGE_NT_HEADERS) >= ptr + filesize ) + __leave; + if ( pINTH->Signature != IMAGE_NT_SIGNATURE ) + __leave; + + int nSections = pINTH->FileHeader.NumberOfSections; + if ( !nSections ) + __leave; + + INT_PTR base; + PIMAGE_DATA_DIRECTORY pIDD; + if ( pINTH->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 && + pINTH->FileHeader.SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER32) && + pINTH->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + pIDD = (PIMAGE_DATA_DIRECTORY)( (PBYTE)pINTH + offsetof( IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory )); + base = *(DWORD*)((PBYTE)pINTH + offsetof(IMAGE_NT_HEADERS32, OptionalHeader.ImageBase)); + } + else if ( pINTH->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 && + pINTH->FileHeader.SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER64) && + pINTH->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + pIDD = (PIMAGE_DATA_DIRECTORY)( (PBYTE)pINTH + offsetof( IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory )); + base = *(ULONGLONG*)((PBYTE)pINTH + offsetof(IMAGE_NT_HEADERS64, OptionalHeader.ImageBase )); + } + else __leave; + + // Export information entry + DWORD expvaddr = pIDD[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + DWORD expsize = pIDD[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; + if (expsize < sizeof(IMAGE_EXPORT_DIRECTORY)) __leave; + + BYTE* pImage = ptr + pIDH->e_lfanew + pINTH->FileHeader.SizeOfOptionalHeader + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER); + IMAGE_SECTION_HEADER *pExp = getSectionByRVA((IMAGE_SECTION_HEADER *)pImage, nSections, pIDD); + if ( !pExp) __leave; + + BYTE *pSecStart = ptr + pExp->PointerToRawData - pExp->VirtualAddress; + IMAGE_EXPORT_DIRECTORY *pED = (PIMAGE_EXPORT_DIRECTORY)&pSecStart[expvaddr]; + DWORD *ptrRVA = (DWORD*)&pSecStart[pED->AddressOfNames]; + WORD *ptrOrdRVA = (WORD*)&pSecStart[pED->AddressOfNameOrdinals]; + DWORD *ptrFuncList = (DWORD*)&pSecStart[pED->AddressOfFunctions]; + + MUUID* pIds; + bool bHasLoad = false, bHasUnload = false, bHasInfo = false, bHasMuuids = false; + for (size_t i=0; i < pED->NumberOfNames; i++, ptrRVA++, ptrOrdRVA++) { + char *szName = (char*)&pSecStart[*ptrRVA]; + if ( !lstrcmpA(szName, "Load")) + bHasLoad = true; + if ( !lstrcmpA(szName, "MirandaPluginInfoEx")) + bHasInfo = true; + else if ( !lstrcmpA(szName, "Unload")) + bHasUnload = true; + else if ( !lstrcmpA(szName, "MirandaInterfaces")) { + bHasMuuids = true; + pIds = (MUUID*)&pSecStart[ ptrFuncList[*ptrOrdRVA]]; + } + } + + // a plugin might have no interfaces + if (bHasLoad && bHasUnload && bHasInfo) + bIsPlugin = true; + + if (!bHasLoad || !bHasMuuids || !bHasUnload) + __leave; + + int nLength = 1; // one for MIID_LAST + for (MUUID* p = pIds; !equalUUID(*p, miid_last); p++) + nLength++; + + pResult = (MUUID*)mir_alloc( sizeof(MUUID)*nLength); + if (pResult) + memcpy(pResult, pIds, sizeof(MUUID)*nLength); + } + __finally + { + UnmapViewOfFile(ptr); + CloseHandle(hFile); + }; + + return pResult; +} diff --git a/src/modules/plugins/newplugins.cpp b/src/modules/plugins/newplugins.cpp index 9712f01d07..3c27b63f22 100644 --- a/src/modules/plugins/newplugins.cpp +++ b/src/modules/plugins/newplugins.cpp @@ -91,14 +91,24 @@ int equalUUID(const MUUID& u1, const MUUID& u2) return memcmp(&u1, &u2, sizeof(MUUID))?0:1; } +bool hasMuuid(const MUUID* p, const MUUID& uuid) +{ + if (p == NULL) + return false; + + for (int i=0; !equalUUID(miid_last, p[i]); i++) + if ( equalUUID(uuid, p[i])) + return true; + + return false; +} + + bool hasMuuid(const BASIC_PLUGIN_INFO& bpi, const MUUID& uuid) { - if (bpi.Interfaces) { - MUUID *piface = bpi.Interfaces(); - for (int i=0; !equalUUID(miid_last, piface[i]); i++) - if ( equalUUID(uuid, piface[i])) - return true; - } + if (bpi.Interfaces) + return hasMuuid(bpi.Interfaces, uuid); + return false; } @@ -162,13 +172,8 @@ MUUID miid_clist = MIID_CLIST; MUUID miid_database = MIID_DATABASE; MUUID miid_servicemode = MIID_SERVICEMODE; -static bool validInterfaceList(Miranda_Plugin_Interfaces ifaceProc) +static bool validInterfaceList(MUUID *piface) { - // we don't need'em anymore in the common case - if (ifaceProc == NULL) - return true; - - MUUID *piface = ifaceProc(); if (piface == NULL) return false; @@ -256,7 +261,7 @@ int checkAPI(TCHAR* plugin, BASIC_PLUGIN_INFO* bpi, DWORD mirandaVersion, int ch bpi->Load = (Miranda_Plugin_Load) GetProcAddress(h, "Load"); bpi->Unload = (Miranda_Plugin_Unload) GetProcAddress(h, "Unload"); bpi->InfoEx = (Miranda_Plugin_InfoEx) GetProcAddress(h, "MirandaPluginInfoEx"); - bpi->Interfaces = (Miranda_Plugin_Interfaces) GetProcAddress(h, "MirandaPluginInterfaces"); + bpi->Interfaces = (MUUID*) GetProcAddress(h, "MirandaInterfaces"); // if they were present if ( !bpi->Load || !bpi->Unload || !bpi->InfoEx) { @@ -367,42 +372,6 @@ static int valid_library_name(TCHAR *name) return 0; } -// returns true if the given file matches dbx_*.dll, which is used to LoadLibrary() -static int validguess_db_name(TCHAR *name) -{ - int rc = 0; - // this is ONLY SAFE because name -> ffd.cFileName == MAX_PATH - TCHAR x = name[4]; - name[4] = 0; - rc = lstrcmpi(name, _T("dbx_")) == 0 || lstrcmpi(name, _T("dbrw")) == 0; - name[4] = x; - return rc; -} - -// returns true if the given file matches clist_*.dll -static int validguess_clist_name(TCHAR *name) -{ - int rc = 0; - // argh evil - TCHAR x = name[6]; - name[6] = 0; - rc = lstrcmpi(name, _T("clist_")) == 0; - name[6] = x; - return rc; -} - -// returns true if the given file matches svc_*.dll -static int validguess_servicemode_name(TCHAR *name) -{ - int rc = 0; - // argh evil - TCHAR x = name[4]; - name[4] = 0; - rc = lstrcmpi(name, _T("svc_")) == 0; - name[4] = x; - return rc; -} - void enumPlugins(SCAN_PLUGINS_CALLBACK cb, WPARAM wParam, LPARAM lParam) { // get miranda's exe path @@ -456,16 +425,27 @@ static INT_PTR PluginsEnum(WPARAM, LPARAM lParam) pluginEntry* OpenPlugin(TCHAR *tszFileName, TCHAR *dir, TCHAR *path) { - BASIC_PLUGIN_INFO bpi; pluginEntry* p = (pluginEntry*)HeapAlloc(hPluginListHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, sizeof(pluginEntry)); _tcsncpy(p->pluginname, tszFileName, SIZEOF(p->pluginname)); - TCHAR buf[MAX_PATH]; - mir_sntprintf(buf, SIZEOF(buf), _T("%s\\%s\\%s"), path, dir, tszFileName); + // add it to the list anyway + pluginList.insert(p); + + TCHAR tszFullPath[MAX_PATH]; + mir_sntprintf(tszFullPath, SIZEOF(tszFullPath), _T("%s\\%s\\%s"), path, dir, tszFileName); + + // map dll into the memory and check its exports + bool bIsPlugin = false; + mir_ptr pIds( GetPluginInterfaces(tszFullPath, bIsPlugin)); + if ( !bIsPlugin) { + p->pclass |= PCLASS_FAILED; // piece of shit + return p; + } - // plugin name suggests its a db module, load it right now - if ( validguess_db_name(tszFileName)) { - if (checkAPI(buf, &bpi, mirandaVersion, CHECKAPI_DB)) { + // plugin declared that it's a database. load it asap! + if ( hasMuuid(pIds, miid_database)) { + BASIC_PLUGIN_INFO bpi; + if (checkAPI(tszFullPath, &bpi, mirandaVersion, CHECKAPI_DB)) { // db plugin is valid p->pclass |= (PCLASS_DB | PCLASS_BASICAPI); // copy the dblink stuff @@ -478,14 +458,18 @@ pluginEntry* OpenPlugin(TCHAR *tszFileName, TCHAR *dir, TCHAR *path) // didn't have basic APIs or DB exports - failed. p->pclass |= PCLASS_FAILED; } - else if ( validguess_clist_name(tszFileName)) { + // plugin declared that it's a contact list. mark it for the future load + else if ( hasMuuid(pIds, miid_clist)) { // keep a note of this plugin for later if (pluginListUI != NULL) p->nextclass = pluginListUI; pluginListUI = p; p->pclass |= PCLASS_CLIST; } - else if ( validguess_servicemode_name(tszFileName)) { - if (checkAPI(buf, &bpi, mirandaVersion, CHECKAPI_NONE)) { + // plugin declared that it's a service mode plugin. + // load it for a profile manager's window + else if ( hasMuuid(pIds, miid_servicemode)) { + BASIC_PLUGIN_INFO bpi; + if (checkAPI(tszFullPath, &bpi, mirandaVersion, CHECKAPI_NONE)) { p->pclass |= (PCLASS_OK | PCLASS_BASICAPI); p->bpi = bpi; if ( hasMuuid(bpi, miid_servicemode)) { @@ -499,28 +483,9 @@ pluginEntry* OpenPlugin(TCHAR *tszFileName, TCHAR *dir, TCHAR *path) // didn't have basic APIs or DB exports - failed. p->pclass |= PCLASS_FAILED; } - - // add it to the list - pluginList.insert(p); return p; } -// called in the first pass to create pluginEntry* structures and validate database plugins -static BOOL scanPluginsDir(WIN32_FIND_DATA *fd, TCHAR *path, WPARAM, LPARAM) -{ - pluginEntry* p = OpenPlugin(fd->cFileName, _T("Plugins"), path); - if ( !(p->pclass & PCLASS_FAILED)) { - if (pluginList_freeimg == NULL && lstrcmpi(fd->cFileName, _T("advaimg.dll")) == 0) - pluginList_freeimg = p; - - if (pluginList_crshdmp == NULL && lstrcmpi(fd->cFileName, _T("svc_crshdmp.dll")) == 0) { - pluginList_crshdmp = p; - p->pclass |= PCLASS_LAST; - } - } - - return TRUE; -} void SetPluginOnWhiteList(const TCHAR* pluginname, int allow) { @@ -572,7 +537,7 @@ bool TryLoadPlugin(pluginEntry *p, bool bDynamic) p->pclass |= PCLASS_OK | PCLASS_BASICAPI; if (p->bpi.Interfaces) { - MUUID *piface = bpi.Interfaces(); + MUUID *piface = bpi.Interfaces; for (int i=0; !equalUUID(miid_last, piface[i]); i++) { int idx = getDefaultPluginIdx( piface[i] ); if (idx != -1 && pluginDefault[idx].pImpl) { @@ -591,7 +556,7 @@ bool TryLoadPlugin(pluginEntry *p, bool bDynamic) p->pclass |= PCLASS_LOADED; if (p->bpi.Interfaces) { - MUUID *piface = bpi.Interfaces(); + MUUID *piface = bpi.Interfaces; for (int i=0; !equalUUID(miid_last, piface[i]); i++) { int idx = getDefaultPluginIdx( piface[i] ); if (idx != -1) @@ -744,7 +709,6 @@ void UnloadNewPlugins(void) } } ///////////////////////////////////////////////////////////////////////////////////////// -// // Loads all plugins int LoadNewPluginsModule(void) @@ -827,6 +791,22 @@ int LoadNewPluginsModule(void) // Plugins module initialization // called before anything real is loaded, incl. database +static BOOL scanPluginsDir(WIN32_FIND_DATA *fd, TCHAR *path, WPARAM, LPARAM) +{ + pluginEntry* p = OpenPlugin(fd->cFileName, _T("Plugins"), path); + if ( !(p->pclass & PCLASS_FAILED)) { + if (pluginList_freeimg == NULL && lstrcmpi(fd->cFileName, _T("advaimg.dll")) == 0) + pluginList_freeimg = p; + + if (pluginList_crshdmp == NULL && lstrcmpi(fd->cFileName, _T("svc_crshdmp.dll")) == 0) { + pluginList_crshdmp = p; + p->pclass |= PCLASS_LAST; + } + } + + return TRUE; +} + int LoadNewPluginsModuleInfos(void) { bModuleInitialized = TRUE; @@ -855,14 +835,13 @@ int LoadNewPluginsModuleInfos(void) void UnloadNewPluginsModule(void) { - int i; - - if ( !bModuleInitialized) return; + if ( !bModuleInitialized) + return; UnloadPluginOptions(); // unload everything but the DB - for (i = pluginList.getCount()-1; i >= 0; i--) { + for (int i = pluginList.getCount()-1; i >= 0; i--) { pluginEntry* p = pluginList[i]; if ( !(p->pclass & PCLASS_DB) && p != pluginList_crshdmp) Plugin_Uninit(p); @@ -872,8 +851,8 @@ void UnloadNewPluginsModule(void) Plugin_Uninit(pluginList_crshdmp); // unload the DB - for (i = pluginList.getCount()-1; i >= 0; i--) { - pluginEntry* p = pluginList[i]; + for (int k = pluginList.getCount()-1; k >= 0; k--) { + pluginEntry* p = pluginList[k]; Plugin_Uninit(p); } diff --git a/src/modules/plugins/pluginopts.cpp b/src/modules/plugins/pluginopts.cpp index 45d8080d98..0b0f8ab821 100644 --- a/src/modules/plugins/pluginopts.cpp +++ b/src/modules/plugins/pluginopts.cpp @@ -89,7 +89,7 @@ static BOOL dialogListPlugins(WIN32_FIND_DATA* fd, TCHAR* path, WPARAM, LPARAM l dat->flags = 0; if (pi.Interfaces) { - MUUID *piface = pi.Interfaces(); + MUUID *piface = pi.Interfaces; for (int i=0; !equalUUID(miid_last, piface[i]); i++) { int idx = getDefaultPluginIdx( piface[i] ); if (idx != -1 ) { diff --git a/src/modules/plugins/plugins.h b/src/modules/plugins/plugins.h index 6cfa8595c7..64118a45ed 100644 --- a/src/modules/plugins/plugins.h +++ b/src/modules/plugins/plugins.h @@ -17,8 +17,6 @@ typedef PLUGININFOEX * (__cdecl * Miranda_Plugin_InfoEx) (DWORD mirandaVersion); typedef DATABASELINK * (__cdecl * Database_Plugin_Info) (void * reserved); // prototype for clists typedef int (__cdecl * CList_Initialise) (void); -// Interface support -typedef MUUID * (__cdecl * Miranda_Plugin_Interfaces) (void); // can all be NULL struct BASIC_PLUGIN_INFO @@ -27,10 +25,10 @@ struct BASIC_PLUGIN_INFO Miranda_Plugin_Load Load; Miranda_Plugin_Unload Unload; Miranda_Plugin_InfoEx InfoEx; - Miranda_Plugin_Interfaces Interfaces; Database_Plugin_Info DbInfo; CList_Initialise clistlink; PLUGININFOEX * pluginInfo; // must be freed if hInst = = NULL then its a copy + MUUID *Interfaces; // array of supported interfaces DATABASELINK * dblink; // only valid during module being in memory }; @@ -66,6 +64,7 @@ void SetPluginOnWhiteList(const TCHAR* pluginname, int allow); int getDefaultPluginIdx(const MUUID& muuid); bool hasMuuid(const BASIC_PLUGIN_INFO&, const MUUID&); +bool hasMuuid(const MUUID* pFirst, const MUUID&); int equalUUID(const MUUID& u1, const MUUID& u2); int checkAPI(TCHAR* plugin, BASIC_PLUGIN_INFO* bpi, DWORD mirandaVersion, int checkTypeAPI); @@ -88,3 +87,5 @@ struct MuuidReplacement extern MuuidReplacement pluginDefault[]; bool LoadCorePlugin( MuuidReplacement& ); + +MUUID* GetPluginInterfaces(const TCHAR* ptszFileName, bool& bIsPlugin); \ No newline at end of file -- cgit v1.2.3