|
|
/****************************************************************************
Copyright (c) Microsoft Corporation 1999 All rights reserved ***************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <winsock2.h>
#include <ntexapi.h>
#include <devioctl.h>
#include <stdlib.h>
#include <rccxport.h>
#include "rcclib.h"
#include "error.h"
//
// Defines for our internal memory, starting size and then buffers are grown by the increment
//
#define START_MEMORY_SIZE 0x100000
#define MEMORY_INCREMENT 0x1000
BOOL EnableDebugPriv( IN PVOID GlobalBuffer );
DWORD RCCLibReportEventA( DWORD EventID, DWORD EventType, DWORD NumStrings, DWORD DataLength, LPSTR *Strings, LPVOID Data ); HANDLE RCCLibSemHandle = NULL;
DWORD RCCLibInit( OUT PVOID *GlobalBuffer, OUT PULONG GlobalBufferCurrentSize ) { NTSTATUS Status; DWORD Error; ULONG Priority; PPROCESS_PRIORITY_CLASS PriorityClass; //
// Check if another copy of this exe is running already
//
RCCLibSemHandle = CreateSemaphore(NULL, 1, 1, "RCCLibSem");
Error = GetLastError();
if (RCCLibSemHandle == NULL) { RCCLibReportEventA(ERROR_RCCLIB_CREATE_SEM_FAILED, EVENTLOG_ERROR_TYPE, 0, sizeof(DWORD), NULL, &Error ); return ERROR_RCCLIB_CREATE_SEM_FAILED; }
if (Error == ERROR_ALREADY_EXISTS) { CloseHandle(RCCLibSemHandle); RCCLibReportEventA(ERROR_RCCLIB_ALREADY_RUNNING, EVENTLOG_ERROR_TYPE, 0, sizeof(DWORD), NULL, &Error ); return ERROR_RCCLIB_ALREADY_RUNNING; }
//
// Allocate memory for our buffers
//
*GlobalBuffer = LocalAlloc(LPTR, START_MEMORY_SIZE * sizeof(char));
if (*GlobalBuffer == NULL) { CloseHandle(RCCLibSemHandle); RCCLibSemHandle = NULL; RCCLibReportEventA(ERROR_RCCLIB_INITIAL_ALLOC_FAILED, EVENTLOG_ERROR_TYPE, 0, 0, NULL, NULL ); return ERROR_RCCLIB_INITIAL_ALLOC_FAILED; }
*GlobalBufferCurrentSize = START_MEMORY_SIZE * sizeof(char);
//
// Give ourselve God access if possible
//
if (!EnableDebugPriv(*GlobalBuffer)) { LocalFree(*GlobalBuffer); CloseHandle(RCCLibSemHandle); RCCLibSemHandle = NULL; Error = GetLastError(); RCCLibReportEventA(ERROR_RCCLIB_PRIVILEDGES_FAILED, EVENTLOG_WARNING_TYPE, 0, sizeof(DWORD), NULL, &Error ); return Error; }
//
// Now try and bump up our priority so that we can guarantee service
//
PriorityClass = (PPROCESS_PRIORITY_CLASS)(*GlobalBuffer); PriorityClass = (PPROCESS_PRIORITY_CLASS)(ALIGN_UP_POINTER(PriorityClass, PROCESS_PRIORITY_CLASS));
Status = NtQueryInformationProcess(NtCurrentProcess(), ProcessPriorityClass, PriorityClass, sizeof(PROCESS_PRIORITY_CLASS), NULL );
if (!NT_SUCCESS(Status)) { LocalFree(*GlobalBuffer); CloseHandle(RCCLibSemHandle); RCCLibSemHandle = NULL; Error = RtlNtStatusToDosError(Status); RCCLibReportEventA(ERROR_RCCLIB_QUERY_PRIORITY_FAILED, EVENTLOG_ERROR_TYPE, 0, sizeof(DWORD), NULL, &Error ); return Error; }
PriorityClass->PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME;
Status = NtSetInformationProcess(NtCurrentProcess(), ProcessPriorityClass, PriorityClass, sizeof(PROCESS_PRIORITY_CLASS) );
if (!NT_SUCCESS(Status)) { LocalFree(*GlobalBuffer); CloseHandle(RCCLibSemHandle); RCCLibSemHandle = NULL; Error = RtlNtStatusToDosError(Status); RCCLibReportEventA(ERROR_RCCLIB_SET_PRIORITY_FAILED, EVENTLOG_ERROR_TYPE, 0, sizeof(DWORD), NULL, &Error ); return Error; }
//
// A *total* DaveC hack is that if you use ThreadBasePriority, and send
// down (HIGH_PRIORITY + 1) / 2, this actually puts you at HIGH_PRIORITY
// such that you cannot be bumped back down in priority... NT trivia for you.
//
Priority = (HIGH_PRIORITY + 1) / 2; Status = NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, &Priority, sizeof(Priority) );
if (!NT_SUCCESS(Status)) { //
// Log an error!
//
Error = RtlNtStatusToDosError(Status); RCCLibReportEventA(ERROR_RCCLIB_SET_PRIORITY_FAILED, EVENTLOG_ERROR_TYPE, 0, sizeof(DWORD), NULL, &Error ); return Error; } }
VOID RCCLibExit( IN PVOID GlobalBuffer, IN ULONG GlobalBufferSize ) { if (RCCLibSemHandle != NULL) { CloseHandle(RCCLibSemHandle); RCCLibSemHandle = NULL; } if (GlobalBuffer != NULL) { LocalFree(GlobalBuffer); } }
DWORD RCCLibReportEventA( DWORD EventID, DWORD EventType, DWORD NumStrings, DWORD DataLength, LPSTR *Strings, LPVOID Data ) /*++
Routine Description:
This function writes the specified (EventID) log at the end of the eventlog.
Arguments:
EventID - The specific event identifier. This identifies the message that goes with this event.
EventType - Specifies the type of event being logged. This parameter can have one of the following
values:
Value Meaning
EVENTLOG_ERROR_TYPE Error event EVENTLOG_WARNING_TYPE Warning event EVENTLOG_INFORMATION_TYPE Information event
NumStrings - Specifies the number of strings that are in the array at 'Strings'. A value of zero indicates no strings are present.
DataLength - Specifies the number of bytes of event-specific raw (binary) data to write to the log. If cbData is zero, no event-specific data is present.
Strings - Points to a buffer containing an array of null-terminated strings that are merged into the message before displaying to the user. This parameter must be a valid pointer (or NULL), even if cStrings is zero.
Data - Buffer containing the raw data. This parameter must be a valid pointer (or NULL), even if cbData is zero.
Return Value:
Returns the WIN32 extended error obtained by GetLastError().
NOTE : This function works slow since it calls the open and close eventlog source everytime.
--*/ { HANDLE EventlogHandle; DWORD ReturnCode;
//
// open eventlog section.
//
EventlogHandle = RegisterEventSourceW(NULL, L"RCCLib");
if (EventlogHandle == NULL) { ReturnCode = GetLastError(); goto Cleanup; }
//
// Log the error code specified
//
if(!ReportEventA(EventlogHandle, (WORD)EventType, 0, // event category
EventID, NULL, (WORD)NumStrings, DataLength, Strings, Data )) {
ReturnCode = GetLastError(); goto Cleanup; }
ReturnCode = ERROR_SUCCESS;
Cleanup:
if (EventlogHandle != NULL) { DeregisterEventSource(EventlogHandle); }
return ReturnCode; }
BOOL EnableDebugPriv( IN PVOID GlobalBuffer )
/*++
Routine Description:
Changes the process's privilige so that kill works properly.
Arguments:
Return Value:
TRUE - success FALSE - failure
--*/
{ HANDLE hToken; LUID DebugValue; PTOKEN_PRIVILEGES ptkp;
//
// Retrieve a handle of the access token
//
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return FALSE; }
//
// Enable the SE_DEBUG_NAME privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_DEBUG_NAME, &DebugValue)) { return FALSE; }
ptkp = (PTOKEN_PRIVILEGES)GlobalBuffer;
ptkp->PrivilegeCount = 4; ptkp->Privileges[0].Luid = DebugValue; ptkp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the INCREASE_BASE_PRIORITY privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_INC_BASE_PRIORITY_NAME, &DebugValue)) { return FALSE; }
ptkp->Privileges[1].Luid = DebugValue; ptkp->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the SHUTDOWN privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_SHUTDOWN_NAME, &DebugValue)) { return FALSE; }
ptkp->Privileges[2].Luid = DebugValue; ptkp->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the QUOTA privilege.
//
if (!LookupPrivilegeValue((LPSTR) NULL, SE_INCREASE_QUOTA_NAME, &DebugValue)) { return FALSE; }
ptkp->Privileges[3].Luid = DebugValue; ptkp->Privileges[3].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, ptkp, sizeof(TOKEN_PRIVILEGES) + (3 * sizeof(LUID_AND_ATTRIBUTES)), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL)) { //
// The return value of AdjustTokenPrivileges be texted
//
return FALSE; }
return TRUE; }
DWORD RCCLibGetTListInfo( OUT PRCC_RSP_TLIST ResponseBuffer, IN LONG ResponseBufferSize, OUT PULONG ResponseDataSize ) { DWORD Error; NTSTATUS Status; TIME_FIELDS UpTime; LARGE_INTEGER Time; PSYSTEM_PAGEFILE_INFORMATION PageFileInfo;
PUCHAR DataBuffer; PUCHAR StartProcessInfo; LONG CurrentBufferSize; ULONG ReturnLength; ULONG TotalOffset; PSYSTEM_PROCESS_INFORMATION ProcessInfo; PSYSTEM_THREAD_INFORMATION ThreadInfo;
*ResponseDataSize = 0;
if (ResponseBufferSize < sizeof(ResponseBuffer)) { return(RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH)); } DataBuffer = (PUCHAR)(ResponseBuffer + 1); CurrentBufferSize = ResponseBufferSize - sizeof(RCC_RSP_TLIST); if (CurrentBufferSize < 0) { return ERROR_OUTOFMEMORY; }
//
// Get system-wide information
//
Status = NtQuerySystemInformation(SystemTimeOfDayInformation, &(ResponseBuffer->TimeOfDayInfo), sizeof(SYSTEM_TIMEOFDAY_INFORMATION), NULL );
if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); }
Status = NtQuerySystemInformation(SystemBasicInformation, &(ResponseBuffer->BasicInfo), sizeof(SYSTEM_BASIC_INFORMATION), NULL );
if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); }
//
// Get pagefile information
//
PageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)DataBuffer; Status = NtQuerySystemInformation(SystemPageFileInformation, PageFileInfo, CurrentBufferSize, &ReturnLength );
if (NT_SUCCESS(Status)) {
ResponseBuffer->PagefileInfoOffset = ResponseBufferSize - CurrentBufferSize; CurrentBufferSize -= ReturnLength; DataBuffer += ReturnLength; if (CurrentBufferSize < 0) { return ERROR_OUTOFMEMORY; }
//
// Go thru each pagefile and fixup the names...
//
for (; ; ) {
if (PageFileInfo->PageFileName.Length > CurrentBufferSize) { return(RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH)); }
RtlCopyMemory(DataBuffer, (PUCHAR)(PageFileInfo->PageFileName.Buffer), PageFileInfo->PageFileName.Length );
PageFileInfo->PageFileName.Buffer = (PWSTR)(ResponseBufferSize - CurrentBufferSize); DataBuffer += PageFileInfo->PageFileName.Length; CurrentBufferSize -= PageFileInfo->PageFileName.Length;
if (CurrentBufferSize < 0) { return ERROR_OUTOFMEMORY; }
if (PageFileInfo->NextEntryOffset == 0) { break; }
PageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR)PageFileInfo + PageFileInfo->NextEntryOffset); }
} else if (((ULONG)CurrentBufferSize) < ReturnLength) { return(RtlNtStatusToDosError(STATUS_INFO_LENGTH_MISMATCH)); } else {
ResponseBuffer->PagefileInfoOffset = 0;
}
//
// Get process information
//
Status = NtQuerySystemInformation(SystemFileCacheInformation, &(ResponseBuffer->FileCache), sizeof(ResponseBuffer->FileCache), NULL );
if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); }
Status = NtQuerySystemInformation(SystemPerformanceInformation, &(ResponseBuffer->PerfInfo), sizeof(ResponseBuffer->PerfInfo), NULL );
if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); }
//
// Realign DataBuffer for the next query
//
DataBuffer = ALIGN_UP_POINTER(DataBuffer, SYSTEM_PROCESS_INFORMATION); CurrentBufferSize = ResponseBufferSize - (((ULONG_PTR)DataBuffer) - ((ULONG_PTR)ResponseBuffer)); if (CurrentBufferSize < 0) { return ERROR_OUTOFMEMORY; }
Status = NtQuerySystemInformation(SystemProcessInformation, DataBuffer, CurrentBufferSize, &ReturnLength );
if (!NT_SUCCESS(Status)) { return(RtlNtStatusToDosError(Status)); }
StartProcessInfo = DataBuffer;
ResponseBuffer->ProcessInfoOffset = ResponseBufferSize - CurrentBufferSize; DataBuffer += ReturnLength; CurrentBufferSize -= ReturnLength;
if (CurrentBufferSize < 0) { return ERROR_OUTOFMEMORY; }
TotalOffset = 0; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)StartProcessInfo;
while (TRUE) {
//
// We have to take the name of each process and change the UNICODE_STRING to an offset, copying
// the name to later in the buffer.
//
if (ProcessInfo->ImageName.Buffer) {
if (CurrentBufferSize < ProcessInfo->ImageName.Length) { return(STATUS_INFO_LENGTH_MISMATCH); }
RtlCopyMemory(DataBuffer, (PUCHAR)(ProcessInfo->ImageName.Buffer), ProcessInfo->ImageName.Length);
ProcessInfo->ImageName.Buffer = (PWSTR)(ResponseBufferSize - CurrentBufferSize);
DataBuffer += ProcessInfo->ImageName.Length; CurrentBufferSize -= ProcessInfo->ImageName.Length; if (CurrentBufferSize < 0) { return ERROR_OUTOFMEMORY; }
}
if (ProcessInfo->NextEntryOffset == 0) { break; }
TotalOffset += ProcessInfo->NextEntryOffset; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&(StartProcessInfo[TotalOffset]); }
*ResponseDataSize = (ULONG)(ResponseBufferSize - CurrentBufferSize);
return ERROR_SUCCESS; }
DWORD RCCLibKillProcess( DWORD ProcessId ) { DWORD Error; HANDLE Handle;
//
// Try to open the process
//
Handle = OpenProcess(PROCESS_TERMINATE, FALSE, ProcessId); if (Handle == NULL) { return GetLastError(); }
//
// Kill it
//
if (!TerminateProcess(Handle, 1)) { CloseHandle(Handle); return GetLastError(); }
//
// All done
//
CloseHandle(Handle); return ERROR_SUCCESS; }
DWORD RCCLibLowerProcessPriority( DWORD ProcessId ) { DWORD Error; HANDLE JobHandle; HANDLE ProcessHandle; JOBOBJECT_EXTENDED_LIMIT_INFORMATION ProposedLimits; TCHAR NameBuffer[50]; BOOLEAN CreatedJobObject; DWORD ReturnedLength;
//
// Create the name for the job object
//
sprintf(NameBuffer, "RCCSrv%d", ProcessId);
//
// Try and open the existing job object
//
JobHandle = OpenJobObject(MAXIMUM_ALLOWED, FALSE, NameBuffer);
if (JobHandle == NULL) { //
// Try to open the process
//
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (ProcessHandle == NULL) { return GetLastError(); }
//
// Now try and create a job object to wrap around this process.
//
JobHandle = CreateJobObject(NULL, NameBuffer);
if (JobHandle == NULL) { CloseHandle(ProcessHandle); return GetLastError(); }
CreatedJobObject = TRUE;
//
// Assign the process to this new job object.
//
if (!AssignProcessToJobObject(JobHandle, ProcessHandle)) { CloseHandle(ProcessHandle); goto ErrorExit; }
CloseHandle(ProcessHandle);
} else {
CreatedJobObject = FALSE;
}
//
// Get the current set of limits
//
if (!QueryInformationJobObject(JobHandle, JobObjectExtendedLimitInformation, &ProposedLimits, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), &ReturnedLength )) { goto ErrorExit; }
//
// Change the scheduling class and priority fields
//
ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS; ProposedLimits.BasicLimitInformation.PriorityClass = IDLE_PRIORITY_CLASS; ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SCHEDULING_CLASS; ProposedLimits.BasicLimitInformation.SchedulingClass = 0;
if (!SetInformationJobObject(JobHandle, JobObjectExtendedLimitInformation, &ProposedLimits, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION) )) { goto ErrorExit;
}
//
// All done - leave the job handle out there, so we can get to it later.
//
return ERROR_SUCCESS;
ErrorExit:
if (CreatedJobObject) { CloseHandle(JobHandle); } return GetLastError(); }
DWORD RCCLibLimitProcessMemory( DWORD ProcessId, DWORD MemoryLimit // in number of KB allowed
) { DWORD Error; HANDLE JobHandle; HANDLE ProcessHandle; JOBOBJECT_EXTENDED_LIMIT_INFORMATION ProposedLimits; TCHAR NameBuffer[50]; BOOLEAN CreatedJobObject; DWORD ReturnedLength;
//
// Create the name for the job object
//
sprintf(NameBuffer, "RCCSrv%d", ProcessId);
//
// Try and open the existing job object
//
JobHandle = OpenJobObject(MAXIMUM_ALLOWED, FALSE, NameBuffer);
if (JobHandle == NULL) { //
// Try to open the process
//
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (ProcessHandle == NULL) { return GetLastError(); }
//
// Now try and create a job object to wrap around this process.
//
JobHandle = CreateJobObject(NULL, NameBuffer);
if (JobHandle == NULL) { CloseHandle(ProcessHandle); return GetLastError(); }
CreatedJobObject = TRUE;
//
// Assign the process to this new job object.
//
if (!AssignProcessToJobObject(JobHandle, ProcessHandle)) { CloseHandle(ProcessHandle); goto ErrorExit; }
CloseHandle(ProcessHandle);
} else {
CreatedJobObject = FALSE;
}
//
// Get the current set of limits
//
if (!QueryInformationJobObject(JobHandle, JobObjectExtendedLimitInformation, &ProposedLimits, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), &ReturnedLength )) { goto ErrorExit; }
//
// Change the memory limits
//
ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY; ProposedLimits.ProcessMemoryLimit = MemoryLimit * 1024 * 1024; ProposedLimits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY; ProposedLimits.JobMemoryLimit = MemoryLimit * 1024 * 1024;
if (!SetInformationJobObject(JobHandle, JobObjectExtendedLimitInformation, &ProposedLimits, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION) )) { goto ErrorExit;
}
//
// All done - leave the job handle out there, so we can get to it later.
//
return ERROR_SUCCESS;
ErrorExit:
if (CreatedJobObject) { CloseHandle(JobHandle); } return GetLastError(); }
DWORD RCCLibIncreaseMemory( OUT PVOID *GlobalBuffer, OUT PULONG GlobalBufferCurrentSize ) { NTSTATUS Status; PVOID NewBuffer; NewBuffer = VirtualAlloc(NULL, *GlobalBufferCurrentSize + MEMORY_INCREMENT, MEM_COMMIT, PAGE_READWRITE | PAGE_NOCACHE );
if (NewBuffer == NULL) { return ERROR_OUTOFMEMORY; } VirtualFree(*GlobalBuffer, *GlobalBufferCurrentSize, MEM_DECOMMIT); *GlobalBufferCurrentSize += MEMORY_INCREMENT; *GlobalBuffer = NewBuffer; return ERROR_SUCCESS; }
|