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