// DelayLoad.cpp: implementation of the CDelayLoad class. // ////////////////////////////////////////////////////////////////////// #include "DelayLoad.h" #include "UtilityFunctions.h" #include "stdlib.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CDelayLoad::CDelayLoad() { // DBGHELP m_hDBGHELP = NULL; m_fDBGHELPInitialized = false; m_fDBGHELPInitializedAttempted = false; m_lpfMakeSureDirectoryPathExists = NULL; // PSAPI m_hPSAPI = NULL; m_fPSAPIInitialized = false; m_fPSAPIInitializedAttempted = false; m_lpfEnumProcesses = NULL; m_lpfEnumProcessModules = NULL; m_lpfGetModuleFileNameEx = NULL; m_lpfEnumDeviceDrivers = NULL; m_lpfGetDeviceDriverFileName = NULL; // TOOLHELP32 m_hTOOLHELP32 = NULL; m_fTOOLHELP32Initialized = false; m_fTOOLHELP32InitializedAttempted = false; m_lpfCreateToolhelp32Snapshot = NULL; m_lpfProcess32First = NULL; m_lpfProcess32Next = NULL; m_lpfModule32First = NULL; m_lpfModule32Next = NULL; // SYMSRV m_hSYMSRV = NULL; m_fSYMSRVInitialized = false; m_fSYMSRVInitializedAttempted = false; m_tszSymSrvDLL = NULL; m_lpfSymbolServer = NULL; m_lpfSymbolServerClose = NULL; } CDelayLoad::~CDelayLoad() { if (m_hDBGHELP) FreeLibrary(m_hDBGHELP); if (m_hPSAPI) FreeLibrary(m_hPSAPI); if (m_hTOOLHELP32) FreeLibrary(m_hTOOLHELP32); if (m_hSYMSRV) FreeLibrary(m_hSYMSRV); if (m_tszSymSrvDLL) { delete [] m_tszSymSrvDLL; } } bool CDelayLoad::Initialize_DBGHELP() { m_fDBGHELPInitialized = false; m_fDBGHELPInitializedAttempted = true; // Load library on DBGHELP.DLL and get the procedures explicitly. m_hDBGHELP = LoadLibrary( TEXT("DBGHELP.DLL") ); if( m_hDBGHELP == NULL ) { // This is fatal, since we need this for cases where we are searching // for DBG files, and creation of directories... _tprintf(TEXT("\nERROR: Unable to load DBGHELP.DLL, which is required for proper operation.\n")); _tprintf(TEXT("You should ensure that a copy of this DLL is on your system path, or in the\n")); _tprintf(TEXT("same directory as this utility.\n")); goto exit; } else { // Get procedure addresses. m_lpfMakeSureDirectoryPathExists = (PfnMakeSureDirectoryPathExists) GetProcAddress( m_hDBGHELP, "MakeSureDirectoryPathExists"); if( (m_lpfMakeSureDirectoryPathExists == NULL) ) { // Consider this fatal _tprintf(TEXT("\nWARNING: The version of DBGHELP.DLL being loaded doesn't have required\n")); _tprintf(TEXT("functions!. Please update this module with a newer version and try again.\n")); FreeLibrary( m_hDBGHELP ) ; m_hDBGHELP = NULL; goto exit; } } m_fDBGHELPInitialized = true; exit: return m_fDBGHELPInitialized; } BOOL CDelayLoad::MakeSureDirectoryPathExists(LPTSTR DirPath) { // If we've never initialized DBGHELP, do so now... if (!m_fDBGHELPInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_DBGHELP()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fDBGHELPInitialized) { return FALSE; } if (!m_lpfMakeSureDirectoryPathExists) return FALSE; // This API normally takes ASCII strings only... char szDirPath[_MAX_PATH]; CUtilityFunctions::CopyTSTRStringToAnsi(DirPath, szDirPath, _MAX_PATH); // Invoke and return... return m_lpfMakeSureDirectoryPathExists(szDirPath); } // PSAPI.DLL - APIs bool CDelayLoad::Initialize_PSAPI() { m_fPSAPIInitialized = false; m_fPSAPIInitializedAttempted = true; // Load library on DBGHELP.DLL and get the procedures explicitly. m_hPSAPI = LoadLibrary( TEXT("PSAPI.DLL") ); if( m_hPSAPI == NULL ) { // This may/may not be fatal... we can always fall back to TOOLHELP32 for Win2000/Win98 goto exit; } else { // Get procedure addresses. m_lpfEnumProcesses = (PfnEnumProcesses) GetProcAddress( m_hPSAPI, "EnumProcesses" ) ; m_lpfEnumProcessModules = (PfnEnumProcessModules) GetProcAddress( m_hPSAPI, "EnumProcessModules" ); #ifdef UNICODE m_lpfGetModuleFileNameEx =(PfnGetModuleFileNameEx) GetProcAddress(m_hPSAPI, "GetModuleFileNameExW" ); m_lpfGetDeviceDriverFileName = (PfnGetDeviceDriverFileName) GetProcAddress(m_hPSAPI, "GetDeviceDriverFileNameW"); #else m_lpfGetModuleFileNameEx =(PfnGetModuleFileNameEx) GetProcAddress(m_hPSAPI, "GetModuleFileNameExA" ); m_lpfGetDeviceDriverFileName = (PfnGetDeviceDriverFileName) GetProcAddress(m_hPSAPI, "GetDeviceDriverFileNameA"); #endif m_lpfEnumDeviceDrivers = (PfnEnumDeviceDrivers) GetProcAddress(m_hPSAPI, "EnumDeviceDrivers" ); if( m_lpfEnumProcesses == NULL || m_lpfEnumProcessModules == NULL || m_lpfGetModuleFileNameEx == NULL || m_lpfEnumDeviceDrivers == NULL || m_lpfGetDeviceDriverFileName == NULL ) { _tprintf(TEXT("The version of PSAPI.DLL being loaded doesn't have required functions!.\n")); FreeLibrary( m_hPSAPI ) ; m_hPSAPI = NULL; goto exit; } } m_fPSAPIInitialized = true; exit: return m_fPSAPIInitialized; } DWORD CDelayLoad::GetModuleFileNameEx(HANDLE hHandle, HMODULE hModule, LPTSTR lpFilename, DWORD nSize) { // If we've never initialized PSAPI, do so now... if (!m_fPSAPIInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_PSAPI()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fPSAPIInitialized) { return FALSE; } if (!m_lpfGetModuleFileNameEx) return FALSE; return m_lpfGetModuleFileNameEx(hHandle, hModule, lpFilename, nSize); } BOOL CDelayLoad::EnumProcessModules(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded) { // If we've never initialized PSAPI, do so now... if (!m_fPSAPIInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_PSAPI()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fPSAPIInitialized) { return FALSE; } if (!m_lpfEnumProcessModules) return FALSE; return m_lpfEnumProcessModules(hProcess, lphModule, cb, lpcbNeeded); } BOOL CDelayLoad::EnumProcesses(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded) { // If we've never initialized PSAPI, do so now... if (!m_fPSAPIInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_PSAPI()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fPSAPIInitialized) { return FALSE; } if (!m_lpfEnumProcesses) return FALSE; return m_lpfEnumProcesses(lpidProcess, cb, cbNeeded); } BOOL CDelayLoad::EnumDeviceDrivers(LPVOID *lpImageBase, DWORD cb, LPDWORD lpcbNeeded) { // If we've never initialized PSAPI, do so now... if (!m_fPSAPIInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_PSAPI()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fPSAPIInitialized) { return FALSE; } if (!m_lpfEnumDeviceDrivers) return FALSE; return m_lpfEnumDeviceDrivers(lpImageBase, cb, lpcbNeeded); } DWORD CDelayLoad::GetDeviceDriverFileName(LPVOID ImageBase, LPTSTR lpFilename, DWORD nSize) { // If we've never initialized PSAPI, do so now... if (!m_fPSAPIInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_PSAPI()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fPSAPIInitialized) { return FALSE; } if (!m_lpfGetDeviceDriverFileName) return FALSE; return m_lpfGetDeviceDriverFileName(ImageBase, lpFilename, nSize); } // TOOLHELP32.DLL - APIs bool CDelayLoad::Initialize_TOOLHELP32() { m_fTOOLHELP32Initialized = false; m_fTOOLHELP32InitializedAttempted = true; // Load library on DBGHELP.DLL and get the procedures explicitly. m_hTOOLHELP32 = LoadLibrary( TEXT("KERNEL32.DLL") ); if( m_hTOOLHELP32 == NULL ) { // This may/may not be fatal... we can always fall back to TOOLHELP32 for Win2000/Win98 goto exit; } else { // // BUG 523 - GREGWI - Unicode version fails to enumerate processes correctly... // (Code added to enumerate the correct DLL entrypoints in KERNEL32.DLL) // // Get procedure addresses based on UNICODE or ANSI m_lpfCreateToolhelp32Snapshot = (PfnCreateToolhelp32Snapshot) GetProcAddress( m_hTOOLHELP32, "CreateToolhelp32Snapshot" ); #ifdef UNICODE m_lpfProcess32First = (PfnProcess32First) GetProcAddress( m_hTOOLHELP32, "Process32FirstW" ); m_lpfProcess32Next = (PfnProcess32Next) GetProcAddress( m_hTOOLHELP32, "Process32NextW" ); m_lpfModule32First = (PfnModule32First) GetProcAddress( m_hTOOLHELP32, "Module32FirstW" ); m_lpfModule32Next = (PfnModule32Next) GetProcAddress( m_hTOOLHELP32, "Module32NextW" ); #else m_lpfProcess32First = (PfnProcess32First) GetProcAddress( m_hTOOLHELP32, "Process32First" ); m_lpfProcess32Next = (PfnProcess32Next) GetProcAddress( m_hTOOLHELP32, "Process32Next" ); m_lpfModule32First = (PfnModule32First) GetProcAddress( m_hTOOLHELP32, "Module32First" ); m_lpfModule32Next = (PfnModule32Next) GetProcAddress( m_hTOOLHELP32, "Module32Next" ); #endif if (!m_lpfCreateToolhelp32Snapshot || !m_lpfProcess32First || !m_lpfProcess32Next || !m_lpfModule32First || !m_lpfModule32Next) { // Free our handle to KERNEL32.DLL FreeLibrary(m_hTOOLHELP32); m_hTOOLHELP32 = NULL; goto exit; } } m_fTOOLHELP32Initialized = true; exit: return m_fTOOLHELP32Initialized; } HANDLE WINAPI CDelayLoad::CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID) { // If we've never initialized TOOLHELP32, do so now... if (!m_fTOOLHELP32InitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_TOOLHELP32()) return INVALID_HANDLE_VALUE; } // If we've attempted, but we failed... then bail now... if (!m_fTOOLHELP32Initialized) { return INVALID_HANDLE_VALUE; } if (!m_lpfCreateToolhelp32Snapshot) return INVALID_HANDLE_VALUE; return m_lpfCreateToolhelp32Snapshot(dwFlags, th32ProcessID); } BOOL WINAPI CDelayLoad::Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) { // If we've never initialized TOOLHELP32, do so now... if (!m_fTOOLHELP32InitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_TOOLHELP32()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fTOOLHELP32Initialized) { return FALSE; } if (!m_lpfProcess32First) return FALSE; return m_lpfProcess32First(hSnapshot, lppe); } BOOL WINAPI CDelayLoad::Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) { // If we've never initialized TOOLHELP32, do so now... if (!m_fTOOLHELP32InitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_TOOLHELP32()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fTOOLHELP32Initialized) { return FALSE; } if (!m_lpfProcess32Next) return FALSE; return m_lpfProcess32Next(hSnapshot, lppe); } BOOL WINAPI CDelayLoad::Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme) { // If we've never initialized TOOLHELP32, do so now... if (!m_fTOOLHELP32InitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_TOOLHELP32()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fTOOLHELP32Initialized) { return FALSE; } if (!m_lpfModule32First) return FALSE; return m_lpfModule32First(hSnapshot, lpme); } BOOL WINAPI CDelayLoad::Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme) { // If we've never initialized TOOLHELP32, do so now... if (!m_fTOOLHELP32InitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_TOOLHELP32()) return FALSE; } // If we've attempted, but we failed... then bail now... if (!m_fTOOLHELP32Initialized) { return FALSE; } if (!m_lpfModule32Next) return FALSE; return m_lpfModule32Next(hSnapshot, lpme); } bool CDelayLoad::Initialize_SYMSRV(LPCTSTR tszSymSrvDLL) { m_fSYMSRVInitialized = false; m_fSYMSRVInitializedAttempted = true; // If we're loading a new SYMSRV DLL file (SYMSRV.DLL or some other Symbol DLL) if (m_hSYMSRV) { // Compare... is this file the same as the last... if so... don't worry about it... if (_tcscmp(tszSymSrvDLL, m_tszSymSrvDLL)) { FreeLibrary(m_hSYMSRV); m_hSYMSRV = NULL; delete [] m_tszSymSrvDLL; // Free the previous string... } } // Load library on SYMSRV.DLL and get the procedures explicitly. m_hSYMSRV = LoadLibrary( tszSymSrvDLL ); if( m_hSYMSRV == NULL ) { // This is fatal, since we need this for cases where we are searching // for DBG files, and creation of directories... _tprintf(TEXT("\nERROR: Unable to load %s, which is required for proper operation.\n"), tszSymSrvDLL); _tprintf(TEXT("You should ensure that a copy of this DLL is on your system path, or in the\n")); _tprintf(TEXT("same directory as this utility.\n")); goto exit; } else { // Get procedure addresses. m_lpfSymbolServer = (PfnSymbolServer) GetProcAddress( m_hSYMSRV, "SymbolServer"); m_lpfSymbolServerClose = (PfnSymbolServerClose) GetProcAddress( m_hSYMSRV, "SymbolServerClose"); if( (m_lpfSymbolServer == NULL) || (m_lpfSymbolServerClose == NULL) ) { // Consider this fatal _tprintf(TEXT("\nWARNING: The version of %s being loaded doesn't have required\n"), tszSymSrvDLL); _tprintf(TEXT("functions!. Please update this module with a newer version and try again.\n")); FreeLibrary( m_hSYMSRV ) ; m_hSYMSRV = NULL; goto exit; } // Copy the provided string m_tszSymSrvDLL = new TCHAR[_tcslen(tszSymSrvDLL)+1]; if (NULL == m_tszSymSrvDLL) goto exit; _tcscpy(m_tszSymSrvDLL, tszSymSrvDLL); } m_fSYMSRVInitialized = true; exit: return m_fSYMSRVInitialized; } BOOL WINAPI CDelayLoad::SymbolServer(LPCSTR params, LPCSTR filename, DWORD num1, DWORD num2, DWORD num3, LPSTR path) { /* // If we've never initialized SYMSRV, do so now... if (!m_fSYMSRVInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_SYMSRV()) return FALSE; } */ // If we've attempted, but we failed... then bail now... if (!m_fSYMSRVInitialized) { return FALSE; } if (!m_lpfSymbolServer) return FALSE; return m_lpfSymbolServer(params, filename, num1, num2, num3, path); } BOOL WINAPI CDelayLoad::SymbolServerClose(VOID) { /* // If we've never initialized SYMSRV, do so now... if (!m_fSYMSRVInitializedAttempted) { // Initialize the DLL if needed... if (FALSE == Initialize_SYMSRV()) return FALSE; } */ // If we've attempted, but we failed... then bail now... if (!m_fSYMSRVInitialized) { return FALSE; } if (!m_lpfSymbolServerClose) return FALSE; return m_lpfSymbolServerClose(); }