1017 lines
24 KiB
C
1017 lines
24 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|