/*
* KProcessHacker
*
* Copyright (C) 2010-2016 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see .
*/
#include
#include
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KpiOpenProcess)
#pragma alloc_text(PAGE, KpiOpenProcessToken)
#pragma alloc_text(PAGE, KpiOpenProcessJob)
#pragma alloc_text(PAGE, KpiTerminateProcess)
#pragma alloc_text(PAGE, KpiQueryInformationProcess)
#pragma alloc_text(PAGE, KpiSetInformationProcess)
#endif
/**
* Opens a process.
*
* \param ProcessHandle A variable which receives the process handle.
* \param DesiredAccess The desired access to the process.
* \param ClientId The identifier of a process or thread. If \a UniqueThread is present, the process
* of the identified thread will be opened. If \a UniqueProcess is present, the identified process
* will be opened.
* \param Key An access key.
* \li If a L2 key is provided, no access checks are performed.
* \li If a L1 key is provided, only read access is permitted but no additional access checks are
* performed.
* \li If no valid key is provided, the function fails.
* \param Client The client that initiated the request.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiOpenProcess(
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in PCLIENT_ID ClientId,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
CLIENT_ID clientId;
PEPROCESS process;
PETHREAD thread;
KPH_KEY_LEVEL requiredKeyLevel;
HANDLE processHandle;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(ProcessHandle, sizeof(HANDLE), sizeof(HANDLE));
ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
clientId = *ClientId;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
{
clientId = *ClientId;
}
// Use the thread ID if it was specified.
if (clientId.UniqueThread)
{
status = PsLookupProcessThreadByCid(&clientId, &process, &thread);
if (NT_SUCCESS(status))
{
// We don't actually need the thread.
ObDereferenceObject(thread);
}
}
else
{
status = PsLookupProcessByProcessId(clientId.UniqueProcess, &process);
}
if (!NT_SUCCESS(status))
return status;
requiredKeyLevel = KphKeyLevel1;
if ((DesiredAccess & KPH_PROCESS_READ_ACCESS) != DesiredAccess)
requiredKeyLevel = KphKeyLevel2;
if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode)))
{
// Always open in KernelMode to skip ordinary access checks.
status = ObOpenObjectByPointer(
process,
0,
NULL,
DesiredAccess,
*PsProcessType,
KernelMode,
&processHandle
);
}
ObDereferenceObject(process);
if (NT_SUCCESS(status))
{
if (AccessMode != KernelMode)
{
__try
{
*ProcessHandle = processHandle;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
*ProcessHandle = processHandle;
}
}
return status;
}
/**
* Opens the token of a process.
*
* \param ProcessHandle A handle to a process.
* \param DesiredAccess The desired access to the token.
* \param TokenHandle A variable which receives the token handle.
* \param Key An access key.
* \li If a L2 key is provided, no access checks are performed.
* \li If a L1 key is provided, only read access is permitted but no additional access checks are
* performed.
* \li If no valid key is provided, the function fails.
* \param Client The client that initiated the request.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiOpenProcessToken(
__in HANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__out PHANDLE TokenHandle,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
PACCESS_TOKEN primaryToken;
KPH_KEY_LEVEL requiredKeyLevel;
HANDLE tokenHandle;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(TokenHandle, sizeof(HANDLE), sizeof(HANDLE));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
if (primaryToken = PsReferencePrimaryToken(process))
{
requiredKeyLevel = KphKeyLevel1;
if ((DesiredAccess & KPH_TOKEN_READ_ACCESS) != DesiredAccess)
requiredKeyLevel = KphKeyLevel2;
if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode)))
{
status = ObOpenObjectByPointer(
primaryToken,
0,
NULL,
DesiredAccess,
*SeTokenObjectType,
KernelMode,
&tokenHandle
);
}
PsDereferencePrimaryToken(primaryToken);
}
else
{
status = STATUS_NO_TOKEN;
}
ObDereferenceObject(process);
if (NT_SUCCESS(status))
{
if (AccessMode != KernelMode)
{
__try
{
*TokenHandle = tokenHandle;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
*TokenHandle = tokenHandle;
}
}
return status;
}
/**
* Opens the job object of a process.
*
* \param ProcessHandle A handle to a process.
* \param DesiredAccess The desired access to the job.
* \param JobHandle A variable which receives the job object handle.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiOpenProcessJob(
__in HANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__out PHANDLE JobHandle,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
PEJOB job;
HANDLE jobHandle = NULL;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(JobHandle, sizeof(HANDLE), sizeof(HANDLE));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
job = PsGetProcessJob(process);
if (job)
{
status = ObOpenObjectByPointer(
job,
0,
NULL,
DesiredAccess,
*PsJobType,
AccessMode,
&jobHandle
);
}
else
{
status = STATUS_NOT_FOUND;
}
ObDereferenceObject(process);
if (NT_SUCCESS(status))
{
if (AccessMode != KernelMode)
{
__try
{
*JobHandle = jobHandle;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
*JobHandle = jobHandle;
}
}
return status;
}
/**
* Terminates a process.
*
* \param ProcessHandle A handle to a process.
* \param ExitStatus A status value which indicates why the process is being terminated.
* \param Key An access key.
* \li If a L2 key is provided, no access checks are performed.
* \li If no valid L2 key is provided, the function fails.
* \param Client The client that initiated the request.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiTerminateProcess(
__in HANDLE ProcessHandle,
__in NTSTATUS ExitStatus,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
PAGED_CODE();
if (!NT_SUCCESS(status = KphValidateKey(KphKeyLevel2, Key, Client, AccessMode)))
return status;
status = ObReferenceObjectByHandle(
ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
if (process != PsGetCurrentProcess())
{
HANDLE newProcessHandle;
// Re-open the process to get a kernel handle.
if (NT_SUCCESS(status = ObOpenObjectByPointer(
process,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_TERMINATE,
*PsProcessType,
KernelMode,
&newProcessHandle
)))
{
status = ZwTerminateProcess(newProcessHandle, ExitStatus);
ZwClose(newProcessHandle);
}
}
else
{
status = STATUS_CANT_TERMINATE_SELF;
}
ObDereferenceObject(process);
return status;
}
/**
* Queries process information.
*
* \param ProcessHandle A handle to a process.
* \param ProcessInformationClass The type of information to query.
* \param ProcessInformation The buffer in which the information will be stored.
* \param ProcessInformationLength The number of bytes available in \a ProcessInformation.
* \param ReturnLength A variable which receives the number of bytes required to be available in
* \a ProcessInformation.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiQueryInformationProcess(
__in HANDLE ProcessHandle,
__in KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass,
__out_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
ULONG returnLength;
PAGED_CODE();
if (AccessMode != KernelMode)
{
ULONG alignment;
switch (ProcessInformationClass)
{
default:
alignment = sizeof(ULONG);
break;
}
__try
{
ProbeForWrite(ProcessInformation, ProcessInformationLength, alignment);
if (ReturnLength)
ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ProcessHandle,
PROCESS_QUERY_INFORMATION,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
switch (ProcessInformationClass)
{
default:
status = STATUS_INVALID_INFO_CLASS;
returnLength = 0;
break;
}
ObDereferenceObject(process);
if (ReturnLength)
{
if (AccessMode != KernelMode)
{
__try
{
*ReturnLength = returnLength;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
NOTHING;
}
}
else
{
*ReturnLength = returnLength;
}
}
return status;
}
/**
* Sets process information.
*
* \param ProcessHandle A handle to a process.
* \param ProcessInformationClass The type of information to set.
* \param ProcessInformation A buffer which contains the information to set.
* \param ProcessInformationLength The number of bytes present in \a ProcessInformation.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiSetInformationProcess(
__in HANDLE ProcessHandle,
__in KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass,
__in_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
PAGED_CODE();
if (AccessMode != KernelMode)
{
ULONG alignment;
switch (ProcessInformationClass)
{
default:
alignment = sizeof(ULONG);
break;
}
__try
{
ProbeForRead(ProcessInformation, ProcessInformationLength, alignment);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ProcessHandle,
PROCESS_SET_INFORMATION,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
switch (ProcessInformationClass)
{
default:
status = STATUS_INVALID_INFO_CLASS;
break;
}
ObDereferenceObject(process);
return status;
}