|
|
//+-------------------------------------------------------------------------
//
// 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; }
|