Download ProcViewer.zip - 195.9 KB
Download Exe ProcessViewer.zip - 118.1 KB
Options Dialog
Privilege Manager Dialog
Introduction
A utility application that lists out all the processes that are running in a system along with dll's loaded by them and also displays path of dll's loaded, their load addresses, dll base, image size and per process information.
Features of this tool
- Display's all loaded dll's of a process
- Symbols in each dll, you click on any of them
- Version of each dll
- Description of dll
- Company name
- View each dll with dependency walker if dependency viewing is enabled
- Full module path, i.e. from where is it loaded?
- Load address
- Entry point
- Image size
- Load order
- Display's all drivers loaded in the system if you click on main kernel process
- Process id's
- Process owner
- Process command line
- Process auto startup information
- Type of application, i.e. whether application is a console, windows or ms-dos app.
- Process priority
- GUI resource usage statistics ( Will be expanded in future version )
- Size of each process
- Symbols in each process
- Window details if any
- Displays windows in it's proper hierarchy i.e. all child windows of a parent window come under it's node.
- Text or caption
- HWND
- Window proc
- Icon if any
- Cursor
- Class of window, i.e Toolbarwindow32 for a toolbar.
- Thread that owns the window
- State of window, i.e Maximized, minimized, restored, or invisible
- Bounds of window
- ID of window
- Parent window HWND
- Class style of window
- Window styles
- All child windows for a window displayed hierarchically
- Process times
- Creation time
- Exit time, i.e. kill a process and then you this time get's updated
- Kernel mode time, how much of kernel time is used
- User mode time
- File times
- Creation time
- Last accessed on
- Last written on
- IO Counters
- Input and output counter values
- Memory details, page fault count etc
- Version of process, you get some information also as a tooltip
- Privileges of a process along with a privilege manager
- Sorting of view
- Switching of layout
- Kill a process and kill all instances of a process
- Search for a process or for a dll/driver loaded
- Options dialog for switching off unwanted details
- You have an about box. ;)
- And an exit button too. ;))
Using the code
I used PSAPI
functions provided by microsoft
to enumerate the modules loaded by a process.
The API's used are
EnumProcesses
-- To get all processes running in the system EnumProcessModules
-- To get all modules/dlls loaded by a process GetModuleBaseName
-- To get the base name of module... for eg: mydll.dll instead of C:\AnyLongPath\mydll.dll. GetModuleFileNameEx
-- To get the long path of a module GetModuleInformation
-- Retrieves information pertaining to a module GetProcessMemoryInfo
-- To get memory details of a process. EnumDeviceDrivers
-- Process with id 4(System) is the process which loads up the drivers, so we use this function to enumerate through these device drivers GetDeviceDriverBaseName --
To get base name of a device driver GetDeviceDriverFileName
-- To get full path of a device driver GetProcessTimes
-- To get the amount of time each process has taken, when did it start, how much kernel mode, and user mode time has it taken. SearchPath
-- Searches PATH
environment variable. Docs give a detailed description of the searching process that takes place. GetFileVersionInfo
-- For extracting version. GetFileVersionInfoSize
-- Size of version VerQueryValue
-- Query for version components. There is a class called FileVersionInfo
written for this purpose. Lookup that class for more information on these version functions and how to use them.
This is the main function...
void GetProcessDetails()
{
ClearItemData();
m_ctvProcess.DeleteAllItems();
m_clvProcessDetails.DeleteAllItems();
#define MAX 10000
DWORD dwSize = 0;
DWORD dwProcIdentifiers[MAX] = { 0 };
EnumProcesses( dwProcIdentifiers, sizeof( dwProcIdentifiers ), &dwSize );
if( !dwSize )
{
return;
}
dwSize /= sizeof( DWORD );
HTREEITEM hItemRoot = m_ctvProcess.InsertItem( _T( "Process list" ), 60, 60 );
TCHAR szName[MAX_PATH] = { 0 };
for( DWORD dwIndex = 0; dwIndex < dwSize; ++ dwIndex )
{
HANDLE hProcModule =
OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
dwProcIdentifiers[dwIndex] );
if( !hProcModule )
{
continue;
}
DWORD dwModuleSize = 0;
HMODULE hModules[MAX] = { 0 };
EnumProcessModules( hProcModule,
hModules,
sizeof( hModules ),
&dwModuleSize );
dwModuleSize /= sizeof( HMODULE );
if( !dwModuleSize )
{
continue;
}
PPROC_INFO_t pstProcInfo = new PROC_INFO_t;
if( !pstProcInfo )
{
return;
}
pstProcInfo->dwProcId = dwProcIdentifiers[dwIndex];
GetModuleFileNameEx( hProcModule, hModules[0], szName, MAX_PATH );
pstProcInfo->csFullPath = szName;
GetModuleBaseName( hProcModule, hModules[0], szName, MAX_PATH );
pstProcInfo->csBaseName = szName;
const BOOL bFullPathChecked =
m_ToolBar.GetToolBarCtrl().IsButtonChecked( ID_OPTIONS_SHOWPATH );
CString csProc;
csProc.Format( _T( "%s, PID: %lu" ),
SCAST( LPCTSTR ,
( bFullPathChecked ?
pstProcInfo->csFullPath : pstProcInfo->csBaseName )),
dwProcIdentifiers[dwIndex] );
HTREEITEM hItem = m_ctvProcess.InsertItem( csProc, 20, 20, hItemRoot );
m_ctvProcess.SetItemData( hItem, RCAST( DWORD, pstProcInfo ));
PROCESS_MEMORY_COUNTERS& pmcProcMemCounter = pstProcInfo->stpmcMemCounters;
GetProcessMemoryInfo( hProcModule,
&pmcProcMemCounter,
sizeof( pmcProcMemCounter ));
HTREEITEM hPInfoItem = m_ctvProcess.InsertItem( _T( "Details" ),
7, 7, hItem );
CString csTempStr;
const int nIconIndex = 63;
csTempStr.Format( _T( "Page fault count: %lu" ),
pmcProcMemCounter.PageFaultCount );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Page file usage: %.02lf KB" ),
pmcProcMemCounter.PagefileUsage/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Peak page file usage: %.02lf KB" ),
pmcProcMemCounter.PeakPagefileUsage/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Peak working set size: %.02lf KB" ),
pmcProcMemCounter.PeakWorkingSetSize/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Quota non paged pool size: %.02lf KB" ),
pmcProcMemCounter.QuotaNonPagedPoolUsage/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Quota paged pool size: %.02lf KB" ),
pmcProcMemCounter.QuotaPagedPoolUsage/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Quota peak non paged pool size: %.02lf KB" ),
pmcProcMemCounter.QuotaPeakNonPagedPoolUsage/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Quota peak paged pool size: %.02lf KB" ),
pmcProcMemCounter.QuotaPeakPagedPoolUsage/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
csTempStr.Format( _T( "Working set size: %.02lf KB" ),
pmcProcMemCounter.WorkingSetSize/1024.0f );
m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
PMODULEINFO_LIST_t* ppModuleInfo = &pstProcInfo->pstModuleInfoList;
for( DWORD dwModIndex = 1; dwModIndex < dwModuleSize; ++ dwModIndex )
{
*ppModuleInfo = new MODULEINFO_LIST_t;;
if( !*ppModuleInfo )
{
continue;
}
GetModuleInformation( hProcModule,
hModules[dwModIndex],
&( *ppModuleInfo )->stmiModInfo,
sizeof( MODULEINFO ));
GetModuleFileNameEx( hProcModule,
hModules[dwModIndex],
szName,
MAX_PATH );
( *ppModuleInfo )->csModuleFullPath = szName;
GetModuleBaseName( hProcModule,
hModules[dwModIndex],
szName,
MAX_PATH );
( *ppModuleInfo )->csModuleBaseName = szName;
ppModuleInfo = &( *ppModuleInfo )->pstNextModuleInfo;
}
CloseHandle( hProcModule );
}
m_ctvProcess.Expand( hItemRoot, TVE_EXPAND );
}
PSAPI Function usage
EnumProcesses:
DWORD dwProcIds[2048];
DWORD dwProcRunning = 0;
EnumProcesses( dwProcIds, sizeof( dwProcIds ) /sizeof( dwProcIds[0] ),
&dwNeeded );
dwProcRunning /= sizeof( DWORD );
EnumProcessModules:
HMODULE hModuleArray[2048];
DWORD dwModuleCount = 0;
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
dwAnyProcessId );
EnumProcessModules( hProcess, hModuleArray,
sizeof( hModuleArray )/ sizeof( hModuleArray[0] ),
&dwModuleCount );
dwModuleCount /= sizeof( HMODULE );
On return the first module in the array is the executable of the process. Rest of the modules will be actual modules loaded.
GetModuleBaseName:
Returns the base name of a module for eg: if module path is C:\MyPath\Averylongpath\Basename.dll
, then this API
will return Basename.dll
TCHAR szName[MAX_PATH] = { 0 }
GetModuleBaseName( hProcess, hModule, szName, MAX_PATH )
GetModuleFileNameEx:
Very similar to the above function except that it returns full path of the requested module.
GetModuleInformation and GetProcessMemoryInfo:
These two API's
are simple to use. Just takes a structure and returns information in them.
MODULEINFO miModInfo = { 0 };
GetModuleInformation( hProcModule, hModule, &miModInfo, sizeof( MODULEINFO ));
PROCESS_MEMORY_COUNTERS pmcProcMemCounter = { 0 };
GetProcessMemoryInfo( hProcModule,
&pmcProcMemCounter,
sizeof( pmcProcMemCounter ));
These are the API's
that I have used. Main code can be found in ProcessViewerDlg.cpp
and ProcessViewerDlg.h
EnumDeviceDrivers
Enumerates device drivers loaded by System process. Even though name of the process is shown as System
, it's real name is %systemroot%\System32\ntoskrnl.exe
.
const int nSize = 2048;
DWORD dwCount = 0;
LPVOID lpvImageBases[nSize] = { 0 };
EnumDeviceDrivers( lpvImageBases, nSize, &dwCount );
dwCount /= sizeof( LPVOID );
Note that first item in the array is name of the process, rest will be drivers.
GetDeviceDriverBaseName
Extracts base name of a device driver.
TCHAR szName[MAX_PATH] = { 0 };
GetDeviceDriverBaseName( lpvImageBases[0], szName, MAX_PATH );
GetDeviceDriverFileName
Extracts full path of device driver. This didn't work well for me though. Most of the path returned was of the form \Windows\System32\Some.sys etc or \??\C:\..\Some.sys. So I had to use SearchPath
API for finding out where exactly these device drivers are located, of course I modified the PATH
variable to force this API to look into Drivers folder too.
TCHAR szName[MAX_PATH] = { 0 };
GetDeviceDriverFileName( lpvImageBases[0], szName, MAX_PATH );
GetProcessTimes
Returns timing information for a specified process.
HANDLE hProcess;
FILETIME ftStartTime = { 0 },
ftExitTime = { 0 },
ftKernelTime = { 0 },
ftUserTime = { 0 }
GetProcessTimes( hProcess,
&ftStartTime,
&ftExitTime,
&ftKernelTime,
&ftUserTime );
You can directly use FILETIME,
but should be careful, except for start time and exit time, others won't work as expected (read the docs). You need to do some addtional work on Kernel time and user time.
Best way is to either call FileTimeToSystemTime
or put it into a COleDateTime
. DavidCrow has written a nice article on this topic. I recommend you read it.
SearchPath
Read the docs carefully for this function. It's a waste of time to describe the working of this API here, since it's well documented.
const int nAllocLength = MAX_PATH * 2;
TCHAR szPathBuffer[ nAllocLength ];
if( SearchPath( 0, _T( "Anyfile.anytype" ), 0<;/span>, nAllocLength, szPathBuffer, 0 ))
{
csFilePath_o = szPathBuffer;
return true;
}
ProcessPriority
To change priority of a process call SetPriorityClass, to retrieve priority of a process call GetPriorityClass.
The six classes of priority are as follows...
- IDLE_PRIORITY_CLASS
- BELOW_NORMAL_PRIORITY_CLASS
- NORMAL_PRIORITY_CLASS
- ABOVE_NORMAL_PRIORITY_CLASS
- HIGH_PRIORITY_CLASS
- REALTIME_PRIORITY_CLASS
Usage:
VERIFY( SetPriorityClass( hProcess, ABOVE_NORMAL_PRIORITY_CLASS ));
VERIFY( GetPriorityClass( hProcess ) != 0 );
Search
Process viewer supports searching of loaded modules. You can search for a particular process or a dll. Search does not support wildcard searching, just does an "StrStrI
" search. So if you search for "shell", hits will be "shell32.dll" "someshell.dll" "myShelldll.dll etc.
Press Ctrl + F to search.
Dependency viewing
Double click on any module or process to view it's dependency. You should have dependency walker installed on your system. This will happen only if dependency viewing is enabled. One of the toolbar buttons(fourth one) is for this purpose.
You can download dependency walker from here.
Full path
To view full path of a loaded process just enable full path by clicking on green tick button(third one) on toolbar.
Refresh
To refresh either click first button on toolbar or press F5.
Swap view
To switch from vertical view to horizontal view or vice versa press F6 or second button on toolbar.
Kill Process
Select a particular process to kill. Press F8 or click on the kill button. When a process is killed it's not removed from the process listing instead the process name is set to bold. Press F5/Refresh for removing killed process from tree. Mainly done for retaining snapshot for that process. Helps if there is some trojan running and you want to see a detailed information for that trojan without having the trojan running.
NOTE: Do not kill the kernel process, I tried and the whole system came down. :)
Options
Press F7 for displaying options dialog. You can set various options for process viewing. Options will include much more.
CSV Module listing and symbol listing
To save list of modules for a process press Ctrl + A to select all modules and then Ctrl + C. Things to remember...
- If cursor is on an item then that subitem(s) will be copied to clipboard. So to copy more than one sub item, first select required sub items then put your cursor on that sub item column to copy to clipboard.
- To copy entire contents take cursor out of list view.
- Open notepad and paste.
- Save as anyfilename.csv.
- Open with a CSV viewer like Excel.
The same applies to symbol listing.
View aspect saving
Saves your settings to an ini file. For eg: column order, divider pane size, main window size. This enables you to change order of list view to your liking. Even sizes of columns are saved.
Utils
There is a FileVersionInfo
class and DividerWnd
class which can reused. A WindowCollection
class for enumerating all open windows.
FileVersionInfo
extracts version information from a module. DividerWnd
is a simple lightweight splitter.
Acknowledgements
- The bitmap that you see in this application's treeview and listview, has been taken from Dominik's Keepass app.
- Hey Ralph thanks for those comments. I have implemented most of them, but a rewrite is pending (Reusability). Thanks once again.
- Thanks to DavidCrow for his article on Process times.
- Thanks to Todd C Wilson for his article on Aggressive optimizations and I am using his header file(I've commented out linker commands that merge sections).
- Thanks to CodeProject. Thanks to all those developers who helped me by posting those wonderful articles. If it was not for them then this wouldn't have happened. :)
History
- Created on 05/08/2007.
- Added "Search" on 05/14/2007.
- Added description of application on 05/16/2007.
- Updated application to show loaded device drivers, version information, size, GUI change.
- Added progress bar, display of icons related to a process, type of process(Console, Windows, MS-DOS) on 05/28/07.
- Added support for sorting, column re-ordering and saving this order on 05/31/2007.
- Added extraction of HWND's from running processes. Changed toolbar format. On 06/02/2007.
- Added HWND style listing, parent listing. On 06/05/2007.
- Made toolbar transparent, enabled window style, id viewing. On 06/07/2007.
- Disabled window style viewing due to some issues, fixing going on. On 06/08/2007.
- Added options dialog and various different tweaks. On 06/09/2007.
- Added kill process option, fixed some issues for saving settings. On 06/10/2007.
- Added process priority, and IO counters. On 06/13/2007.
- Now correctly displays kids of windows. On 06/18/2007.
- Added symbol listing for each loaded module of a process. On 06/25/2007.
- Added process privilege display. On 06/26/2007.
- Added process owner, process command line, and total window count display. On 10/14/2007.
- Added dynamic process priority updation support. On 10/24/07
- Added window icon, charset, enabled/disabled status to window details and added support for process priority boost. On 10/26/07
- Changed root node text to machine name and user name ;). On 11/2/2007
- Finds out whether a process is registered for auto startup. On 11/12/2007
- Re-Enabled window style viewing after some bug fixes. On 11/27/2007
- Fixed compilation errors in VS2005 (Note: You will get a manifest error in VS2005, so as a fix delete the manifest resource in the resource editor).
- Kill all instances of an application support added. Also added "Open parent folder", "Properties" support for processes.
- Added privilege manager which helps in dynamically enabling/disabling/removing privileges(Fine tuning needed though). On 1/Jan/2008(Happy new year!)
- Optimized a bit for speed!