833 lines
23 KiB
C++
833 lines
23 KiB
C++
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
//
|
||
|
// Copyright (C) Microsoft Corporation, 1999 - 2000
|
||
|
//
|
||
|
// File: processes.cpp
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
// Processes.cpp: implementation of the CProcesses class.
|
||
|
//
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#ifndef NO_STRICT
|
||
|
#ifndef STRICT
|
||
|
#define STRICT 1
|
||
|
#endif
|
||
|
#endif /* NO_STRICT */
|
||
|
|
||
|
#include <WINDOWS.H>
|
||
|
#include <STDIO.H>
|
||
|
#include <TCHAR.H>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "Globals.h"
|
||
|
#include "Processes.h"
|
||
|
#include "ProcessInfo.h"
|
||
|
#include "ProcessInfoNode.h"
|
||
|
#include "FileData.h"
|
||
|
#include "UtilityFunctions.h"
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// Construction/Destruction
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
CProcesses::CProcesses()
|
||
|
{
|
||
|
m_fInitialized = false;
|
||
|
m_iNumberOfProcesses = 0;
|
||
|
|
||
|
m_enumProcessCollectionMethod = NO_METHOD;
|
||
|
|
||
|
// Contained Objects
|
||
|
m_lpProcessInfoHead = NULL;
|
||
|
m_ProcessInfoHeadMutex = NULL;
|
||
|
// m_lpProgramOptions = NULL;
|
||
|
m_lpModuleInfoCache = NULL;
|
||
|
m_lpOutputFile = NULL;
|
||
|
m_lpInputFile = NULL;
|
||
|
}
|
||
|
|
||
|
CProcesses::~CProcesses()
|
||
|
{
|
||
|
WaitForSingleObject(m_ProcessInfoHeadMutex, INFINITE);
|
||
|
|
||
|
// If we have Process Info Objects... nuke them now...
|
||
|
if (m_lpProcessInfoHead)
|
||
|
{
|
||
|
|
||
|
CProcessInfoNode * lpProcessInfoNodePointer = m_lpProcessInfoHead;
|
||
|
CProcessInfoNode * lpProcessInfoNodePointerToDelete = m_lpProcessInfoHead;
|
||
|
|
||
|
// Traverse the linked list to the end..
|
||
|
while (lpProcessInfoNodePointer)
|
||
|
{ // Keep looking for the end...
|
||
|
// Advance our pointer to the next node...
|
||
|
lpProcessInfoNodePointer = lpProcessInfoNodePointer->m_lpNextProcessInfoNode;
|
||
|
|
||
|
// Delete the one behind us...
|
||
|
delete lpProcessInfoNodePointerToDelete;
|
||
|
|
||
|
// Set the node to delete to the current...
|
||
|
lpProcessInfoNodePointerToDelete = lpProcessInfoNodePointer;
|
||
|
}
|
||
|
|
||
|
// Now, clear out the Head pointer...
|
||
|
|
||
|
m_lpProcessInfoHead = NULL;
|
||
|
}
|
||
|
|
||
|
// Be a good citizen and release the Mutex
|
||
|
ReleaseMutex(m_ProcessInfoHeadMutex);
|
||
|
|
||
|
// Now, close the Mutex
|
||
|
if (m_ProcessInfoHeadMutex)
|
||
|
{
|
||
|
CloseHandle(m_ProcessInfoHeadMutex);
|
||
|
m_ProcessInfoHeadMutex = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//bool CProcesses::Initialize(CProgramOptions * lpProgramOptions, CModuleInfoCache * lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile)
|
||
|
bool CProcesses::Initialize(CModuleInfoCache * lpModuleInfoCache, CFileData * lpInputFile, CFileData * lpOutputFile)
|
||
|
{
|
||
|
// We need the following objects to do business...
|
||
|
// if ( lpProgramOptions == NULL || lpModuleInfoCache == NULL)
|
||
|
if ( lpModuleInfoCache == NULL)
|
||
|
return false;
|
||
|
|
||
|
// Let's save away our program options (beats passing it as an
|
||
|
// argument to every method...)
|
||
|
// m_lpProgramOptions = lpProgramOptions;
|
||
|
m_lpInputFile = lpInputFile;
|
||
|
m_lpOutputFile = lpOutputFile;
|
||
|
m_lpModuleInfoCache = lpModuleInfoCache;
|
||
|
|
||
|
m_ProcessInfoHeadMutex = CreateMutex(NULL, FALSE, NULL);
|
||
|
|
||
|
if (m_ProcessInfoHeadMutex == NULL)
|
||
|
return false;
|
||
|
|
||
|
// We only need to grab these exported functions if we intend to
|
||
|
// actively query our local machine's processes directly...
|
||
|
if (g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesFromLiveSystemMode))
|
||
|
{
|
||
|
// PSAPI.DLL API's ARE NOW PREFERRED!!
|
||
|
// It doesn't tend to hang when enumerating modules for a process that is being debugged.
|
||
|
// The Toolhelp32 APIs seem to hang occasionally taking a snapshot of a process being debugged
|
||
|
// and this impacts Exception Monitor (which runs from a script against a process under
|
||
|
// windbg)
|
||
|
|
||
|
if ( g_lpProgramOptions->IsRunningWindowsNT() )
|
||
|
{
|
||
|
// Get the functions for Windows NT 4.0/2000
|
||
|
|
||
|
// Load library and get the procedures explicitly. We do
|
||
|
// this so that we don't have to worry about modules using
|
||
|
// this code failing to load under Windows 95, because
|
||
|
// it can't resolve references to the PSAPI.DLL.
|
||
|
|
||
|
if (g_lpDelayLoad->Initialize_PSAPI())
|
||
|
{
|
||
|
m_enumProcessCollectionMethod = PSAPI_METHOD;
|
||
|
} else
|
||
|
{
|
||
|
_tprintf(TEXT("Unable to load PSAPI.DLL, which may be required for enumeration of processes.\n"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_enumProcessCollectionMethod == NO_METHOD )
|
||
|
{
|
||
|
if (g_lpDelayLoad->Initialize_TOOLHELP32())
|
||
|
{
|
||
|
m_enumProcessCollectionMethod = TOOLHELP32_METHOD;
|
||
|
} else
|
||
|
{
|
||
|
_tprintf(TEXT("KERNEL32.DLL is missing required function entry points!!\n"));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// On Windows NT, we need to enable SeDebugPrivilege to open some processes...
|
||
|
if ( ( m_enumProcessCollectionMethod != NO_METHOD ) &&
|
||
|
g_lpProgramOptions->IsRunningWindowsNT() )
|
||
|
{
|
||
|
HANDLE hOurProcessToken = 0;
|
||
|
bool fPrivilegeSet = false;
|
||
|
|
||
|
// To permit as much access to obtain a process handle as possible,
|
||
|
// we need to set the SeDebugPrivilege on our process handle, we can
|
||
|
// then open nearly any process...
|
||
|
|
||
|
if(OpenProcessToken( GetCurrentProcess(),
|
||
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
||
|
&hOurProcessToken))
|
||
|
{
|
||
|
// We got our Process Token...
|
||
|
|
||
|
if(SetPrivilege(hOurProcessToken, SE_DEBUG_NAME, TRUE))
|
||
|
{
|
||
|
fPrivilegeSet = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!fPrivilegeSet)
|
||
|
{
|
||
|
_tprintf(TEXT("\nWARNING: A required privilege (SeDebugPrivilege) is not held by the user\n"));
|
||
|
_tprintf(TEXT("running this program. Due to security, some processes running on this\n"));
|
||
|
_tprintf(TEXT("system may not be accessible. An administrator of this machine can grant\n"));
|
||
|
_tprintf(TEXT("you this privilege by using User Manager to enable the advanced User Right\n"));
|
||
|
_tprintf(TEXT("\"Debug Programs\" to enable complete access to this system.\n"));
|
||
|
}
|
||
|
|
||
|
if (hOurProcessToken)
|
||
|
CloseHandle(hOurProcessToken);
|
||
|
}
|
||
|
|
||
|
// We are initialized if we were able to enable a Process Collection Method
|
||
|
m_fInitialized = ( m_enumProcessCollectionMethod != NO_METHOD );
|
||
|
|
||
|
} else
|
||
|
{
|
||
|
m_fInitialized = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
return m_fInitialized;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::GetProcessesDataForRunningProcessesUsingPSAPI()
|
||
|
{
|
||
|
LPDWORD lpdwPIDs = NULL;
|
||
|
DWORD dwProcessIDHeapSizeUsed, dwProcessIDHeapSize, dwIndex ;
|
||
|
CProcessInfo * lpProcessInfo = NULL;
|
||
|
bool fRetval = false;
|
||
|
|
||
|
if (!m_fInitialized)
|
||
|
return false;
|
||
|
|
||
|
// It's possible the user provided a PID directly... if so,
|
||
|
// we can circumvent the whole search of PIDs on the system...
|
||
|
if (g_lpProgramOptions->GetProcessID())
|
||
|
{
|
||
|
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
|
||
|
lpProcessInfo = new CProcessInfo();
|
||
|
if (lpProcessInfo == NULL)
|
||
|
goto error_cleanup;
|
||
|
|
||
|
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
|
||
|
{
|
||
|
goto error_cleanup;
|
||
|
}
|
||
|
|
||
|
if (lpProcessInfo->EnumerateModules(g_lpProgramOptions->GetProcessID(), this, NULL))
|
||
|
{
|
||
|
// Success... add this to the Processes Object...
|
||
|
if (!AddNewProcessInfoObject(lpProcessInfo))
|
||
|
{ // Failure adding the node...
|
||
|
goto error_cleanup; // For now, let's just error on out...
|
||
|
}
|
||
|
|
||
|
} else
|
||
|
{
|
||
|
// Failure enumerating modules on the only PID of interest... very bad...
|
||
|
goto error_cleanup;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{ // Nope, we brute force this baby...
|
||
|
|
||
|
// Call the PSAPI function EnumProcesses to get all of the
|
||
|
// ProcID's currently in the system.
|
||
|
|
||
|
// 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.
|
||
|
|
||
|
// NOTE: The loop here ensures that we
|
||
|
// actually allocate a buffer large enough for all the
|
||
|
// PIDs in the system.
|
||
|
dwProcessIDHeapSize = 256 * sizeof( DWORD ) ;
|
||
|
lpdwPIDs = NULL ;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if( lpdwPIDs )
|
||
|
{ // Hmm.. we've been through this loop already, double the HeapSize and try again.
|
||
|
delete [] lpdwPIDs;
|
||
|
dwProcessIDHeapSize *= 2 ;
|
||
|
}
|
||
|
|
||
|
lpdwPIDs = (LPDWORD) new DWORD[dwProcessIDHeapSize];
|
||
|
|
||
|
if( lpdwPIDs == NULL )
|
||
|
{
|
||
|
goto error_cleanup;
|
||
|
}
|
||
|
|
||
|
// Query the system for the total number of processes
|
||
|
if( !g_lpDelayLoad->EnumProcesses( lpdwPIDs, dwProcessIDHeapSize, &dwProcessIDHeapSizeUsed ) )
|
||
|
{
|
||
|
// It's bad if we can't enum processes... no place to go but to bail out...
|
||
|
goto error_cleanup;
|
||
|
}
|
||
|
} while( dwProcessIDHeapSizeUsed == dwProcessIDHeapSize );
|
||
|
|
||
|
// How many ProcID's did we get?
|
||
|
DWORD dwNumberOfPIDs = dwProcessIDHeapSizeUsed / sizeof( DWORD ) ;
|
||
|
|
||
|
// Loop through each ProcID.
|
||
|
for( dwIndex = 0 ; dwIndex < dwNumberOfPIDs; dwIndex++ )
|
||
|
{
|
||
|
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
|
||
|
// Each Process gets its own
|
||
|
lpProcessInfo = new CProcessInfo();
|
||
|
if (lpProcessInfo == NULL)
|
||
|
goto error_cleanup;
|
||
|
|
||
|
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
|
||
|
{ // Failure initializing the ProcessInfo object?!?
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (lpProcessInfo->EnumerateModules(lpdwPIDs[dwIndex], this, NULL))
|
||
|
{
|
||
|
// Success... add this to the Processes Object...
|
||
|
if (!AddNewProcessInfoObject(lpProcessInfo))
|
||
|
{ // Failure adding the node...
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
// For now, let's error out...
|
||
|
|
||
|
} else
|
||
|
{
|
||
|
// An error enumerating modules might be normal...
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fRetval = true;
|
||
|
goto cleanup;
|
||
|
|
||
|
error_cleanup:
|
||
|
if (lpProcessInfo)
|
||
|
delete lpProcessInfo;
|
||
|
|
||
|
cleanup:
|
||
|
if (lpdwPIDs)
|
||
|
{
|
||
|
delete [] lpdwPIDs;
|
||
|
}
|
||
|
|
||
|
return fRetval;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::AddNewProcessInfoObject(CProcessInfo * lpProcessInfo)
|
||
|
{
|
||
|
if (!m_fInitialized)
|
||
|
return false;
|
||
|
|
||
|
// First, create a ProcessInfoNode object and then attach it to the bottom of the
|
||
|
// linked list of nodes...
|
||
|
CProcessInfoNode * lpProcessInfoNode = new CProcessInfoNode(lpProcessInfo);
|
||
|
/*
|
||
|
#ifdef _DEBUG
|
||
|
_tprintf(TEXT("Adding Process Info Object for [%s]\n"), lpProcessInfo->m_tszProcessName);
|
||
|
#endif
|
||
|
*/
|
||
|
if (lpProcessInfoNode == NULL)
|
||
|
return false; // Couldn't allocate memory..
|
||
|
|
||
|
// Acquire Mutex object to protect the linked-list...
|
||
|
WaitForSingleObject(m_ProcessInfoHeadMutex, INFINITE);
|
||
|
|
||
|
CProcessInfoNode * lpProcessInfoNodePointer = m_lpProcessInfoHead;
|
||
|
|
||
|
if (lpProcessInfoNodePointer) {
|
||
|
|
||
|
// Traverse the linked list to the end..
|
||
|
while (lpProcessInfoNodePointer->m_lpNextProcessInfoNode)
|
||
|
{ // Keep looking for the end...
|
||
|
lpProcessInfoNodePointer = lpProcessInfoNodePointer->m_lpNextProcessInfoNode;
|
||
|
}
|
||
|
|
||
|
lpProcessInfoNodePointer->m_lpNextProcessInfoNode = lpProcessInfoNode;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{ // First time through, the Process Info Head pointer is null...
|
||
|
m_lpProcessInfoHead = lpProcessInfoNode;
|
||
|
}
|
||
|
|
||
|
// Be a good citizen and release the Mutex
|
||
|
ReleaseMutex(m_ProcessInfoHeadMutex);
|
||
|
|
||
|
InterlockedIncrement(&m_iNumberOfProcesses);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::SetPrivilege(HANDLE hToken, LPCTSTR Privilege, bool bEnablePrivilege)
|
||
|
{
|
||
|
TOKEN_PRIVILEGES tp;
|
||
|
LUID luid;
|
||
|
TOKEN_PRIVILEGES tpPrevious = {0};
|
||
|
DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
|
||
|
|
||
|
if(!LookupPrivilegeValue( NULL, Privilege, &luid )) return false;
|
||
|
|
||
|
//
|
||
|
// first pass. get current privilege setting
|
||
|
//
|
||
|
tp.PrivilegeCount = 1;
|
||
|
tp.Privileges[0].Luid = luid;
|
||
|
tp.Privileges[0].Attributes = 0;
|
||
|
|
||
|
AdjustTokenPrivileges(
|
||
|
hToken,
|
||
|
FALSE,
|
||
|
&tp,
|
||
|
sizeof(TOKEN_PRIVILEGES),
|
||
|
&tpPrevious,
|
||
|
&cbPrevious
|
||
|
);
|
||
|
|
||
|
if (GetLastError() != ERROR_SUCCESS) return false;
|
||
|
|
||
|
//
|
||
|
// second pass. set privilege based on previous setting
|
||
|
//
|
||
|
tpPrevious.PrivilegeCount = 1;
|
||
|
tpPrevious.Privileges[0].Luid = luid;
|
||
|
|
||
|
if(bEnablePrivilege) {
|
||
|
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
|
||
|
}
|
||
|
else {
|
||
|
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
|
||
|
tpPrevious.Privileges[0].Attributes);
|
||
|
}
|
||
|
|
||
|
AdjustTokenPrivileges(
|
||
|
hToken,
|
||
|
FALSE,
|
||
|
&tpPrevious,
|
||
|
cbPrevious,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if (GetLastError() != ERROR_SUCCESS) return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::OutputProcessesData(CollectionTypes enumCollectionType, bool fCSVFileContext, bool fDumpHeader)
|
||
|
{
|
||
|
// Output to file?
|
||
|
if ( !g_lpProgramOptions->GetMode(CProgramOptions::QuietMode) &&
|
||
|
!g_lpProgramOptions->GetMode(CProgramOptions::PrintTaskListMode) )
|
||
|
{
|
||
|
// Output to Stdout?
|
||
|
if (!OutputProcessesDataToStdout(enumCollectionType, fCSVFileContext, fDumpHeader))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Output to file?
|
||
|
if (g_lpProgramOptions->GetMode(CProgramOptions::OutputCSVFileMode))
|
||
|
{
|
||
|
// Try and output to file...
|
||
|
if (!OutputProcessesDataToFile(enumCollectionType, fDumpHeader))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (m_lpProcessInfoHead) {
|
||
|
CProcessInfoNode * lpCurrentProcessInfoNode = m_lpProcessInfoHead;
|
||
|
|
||
|
while (lpCurrentProcessInfoNode)
|
||
|
{
|
||
|
// We have a node... print out Process Info for it, then the Modules Data...
|
||
|
if (lpCurrentProcessInfoNode->m_lpProcessInfo)
|
||
|
{
|
||
|
lpCurrentProcessInfoNode->m_lpProcessInfo->OutputProcessData(enumCollectionType, fCSVFileContext, false);
|
||
|
}
|
||
|
|
||
|
lpCurrentProcessInfoNode = lpCurrentProcessInfoNode->m_lpNextProcessInfoNode;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::OutputProcessesDataToStdout(CollectionTypes enumCollectionType, bool fCSVFileContext, bool fDumpHeader)
|
||
|
{
|
||
|
if (fDumpHeader)
|
||
|
{
|
||
|
// Output to stdout...
|
||
|
_tprintf(TEXT("\n"));
|
||
|
CUtilityFunctions::OutputLineOfStars();
|
||
|
_tprintf(TEXT("%s - Printing Process Information for %d Processes.\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, m_iNumberOfProcesses);
|
||
|
_tprintf(TEXT("%s - Context: %s\n"), g_tszCollectionArray[enumCollectionType].tszCSVLabel, fCSVFileContext ? g_tszCollectionArray[enumCollectionType].tszCSVContext : g_tszCollectionArray[enumCollectionType].tszLocalContext);
|
||
|
CUtilityFunctions::OutputLineOfStars();
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::OutputProcessesDataToFile(CollectionTypes enumCollectionType, bool fDumpHeader)
|
||
|
{
|
||
|
// Don't write anything if there are no processes to report...
|
||
|
if (0 == m_iNumberOfProcesses)
|
||
|
return true;
|
||
|
|
||
|
if (fDumpHeader)
|
||
|
{
|
||
|
// We skip output of the [PROCESSES] header if -E was specified...
|
||
|
if (!g_lpProgramOptions->GetMode(CProgramOptions::ExceptionMonitorMode))
|
||
|
{
|
||
|
// Write out the Processes 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We have different output for -E
|
||
|
if (g_lpProgramOptions->GetMode(CProgramOptions::ExceptionMonitorMode))
|
||
|
{
|
||
|
// Write out the header... for the -E option...
|
||
|
if (!m_lpOutputFile->WriteString(TEXT("Module Path,Symbol Status,Time/Date String,File Version,Company Name,File Description,File Time/Date String,Local DBG Status,Local DBG,Local PDB Status,Local PDB\r\n")))
|
||
|
{
|
||
|
_tprintf(TEXT("Failure writing CSV header to file [%s]!"), m_lpOutputFile->GetFilePath());
|
||
|
m_lpOutputFile->PrintLastError();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
} else
|
||
|
{
|
||
|
// Write out the Processes 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 CProcesses::GetProcessesData()
|
||
|
{
|
||
|
// Is this being collected interactively?
|
||
|
if (g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesFromLiveSystemMode))
|
||
|
{
|
||
|
// Invoke the correct Process Collection Method
|
||
|
if (GetProcessCollectionMethod() == TOOLHELP32_METHOD)
|
||
|
{
|
||
|
GetProcessesDataForRunningProcessesUsingTOOLHELP32();
|
||
|
}
|
||
|
else if (GetProcessCollectionMethod() == PSAPI_METHOD)
|
||
|
{
|
||
|
GetProcessesDataForRunningProcessesUsingPSAPI();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Is this being collected from a file?
|
||
|
if (g_lpProgramOptions->GetMode(CProgramOptions::InputCSVFileMode))
|
||
|
GetProcessesDataFromFile();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::GetProcessesDataFromFile()
|
||
|
{
|
||
|
CProcessInfo * lpProcessInfo = NULL;
|
||
|
|
||
|
char szProcessNameToMatch[_MAX_FNAME+1];
|
||
|
|
||
|
// In case we're matching against these...
|
||
|
LPTSTR tszProcessNameToMatch = g_lpProgramOptions->GetProcessName();
|
||
|
DWORD dwProcessIDToMatch = g_lpProgramOptions->GetProcessID();
|
||
|
|
||
|
// If we're going to be matching on a process name, go ahead and grab it
|
||
|
// once now, convert to ANSI if necessary (since it comes from the CSV
|
||
|
// file in ANSI), and upper case it...
|
||
|
if ( ( g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesWithMatchingNameOrPID) ) &&
|
||
|
( tszProcessNameToMatch )
|
||
|
)
|
||
|
{
|
||
|
|
||
|
// Let's save away the Process Name...
|
||
|
CUtilityFunctions::CopyTSTRStringToAnsi(tszProcessNameToMatch, szProcessNameToMatch, _MAX_FNAME+1);
|
||
|
|
||
|
// Upper case the process name... we should be ready to match on this now...
|
||
|
_strupr(szProcessNameToMatch);
|
||
|
}
|
||
|
|
||
|
// Read the Process Header Line
|
||
|
if (!m_lpInputFile->ReadFileLine())
|
||
|
return false;
|
||
|
|
||
|
// Currently, we don't actually read the data...
|
||
|
|
||
|
enum { BUFFER_SIZE = 128};
|
||
|
char szProcessName[BUFFER_SIZE];
|
||
|
|
||
|
TCHAR tszProcessName[BUFFER_SIZE];
|
||
|
|
||
|
DWORD iProcessID;
|
||
|
|
||
|
// Read the first field (should be blank, unless this is a new collection type
|
||
|
if (m_lpInputFile->ReadString())
|
||
|
return true;
|
||
|
|
||
|
bool fReturn = true;
|
||
|
while (fReturn == true)
|
||
|
{
|
||
|
// Read the process name...
|
||
|
if (0 == m_lpInputFile->ReadString(szProcessName, BUFFER_SIZE))
|
||
|
break;
|
||
|
|
||
|
if (!m_lpInputFile->ReadDWORD(&iProcessID))
|
||
|
{
|
||
|
fReturn = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( g_lpProgramOptions->GetMode(CProgramOptions::InputProcessesWithMatchingNameOrPID) )
|
||
|
{
|
||
|
// Okay, the user has provided us something to match against our data...
|
||
|
if ( tszProcessNameToMatch )
|
||
|
{
|
||
|
// Process name provided... does it match?
|
||
|
if ( strcmp(szProcessNameToMatch, szProcessName) )
|
||
|
{
|
||
|
// Nope... well then, we should nuke this line...
|
||
|
m_lpInputFile->ReadFileLine();
|
||
|
|
||
|
// Then, jump to the next line processing...
|
||
|
goto ReadNewLine;
|
||
|
}
|
||
|
#ifdef _DEBUG
|
||
|
else
|
||
|
{
|
||
|
printf("DEBUG: MATCH FOUND ON Process Name [%s]\n", szProcessName);
|
||
|
}
|
||
|
#endif
|
||
|
} else
|
||
|
{
|
||
|
if ( dwProcessIDToMatch != iProcessID )
|
||
|
{
|
||
|
// Nope... well then, we should nuke this line...
|
||
|
m_lpInputFile->ReadFileLine();
|
||
|
|
||
|
// Then, jump to the next line processing...
|
||
|
goto ReadNewLine;
|
||
|
}
|
||
|
#ifdef _DEBUG
|
||
|
else
|
||
|
{
|
||
|
_tprintf(TEXT("DEBUG: MATCH FOUND ON Process ID [%d]\n"), iProcessID);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
|
||
|
// Each Process gets its own
|
||
|
lpProcessInfo = new CProcessInfo();
|
||
|
|
||
|
if (lpProcessInfo == NULL)
|
||
|
{
|
||
|
fReturn = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, m_lpInputFile, m_lpOutputFile, NULL))
|
||
|
{ // Failure initializing the ProcessInfo object?!?
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
fReturn = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// We need to convert this to Unicode possibly... (it will be copied in EnumModules())
|
||
|
CUtilityFunctions::CopyAnsiStringToTSTR(szProcessName, tszProcessName, BUFFER_SIZE);
|
||
|
|
||
|
// Save the process name...
|
||
|
lpProcessInfo->SetProcessName(tszProcessName);
|
||
|
|
||
|
// Enumerate the modules for the process
|
||
|
if (!lpProcessInfo->EnumerateModules(iProcessID, this, tszProcessName))
|
||
|
{
|
||
|
fReturn = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Success... add this to the Processes Object...
|
||
|
if (!AddNewProcessInfoObject(lpProcessInfo))
|
||
|
{ // Failure adding the node...
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ReadNewLine:
|
||
|
|
||
|
// Before we read a new line... are we already pointing to the end?
|
||
|
if (m_lpInputFile->EndOfFile())
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Read the first field (should be blank, unless this is a new collection type
|
||
|
if (m_lpInputFile->ReadString())
|
||
|
break;
|
||
|
}
|
||
|
// We don't expect to find anything...
|
||
|
|
||
|
return fReturn;
|
||
|
}
|
||
|
|
||
|
CProcesses::ProcessCollectionMethod CProcesses::GetProcessCollectionMethod()
|
||
|
{
|
||
|
return m_enumProcessCollectionMethod;
|
||
|
}
|
||
|
|
||
|
bool CProcesses::GetProcessesDataForRunningProcessesUsingTOOLHELP32()
|
||
|
{
|
||
|
CProcessInfo * lpProcessInfo = NULL;
|
||
|
HANDLE hSnapShot = NULL;
|
||
|
bool fReturn = false;
|
||
|
|
||
|
if (!m_fInitialized)
|
||
|
return false;
|
||
|
|
||
|
// It's possible the user provided a PID directly... if so,
|
||
|
// we can circumvent the whole search of PIDs on the system...
|
||
|
if (g_lpProgramOptions->GetProcessID())
|
||
|
{
|
||
|
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
|
||
|
|
||
|
lpProcessInfo = new CProcessInfo();
|
||
|
if (lpProcessInfo == NULL)
|
||
|
goto error_cleanup;
|
||
|
|
||
|
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
|
||
|
{
|
||
|
goto error_cleanup;
|
||
|
}
|
||
|
|
||
|
if (lpProcessInfo->EnumerateModules(g_lpProgramOptions->GetProcessID(), this, NULL))
|
||
|
{
|
||
|
// Success... add this to the Processes Object...
|
||
|
if (!AddNewProcessInfoObject(lpProcessInfo))
|
||
|
{ // Failure adding the node...
|
||
|
goto error_cleanup; // For now, let's just error on out...
|
||
|
}
|
||
|
|
||
|
} else
|
||
|
{
|
||
|
// Failure enumerating modules on the only PID of interest... very bad...
|
||
|
goto error_cleanup;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PROCESSENTRY32 procentry;
|
||
|
BOOL bFlag;
|
||
|
|
||
|
// Get a handle to a Toolhelp snapshot of the systems processes.
|
||
|
hSnapShot = g_lpDelayLoad->CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||
|
|
||
|
if( hSnapShot == INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
goto error_cleanup ;
|
||
|
}
|
||
|
|
||
|
// Clear this structure
|
||
|
memset(&procentry, 0, sizeof(procentry));
|
||
|
|
||
|
// Get the first process' information.
|
||
|
procentry.dwSize = sizeof(PROCESSENTRY32) ;
|
||
|
bFlag = g_lpDelayLoad->Process32First( hSnapShot, &procentry ) ;
|
||
|
|
||
|
// While there are processes, keep looping.
|
||
|
while( bFlag )
|
||
|
{
|
||
|
// Okay, let's create a ProcessInfo object and pass this down to EnumerateModules()
|
||
|
// Each Process gets its own
|
||
|
lpProcessInfo = new CProcessInfo();
|
||
|
if (lpProcessInfo == NULL)
|
||
|
goto error_cleanup;
|
||
|
|
||
|
if (!lpProcessInfo->Initialize(m_lpModuleInfoCache, NULL, m_lpOutputFile, NULL))
|
||
|
{
|
||
|
// Failure initializing the ProcessInfo object?!?
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
|
||
|
// Clear this structure
|
||
|
memset(&procentry, 0, sizeof(procentry));
|
||
|
|
||
|
// Get the next Process...
|
||
|
procentry.dwSize = sizeof(PROCESSENTRY32) ;
|
||
|
bFlag = g_lpDelayLoad->Process32Next( hSnapShot, &procentry );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Enumerate the modules for this process...
|
||
|
if (lpProcessInfo->EnumerateModules(procentry.th32ProcessID, this, procentry.szExeFile))
|
||
|
{
|
||
|
// Success... add this to the Processes Object...
|
||
|
if (!AddNewProcessInfoObject(lpProcessInfo))
|
||
|
{
|
||
|
// Failure adding the node...
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
}
|
||
|
|
||
|
} else
|
||
|
{
|
||
|
// An error enumerating modules might be normal...
|
||
|
delete lpProcessInfo;
|
||
|
lpProcessInfo = NULL;
|
||
|
}
|
||
|
|
||
|
// Get the next Process...
|
||
|
procentry.dwSize = sizeof(PROCESSENTRY32) ;
|
||
|
bFlag = g_lpDelayLoad->Process32Next( hSnapShot, &procentry );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fReturn = true;
|
||
|
goto cleanup;
|
||
|
|
||
|
error_cleanup:
|
||
|
if (lpProcessInfo)
|
||
|
delete lpProcessInfo;
|
||
|
|
||
|
cleanup:
|
||
|
if (hSnapShot != INVALID_HANDLE_VALUE)
|
||
|
CloseHandle(hSnapShot);
|
||
|
|
||
|
return fReturn;
|
||
|
}
|