windows-nt/Source/XPSP1/NT/sdktools/tlist/common.c
2020-09-26 16:20:57 +08:00

1053 lines
25 KiB
C

/*++
Copyright (c) 1994-1998 Microsoft Corporation
Module Name:
common.c
Abstract:
This module contains common apis used by tlist & kill.
Author:
Wesley Witt (wesw) 20-May-1994
Environment:
User Mode
--*/
#include "pch.h"
#pragma hdrstop
//
// global variables
//
PUCHAR CommonLargeBuffer;
ULONG CommonLargeBufferSize = 64*1024;
//
// prototypes
//
BOOL CALLBACK
EnumWindowsProc(
HWND hwnd,
LPARAM lParam
);
BOOL CALLBACK
EnumWindowStationsFunc(
LPSTR lpstr,
LPARAM lParam
);
BOOL CALLBACK
EnumDesktopsFunc(
LPSTR lpstr,
LPARAM lParam
);
DWORD
GetServiceProcessInfo(
LPENUM_SERVICE_STATUS_PROCESS* ppInfo
)
/*++
Routine Description:
Provides an API for getting a list of process information for Win 32
services that are running at the time of the API call.
Arguments:
ppInfo - address of a pointer to return the information.
*ppInfo points to memory allocated with malloc.
Return Value:
Number of ENUM_SERVICE_STATUS_PROCESS structures pointed at by *ppInfo.
--*/
{
DWORD dwNumServices = 0;
SC_HANDLE hScm;
typedef
BOOL
(__stdcall * PFN_ENUMSERVICSESTATUSEXA) (
SC_HANDLE hSCManager,
SC_ENUM_TYPE InfoLevel,
DWORD dwServiceType,
DWORD dwServiceState,
LPBYTE lpServices,
DWORD cbBufSize,
LPDWORD pcbBytesNeeded,
LPDWORD lpServicesReturned,
LPDWORD lpResumeHandle,
LPCSTR pszGroupName);
PFN_ENUMSERVICSESTATUSEXA p_EnumServicesStatusEx;
HINSTANCE hAdv = LoadLibrary("advapi32.dll");
// Initialize the output parmeter.
*ppInfo = NULL;
if (hAdv)
{
p_EnumServicesStatusEx = (PFN_ENUMSERVICSESTATUSEXA)
GetProcAddress(hAdv, "EnumServicesStatusExA");
if (!p_EnumServicesStatusEx)
{
return 0;
}
} else {
return 0;
}
// Connect to the service controller.
//
hScm = OpenSCManager(
NULL,
NULL,
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
if (hScm) {
LPENUM_SERVICE_STATUS_PROCESS pInfo = NULL;
DWORD cbInfo = 4 * 1024;
DWORD cbExtraNeeded = 0;
DWORD dwErr;
DWORD dwResume;
DWORD cLoop = 0;
const DWORD cLoopMax = 2;
// First pass through the loop allocates from an initial guess. (4K)
// If that isn't sufficient, we make another pass and allocate
// what is actually needed. (We only go through the loop a
// maximum of two times.)
//
do {
free (pInfo);
cbInfo += cbExtraNeeded;
pInfo = (LPENUM_SERVICE_STATUS_PROCESS)malloc(cbInfo);
if (!pInfo) {
dwErr = ERROR_OUTOFMEMORY;
break;
}
dwErr = ERROR_SUCCESS;
dwResume = 0;
if (!p_EnumServicesStatusEx(
hScm,
SC_ENUM_PROCESS_INFO,
SERVICE_WIN32,
SERVICE_ACTIVE,
(LPBYTE)pInfo,
cbInfo,
&cbExtraNeeded,
&dwNumServices,
&dwResume,
NULL)) {
dwErr = GetLastError();
}
}
while ((ERROR_MORE_DATA == dwErr) && (++cLoop < cLoopMax));
if ((ERROR_SUCCESS == dwErr) && dwNumServices) {
*ppInfo = pInfo;
} else {
free (pInfo);
dwNumServices = 0;
}
CloseServiceHandle(hScm);
}
return dwNumServices;
}
DWORD
GetTaskListEx(
PTASK_LIST pTask,
DWORD dwNumTasks,
BOOL fThreadInfo,
DWORD dwNumServices,
const ENUM_SERVICE_STATUS_PROCESS* pServiceInfo
)
/*++
Routine Description:
Provides an API for getting a list of tasks running at the time of the
API call. This function uses internal NT apis and data structures. This
api is MUCH faster that the non-internal version that uses the registry.
Arguments:
pTask - Array of TASK_LIST structures to fill.
dwNumTasks - Maximum number of tasks that the pTask array can hold.
fThreadInfo - TRUE if thread information is desired.
dwNumServices - Maximum number of entries in pServiceInfo.
pServiceInfo - Array of service status structures to reference
for supporting services in processes.
Return Value:
Number of tasks placed into the pTask array.
--*/
{
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
NTSTATUS status;
ANSI_STRING pname;
PCHAR p;
ULONG TotalOffset;
ULONG totalTasks = 0;
retry:
if (CommonLargeBuffer == NULL) {
CommonLargeBuffer = VirtualAlloc (NULL,
CommonLargeBufferSize,
MEM_COMMIT,
PAGE_READWRITE);
if (CommonLargeBuffer == NULL) {
return 0;
}
}
status = NtQuerySystemInformation(
SystemProcessInformation,
CommonLargeBuffer,
CommonLargeBufferSize,
NULL
);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
CommonLargeBufferSize += 8192;
VirtualFree (CommonLargeBuffer, 0, MEM_RELEASE);
CommonLargeBuffer = NULL;
goto retry;
}
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) CommonLargeBuffer;
TotalOffset = 0;
while (TRUE) {
pname.Buffer = NULL;
if ( ProcessInfo->ImageName.Buffer ) {
RtlUnicodeStringToAnsiString(&pname,(PUNICODE_STRING)&ProcessInfo->ImageName,TRUE);
if (pname.Buffer) {
p = strrchr(pname.Buffer,'\\');
if ( p ) {
p++;
}
else {
p = pname.Buffer;
}
} else {
p = "";
}
}
else {
p = "System Process";
}
strncpy( pTask->ProcessName, p, PROCESS_SIZE );
pTask->ProcessName[PROCESS_SIZE-1] = '\0';
pTask->flags = 0;
pTask->dwProcessId = (DWORD)(DWORD_PTR)ProcessInfo->UniqueProcessId;
pTask->dwInheritedFromProcessId = (DWORD)(DWORD_PTR)ProcessInfo->InheritedFromUniqueProcessId;
pTask->CreateTime.QuadPart = (ULONGLONG)ProcessInfo->CreateTime.QuadPart;
pTask->PeakVirtualSize = ProcessInfo->PeakVirtualSize;
pTask->VirtualSize = ProcessInfo->VirtualSize;
pTask->PageFaultCount = ProcessInfo->PageFaultCount;
pTask->PeakWorkingSetSize = ProcessInfo->PeakWorkingSetSize;
pTask->WorkingSetSize = ProcessInfo->WorkingSetSize;
pTask->NumberOfThreads = ProcessInfo->NumberOfThreads;
if (fThreadInfo) {
if (pTask->pThreadInfo = malloc(pTask->NumberOfThreads * sizeof(THREAD_INFO))) {
UINT nThread = pTask->NumberOfThreads;
PTHREAD_INFO pThreadInfo = pTask->pThreadInfo;
PSYSTEM_THREAD_INFORMATION pSysThreadInfo =
(PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
while (nThread--) {
pThreadInfo->ThreadState = pSysThreadInfo->ThreadState;
pThreadInfo->UniqueThread = pSysThreadInfo->ClientId.UniqueThread;
pThreadInfo++;
pSysThreadInfo++;
}
}
} else {
pTask->pThreadInfo = NULL;
}
pTask->MtsPackageNames[0] = 0;
// Initialize the ServiceNames if this task hosts any.
//
*pTask->ServiceNames = 0;
if (dwNumServices)
{
// For each service with this process id, append it's service
// name to the buffer. Separate each with a comma.
//
BOOL fFirstTime = TRUE;
DWORD iSvc;
size_t cchRemain = SERVICENAMES_SIZE - 1;
size_t cch;
for (iSvc = 0; iSvc < dwNumServices; iSvc++) {
if (pTask->dwProcessId == pServiceInfo[iSvc].ServiceStatusProcess.dwProcessId) {
cch = strlen(pServiceInfo[iSvc].lpServiceName);
if (fFirstTime) {
fFirstTime = FALSE;
strncpy(
pTask->ServiceNames,
pServiceInfo[iSvc].lpServiceName,
cchRemain);
// strncpy may not terminate the string if
// cchRemain <= cch so we do it regardless.
//
pTask->ServiceNames[cchRemain] = 0;
} else if (cchRemain > 1) { // ensure room for the comma
strncat(
pTask->ServiceNames,
",",
cchRemain--);
strncat(
pTask->ServiceNames,
pServiceInfo[iSvc].lpServiceName,
cchRemain);
}
// Counts are unsigned so we have to check before
// subtracting.
//
if (cchRemain < cch) {
// No more room for any more.
break;
} else {
cchRemain -= cch;
}
}
}
}
pTask++;
totalTasks++;
if (totalTasks == dwNumTasks) {
break;
}
if (ProcessInfo->NextEntryOffset == 0) {
break;
}
TotalOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&CommonLargeBuffer[TotalOffset];
}
return totalTasks;
}
DWORD
GetTaskList(
PTASK_LIST pTask,
DWORD dwNumTasks
)
{
return GetTaskListEx(pTask, dwNumTasks, FALSE, 0, NULL);
}
void
AddMtsPackageNames(
PTASK_LIST Tasks,
DWORD NumTasks
)
{
HRESULT Hr;
IMtsGrp* MtsGroup;
long Packages;
long i;
if ((Hr = CoInitialize(NULL)) != S_OK) {
return;
}
if ((Hr = CoCreateInstance(&CLSID_MtsGrp, NULL, CLSCTX_ALL,
&IID_IMtsGrp, (void **)&MtsGroup)) != S_OK) {
goto Uninit;
}
if ((Hr = MtsGroup->lpVtbl->Refresh(MtsGroup)) != S_OK ||
(Hr = MtsGroup->lpVtbl->get_Count(MtsGroup, &Packages)) != S_OK) {
goto ReleaseGroup;
}
for (i = 0; i < Packages; i++) {
IUnknown* Unk;
IMtsEvents* Events;
BSTR Name;
DWORD Pid;
DWORD TaskIdx;
if ((Hr = MtsGroup->lpVtbl->Item(MtsGroup, i, &Unk)) != S_OK) {
continue;
}
Hr = Unk->lpVtbl->QueryInterface(Unk, &IID_IMtsEvents,
(void **)&Events);
Unk->lpVtbl->Release(Unk);
if (Hr != S_OK) {
continue;
}
Hr = Events->lpVtbl->GetProcessID(Events, (PLONG)&Pid);
if (Hr == S_OK) {
Hr = Events->lpVtbl->get_PackageName(Events, &Name);
}
Events->lpVtbl->Release(Events);
if (Hr != S_OK) {
continue;
}
for (TaskIdx = 0; TaskIdx < NumTasks; TaskIdx++) {
if (Tasks[TaskIdx].dwProcessId == Pid) {
break;
}
}
if (TaskIdx < NumTasks) {
PSTR Str;
int Conv;
Str = Tasks[TaskIdx].MtsPackageNames +
strlen(Tasks[TaskIdx].MtsPackageNames);
if (Str > Tasks[TaskIdx].MtsPackageNames) {
*Str++ = ',';
}
Conv = WideCharToMultiByte(
CP_ACP,
0,
Name,
-1,
Str,
MTS_PACKAGE_NAMES_SIZE -
(DWORD)(Str - Tasks[TaskIdx].MtsPackageNames) - 2,
NULL,
NULL
);
SysFreeString(Name);
if (Conv == 0 && Str > Tasks[TaskIdx].MtsPackageNames &&
*(Str - 1) == ',') {
*(Str - 1) = 0;
}
}
}
ReleaseGroup:
MtsGroup->lpVtbl->Release(MtsGroup);
Uninit:
CoUninitialize();
return;
}
BOOL
DetectOrphans(
PTASK_LIST pTask,
DWORD dwNumTasks
)
{
DWORD i, j;
BOOL Result = FALSE;
for (i=0; i<dwNumTasks; i++) {
if (pTask[i].dwInheritedFromProcessId != 0) {
for (j=0; j<dwNumTasks; j++) {
if (i != j && pTask[i].dwInheritedFromProcessId == pTask[j].dwProcessId) {
if (pTask[i].CreateTime.QuadPart <= pTask[j].CreateTime.QuadPart) {
pTask[i].dwInheritedFromProcessId = 0;
Result = TRUE;
}
break;
}
}
}
}
return Result;
}
BOOL
EnableDebugPriv(
VOID
)
/*++
Routine Description:
Changes the tlist process's privilige so that kill works properly.
Arguments:
Return Value:
TRUE - success
FALSE - failure
--*/
{
HANDLE hToken;
LUID DebugValue;
TOKEN_PRIVILEGES tkp;
//
// Retrieve a handle of the access token
//
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken)) {
printf("OpenProcessToken failed with %d\n", GetLastError());
return FALSE;
}
//
// Enable the SE_DEBUG_NAME privilege or disable
// all privileges, depending on the fEnable flag.
//
if (!LookupPrivilegeValue((LPSTR) NULL,
SE_DEBUG_NAME,
&DebugValue)) {
printf("LookupPrivilegeValue failed with %d\n", GetLastError());
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = DebugValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tkp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL)) {
//
// The return value of AdjustTokenPrivileges be texted
//
printf("AdjustTokenPrivileges failed with %d\n", GetLastError());
return FALSE;
}
return TRUE;
}
BOOL
KillProcess(
PTASK_LIST tlist,
BOOL fForce
)
{
HANDLE hProcess, hProcess1;
HDESK hdeskSave;
HDESK hdesk;
HWINSTA hwinsta;
HWINSTA hwinstaSave;
if (fForce || !tlist->hwnd) {
hProcess1 = OpenProcess( PROCESS_ALL_ACCESS, FALSE, tlist->dwProcessId );
if (hProcess1) {
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, tlist->dwProcessId );
if (hProcess == NULL) {
CloseHandle(hProcess1);
return FALSE;
}
if (!TerminateProcess( hProcess, 1 )) {
CloseHandle( hProcess );
CloseHandle( hProcess1 );
return FALSE;
}
CloseHandle( hProcess );
CloseHandle( hProcess1 );
return TRUE;
}
}
//
// save the current windowstation
//
hwinstaSave = GetProcessWindowStation();
//
// save the current desktop
//
hdeskSave = GetThreadDesktop( GetCurrentThreadId() );
//
// open the windowstation
//
hwinsta = OpenWindowStation( tlist->lpWinsta, FALSE, MAXIMUM_ALLOWED );
if (!hwinsta) {
return FALSE;
}
//
// change the context to the new windowstation
//
SetProcessWindowStation( hwinsta );
//
// open the desktop
//
hdesk = OpenDesktop( tlist->lpDesk, 0, FALSE, MAXIMUM_ALLOWED );
if (!hdesk) {
return FALSE;
}
//
// change the context to the new desktop
//
SetThreadDesktop( hdesk );
//
// kill the process
//
PostMessage( tlist->hwnd, WM_CLOSE, 0, 0 );
//
// restore the previous desktop
//
if (hdesk != hdeskSave) {
SetThreadDesktop( hdeskSave );
CloseDesktop( hdesk );
}
//
// restore the context to the previous windowstation
//
if (hwinsta != hwinstaSave) {
SetProcessWindowStation( hwinstaSave );
CloseWindowStation( hwinsta );
}
return TRUE;
}
VOID
GetWindowTitles(
PTASK_LIST_ENUM te
)
{
//
// enumerate all windows and try to get the window
// titles for each task
//
EnumWindowStations( EnumWindowStationsFunc, (LPARAM)te );
}
BOOL CALLBACK
EnumWindowStationsFunc(
LPSTR lpstr,
LPARAM lParam
)
/*++
Routine Description:
Callback function for windowstation enumeration.
Arguments:
lpstr - windowstation name
lParam - ** not used **
Return Value:
TRUE - continues the enumeration
--*/
{
PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam;
HWINSTA hwinsta;
HWINSTA hwinstaSave;
//
// open the windowstation
//
hwinsta = OpenWindowStation( lpstr, FALSE, MAXIMUM_ALLOWED );
if (!hwinsta) {
return FALSE;
}
//
// save the current windowstation
//
hwinstaSave = GetProcessWindowStation();
//
// change the context to the new windowstation
//
SetProcessWindowStation( hwinsta );
te->lpWinsta = _strdup( lpstr );
//
// enumerate all the desktops for this windowstation
//
EnumDesktops( hwinsta, EnumDesktopsFunc, lParam );
//
// restore the context to the previous windowstation
//
if (hwinsta != hwinstaSave) {
SetProcessWindowStation( hwinstaSave );
CloseWindowStation( hwinsta );
}
//
// continue the enumeration
//
return TRUE;
}
BOOL CALLBACK
EnumMessageWindows(
WNDENUMPROC lpEnumFunc,
LPARAM lParam
)
/*++
Routine Description:
Enumarates message windows (which are not enumarated by EnumWindows)
Arguments:
lpEnumFunc - Callback function
lParam - Caller data
Return Value:
TRUE
--*/
{
HWND hwnd = NULL;
do {
hwnd = FindWindowEx(HWND_MESSAGE, hwnd, NULL, NULL);
if (hwnd != NULL) {
if (!(*lpEnumFunc)(hwnd, lParam)) {
break;
}
}
} while (hwnd != NULL);
return TRUE;
}
BOOL CALLBACK
EnumDesktopsFunc(
LPSTR lpstr,
LPARAM lParam
)
/*++
Routine Description:
Callback function for desktop enumeration.
Arguments:
lpstr - desktop name
lParam - ** not used **
Return Value:
TRUE - continues the enumeration
--*/
{
PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam;
HDESK hdeskSave;
HDESK hdesk;
//
// open the desktop
//
hdesk = OpenDesktop( lpstr, 0, FALSE, MAXIMUM_ALLOWED );
if (!hdesk) {
return FALSE;
}
//
// save the current desktop
//
hdeskSave = GetThreadDesktop( GetCurrentThreadId() );
//
// change the context to the new desktop
//
SetThreadDesktop( hdesk );
te->lpDesk = _strdup( lpstr );
//
// enumerate all windows in the new desktop
//
((PTASK_LIST_ENUM)lParam)->bFirstLoop = TRUE;
EnumWindows( (WNDENUMPROC)EnumWindowsProc, lParam );
EnumMessageWindows( (WNDENUMPROC)EnumWindowsProc, lParam );
((PTASK_LIST_ENUM)lParam)->bFirstLoop = FALSE;
EnumWindows( (WNDENUMPROC)EnumWindowsProc, lParam );
EnumMessageWindows( (WNDENUMPROC)EnumWindowsProc, lParam );
//
// restore the previous desktop
//
if (hdesk != hdeskSave) {
SetThreadDesktop( hdeskSave );
CloseDesktop( hdesk );
}
return TRUE;
}
BOOL CALLBACK
EnumWindowsProc(
HWND hwnd,
LPARAM lParam
)
/*++
Routine Description:
Callback function for window enumeration.
Arguments:
hwnd - window handle
lParam - pte
Return Value:
TRUE - continues the enumeration
--*/
{
DWORD pid = 0;
DWORD i;
CHAR buf[TITLE_SIZE];
PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam;
PTASK_LIST tlist = te->tlist;
DWORD numTasks = te->numtasks;
//
// Use try/except block when enumerating windows,
// as a window may be destroyed by another thread
// when being enumerated.
//
try {
//
// get the processid for this window
//
if (!GetWindowThreadProcessId( hwnd, &pid )) {
return TRUE;
}
if ((GetWindow( hwnd, GW_OWNER )) ||
(!(GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE)) && te->bFirstLoop) {
//
// not a top level window
//
return TRUE;
}
//
// look for the task in the task list for this window
// If this is the second time let invisible windows through if we don't
// have a window already
//
for (i=0; i<numTasks; i++) {
if ((tlist[i].dwProcessId == pid) && (te->bFirstLoop || (tlist[i].hwnd == 0))) {
tlist[i].hwnd = hwnd;
tlist[i].lpWinsta = te->lpWinsta;
tlist[i].lpDesk = te->lpDesk;
//
// we found the task no lets try to get the
// window text
//
if (GetWindowText( tlist[i].hwnd, buf, sizeof(buf) )) {
//
// go it, so lets save it
//
strcpy( tlist[i].WindowTitle, buf );
}
break;
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
}
//
// continue the enumeration
//
return TRUE;
}
BOOL
MatchPattern(
PUCHAR String,
PUCHAR Pattern
)
{
INT c, p, l;
for (; ;) {
switch (p = *Pattern++) {
case 0: // end of pattern
return *String ? FALSE : TRUE; // if end of string TRUE
case '*':
while (*String) { // match zero or more char
if (MatchPattern (String++, Pattern))
return TRUE;
}
return MatchPattern (String, Pattern);
case '?':
if (*String++ == 0) // match any one char
return FALSE; // not end of string
break;
case '[':
if ( (c = *String++) == 0) // match char set
return FALSE; // syntax
c = toupper(c);
l = 0;
while (p = *Pattern++) {
if (p == ']') // if end of char set, then
return FALSE; // no match found
if (p == '-') { // check a range of chars?
p = *Pattern; // get high limit of range
if (p == 0 || p == ']')
return FALSE; // syntax
if (c >= l && c <= p)
break; // if in range, move on
}
l = p;
if (c == p) // if char matches this element
break; // move on
}
while (p && p != ']') // got a match in char set
p = *Pattern++; // skip to end of set
break;
default:
c = *String++;
if (toupper(c) != p) // check for exact char
return FALSE; // not a match
break;
}
}
}
BOOL
EmptyProcessWorkingSet(
DWORD pid
)
{
HANDLE hProcess;
SIZE_T dwMinimumWorkingSetSize;
SIZE_T dwMaximumWorkingSetSize;
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid );
if (hProcess == NULL) {
return FALSE;
}
if (!GetProcessWorkingSetSize(
hProcess,
&dwMinimumWorkingSetSize,
&dwMaximumWorkingSetSize
)) {
CloseHandle( hProcess );
return FALSE;
}
SetProcessWorkingSetSize( hProcess, 0xffffffff, 0xffffffff );
CloseHandle( hProcess );
return TRUE;
}
BOOL
EmptySystemWorkingSet(
VOID
)
{
SYSTEM_FILECACHE_INFORMATION info;
NTSTATUS status;
info.MinimumWorkingSet = 0xffffffff;
info.MaximumWorkingSet = 0xffffffff;
if (!NT_SUCCESS (status = NtSetSystemInformation(
SystemFileCacheInformation,
&info,
sizeof (info)))) {
return FALSE;
}
return TRUE;
}