You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
629 lines
17 KiB
629 lines
17 KiB
/*******************************************************************************
|
|
*
|
|
* CTXMON.C
|
|
*
|
|
* This module contains code to monitor user processes
|
|
*
|
|
* Copyright Microsoft, 1997
|
|
*
|
|
* Author:
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <ntexapi.h>
|
|
#include "winreg.h"
|
|
|
|
/*=============================================================================
|
|
== Local Defines
|
|
=============================================================================*/
|
|
|
|
#define BUFFER_SIZE 32*1024
|
|
#define MAXNAME 18
|
|
|
|
|
|
BOOL IsSystemLUID(HANDLE ProcessId);
|
|
//
|
|
// This table contains common NT system programs
|
|
//
|
|
DWORD dwNumberofSysProcs = 0;
|
|
LPTSTR *SysProcTable;
|
|
|
|
typedef struct {
|
|
HANDLE hProcess;
|
|
HANDLE TerminateEvent;
|
|
//HWND hwndNotify;
|
|
HANDLE Thread;
|
|
} USER_PROCESS_MONITOR, * PUSER_PROCESS_MONITOR;
|
|
|
|
|
|
/*=============================================================================
|
|
== Internal Procedures
|
|
=============================================================================*/
|
|
|
|
HANDLE OpenUserProcessHandle();
|
|
BOOLEAN IsSystemProcess( PSYSTEM_PROCESS_INFORMATION);
|
|
|
|
/*=============================================================================
|
|
== External Procedures
|
|
=============================================================================*/
|
|
|
|
VOID LookupSidUser( PSID pSid, PWCHAR pUserName, PULONG pcbUserName );
|
|
|
|
HANDLE
|
|
ImpersonateUser(
|
|
HANDLE UserToken,
|
|
HANDLE ThreadHandle
|
|
);
|
|
|
|
BOOL
|
|
StopImpersonating(
|
|
HANDLE ThreadHandle
|
|
);
|
|
|
|
|
|
BOOL CreateSystemProcList ()
|
|
{
|
|
DWORD dwIndex;
|
|
DWORD dwLongestProcName = 0;
|
|
DWORD dwSize = 0;
|
|
HKEY hKeySysProcs = NULL;
|
|
DWORD iValueIndex = 0;
|
|
LONG lResult;
|
|
|
|
const LPCTSTR SYS_PROC_KEY = TEXT("SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\SysProcs");
|
|
|
|
// to start with.
|
|
SysProcTable = NULL;
|
|
dwNumberofSysProcs = 0;
|
|
|
|
|
|
lResult = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE, // handle of open key
|
|
SYS_PROC_KEY, // address of name of subkey to open
|
|
0 , // reserved
|
|
KEY_READ, // security access mask
|
|
&hKeySysProcs // address of handle of open key
|
|
);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lResult = RegQueryInfoKey(
|
|
hKeySysProcs, // handle to key
|
|
NULL, // class buffer
|
|
NULL, // size of class buffer
|
|
NULL, // reserved
|
|
NULL, // number of subkeys
|
|
NULL, // longest subkey name
|
|
NULL, // longest class string
|
|
&dwNumberofSysProcs, // number of value entries
|
|
&dwLongestProcName, // longest value name
|
|
NULL, // longest value data
|
|
NULL, // descriptor length
|
|
NULL // last write time
|
|
);
|
|
|
|
if (lResult != ERROR_SUCCESS || dwNumberofSysProcs == 0)
|
|
{
|
|
dwNumberofSysProcs = 0;
|
|
RegCloseKey(hKeySysProcs);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
dwLongestProcName += 1; // provision for the terminating null
|
|
SysProcTable = LocalAlloc(LPTR, sizeof(LPTSTR) * dwNumberofSysProcs);
|
|
if (!SysProcTable)
|
|
{
|
|
SysProcTable = NULL;
|
|
dwNumberofSysProcs = 0;
|
|
RegCloseKey(hKeySysProcs);
|
|
return FALSE;
|
|
}
|
|
|
|
for (dwIndex = 0; dwIndex < dwNumberofSysProcs; dwIndex++)
|
|
{
|
|
SysProcTable[dwIndex] = (LPTSTR) LocalAlloc(LPTR, dwLongestProcName * sizeof(TCHAR));
|
|
|
|
if (SysProcTable[dwIndex] == NULL)
|
|
{
|
|
//
|
|
// if we failed to alloc bail out.
|
|
//
|
|
|
|
while (dwIndex)
|
|
{
|
|
LocalFree(SysProcTable[dwIndex-1]);
|
|
dwIndex--;
|
|
}
|
|
|
|
LocalFree(SysProcTable);
|
|
SysProcTable = NULL;
|
|
dwNumberofSysProcs = 0;
|
|
RegCloseKey(hKeySysProcs);
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
iValueIndex = 0;
|
|
while (iValueIndex < dwNumberofSysProcs)
|
|
{
|
|
dwSize = dwLongestProcName;
|
|
lResult = RegEnumValue(
|
|
hKeySysProcs, // handle of key to query
|
|
iValueIndex, // index of value to query
|
|
SysProcTable[iValueIndex], // address of buffer for value string
|
|
&dwSize, // address for size of value buffer
|
|
0, // reserved
|
|
NULL, // address of buffer for type code
|
|
NULL, // address of buffer for value data
|
|
NULL // address for size of data buffer
|
|
);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
{
|
|
lstrcpy(SysProcTable[iValueIndex], TEXT("")); // this is an invalid entry.
|
|
|
|
if (lResult == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
}
|
|
|
|
iValueIndex++;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void DestroySystemprocList ()
|
|
{
|
|
DWORD dwIndex;
|
|
if (SysProcTable)
|
|
{
|
|
for (dwIndex = 0; dwIndex < dwNumberofSysProcs; dwIndex++)
|
|
{
|
|
if (SysProcTable[dwIndex])
|
|
{
|
|
LocalFree(SysProcTable[dwIndex]);
|
|
}
|
|
|
|
}
|
|
|
|
LocalFree(SysProcTable);
|
|
SysProcTable = NULL;
|
|
dwNumberofSysProcs = 0;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: UserProcessMonitorThread
|
|
*
|
|
* Thread monitoring user processes. It intiates a LOGOFF when there
|
|
* are no more user processes.
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
DWORD UserProcessMonitorThread(
|
|
LPVOID lpThreadParameter
|
|
)
|
|
{
|
|
PUSER_PROCESS_MONITOR pMonitor = (PUSER_PROCESS_MONITOR)lpThreadParameter;
|
|
HANDLE ImpersonationHandle;
|
|
DWORD WaitResult;
|
|
HANDLE WaitHandle;
|
|
HKEY hKey;
|
|
DWORD dwVal = 0;
|
|
|
|
|
|
//This value should be per user
|
|
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\WOW", 0,
|
|
KEY_WRITE, &hKey) == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Set the SharedWowTimeout to zero so that WOW exits as soon as all the 16 bit
|
|
// processes are gone
|
|
//
|
|
RegSetValueEx (hKey, L"SharedWowTimeout", 0, REG_DWORD, (LPBYTE)&dwVal,
|
|
sizeof(DWORD));
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
if (!CreateSystemProcList ())
|
|
DebugLog((DEB_ERROR, "ERROR, CreateSystemProcList failed.\n", GetLastError()));
|
|
|
|
for (;;) {
|
|
if ( !(pMonitor->hProcess = OpenUserProcessHandle()) ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Wait for process to exit or terminate event to be signaled
|
|
WaitResult = WaitForMultipleObjects( 2, &pMonitor->hProcess,
|
|
FALSE, (DWORD)-1 );
|
|
|
|
if ( WaitResult == 1 ) { // if terminate event was signaled
|
|
CloseHandle( pMonitor->hProcess );
|
|
DestroySystemprocList ();
|
|
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
DestroySystemprocList ();
|
|
|
|
|
|
//
|
|
// Initiate logoff
|
|
//
|
|
|
|
ImpersonationHandle = ImpersonateUser(g_UserToken , NULL );
|
|
|
|
if( ImpersonationHandle ) {
|
|
ExitWindowsEx(EWX_LOGOFF, 0);
|
|
StopImpersonating(ImpersonationHandle);
|
|
}
|
|
|
|
WaitForSingleObject( pMonitor->TerminateEvent, (DWORD)-1 );
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: StartUserProcessMonitor
|
|
*
|
|
* PURPOSE: Creates a thread to monitor user processes
|
|
*
|
|
\***************************************************************************/
|
|
|
|
LPVOID
|
|
StartUserProcessMonitor(
|
|
//HWND hwndNotify
|
|
)
|
|
{
|
|
PUSER_PROCESS_MONITOR pMonitor;
|
|
DWORD ThreadId;
|
|
|
|
pMonitor = LocalAlloc(LPTR, sizeof(USER_PROCESS_MONITOR));
|
|
if (pMonitor == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Initialize monitor fields
|
|
//
|
|
|
|
//pMonitor->hwndNotify = hwndNotify;
|
|
pMonitor->TerminateEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
|
|
|
//
|
|
// Create the monitor thread
|
|
//
|
|
|
|
pMonitor->Thread = CreateThread(
|
|
NULL, // Use default ACL
|
|
0, // Same stack size
|
|
UserProcessMonitorThread, // Start address
|
|
(LPVOID)pMonitor, // Parameter
|
|
0, // Creation flags
|
|
&ThreadId // Get the id back here
|
|
);
|
|
if (pMonitor->Thread == NULL) {
|
|
DebugLog((DEB_ERROR, "Failed to create monitor thread, error = %d", GetLastError()));
|
|
LocalFree(pMonitor);
|
|
return(NULL);
|
|
}
|
|
|
|
return((LPVOID)pMonitor);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DeleteUserProcessMonitor(
|
|
LPVOID Parameter
|
|
)
|
|
{
|
|
PUSER_PROCESS_MONITOR pMonitor = (PUSER_PROCESS_MONITOR)Parameter;
|
|
BOOL Result;
|
|
|
|
|
|
if (!pMonitor)
|
|
return;
|
|
|
|
// Set the terminate event for this monitor
|
|
// and wait for the monitor thread to exit
|
|
SetEvent( pMonitor->TerminateEvent );
|
|
if ( WaitForSingleObject( pMonitor->Thread, 5000 ) == WAIT_TIMEOUT )
|
|
(VOID)TerminateThread(pMonitor->Thread, ERROR_SUCCESS);
|
|
|
|
//
|
|
// Close our handle to the monitor thread
|
|
//
|
|
|
|
Result = CloseHandle(pMonitor->Thread);
|
|
if (!Result) {
|
|
DebugLog((DEB_ERROR, "DeleteUserProcessMonitor: failed to close monitor thread, error = %d\n", GetLastError()));
|
|
}
|
|
|
|
//
|
|
// Delete monitor object
|
|
//
|
|
|
|
CloseHandle(pMonitor->TerminateEvent);
|
|
LocalFree(pMonitor);
|
|
}
|
|
|
|
|
|
HANDLE
|
|
OpenUserProcessHandle()
|
|
{
|
|
HANDLE ProcessHandle = NULL; // handle to notify process
|
|
int rc;
|
|
//WCHAR UserName[USERNAME_LENGTH];
|
|
ULONG SessionId;
|
|
//PSID pUserSid;
|
|
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
|
|
SYSTEM_SESSION_PROCESS_INFORMATION SessionProcessInfo;
|
|
PSYSTEM_THREAD_INFORMATION ThreadInfo;
|
|
PBYTE pBuffer;
|
|
ULONG ByteCount;
|
|
NTSTATUS status;
|
|
ULONG MaxLen;
|
|
ULONG TotalOffset;
|
|
BOOL RetryIfNoneFound;
|
|
ULONG retlen = 0;
|
|
|
|
ByteCount = BUFFER_SIZE;
|
|
|
|
Retry:
|
|
RetryIfNoneFound = FALSE;
|
|
|
|
SessionProcessInfo.SessionId = g_SessionId;
|
|
|
|
for(;;) {
|
|
|
|
if ( (pBuffer = LocalAlloc(LPTR, ByteCount )) == NULL ) {
|
|
return(NULL);
|
|
}
|
|
|
|
SessionProcessInfo.Buffer = pBuffer;
|
|
SessionProcessInfo.SizeOfBuf = ByteCount;
|
|
|
|
/*
|
|
* get process info
|
|
*/
|
|
status = NtQuerySystemInformation(
|
|
SystemSessionProcessInformation,
|
|
&SessionProcessInfo,
|
|
sizeof(SessionProcessInfo),
|
|
&retlen );
|
|
|
|
if ( NT_SUCCESS(status) )
|
|
break;
|
|
|
|
/*
|
|
* Make sure buffer is big enough
|
|
*/
|
|
if ( status != STATUS_INFO_LENGTH_MISMATCH ) {
|
|
LocalFree ( pBuffer );
|
|
return(NULL);
|
|
}
|
|
|
|
LocalFree( pBuffer );
|
|
ByteCount *= 2;
|
|
}
|
|
|
|
if (retlen == 0) {
|
|
LocalFree(pBuffer);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Loop through all processes. Find first process running on this station
|
|
*/
|
|
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
|
|
TotalOffset = 0;
|
|
rc = 0;
|
|
|
|
for(;;) {
|
|
|
|
ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
|
|
|
|
// SessionId = ProcessInfo->SessionId;
|
|
|
|
// if (SessionId == g_SessionId) {
|
|
|
|
/*
|
|
* Get the User name for the SID of the process.
|
|
*/
|
|
MaxLen = USERNAME_LENGTH;
|
|
|
|
//LookupSidUser( pUserSid, UserName, &MaxLen);
|
|
|
|
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
|
|
(DWORD)(UINT_PTR)ProcessInfo->UniqueProcessId);
|
|
|
|
//
|
|
// OpenProcess may fail for System processes like csrss.exe if we do not have enough privilege
|
|
// In that case, we just skip that process because we r not worried about System processes anyway
|
|
//
|
|
if (!ProcessHandle && (GetLastError() == ERROR_ACCESS_DENIED) ) {
|
|
goto NextProcess;
|
|
}
|
|
|
|
if ( ProcessHandle && !IsSystemLUID(ProcessHandle) && !IsSystemProcess( ProcessInfo) &&
|
|
(ThreadInfo->ThreadState != 4) ) {
|
|
//
|
|
// Open the process for the monitor thread.
|
|
//
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
if (ProcessHandle) {
|
|
CloseHandle(ProcessHandle);
|
|
ProcessHandle = NULL;
|
|
} else {
|
|
|
|
//
|
|
// When OpenProcess fails, it means the process is already
|
|
// gone. This can happen if the list is sufficiently long.
|
|
// If for example the process was userinit.exe, it may have
|
|
// spawned progman and exited by the time we see the entry
|
|
// for userinit. But, since this is a snapshot of the list
|
|
// of processes, progman may not be in this snapshot. So,
|
|
// if we don't find any processes in this list, we have to
|
|
// get another snapshot to avoid prematurely logging off the
|
|
// user.
|
|
//
|
|
RetryIfNoneFound = TRUE;
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
NextProcess:
|
|
if( ProcessInfo->NextEntryOffset == 0 ) {
|
|
break;
|
|
}
|
|
TotalOffset += ProcessInfo->NextEntryOffset;
|
|
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&pBuffer[TotalOffset];
|
|
}
|
|
|
|
LocalFree( pBuffer );
|
|
|
|
if (!ProcessHandle && RetryIfNoneFound) {
|
|
Sleep(4000);
|
|
goto Retry;
|
|
}
|
|
|
|
return(ProcessHandle);
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL IsSystemLUID(HANDLE ProcessId)
|
|
{
|
|
HANDLE TokenHandle;
|
|
UCHAR TokenInformation[ sizeof( TOKEN_STATISTICS ) ];
|
|
ULONG ReturnLength;
|
|
LUID CurrentLUID = { 0, 0 };
|
|
LUID SystemLUID = SYSTEM_LUID;
|
|
NTSTATUS Status;
|
|
|
|
|
|
Status = NtOpenProcessToken( ProcessId,
|
|
TOKEN_QUERY,
|
|
&TokenHandle );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
return(TRUE);
|
|
|
|
NtQueryInformationToken( TokenHandle, TokenStatistics, &TokenInformation,
|
|
sizeof(TokenInformation), &ReturnLength );
|
|
NtClose( TokenHandle );
|
|
|
|
RtlCopyLuid(&CurrentLUID,
|
|
&(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId));
|
|
|
|
|
|
if (RtlEqualLuid(&CurrentLUID, &SystemLUID)) {
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* IsSystemProcess
|
|
*
|
|
* Return whether the given process described by SYSTEM_PROCESS_INFORMATION
|
|
* is an NT "system" process, and not a user program.
|
|
*
|
|
* ENTRY:
|
|
* pProcessInfo (input)
|
|
* Pointer to an NT SYSTEM_PROCESS_INFORMATION structure for a single
|
|
* process.
|
|
* EXIT:
|
|
* TRUE if this is an NT system process; FALSE if a general user process.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
IsSystemProcess( PSYSTEM_PROCESS_INFORMATION pSysProcessInfo)
|
|
{
|
|
DWORD dwIndex;
|
|
WCHAR *WellKnownSysProcTable[] = {
|
|
L"csrss.exe",
|
|
L"smss.exe",
|
|
L"screg.exe",
|
|
L"lsass.exe",
|
|
L"spoolss.exe",
|
|
L"EventLog.exe",
|
|
L"netdde.exe",
|
|
L"clipsrv.exe",
|
|
L"lmsvcs.exe",
|
|
L"MsgSvc.exe",
|
|
L"winlogon.exe",
|
|
L"NETSTRS.EXE",
|
|
L"nddeagnt.exe",
|
|
L"os2srv.exe",
|
|
L"wfshell.exe",
|
|
L"win.com",
|
|
L"rdpclip.exe",
|
|
L"conime.exe",
|
|
L"proquota.exe",
|
|
L"imepadsv.exe",
|
|
L"ctfmon.exe",
|
|
NULL
|
|
};
|
|
|
|
|
|
if (dwNumberofSysProcs == 0)
|
|
{
|
|
/*
|
|
* we failed to read the sys processes from registry. so lets fall back to our well known proc list.
|
|
*/
|
|
for( dwIndex=0; WellKnownSysProcTable[dwIndex]; dwIndex++) {
|
|
if ( !_wcsnicmp( pSysProcessInfo->ImageName.Buffer,
|
|
WellKnownSysProcTable[dwIndex],
|
|
pSysProcessInfo->ImageName.Length) ) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Compare its image name against some well known system image names.
|
|
*/
|
|
for( dwIndex=0; dwIndex < dwNumberofSysProcs; dwIndex++) {
|
|
if ( !_wcsnicmp( pSysProcessInfo->ImageName.Buffer,
|
|
SysProcTable[dwIndex],
|
|
pSysProcessInfo->ImageName.Length) ) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
return(FALSE);
|
|
|
|
} /* IsSystemProcess() */
|
|
|
|
|