|
|
/*++
Copyright (c) 1994-1998 Microsoft Corporation
Module Name:
tlist.c
Abstract:
This module implements a task list application.
Author:
Wesley Witt (wesw) 20-May-1994 Mike Sartain (mikesart) 28-Oct-1994 Added detailed task information Julian Jiggins (julianj) 19-Mar-1998 Added list processes using specific module feature Shaun Cox (shaunco) 9-Jul-1998 Display services running in processes
Environment:
User Mode
--*/
#include "pch.h"
#pragma hdrstop
#include <dbghelp.h>
#include "psapi.h"
#define BAD_PID ((DWORD)-1)
DWORD numTasks; TASK_LIST tlist[MAX_TASKS]; BOOL fShowServices; BOOL fShowMtsPackages;
const char *Blanks = " ";
VOID Usage(VOID); VOID PrintThreadInfo(PTASK_LIST pTaskList); BOOL FMatchTaskName(LPTSTR szPN, LPTSTR szWindowTitle, LPTSTR szProcessName); VOID GetFirstPidWithName(LPTSTR szTask);
VOID PrintTask( DWORD i ) { BOOL NameShown = FALSE; printf( "%4d %-16s", tlist[i].dwProcessId, tlist[i].ProcessName );
if (fShowServices && tlist[i].ServiceNames[0]) { printf( "Svcs: %s", tlist[i].ServiceNames); NameShown = TRUE; }
if (fShowMtsPackages && tlist[i].MtsPackageNames[0]) { printf( "%sMts: %s", NameShown ? " " : "", tlist[i].MtsPackageNames); NameShown = TRUE; }
if (!NameShown && tlist[i].hwnd) { if (fShowServices || fShowMtsPackages) { printf( "Title: %s", tlist[i].WindowTitle ); } else { printf( " %s", tlist[i].WindowTitle ); } }
printf( "\n" ); }
VOID PrintTaskTree( DWORD level, DWORD id ) { DWORD i;
DetectOrphans( tlist, numTasks ); for (i=0; i<numTasks; i++) { if (tlist[i].flags) { continue; }
// NOTE: The format of the output below should stay fixed forever. There are tools
// at MS that depend on it.
if (level == 0 || tlist[i].dwInheritedFromProcessId == id) { printf( "%.*s", level*2, Blanks ); printf( "%s (%d)", tlist[i].ProcessName, tlist[i].dwProcessId ); if (tlist[i].hwnd) { printf( " %s", tlist[i].WindowTitle ); } printf( "\n" ); tlist[i].flags = TRUE; if (tlist[i].dwProcessId != 0) { PrintTaskTree( level+1, tlist[i].dwProcessId ); } } } }
int __cdecl main( int argc, char *argv[] )
/*++
Routine Description:
Main entrypoint for the TLIST application. This app prints a task list to stdout. The task list include the process id, task name, ant the window title.
Arguments:
argc - argument count argv - array of pointers to arguments
Return Value:
0 - success
--*/
{ DWORD i; TASK_LIST_ENUM te; BOOL fTree; BOOL fFindTasksUsingModule; BOOL fPidOnly = FALSE; DWORD cchPN = 0; LPSTR szPN = NULL; DWORD dwPID = BAD_PID; DWORD dwNumServices = 0; LPENUM_SERVICE_STATUS_PROCESS pServiceInfo = NULL;
if (argc > 1 && (argv[1][0] == '-' || argv[1][0] == '/') && argv[1][1] == '?') { Usage(); }
fTree = FALSE; fFindTasksUsingModule = FALSE; if (argc > 1) { if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 't' || argv[1][1] == 'T')) { fTree = TRUE; } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 's' || argv[1][1] == 'S')) { fShowServices = TRUE; } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'k' || argv[1][1] == 'K')) { fShowMtsPackages = TRUE; } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'p' || argv[1][1] == 'P') && argc == 3) { _strlwr(argv[2]); if (!strcmp(argv[2], "system process")) { printf("0\n"); return 0; } fPidOnly = TRUE; } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'p' || argv[1][1] == 'P') && argc == 4) { _strlwr(argv[2]); if (!strcmp(argv[2], "system")) { _strlwr(argv[3]); if (!strcmp(argv[3], "process")) { printf("0\n"); return 0; } } Usage(); } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'm' || argv[1][1] == 'M') && argc == 3) { fFindTasksUsingModule = TRUE; } else { szPN = argv[1]; if (!(dwPID = atol(szPN)) && szPN[0] != '0' && szPN[1] != 0) { dwPID = BAD_PID; cchPN = strlen(szPN); _strupr(szPN); } } }
//
// lets be god
//
EnableDebugPriv();
//#if 0 // XXX olegk - enable the block after RI with debugger tree
//
// Include 32bit modules in enumeration
//
{ DWORD SymOpt = SymGetOptions(); SymOpt |= SYMOPT_INCLUDE_32BIT_MODULES; SymSetOptions(SYMOPT_INCLUDE_32BIT_MODULES); } //#endif // XXX olegk
//
// get the task list for the system
//
//
// Get the process information for all active Win32 services.
// This allows us to print the service names next to the processes
// that host them.
//
dwNumServices = GetServiceProcessInfo( &pServiceInfo );
numTasks = GetTaskListEx( tlist, MAX_TASKS, cchPN || (dwPID != BAD_PID), dwNumServices, pServiceInfo);
free( pServiceInfo );
if (fShowMtsPackages) { AddMtsPackageNames(tlist, numTasks); } //
// enumerate all windows and try to get the window
// titles for each task
//
te.tlist = tlist; te.numtasks = numTasks; GetWindowTitles( &te );
//
// print the task list
//
if (fTree) { PrintTaskTree( 0, 0 ); } else if (fFindTasksUsingModule) { PrintTasksUsingModule(argv[2]); } else if (fPidOnly) { GetFirstPidWithName(argv[2]); } else { for (i=0; i<numTasks; i++) { if ((dwPID == BAD_PID) && (!cchPN)) { PrintTask( i ); } else if ((dwPID == tlist[i].dwProcessId) || (cchPN && FMatchTaskName(szPN, tlist[i].WindowTitle, tlist[i].ProcessName))) { PrintTask( i ); PrintThreadInfo(tlist + i); }
if (tlist[i].pThreadInfo) { free(tlist[i].pThreadInfo); } } }
//
// end of program
//
return 0; }
VOID GetFirstPidWithName( LPTSTR szTask ) /*++
Routine Description:
Returns the PID of the first task with a Name matching the specified Name. IF no task is found -1 is returned Arguments:
szTask - module name to search for
--*/ { DWORD i; CHAR szPName[PROCESS_SIZE + 1]; CHAR szNameWExe[PROCESS_SIZE + 1];
strcpy(szNameWExe, szTask); strcat(szNameWExe, ".exe");
for (i=0; i<numTasks; i++) { strcpy(szPName, tlist[i].ProcessName); _strlwr(szPName);
if ((!strcmp(szPName, szTask))||(!strcmp(szPName, szNameWExe))) { if (tlist[i].dwProcessId != 0) { printf("%d\n", tlist[i].dwProcessId); return; } } } printf("-1\n"); }
VOID Usage( VOID )
/*++
Routine Description:
Prints usage text for this tool.
Arguments:
None.
Return Value:
None.
--*/
{ fprintf( stderr, "Microsoft (R) Windows NT (TM) Version 5.1 TLIST\n" VER_LEGALCOPYRIGHT_STR "\n\n" "usage: TLIST" " <<-m <pattern>> | <-t> | <pid> | <pattern> | <-p <processname>>> | <-k> | <-s>\n" " [options]:\n" " -t\n" " Print Task Tree\n\n" " <pid>\n" " List module information for this task.\n\n" " <pattern>\n" " The pattern can be a complete task\n" " name or a regular expression pattern\n" " to use as a match. Tlist matches the\n" " supplied pattern against the task names\n" " and the window titles.\n\n" " -k\n" " Show MTS packages active in each process.\n\n" " -m <pattern>\n" " Lists all tasks that have DLL modules loaded\n" " in them that match the given pattern name\n\n" " -s\n" " Show services active in each process.\n\n" " -p <processname>\n" " Returns the PID of the process specified or -1\n" " if the specified process doesn't exist. If there\n" " are multiple instances of the process running only\n" " the instance with the first PID value is returned.\n\n" "\n"); ExitProcess(0); }
//
// Routines used to list all processes that have a specific module in use
//
BOOL FindSpecificModuleCallback( LPSTR Name, DWORD_PTR Base, DWORD Size, PVOID Context )
/*++
Routine Description:
Callback function for module enumeration to find a specific module
Arguments:
Name - Module name Base - Base address Size - Size of image Context - User context pointer
Return Value:
TRUE - Continue enumeration FALSE - Stop enumeration
--*/
{ PFIND_MODULE_INFO pFindModuleInfo;
pFindModuleInfo = (PFIND_MODULE_INFO)Context;
if (MatchPattern(Name, pFindModuleInfo->szModuleToFind)) { pFindModuleInfo->fFound = TRUE; strcpy(pFindModuleInfo->szMatchingModuleName, Name); return FALSE; // Found Module so stop enumerating
}
return TRUE; }
BOOL IsTaskUsingModule( PTASK_LIST pTask, LPTSTR szModuleName, LPTSTR szMatchingModuleName )
/*++
Routine Description:
Checks if the given task has the given module loaded
Arguments:
pTaskList - task to search for module szModule - module name to search for
Return Value:
TRUE - if the module is loaded in the task FALSE - if the module is not loaded in the task
--*/
{ FIND_MODULE_INFO FindModuleInfo;
FindModuleInfo.fFound = FALSE; FindModuleInfo.szModuleToFind = szModuleName; FindModuleInfo.szMatchingModuleName = szMatchingModuleName;
EnumerateLoadedModules( (HANDLE) UlongToPtr(pTask->dwProcessId), FindSpecificModuleCallback, &FindModuleInfo );
return FindModuleInfo.fFound; }
void PrintTasksUsingModule( LPTSTR szModuleName )
/*++
Routine Description:
Enumerates through all the tasks in the system looking for those that have loaded modules of the given name.
Arguments:
szModule - module name to search for
Return Value:
None
--*/
{ BOOL fUsed = FALSE; DWORD i; CHAR szMatchingModuleName[64];
_strupr(szModuleName); // Needed for wildcarding
for (i=0; i<numTasks; i++) { if (IsTaskUsingModule(tlist + i, szModuleName, szMatchingModuleName)) { printf("%s - ", szMatchingModuleName); PrintTask( i ); fUsed = TRUE; } } if (!fUsed) { printf( "No tasks found using %s\n", szModuleName ); } }
BOOL GetVersionStuff( LPTSTR szFileName, VS_FIXEDFILEINFO *pvsRet )
/*++
Routine Description:
Get fixedfileinfo for szFileName.
Arguments:
szFileName - name of file pvsRet - fixedfileinfo return struct
Return Value:
TRUE - success FALSE - failure
--*/
{ DWORD dwHandle; DWORD dwLength; BOOL fRet = FALSE; LPVOID lpvData = NULL;
if (!(dwLength = GetFileVersionInfoSize(szFileName, &dwHandle))) { goto err; }
if (lpvData = malloc(dwLength)) { if (GetFileVersionInfo(szFileName, 0, dwLength, lpvData)) {
UINT uLen; VS_FIXEDFILEINFO *pvs; DWORD *pdwTranslation; DWORD dwDefLang = 0x409;
if (!VerQueryValue(lpvData, "\\VarFileInfo\\Translation", &pdwTranslation, &uLen)) { // if we can't get the langid, default to usa
pdwTranslation = &dwDefLang; uLen = sizeof(DWORD); }
if (VerQueryValue(lpvData, "\\", (LPVOID *)&pvs, &uLen)) { *pvsRet = *pvs; fRet = TRUE; } } }
err: if (lpvData) free(lpvData); return fRet; }
BOOL EnumLoadedModulesCallback( LPSTR Name, DWORD_PTR Base, DWORD Size, PVOID Context )
/*++
Routine Description:
Callback function for module enumeration
Arguments:
Name - Module name Base - Base address Size - Size of image Context - User context pointer
Return Value:
TRUE - Continue enumeration FALSE - Stop enumeration
--*/
{ VS_FIXEDFILEINFO vs; CHAR szBuffer[100];
szBuffer[0] = 0; if (GetVersionStuff( Name, &vs )) { wsprintf( szBuffer, "%u.%u.%u.%u %s", HIWORD(vs.dwFileVersionMS), LOWORD(vs.dwFileVersionMS), HIWORD(vs.dwFileVersionLS), LOWORD(vs.dwFileVersionLS), vs.dwFileFlags & VS_FF_DEBUG ? "dbg" : "shp" ); } printf( " %18.18s 0x%08x %s\n", szBuffer, Base, Name ); return TRUE; }
BOOL PrintModuleList( ULONG ProcessId )
/*++
Routine Description:
Prints list of modules in ProcessId
Arguments:
ProcessID - process id
Return Value:
TRUE - success FALSE - failure
--*/
{ EnumerateLoadedModules( (HANDLE) UlongToPtr(ProcessId), EnumLoadedModulesCallback, NULL ); return TRUE; }
DWORD GetWin32StartAddress( HANDLE hThread )
/*++
Routine Description:
Get starting address for thread
Arguments:
hThread
Return Value:
Starting Thread address or 0
--*/
{ NTSTATUS Status; DWORD ThreadInformation;
// make sure we have a handle
if (!hThread) return 0;
// get the threadinfo
Status = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &ThreadInformation, sizeof(ThreadInformation), NULL); if (!NT_SUCCESS(Status)) return 0;
return ThreadInformation; }
ULONG GetLastThreadErr( HANDLE hThread )
/*++
Routine Description:
Get Last Error for a Thread
Arguments:
hThread
Return Value:
LastError or 0
--*/
{ TEB Teb; NTSTATUS Status; HANDLE hProcess; ULONG LastErrorValue; THREAD_BASIC_INFORMATION ThreadInformation;
// make sure we have a handle
if (!hThread) return 0;
// query for basic thread info
Status = NtQueryInformationThread(hThread, ThreadBasicInformation, &ThreadInformation, sizeof(ThreadInformation), NULL); if (!NT_SUCCESS(Status)) return 0;
// get handle to process
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)(DWORD_PTR)ThreadInformation.ClientId.UniqueProcess))) { return 0; }
__try { // read the TEB from the process and get the last error value
if (ReadProcessMemory(hProcess, ThreadInformation.TebBaseAddress, &Teb, sizeof(TEB), NULL)) { LastErrorValue = Teb.LastErrorValue; } } __except(EXCEPTION_EXECUTE_HANDLER) { }
// close the hProcess
CloseHandle(hProcess);
return LastErrorValue; }
BOOL FPrintPEBInfo( HANDLE hProcess )
/*++
Routine Description:
Prints cmdline and cwd of hProcess
Arguments:
hProcess.
Return Value:
TRUE - success FALSE - failure
--*/
{ PEB Peb; NTSTATUS Status; PROCESS_BASIC_INFORMATION BasicInfo; BOOL fRet = FALSE; WCHAR szT[MAX_PATH * 2]; RTL_USER_PROCESS_PARAMETERS ProcessParameters;
Status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL); if (!NT_SUCCESS(Status)) { SetLastError(RtlNtStatusToDosError(Status)); return fRet; }
__try { // get the PEB
if (ReadProcessMemory(hProcess, BasicInfo.PebBaseAddress, &Peb, sizeof(PEB), NULL)) { // get the processparameters
if (ReadProcessMemory(hProcess, Peb.ProcessParameters, &ProcessParameters, sizeof(ProcessParameters), NULL)) { // get the CWD
if (ReadProcessMemory(hProcess, ProcessParameters.CurrentDirectory.DosPath.Buffer, szT, sizeof(szT), NULL)) { wprintf(L" CWD: %s\n", szT); }
// get cmdline
if (ReadProcessMemory(hProcess, ProcessParameters.CommandLine.Buffer, szT, sizeof(szT), NULL)) { wprintf(L" CmdLine: %s\n", szT); }
fRet = TRUE; } } } __except(EXCEPTION_EXECUTE_HANDLER) { }
return fRet; }
// copied from the win32 API code since we need to run on NT 4 and this is a
// new API to NT 5
HANDLE TlistOpenThread( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId )
/*++
Routine Description:
A handle to a thread object may be created using OpenThread.
Opening a thread creates a handle to the specified thread. Associated with the thread handle is a set of access rights that may be performed using the thread handle. The caller specifies the desired access to the thread using the DesiredAccess parameter.
Arguments:
mDesiredAccess - Supplies the desired access to the thread object. For NT/Win32, this access is checked against any security descriptor on the target thread. The following object type specific access flags can be specified in addition to the STANDARD_RIGHTS_REQUIRED access flags.
DesiredAccess Flags:
THREAD_TERMINATE - This access is required to terminate the thread using TerminateThread.
THREAD_SUSPEND_RESUME - This access is required to suspend and resume the thread using SuspendThread and ResumeThread.
THREAD_GET_CONTEXT - This access is required to use the GetThreadContext API on a thread object.
THREAD_SET_CONTEXT - This access is required to use the SetThreadContext API on a thread object.
THREAD_SET_INFORMATION - This access is required to set certain information in the thread object.
THREAD_SET_THREAD_TOKEN - This access is required to set the thread token using SetTokenInformation.
THREAD_QUERY_INFORMATION - This access is required to read certain information from the thread object.
SYNCHRONIZE - This access is required to wait on a thread object.
THREAD_ALL_ACCESS - This set of access flags specifies all of the possible access flags for a thread object.
bInheritHandle - Supplies a flag that indicates whether or not the returned handle is to be inherited by a new process during process creation. A value of TRUE indicates that the new process will inherit the handle.
dwThreadId - Supplies the thread id of the thread to open.
Return Value:
NON-NULL - Returns an open handle to the specified thread. The handle may be used by the calling process in any API that requires a handle to a thread. If the open is successful, the handle is granted access to the thread object only to the extent that it requested access through the DesiredAccess parameter.
NULL - The operation failed. Extended error status is available using GetLastError.
--*/
{ NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE Handle; CLIENT_ID ClientId;
ClientId.UniqueThread = (HANDLE)LongToHandle(dwThreadId); ClientId.UniqueProcess = (HANDLE)NULL;
InitializeObjectAttributes( &Obja, NULL, (bInheritHandle ? OBJ_INHERIT : 0), NULL, NULL ); Status = NtOpenThread( &Handle, (ACCESS_MASK)dwDesiredAccess, &Obja, &ClientId ); if ( NT_SUCCESS(Status) ) { return Handle; } else { return NULL; } }
VOID PrintThreadInfo( PTASK_LIST pTaskList )
/*++
Routine Description:
Prints all kinds of info about a task
Arguments:
PTASK_LIST of task to print
Return Value:
None.
--*/
{ UINT nThread; HANDLE hProcess;
// from \\kernel\razzle2\src\ntos\inc\ke.h
#define MAX_THREADSTATE (sizeof(szThreadState) / sizeof(TCHAR *))
static const TCHAR *szThreadState[] = { "Initialized", "Ready ", "Running ", "Standby ", "Terminated", "Waiting ", "Transition", "??? " };
// get a handle to the process
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pTaskList->dwProcessId); if (!hProcess) return;
// print the CWD and CmdLine
FPrintPEBInfo(hProcess);
printf( " VirtualSize: %6ld KB" " PeakVirtualSize: %6ld KB\n", pTaskList->VirtualSize / 1024, pTaskList->PeakVirtualSize / 1024);
printf( " WorkingSetSize:%6ld KB" " PeakWorkingSetSize:%6ld KB\n", pTaskList->WorkingSetSize / 1024, pTaskList->PeakWorkingSetSize / 1024);
printf( " NumberOfThreads: %ld\n", pTaskList->NumberOfThreads);
// if we got any threadinfo, spit it out
if (pTaskList->pThreadInfo) { for (nThread = 0; nThread < pTaskList->NumberOfThreads; nThread++) {
PTHREAD_INFO pThreadInfo = &pTaskList->pThreadInfo[nThread]; HANDLE hThread = TlistOpenThread(THREAD_QUERY_INFORMATION, FALSE, (DWORD)(DWORD_PTR)pThreadInfo->UniqueThread);
printf(" %4d Win32StartAddr:0x%08x LastErr:0x%08x State:%s\n", HandleToUlong(pThreadInfo->UniqueThread), GetWin32StartAddress(hThread), GetLastThreadErr(hThread), szThreadState[min(pThreadInfo->ThreadState, MAX_THREADSTATE - 1)]);
if (hThread) NtClose(hThread); } }
// print the modules
PrintModuleList( pTaskList->dwProcessId );
// close the hProcess
CloseHandle(hProcess); }
BOOL FMatchTaskName( LPTSTR szPN, LPTSTR szWindowTitle, LPTSTR szProcessName ) { LPTSTR szT; TCHAR szTName[PROCESS_SIZE];
strncpy( szTName, szProcessName, PROCESS_SIZE ); if (szT = strchr( szTName, '.' )) szT[0] = '\0';
if (MatchPattern( szTName, szPN ) || MatchPattern( szProcessName, szPN ) || MatchPattern( szWindowTitle, szPN )) { return TRUE; }
return FALSE; }
|