/******************************************************************** Copyright (c) 1999 Microsoft Corporation Module Name: PCH_SystemHook.CPP Abstract: WBEM provider class implementation for PCH_SystemHook class Revision History: Ghim-Sim Chua (gschua) 05/05/99 - Created Jim Martin (a-jammar) 06/01/99 - Populated data, added Dr. Watson code. ********************************************************************/ #include "pchealth.h" #include "PCH_SystemHook.h" ///////////////////////////////////////////////////////////////////////////// // tracing stuff #ifdef THIS_FILE #undef THIS_FILE #endif static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile #define TRACE_ID DCID_SYSTEMHOOK CPCH_SystemHook MyPCH_SystemHookSet (PROVIDER_NAME_PCH_SYSTEMHOOK, PCH_NAMESPACE); // Property names const static WCHAR * pTimeStamp = L"TimeStamp"; const static WCHAR * pChange = L"Change"; const static WCHAR * pApplication = L"Application"; const static WCHAR * pApplicationPath = L"ApplicationPath"; const static WCHAR * pDLLPath = L"DLLPath"; const static WCHAR * pFullPath = L"FullPath"; const static WCHAR * pHookedBy = L"HookedBy"; const static WCHAR * pHookType = L"HookType"; //----------------------------------------------------------------------------- // Here's a simple collection class to hold the hook information. //----------------------------------------------------------------------------- class CHookInfo { public: CHookInfo() : m_pList(NULL) {}; ~CHookInfo(); BOOL QueryHooks(); BOOL GetHook(DWORD dwIndex, int * iHook, char ** pszDll, char ** pszExe); private: // Functions pulled from the Dr. Watson code. BOOL NTAPI Hook_PreInit(); void NTAPI Hook_RecordHook(PHOOK16 phk, PHOOKWALKINFO phwi); // A struct to hold each parsed hook. struct SHookItem { int m_iHook; char m_szHookDll[MAX_PATH]; char m_szHookExe[MAX_PATH]; SHookItem * m_pNext; SHookItem(int iHook, const char * szDll, const char * szExe, SHookItem * pNext) : m_iHook(iHook), m_pNext(pNext) { strcpy(m_szHookDll, szDll); strcpy(m_szHookExe, szExe); } }; SHookItem * m_pList; }; //----------------------------------------------------------------------------- // The destructor gets rid of the list of hooks. //----------------------------------------------------------------------------- CHookInfo::~CHookInfo() { TraceFunctEnter("CHookInfo::~CHookInfo"); SHookItem * pNext; while (m_pList) { pNext = m_pList->m_pNext; delete m_pList; m_pList = pNext; } TraceFunctLeave(); } //----------------------------------------------------------------------------- // QueryHooks creates the list of system hooks (by calling the Dr. Watson code). //----------------------------------------------------------------------------- extern "C" void ThunkInit(void); BOOL CHookInfo::QueryHooks() { ThunkInit(); return Hook_PreInit(); } //----------------------------------------------------------------------------- // GetHook returns information for the hook specified by dwIndex. If there is // no hook at that index, FALSE is returned. The string pointers are changed // to point at the appropriate strings in the CHookInfo list. This pointers // will be invalid when the CHookInfo object is destroyed. //----------------------------------------------------------------------------- BOOL CHookInfo::GetHook(DWORD dwIndex, int * iHook, char ** pszDll, char ** pszExe) { TraceFunctEnter("CHookInfo::GetHook"); SHookItem * pItem = m_pList; BOOL fReturn = FALSE; for (DWORD i = 0; i < dwIndex && pItem; i++) pItem = pItem->m_pNext; if (pItem) { *iHook = pItem->m_iHook; *pszDll = pItem->m_szHookDll; *pszExe = pItem->m_szHookExe; fReturn = TRUE; } TraceFunctLeave(); return fReturn; } //----------------------------------------------------------------------------- // The following table is used for indicating the hook type. It's referenced // by the iHook value returned from CHookInfo::GetHook. One has to be added // to the iHook value to reference this table, since it starts from -1. //----------------------------------------------------------------------------- LPCTSTR aszHookType[] = { _T("Message Filter"), _T("Journal Record"), _T("Journal Playback"), _T("Keyboard"), _T("GetMessage"), _T("Window Procedure"), _T("CBT"), _T("System MsgFilter"), _T("Mouse"), _T("Hardware"), _T("Debug"), _T("Shell"), _T("Foreground Idle"), _T("Window Procedure Result") }; //----------------------------------------------------------------------------- // The EnumeratInstances function is called by WMI to enurate the instances // of the class. Here we use the CHookInfo class to create a list of system // hooks which we enumerate, creating a WMI object for each one. //----------------------------------------------------------------------------- HRESULT CPCH_SystemHook::EnumerateInstances(MethodContext * pMethodContext, long lFlags) { TraceFunctEnter("CPCH_SystemHook::EnumerateInstances"); HRESULT hRes = WBEM_S_NO_ERROR; // Get the date and time SYSTEMTIME stUTCTime; GetSystemTime(&stUTCTime); CHookInfo hookinfo; if (hookinfo.QueryHooks()) { int iHook; char * szDll; char * szExe; for (DWORD dwIndex = 0; hookinfo.GetHook(dwIndex, &iHook, &szDll, &szExe); dwIndex++) { CInstancePtr pInstance(CreateNewInstance(pMethodContext), false); // Set the change and timestamp fields to "Snapshot" and the current time. if (!pInstance->SetDateTime(pTimeStamp, WBEMTime(stUTCTime))) ErrorTrace(TRACE_ID, "SetDateTime on Timestamp field failed."); if (!pInstance->SetCHString(pChange, L"Snapshot")) ErrorTrace(TRACE_ID, "SetCHString on Change field failed."); // Set the properties we found for the hook. if (!pInstance->SetCHString(pDLLPath, szDll)) ErrorTrace(TRACE_ID, "SetCHString on DLL field failed."); if (!pInstance->SetCHString(pApplicationPath, szExe)) ErrorTrace(TRACE_ID, "SetCHString on application path field failed."); if (!pInstance->SetCHString(pApplication, PathFindFileName(szExe))) ErrorTrace(TRACE_ID, "SetCHString on application field failed."); if (!pInstance->SetCHString(pFullPath, szDll)) ErrorTrace(TRACE_ID, "SetCHString on pFullPath field failed."); if (!pInstance->SetCHString(pHookedBy, PathFindFileName(szDll))) ErrorTrace(TRACE_ID, "SetCHString on pHookedBy field failed."); if (iHook >= -1 && iHook <= 12) { if (!pInstance->SetCHString(pHookType, aszHookType[iHook + 1])) ErrorTrace(TRACE_ID, "SetCHString on pHookType field failed."); } else ErrorTrace(TRACE_ID, "Bad hook type."); hRes = pInstance->Commit(); if (FAILED(hRes)) ErrorTrace(TRACE_ID, "Commit on Instance failed."); } } TraceFunctLeave(); return hRes; } //----------------------------------------------------------------------------- // Gather the system hook information. //----------------------------------------------------------------------------- TCHAR g_tszShell[MAX_PATH]; BOOL NTAPI CHookInfo::Hook_PreInit() { TraceFunctEnter("CHookInfo::Hook_PreInit"); BOOL fRc = FALSE; // Initialize the g_tszShell variable if necessary. if (!g_tszShell[0]) { TCHAR tszPath[MAX_PATH]; GetPrivateProfileString(TEXT("boot"), TEXT("shell"), TEXT("explorer.exe"), tszPath, cA(tszPath), TEXT("system.ini")); lstrcpy(g_tszShell, PathFindFileName(tszPath)); } if (g_pvWin16Lock) { PV pvUser = MapSL((PV)MAKELONG(0, (DWORD)g_hinstUser)); LPWORD pwRghhk; DWORD dwHhk; dwHhk = GetUserHookTable(); switch (HIWORD(dwHhk)) { case 1: // We "know" the structure of the USER hook chain // of type 1. pwRghhk = (LPWORD)pvAddPvCb(pvUser, LOWORD(dwHhk) + 2); break; default: // Unknown hook style. Oh well. pwRghhk = 0; break; } if (pwRghhk) { // Walk the hook list (under the Win16 lock) // and parse out each installed hook. int ihk; HOOKWALKINFO hwi; EnterSysLevel(g_pvWin16Lock); for (ihk = WH_MIN; ihk <= WH_MAX; ihk++) { WORD hhk = pwRghhk[ihk]; while (hhk) { PHOOK16 phk = (PHOOK16)pvAddPvCb(pvUser, hhk); if (phk->hkMagic != HK_MAGIC || phk->idHook != ihk) break; // Weird. Stop before we GPF Hook_RecordHook(phk, &hwi); hhk = phk->phkNext; fRc = TRUE; } } LeaveSysLevel(g_pvWin16Lock); } } TraceFunctLeave(); return fRc; } //----------------------------------------------------------------------------- // Record information about a single hook during the hookwalk process. //----------------------------------------------------------------------------- void NTAPI CHookInfo::Hook_RecordHook(PHOOK16 phk, PHOOKWALKINFO phwi) { TraceFunctEnter("CHookInfo::Hook_RecordHook"); HOOKINFO hi; PQ16 pq; ZeroX(hi); hi.iHook = phk->idHook; // Get the name of the app that installed the hook. // We wished we could pass the queue handle to GetModuleFileName, // but that will just return USER. // // It is possible that the queue is null; this means that the // hook was installed by a device driver (like XMOUSE). pq = (PQ16)MapSL((PV)MAKELONG(0, phk->hqCreator)); if (pq) GetModuleFileName16(pq->htask, hi.szHookExe, cA(hi.szHookExe)); else hi.szHookExe[0] = TEXT('\0'); // Now get the DLL name. This varies depending on whether the // DLL is 16-bit or 32-bit. if (phk->uiFlags & HOOK_32BIT) { // If a 32-bit hook, then the DLL name is saved in an atom. GetUserAtomName(phk->atomModule, hi.szHookDll); PathAdjustCase(hi.szHookDll, 0); } else { // If a 16-bit hook, then the DLL name is saved as hmodule16. GetModuleFileName16((HMODULE16)phk->hmodOwner, hi.szHookDll, cA(hi.szHookDll)); PathAdjustCase(hi.szHookDll, 1); } // Add the hook to the collection of hooks. Don't add the explorer shell to the // list. if (hi.iHook != WH_SHELL || lstrcmpi(PathFindFileName(hi.szHookExe), g_tszShell) != 0) { SHookItem * pNew = new SHookItem(hi.iHook, hi.szHookDll, hi.szHookExe, m_pList); if (pNew) m_pList = pNew; else throw CHeap_Exception(CHeap_Exception::E_ALLOCATION_ERROR); } TraceFunctLeave(); } //----------------------------------------------------------------------------- // Function pulled in from Dr. Watson. // // If f16, then it is a 16-bit thing, and we uppercase it all. // If !f16, then it is a 32-bit thing, and we uppercase the // first and downcase the rest. // // If the first letter is a DBCS thing, then we don't touch it. //----------------------------------------------------------------------------- void NTAPI PathAdjustCase(LPSTR psz, BOOL f16) { TraceFunctEnter("PathAdjustCase"); psz = PathFindFileName(psz); if (f16) CharUpper(psz); else if (!IsDBCSLeadByte(*psz)) { CharUpperBuff(psz, 1); CharLower(psz+1); } TraceFunctLeave(); }