566 lines
14 KiB
C++
566 lines
14 KiB
C++
// 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();
|
|
}
|
|
|