windows-nt/Source/XPSP1/NT/sdktools/checksym/modules.cpp
2020-09-26 16:20:57 +08:00

687 lines
19 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 2000
//
// File: modules.cpp
//
//--------------------------------------------------------------------------
// Modules.cpp: implementation of the CModules class.
//
//////////////////////////////////////////////////////////////////////
#ifndef NO_STRICT
#ifndef STRICT
#define STRICT 1
#endif
#endif /* NO_STRICT */
#include <WINDOWS.H>
#include <STDIO.H>
#include <TCHAR.H>
#include "Globals.h"
#include "Modules.h"
#include "ProgramOptions.h"
#include "UtilityFunctions.h"
#include "ModuleInfo.h"
#include "ModuleInfoNode.h"
#include "ModuleInfoCache.h"
#include "FileData.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CModules::CModules()
{
m_lpModuleInfoHead = NULL;
m_hModuleInfoHeadMutex = NULL;
m_lpDmpFile = NULL;
m_fInitialized = false;
m_iNumberOfModules = 0;
}
CModules::~CModules()
{
WaitForSingleObject(m_hModuleInfoHeadMutex, INFINITE);
// If we have Module Info Objects... nuke them now...
if (m_lpModuleInfoHead)
{
CModuleInfoNode * lpModuleInfoNodePointer = m_lpModuleInfoHead;
CModuleInfoNode * lpModuleInfoNodePointerToDelete = m_lpModuleInfoHead;
// Traverse the linked list to the end..
while (lpModuleInfoNodePointer)
{ // Keep looking for the end...
// Advance our pointer to the next node...
lpModuleInfoNodePointer = lpModuleInfoNodePointer->m_lpNextModuleInfoNode;
// Delete the one behind us...
delete lpModuleInfoNodePointerToDelete;
// Set the node to delete to the current...
lpModuleInfoNodePointerToDelete = lpModuleInfoNodePointer;
}
// Now, clear out the Head pointer...
m_lpModuleInfoHead = NULL;
}
// Be a good citizen and release the Mutex
ReleaseMutex(m_hModuleInfoHeadMutex);
// Now, close the Mutex
if (m_hModuleInfoHeadMutex)
{
CloseHandle(m_hModuleInfoHeadMutex);
m_hModuleInfoHeadMutex = NULL;
}
}
bool CModules::Initialize(CModuleInfoCache *lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile, CDmpFile * lpDmpFile)
{
// We need the following objects to do business...
if ( lpModuleInfoCache == NULL)
return false;
m_lpModuleInfoCache = lpModuleInfoCache;
m_lpInputFile = lpInputFile;
m_lpOutputFile = lpOutputFile;
m_lpDmpFile = lpDmpFile;
m_hModuleInfoHeadMutex = CreateMutex(NULL, FALSE, NULL);
if (m_hModuleInfoHeadMutex == NULL)
return false;
m_fInitialized = true;
return true;
}
bool CModules::GetModulesData(CProgramOptions::ProgramModes enumProgramModes, bool fGetDataFromCSVFile)
{
switch (enumProgramModes)
{
case CProgramOptions::InputModulesDataFromFileSystemMode:
if (fGetDataFromCSVFile)
{
GetModulesDataFromFile();
} else
{
GetModulesDataFromFileSystem();
}
break;
case CProgramOptions::InputDriversFromLiveSystemMode:
if (fGetDataFromCSVFile)
{
GetModulesDataFromFile(); // ISSUE-2000/07/24-GREGWI: I think we can use the same method as above ????
} else
{
GetModulesDataFromDeviceDrivers();
}
break;
default:
break;
}
return true;
}
bool CModules::GetModulesDataFromFileSystem()
{
bool fProcessPath = true;
// Okay... here we go...
//#ifdef _DEBUG
// _tprintf(TEXT("Processing the path [%s]\n"), m_lpProgramOptions->GetInputModulesDataFromFileSystemPath());
//#endif
LPTSTR tszExpandedSymbolPath= NULL, tszSymbolPathStart, tszSymbolPathEnd;
// Mark the start of the path to process
tszSymbolPathStart = g_lpProgramOptions->GetInputModulesDataFromFileSystemPath();
// Find the end of the path
tszSymbolPathEnd = _tcschr( tszSymbolPathStart, ';' );
// If tszSymbolPathEnd is non-zero, then there is another path following...
if (tszSymbolPathEnd)
*tszSymbolPathEnd = '\0'; // Change the ';' to a Null temporarily...
while (fProcessPath)
{
//#ifdef _DEBUG
// _tprintf(TEXT("\n\nProcessing Path [%s]\n"), tszSymbolPathStart);
//#endif
// Begin the "madness"... ;)
ScavengeForFiles(tszSymbolPathStart, 1);
// Post processing... replace the null if necessary, and advance to next string
if (tszSymbolPathEnd)
{
*tszSymbolPathEnd = ';';
tszSymbolPathStart = tszSymbolPathEnd + 1;
tszSymbolPathEnd = _tcschr( tszSymbolPathStart, ';' );
if (tszSymbolPathEnd) {
*tszSymbolPathEnd = '\0';
}
} else
fProcessPath = false;
}
if (tszExpandedSymbolPath)
{
delete [] tszExpandedSymbolPath;
}
return true;
}
bool CModules::ScavengeForFiles(LPCTSTR tszSymbolPathStart, int iRecurseDepth)
{
// Bale if we're in too deep...
if (iRecurseDepth > MAX_RECURSE_DEPTH)
return true;
TCHAR tszFileBuffer[MAX_PATH+1];
TCHAR drive[_MAX_DRIVE];
TCHAR dir[_MAX_DIR];
TCHAR fname[_MAX_FNAME];
TCHAR ext[_MAX_EXT];
bool fNew;
CModuleInfo * lpModuleInfo;
_tsplitpath(tszSymbolPathStart, drive, dir, fname, ext);
WIN32_FIND_DATA lpFindFileData;
HANDLE hFileOrDirectoryHandle = FindFirstFile(tszSymbolPathStart, &lpFindFileData);
while ( INVALID_HANDLE_VALUE != hFileOrDirectoryHandle )
{
// Compose the path to the file or directory...
_tmakepath(tszFileBuffer, drive, dir, NULL, NULL);
_tcscat(tszFileBuffer, lpFindFileData.cFileName);
if (lpFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// Check to see if we've got the . or .. directories!
if ( ( 0 == _tcscmp(lpFindFileData.cFileName, TEXT(".")) ) ||
( 0 == _tcscmp(lpFindFileData.cFileName, TEXT("..")) )
)
{
// Skip this one...
if (!FindNextFile(hFileOrDirectoryHandle, &lpFindFileData))
break;
// Go up for more fun...
continue;
}
//#ifdef _DEBUG
// _tprintf(TEXT("DIRECTORY FOUND: [%s]\n"), tszFileBuffer);
//#endif
// If this is a directory, and no wild card was provided.. then use *.*
if ( CUtilityFunctions::ContainsWildCardCharacter(tszSymbolPathStart) )
{
// We need to preserve the Wild Char stuff...
_tcscat(tszFileBuffer,TEXT("\\"));
_tcscat(tszFileBuffer,fname);
_tcscat(tszFileBuffer, ext);
} else
{
// Append the *.*
_tcscat(tszFileBuffer, TEXT("\\*.*"));
}
ScavengeForFiles(tszFileBuffer, iRecurseDepth+1);
} else
{
//#ifdef _DEBUG
// _tprintf(TEXT("FILE FOUND: [%s]\n"), tszFileBuffer);
//#endif
fNew = false;
TCHAR tszFullFileBuffer[_MAX_PATH+1];
LPTSTR tszFileNamePointer;
DWORD cbBytesCopied = GetFullPathName(tszFileBuffer , _MAX_PATH+1, tszFullFileBuffer, &tszFileNamePointer);
if (cbBytesCopied)
{
// If "-MATCH" was specified, look to see if this filename meets our criteria
// before we save this away in our module cache...
if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszFileBuffer))
goto getnextmodule;
// Okay, let's go ahead and get a ModuleInfo Object from our cache...
// If pfNew returns TRUE, then this object is new and we'll need
// to populate it with data...
lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszFullFileBuffer, &fNew);
if (false == fNew)
{
// We may have the object in the cache... now we need to
// save a pointer to this object in our Process Info list
AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
// We save having to get the module info again for this module...
goto getnextmodule;
}
// Not in the cache... so we need to init it, and get the module info...
// Okay, let's create a ModuleInfo object and pass this down
// routines that will populate it full of data...
if (lpModuleInfo->Initialize(NULL, m_lpOutputFile, NULL))
{
// Let's do it!! Populate the ModuleInfo object with data!!!!
if (lpModuleInfo->GetModuleInfo(tszFileBuffer))
{
// Start obtaining information about the modules...
/*
#ifdef _DEBUG
_tprintf(TEXT("Module[%3d] = [%s]\n"), i+1, szFileName);
#endif
*/
// We may have the object in the cache... now we need to
// save a pointer to this object in our Process Info list
if (AddNewModuleInfoObject(lpModuleInfo))
{
}
}
}
}
}
getnextmodule:
if (!FindNextFile(hFileOrDirectoryHandle, &lpFindFileData))
break;
}
if ( INVALID_HANDLE_VALUE != hFileOrDirectoryHandle )
FindClose(hFileOrDirectoryHandle);
return true;
}
bool CModules::AddNewModuleInfoObject(CModuleInfo *lpModuleInfo)
{
if (!m_fInitialized)
return false;
// First, create a ModuleInfoNode object and then attach it to the bottom of the
// linked list of nodes...
CModuleInfoNode * lpModuleInfoNode = new CModuleInfoNode(lpModuleInfo);
//#ifdef _DEBUG
// _tprintf(TEXT("Adding Module Info Object for [%s]\n"), lpModuleInfo->GetModulePath());
//#endif
if (lpModuleInfoNode == NULL)
return false; // Couldn't allocate memory..
// Acquire Mutex object to protect the linked-list...
WaitForSingleObject(m_hModuleInfoHeadMutex, INFINITE);
CModuleInfoNode * lpModuleInfoNodePointer = m_lpModuleInfoHead;
if (lpModuleInfoNodePointer) {
// Traverse the linked list to the end..
while (lpModuleInfoNodePointer->m_lpNextModuleInfoNode)
{ // Keep looking for the end...
lpModuleInfoNodePointer = lpModuleInfoNodePointer->m_lpNextModuleInfoNode;
}
lpModuleInfoNodePointer->m_lpNextModuleInfoNode = lpModuleInfoNode;
}
else
{ // First time through, the Process Info Head pointer is null...
m_lpModuleInfoHead = lpModuleInfoNode;
}
// Be a good citizen and release the Mutex
ReleaseMutex(m_hModuleInfoHeadMutex);
InterlockedIncrement(&m_iNumberOfModules);
return true;
}
//bool CModules::OutputModulesData(LPCTSTR tszOutputContext)
bool CModules::OutputModulesData(CollectionTypes enumCollectionType, bool fCSVFileContext)
{
// Are we in quiet mode?
if ( !g_lpProgramOptions->GetMode(CProgramOptions::QuietMode) )
{
// Output to Stdout?
if (!OutputModulesDataToStdout(enumCollectionType, fCSVFileContext))
return false;
}
// Output to file?
if (g_lpProgramOptions->GetMode(CProgramOptions::OutputCSVFileMode))
{
// Try and output to file...
if (!OutputModulesDataToFile(enumCollectionType))
return false;
}
if (m_lpModuleInfoHead) {
CModuleInfoNode * lpCurrentModuleInfoNode = m_lpModuleInfoHead;
DWORD dwModuleNumber = 1;
while (lpCurrentModuleInfoNode)
{
// We have a node... print out Module Info for it, then the Modules Data...
if (lpCurrentModuleInfoNode->m_lpModuleInfo)
{
lpCurrentModuleInfoNode->m_lpModuleInfo->OutputData(NULL, 0, dwModuleNumber);
dwModuleNumber++;
}
lpCurrentModuleInfoNode = lpCurrentModuleInfoNode->m_lpNextModuleInfoNode;
}
}
return true;
}
bool CModules::OutputModulesDataToStdout(CollectionTypes enumCollectionType, bool fCSVFileContext)
{
_tprintf(TEXT("\n"));
CUtilityFunctions::OutputLineOfStars();
// Output to stdout...
if (m_iNumberOfModules)
{
_tprintf(TEXT("%s - Printing Module Information for %d Modules.\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, m_iNumberOfModules);
_tprintf(TEXT("%s - Context: %s\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, fCSVFileContext ? g_tszCollectionArray[enumCollectionType].tszCSVContext : g_tszCollectionArray[enumCollectionType].tszLocalContext);
} else
{
_tprintf(TEXT("\n%s - No modules were found!\n\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel);
}
CUtilityFunctions::OutputLineOfStars();
_tprintf(TEXT("\n"));
return true;
}
bool CModules::OutputModulesDataToFile(CollectionTypes enumCollectionType)
{
// Don't write anything if there are no processes to report...
if (0 == m_iNumberOfModules)
return true;
// Write out the Modules tag so I can detect this output format...
if (!m_lpOutputFile->WriteString(TEXT("\r\n")) ||
!m_lpOutputFile->WriteString(g_tszCollectionArray[enumCollectionType].tszCSVLabel) ||
!m_lpOutputFile->WriteString(TEXT("\r\n"))
)
{
_tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath());
m_lpOutputFile->PrintLastError();
return false;
}
// Write out the [Modules] header...
if (!m_lpOutputFile->WriteString(g_tszCollectionArray[enumCollectionType].tszCSVColumnHeaders))
{
_tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath());
m_lpOutputFile->PrintLastError();
return false;
}
return true;
}
bool CModules::GetModulesDataFromFile()
{
CModuleInfo * lpModuleInfo;
// Read the Modules Header Line
if (!m_lpInputFile->ReadFileLine())
return false;
// I need these near the end when I probe to see if the next module
// is for this process...
enum { BUFFER_SIZE = 128};
// Unfortunately, when reading from the CSV file, the data is MBCS... so I need
// to convert...
// Read the first field (should be blank, unless this is a new collection type
if (m_lpInputFile->ReadString())
return true;
// Read the second field (should be blank)
if (m_lpInputFile->ReadString())
return true;
// Read the second field (should be blank)
if (m_lpInputFile->ReadString())
return true;
// Local buffer for reading data...
char szModulePath[_MAX_PATH+1];
TCHAR tszModulePath[_MAX_PATH+1];
bool fDone = false;
bool fNew = false;
while (!fDone)
{
// Read in the Module Path
if (!m_lpInputFile->ReadString(szModulePath, _MAX_PATH+1))
return true;
CUtilityFunctions::CopyAnsiStringToTSTR(szModulePath, tszModulePath, _MAX_PATH+1);
// If "-MATCH" was specified, look to see if this filename meets our criteria
// before we save this away in our module cache...
if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszModulePath))
{
// Okay... read to the start of the next line...
if (!m_lpInputFile->ReadFileLine())
goto cleanup;
goto probe_line; // We save having to get the module info again for this module...
}
// Okay, let's go ahead and get a ModuleInfo Object from our cache...
// If pfNew returns TRUE, then this object is new and we'll need
// to populate it with data...
lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszModulePath, &fNew);
if (false == fNew)
{
// We may have the object in the cache... now we need to
// save a pointer to this object in our Process Info list
AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
// Okay... read to the start of the next line...
if ( !m_lpInputFile->ReadFileLine() )
goto cleanup;
goto probe_line; // We save having to get the module info again for this module...
}
// Not in the cache... so we need to init it, and get the module info...
if (!lpModuleInfo->Initialize(m_lpInputFile, m_lpOutputFile, NULL))
{
return false; // Hmmm... memory error?
}
// Let's do it!! Populate the ModuleInfo object with data!!!!
if (!lpModuleInfo->GetModuleInfo(tszModulePath, false, 0, true))
{
// Well, we tried and failed...
return false;
}
// Start obtaining information about the modules...
if (!AddNewModuleInfoObject(lpModuleInfo))
{ // Failure adding the node.... This is pretty serious...
return false;
}
// Okay, let's go ahead and probe to see what's coming...
probe_line:
if ( m_lpInputFile->EndOfFile() )
goto cleanup;
// Read the first field (should be blank, unless this is a new collection type
if (m_lpInputFile->ReadString())
goto cleanup;
// Read the second field (should be blank)
if (m_lpInputFile->ReadString())
return true;
// Read the second field (should be blank)
if (m_lpInputFile->ReadString())
return true;
}
cleanup:
// We need to reset out pointer so the functions above us can re-read
// them (they expect to)...
m_lpInputFile->ResetBufferPointerToStart();
return true;
}
// We need to enumerate device drivers on this system
bool CModules::GetModulesDataFromDeviceDrivers()
{
LPVOID * lpImageBaseArray = NULL;
DWORD dwImageBaseArraySizeUsed, dwImageBaseArraySize, dwNumberOfDeviceDrivers, dwIndex;
TCHAR tszModulePath[_MAX_PATH];
CModuleInfo * lpModuleInfo = NULL;
bool fReturn = false, fNew = false;
// NOTE: In the documentation, the third parameter of
// EnumProcesses is named cbNeeded, which implies that you
// can call the function once to find out how much space to
// allocate for a buffer and again to fill the buffer.
// This is not the case. The cbNeeded parameter returns
// the number of PIDs returned, so if your buffer size is
// zero cbNeeded returns zero.
dwImageBaseArraySize = 256 * sizeof( LPVOID ) ;
do
{
if( lpImageBaseArray )
{ // Hmm.. we've been through this loop already, double the HeapSize and try again.
delete [] lpImageBaseArray;
dwImageBaseArraySize *= 2 ;
}
lpImageBaseArray = (LPVOID *) new DWORD[dwImageBaseArraySize];
if( lpImageBaseArray == NULL )
{
goto error_cleanup;
}
// Query the system for the total number of processes
if( !g_lpDelayLoad->EnumDeviceDrivers(lpImageBaseArray, dwImageBaseArraySize, &dwImageBaseArraySizeUsed ) )
{
// It's bad if we can't enum device drivers... no place to go but to bail out...
goto error_cleanup;
}
} while( dwImageBaseArraySizeUsed == dwImageBaseArraySize );
// How many DeviceDrivers did we get?
dwNumberOfDeviceDrivers = dwImageBaseArraySizeUsed / sizeof( LPVOID ) ;
// Loop through each Device Drivers
for(dwIndex = 0 ; dwIndex < dwNumberOfDeviceDrivers; dwIndex++ )
{
// Spin until we get a device driver filename!
if (!g_lpDelayLoad->GetDeviceDriverFileName(lpImageBaseArray[dwIndex], tszModulePath, _MAX_PATH))
continue;
CUtilityFunctions::UnMungePathIfNecessary(tszModulePath);
// For some reason, even though GetDeviceDriverFileName() is supposed to return the fullpath to the device
// driver... it don't always... sometimes it returns only the base file name...
CUtilityFunctions::FixupDeviceDriverPathIfNecessary(tszModulePath, _MAX_PATH);
if (!g_lpProgramOptions->fDoesModuleMatchOurSearch(tszModulePath))
continue;
// Okay, let's go ahead and get a ModuleInfo Object from our cache...
// If pfNew returns TRUE, then this object is new and we'll need
// to populate it with data...
lpModuleInfo = m_lpModuleInfoCache->AddNewModuleInfoObject(tszModulePath, &fNew);
if (false == fNew)
{
// We may have the object in the cache... now we need to
// save a pointer to this object in our Process Info list
AddNewModuleInfoObject(lpModuleInfo); // Just do our best...
continue; // We save having to get the module info again for this module...
}
// Not in the cache... so we need to init it, and get the module info...
if (!lpModuleInfo->Initialize(m_lpInputFile, m_lpOutputFile, NULL))
{
continue;
}
// Let's do it!! Populate the ModuleInfo object with data!!!!
if (!lpModuleInfo->GetModuleInfo(tszModulePath, false, 0, false))
{
// Well, we tried and failed...
continue;
}
// We may have the object in the cache... now we need to
// save a pointer to this object in our Process Info list
if (!AddNewModuleInfoObject(lpModuleInfo))
{ // Failure adding the node.... This is pretty serious...
continue;
}
}
fReturn = true;
goto cleanup;
error_cleanup:
cleanup:
if (lpImageBaseArray)
{
delete [] lpImageBaseArray;
}
return fReturn;
}