go my file uploader

This commit is contained in:
AirDog46
2025-05-13 19:45:22 +03:00
commit c5fab8aa94
708 changed files with 343216 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Win8 Debug|Win32">
<Configuration>Win8 Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Win8 Release|Win32">
<Configuration>Win8 Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Win8 Debug|x64">
<Configuration>Win8 Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Win8 Release|x64">
<Configuration>Win8 Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F4853009-C5D2-4A25-BE4D-BB0D9F84E2FF}</ProjectGuid>
<TemplateGuid>{dd38f7fc-d7bd-488b-9242-7d8754cde80d}</TemplateGuid>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<Configuration>Win8 Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
</PropertyGroup>
<PropertyGroup Label="Globals">
<RootNamespace>KProcessHacker</RootNamespace>
<VCTargetsPath Condition="'$(VCTargetsPath11)' != '' and '$(VisualStudioVersion)' == '11.0'">$(VCTargetsPath11)</VCTargetsPath>
</PropertyGroup>
<PropertyGroup Label="PropertySheets">
<PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'">
<OutDir>$(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\</OutDir>
<IntDir>$(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\</IntDir>
<TargetName>kprocesshacker</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">
<OutDir>$(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\</OutDir>
<IntDir>$(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\</IntDir>
<TargetName>kprocesshacker</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'">
<OutDir>$(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\</OutDir>
<IntDir>$(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\</IntDir>
<TargetName>kprocesshacker</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">
<OutDir>$(ProjectDir)bin\$(Configuration) $(PlatformArchitecture)\</OutDir>
<IntDir>$(ProjectDir)obj\$(Configuration) $(PlatformArchitecture)\</IntDir>
<TargetName>kprocesshacker</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>KPH_CONFIG_CLEAN;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>KPH_CONFIG_CLEAN;_X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'">
<ClCompile>
<AdditionalIncludeDirectories>../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>KPH_CONFIG_CLEAN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>../phnt/include;../phlib/include;include;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>KPH_CONFIG_CLEAN;_WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<FilesToPackage Include="$(TargetPath)" />
<FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="devctrl.c" />
<ClCompile Include="dyndata.c" />
<ClCompile Include="dynimp.c" />
<ClCompile Include="main.c" />
<ClCompile Include="object.c" />
<ClCompile Include="process.c" />
<ClCompile Include="qrydrv.c" />
<ClCompile Include="thread.c" />
<ClCompile Include="vm.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="resource.rc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\dyndata.h" />
<ClInclude Include="include\kph.h" />
<ClInclude Include="include\ntfill.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Driver Files">
<UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
<Extensions>inf;inv;inx;mof;mc;</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="devctrl.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dyndata.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dynimp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="object.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="process.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="qrydrv.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="thread.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vm.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="resource.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\dyndata.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\kph.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\ntfill.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
!INCLUDE $(NTMAKEENV)\makefile.def

View File

@@ -0,0 +1,7 @@
!IF 0
This builds a clean version of KProcessHacker suitable for driver signing.
!ENDIF
!include ..\sources.inc

566
KProcessHacker/devctrl.c Normal file
View File

@@ -0,0 +1,566 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
NTSTATUS KphDispatchDeviceControl(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP Irp
)
{
NTSTATUS status;
PIO_STACK_LOCATION stackLocation;
PFILE_OBJECT fileObject;
PKPH_CLIENT client;
PVOID originalInput;
ULONG inputLength;
ULONG ioControlCode;
KPROCESSOR_MODE accessMode;
UCHAR capturedInput[16 * sizeof(ULONG_PTR)];
PVOID capturedInputPointer;
#define VERIFY_INPUT_LENGTH \
do { \
/* Ensure at compile time that our local buffer fits this particular call. */ \
C_ASSERT(sizeof(*input) <= sizeof(capturedInput)); \
\
if (inputLength != sizeof(*input)) \
{ \
status = STATUS_INFO_LENGTH_MISMATCH; \
goto ControlEnd; \
} \
} while (0)
stackLocation = IoGetCurrentIrpStackLocation(Irp);
fileObject = stackLocation->FileObject;
client = fileObject->FsContext;
originalInput = stackLocation->Parameters.DeviceIoControl.Type3InputBuffer;
inputLength = stackLocation->Parameters.DeviceIoControl.InputBufferLength;
ioControlCode = stackLocation->Parameters.DeviceIoControl.IoControlCode;
accessMode = Irp->RequestorMode;
// Make sure we have a client object.
if (!client)
{
status = STATUS_INTERNAL_ERROR;
goto ControlEnd;
}
// Enforce signature requirement if necessary.
if ((ioControlCode != KPH_GETFEATURES && ioControlCode != KPH_VERIFYCLIENT) &&
(KphParameters.SecurityLevel == KphSecuritySignatureCheck ||
KphParameters.SecurityLevel == KphSecuritySignatureAndPrivilegeCheck) &&
!client->VerificationSucceeded)
{
status = STATUS_ACCESS_DENIED;
goto ControlEnd;
}
// Make sure we actually have input if the input length is non-zero.
if (inputLength != 0 && !originalInput)
{
status = STATUS_INVALID_BUFFER_SIZE;
goto ControlEnd;
}
// Make sure the caller isn't giving us a huge buffer. If they are, it can't be correct because
// we have a compile-time check that makes sure our buffer can store the arguments for all the
// calls.
if (inputLength > sizeof(capturedInput))
{
status = STATUS_INVALID_BUFFER_SIZE;
goto ControlEnd;
}
// Probe and capture the input buffer.
if (accessMode != KernelMode)
{
__try
{
ProbeForRead(originalInput, inputLength, sizeof(UCHAR));
memcpy(capturedInput, originalInput, inputLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto ControlEnd;
}
}
else
{
memcpy(capturedInput, originalInput, inputLength);
}
capturedInputPointer = capturedInput; // avoid casting below
switch (ioControlCode)
{
case KPH_GETFEATURES:
{
struct
{
PULONG Features;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiGetFeatures(
input->Features,
accessMode
);
}
break;
case KPH_VERIFYCLIENT:
{
struct
{
PVOID CodeAddress;
PVOID Signature;
ULONG SignatureSize;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
if (accessMode == UserMode)
{
status = KpiVerifyClient(
input->CodeAddress,
input->Signature,
input->SignatureSize,
client
);
}
else
{
status = STATUS_UNSUCCESSFUL;
}
}
break;
case KPH_RETRIEVEKEY:
{
struct
{
KPH_KEY_LEVEL KeyLevel;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
if (accessMode == UserMode)
{
status = KphRetrieveKeyViaApc(
client,
input->KeyLevel,
Irp
);
}
else
{
status = STATUS_UNSUCCESSFUL;
}
}
break;
case KPH_OPENPROCESS:
{
struct
{
PHANDLE ProcessHandle;
ACCESS_MASK DesiredAccess;
PCLIENT_ID ClientId;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenProcess(
input->ProcessHandle,
input->DesiredAccess,
input->ClientId,
input->Key,
client,
accessMode
);
}
break;
case KPH_OPENPROCESSTOKEN:
{
struct
{
HANDLE ProcessHandle;
ACCESS_MASK DesiredAccess;
PHANDLE TokenHandle;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenProcessToken(
input->ProcessHandle,
input->DesiredAccess,
input->TokenHandle,
input->Key,
client,
accessMode
);
}
break;
case KPH_OPENPROCESSJOB:
{
struct
{
HANDLE ProcessHandle;
ACCESS_MASK DesiredAccess;
PHANDLE JobHandle;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenProcessJob(
input->ProcessHandle,
input->DesiredAccess,
input->JobHandle,
accessMode
);
}
break;
case KPH_TERMINATEPROCESS:
{
struct
{
HANDLE ProcessHandle;
NTSTATUS ExitStatus;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiTerminateProcess(
input->ProcessHandle,
input->ExitStatus,
input->Key,
client,
accessMode
);
}
break;
case KPH_READVIRTUALMEMORYUNSAFE:
{
struct
{
HANDLE ProcessHandle;
PVOID BaseAddress;
PVOID Buffer;
SIZE_T BufferSize;
PSIZE_T NumberOfBytesRead;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiReadVirtualMemoryUnsafe(
input->ProcessHandle,
input->BaseAddress,
input->Buffer,
input->BufferSize,
input->NumberOfBytesRead,
input->Key,
client,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONPROCESS:
{
struct
{
HANDLE ProcessHandle;
KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass;
PVOID ProcessInformation;
ULONG ProcessInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationProcess(
input->ProcessHandle,
input->ProcessInformationClass,
input->ProcessInformation,
input->ProcessInformationLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_SETINFORMATIONPROCESS:
{
struct
{
HANDLE ProcessHandle;
KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass;
PVOID ProcessInformation;
ULONG ProcessInformationLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiSetInformationProcess(
input->ProcessHandle,
input->ProcessInformationClass,
input->ProcessInformation,
input->ProcessInformationLength,
accessMode
);
}
break;
case KPH_OPENTHREAD:
{
struct
{
PHANDLE ThreadHandle;
ACCESS_MASK DesiredAccess;
PCLIENT_ID ClientId;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenThread(
input->ThreadHandle,
input->DesiredAccess,
input->ClientId,
input->Key,
client,
accessMode
);
}
break;
case KPH_OPENTHREADPROCESS:
{
struct
{
HANDLE ThreadHandle;
ACCESS_MASK DesiredAccess;
PHANDLE ProcessHandle;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenThreadProcess(
input->ThreadHandle,
input->DesiredAccess,
input->ProcessHandle,
accessMode
);
}
break;
case KPH_CAPTURESTACKBACKTRACETHREAD:
{
struct
{
HANDLE ThreadHandle;
ULONG FramesToSkip;
ULONG FramesToCapture;
PVOID *BackTrace;
PULONG CapturedFrames;
PULONG BackTraceHash;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiCaptureStackBackTraceThread(
input->ThreadHandle,
input->FramesToSkip,
input->FramesToCapture,
input->BackTrace,
input->CapturedFrames,
input->BackTraceHash,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONTHREAD:
{
struct
{
HANDLE ThreadHandle;
KPH_THREAD_INFORMATION_CLASS ThreadInformationClass;
PVOID ThreadInformation;
ULONG ThreadInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationThread(
input->ThreadHandle,
input->ThreadInformationClass,
input->ThreadInformation,
input->ThreadInformationLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_SETINFORMATIONTHREAD:
{
struct
{
HANDLE ThreadHandle;
KPH_THREAD_INFORMATION_CLASS ThreadInformationClass;
PVOID ThreadInformation;
ULONG ThreadInformationLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiSetInformationThread(
input->ThreadHandle,
input->ThreadInformationClass,
input->ThreadInformation,
input->ThreadInformationLength,
accessMode
);
}
break;
case KPH_ENUMERATEPROCESSHANDLES:
{
struct
{
HANDLE ProcessHandle;
PVOID Buffer;
ULONG BufferLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiEnumerateProcessHandles(
input->ProcessHandle,
input->Buffer,
input->BufferLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONOBJECT:
{
struct
{
HANDLE ProcessHandle;
HANDLE Handle;
KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass;
PVOID ObjectInformation;
ULONG ObjectInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationObject(
input->ProcessHandle,
input->Handle,
input->ObjectInformationClass,
input->ObjectInformation,
input->ObjectInformationLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_SETINFORMATIONOBJECT:
{
struct
{
HANDLE ProcessHandle;
HANDLE Handle;
KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass;
PVOID ObjectInformation;
ULONG ObjectInformationLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiSetInformationObject(
input->ProcessHandle,
input->Handle,
input->ObjectInformationClass,
input->ObjectInformation,
input->ObjectInformationLength,
accessMode
);
}
break;
case KPH_OPENDRIVER:
{
struct
{
PHANDLE DriverHandle;
ACCESS_MASK DesiredAccess;
POBJECT_ATTRIBUTES ObjectAttributes;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenDriver(
input->DriverHandle,
input->DesiredAccess,
input->ObjectAttributes,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONDRIVER:
{
struct
{
HANDLE DriverHandle;
DRIVER_INFORMATION_CLASS DriverInformationClass;
PVOID DriverInformation;
ULONG DriverInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationDriver(
input->DriverHandle,
input->DriverInformationClass,
input->DriverInformation,
input->DriverInformationLength,
input->ReturnLength,
accessMode
);
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
ControlEnd:
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}

1
KProcessHacker/dirs Normal file
View File

@@ -0,0 +1 @@
DIRS=clean

167
KProcessHacker/dyndata.c Normal file
View File

@@ -0,0 +1,167 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
#define _DYNDATA_PRIVATE
#include <dyndata.h>
#define C_2sTo4(x) ((unsigned int)(signed short)(x))
NTSTATUS KphpLoadDynamicConfiguration(
__in PVOID Buffer,
__in ULONG Length
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KphDynamicDataInitialization)
#pragma alloc_text(PAGE, KphReadDynamicDataParameters)
#pragma alloc_text(PAGE, KphpLoadDynamicConfiguration)
#endif
NTSTATUS KphDynamicDataInitialization(
VOID
)
{
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
// Get Windows version information.
KphDynOsVersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
status = RtlGetVersion((PRTL_OSVERSIONINFOW)&KphDynOsVersionInfo);
return status;
}
NTSTATUS KphReadDynamicDataParameters(
__in_opt HANDLE KeyHandle
)
{
NTSTATUS status;
UNICODE_STRING valueName;
PKEY_VALUE_PARTIAL_INFORMATION info;
ULONG resultLength;
PAGED_CODE();
if (!KeyHandle)
return STATUS_UNSUCCESSFUL;
RtlInitUnicodeString(&valueName, L"DynamicConfiguration");
status = ZwQueryValueKey(
KeyHandle,
&valueName,
KeyValuePartialInformation,
NULL,
0,
&resultLength
);
if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL)
{
// Unexpected status; fail now.
return STATUS_UNSUCCESSFUL;
}
info = ExAllocatePoolWithTag(PagedPool, resultLength, 'ThpK');
if (!info)
return STATUS_INSUFFICIENT_RESOURCES;
status = ZwQueryValueKey(
KeyHandle,
&valueName,
KeyValuePartialInformation,
info,
resultLength,
&resultLength
);
if (NT_SUCCESS(status))
{
if (info->Type == REG_BINARY)
status = KphpLoadDynamicConfiguration(info->Data, info->DataLength);
else
status = STATUS_OBJECT_TYPE_MISMATCH;
if (!NT_SUCCESS(status))
dprintf("Unable to load dynamic configuration: 0x%x\n", status);
}
ExFreePoolWithTag(info, 'ThpK');
return status;
}
NTSTATUS KphpLoadDynamicConfiguration(
__in PVOID Buffer,
__in ULONG Length
)
{
PKPH_DYN_CONFIGURATION config;
ULONG i;
PKPH_DYN_PACKAGE package;
PAGED_CODE();
config = Buffer;
if (Length < FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages))
return STATUS_INVALID_PARAMETER;
if (config->Version != KPH_DYN_CONFIGURATION_VERSION)
return STATUS_INVALID_PARAMETER;
if (config->NumberOfPackages > KPH_DYN_MAXIMUM_PACKAGES)
return STATUS_INVALID_PARAMETER;
if (Length < FIELD_OFFSET(KPH_DYN_CONFIGURATION, Packages) + config->NumberOfPackages * sizeof(KPH_DYN_PACKAGE))
return STATUS_INVALID_PARAMETER;
dprintf("Loading dynamic configuration with %u package(s)\n", config->NumberOfPackages);
for (i = 0; i < config->NumberOfPackages; i++)
{
package = &config->Packages[i];
if (package->MajorVersion == KphDynOsVersionInfo.dwMajorVersion &&
package->MinorVersion == KphDynOsVersionInfo.dwMinorVersion &&
(package->ServicePackMajor == (USHORT)-1 || package->ServicePackMajor == KphDynOsVersionInfo.wServicePackMajor) &&
(package->BuildNumber == (USHORT)-1 || package->BuildNumber == KphDynOsVersionInfo.dwBuildNumber))
{
dprintf("Found matching package at index %u for Windows %u.%u\n", i, package->MajorVersion, package->MinorVersion);
KphDynNtVersion = package->ResultingNtVersion;
KphDynEgeGuid = C_2sTo4(package->StructData.EgeGuid);
KphDynEpObjectTable = C_2sTo4(package->StructData.EpObjectTable);
KphDynEreGuidEntry = C_2sTo4(package->StructData.EreGuidEntry);
KphDynHtHandleContentionEvent = C_2sTo4(package->StructData.HtHandleContentionEvent);
KphDynOtName = C_2sTo4(package->StructData.OtName);
KphDynOtIndex = C_2sTo4(package->StructData.OtIndex);
KphDynObDecodeShift = C_2sTo4(package->StructData.ObDecodeShift);
KphDynObAttributesShift = C_2sTo4(package->StructData.ObAttributesShift);
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}

60
KProcessHacker/dynimp.c Normal file
View File

@@ -0,0 +1,60 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
#include <dyndata.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KphGetSystemRoutineAddress)
#pragma alloc_text(PAGE, KphDynamicImport)
#endif
/**
* Dynamically imports routines.
*/
VOID KphDynamicImport(
VOID
)
{
PAGED_CODE();
NOTHING;
}
/**
* Retrieves the address of a function exported by NTOS or HAL.
*
* \param SystemRoutineName The name of the function.
*
* \return The address of the function, or NULL if the function could
* not be found.
*/
PVOID KphGetSystemRoutineAddress(
__in PWSTR SystemRoutineName
)
{
UNICODE_STRING systemRoutineName;
PAGED_CODE();
RtlInitUnicodeString(&systemRoutineName, SystemRoutineName);
return MmGetSystemRoutineAddress(&systemRoutineName);
}

View File

@@ -0,0 +1,47 @@
#ifndef DYNDATA_H
#define DYNDATA_H
#ifdef EXT
#undef EXT
#endif
#ifdef _DYNDATA_PRIVATE
#define EXT
#define OFFDEFAULT = -1
#else
#define EXT extern
#define OFFDEFAULT
#endif
EXT ULONG KphDynNtVersion;
EXT RTL_OSVERSIONINFOEXW KphDynOsVersionInfo;
// Structures
// Ege: ETW_GUID_ENTRY
// Ep: EPROCESS
// Ere: ETW_REG_ENTRY
// Et: ETHREAD
// Ht: HANDLE_TABLE
// Oh: OBJECT_HEADER
// Ot: OBJECT_TYPE
// Oti: OBJECT_TYPE_INITIALIZER, offset measured from an OBJECT_TYPE
// ObDecodeShift: shift value in ObpDecodeObject
// ObAttributesShift: shift value in ObpGetHandleAttributes
EXT ULONG KphDynEgeGuid OFFDEFAULT;
EXT ULONG KphDynEpObjectTable OFFDEFAULT;
EXT ULONG KphDynEreGuidEntry OFFDEFAULT;
EXT ULONG KphDynHtHandleContentionEvent OFFDEFAULT;
EXT ULONG KphDynOtName OFFDEFAULT;
EXT ULONG KphDynOtIndex OFFDEFAULT;
EXT ULONG KphDynObDecodeShift OFFDEFAULT;
EXT ULONG KphDynObAttributesShift OFFDEFAULT;
NTSTATUS KphDynamicDataInitialization(
VOID
);
NTSTATUS KphReadDynamicDataParameters(
__in_opt HANDLE KeyHandle
);
#endif

View File

@@ -0,0 +1,365 @@
#ifndef KPH_H
#define KPH_H
#include <ntifs.h>
#define PHNT_MODE PHNT_MODE_KERNEL
#include <phnt.h>
#include <ntfill.h>
#include <bcrypt.h>
#include <kphapi.h>
// Debugging
#ifdef DBG
#define dprintf(Format, ...) DbgPrint("KProcessHacker: " Format, __VA_ARGS__)
#else
#define dprintf
#endif
typedef struct _KPH_CLIENT
{
struct
{
ULONG VerificationPerformed : 1;
ULONG VerificationSucceeded : 1;
ULONG KeysGenerated : 1;
ULONG SpareBits : 29;
};
FAST_MUTEX StateMutex;
NTSTATUS VerificationStatus;
PVOID VerifiedProcess; // EPROCESS (for equality checking only - do not access contents)
HANDLE VerifiedProcessId;
PVOID VerifiedRangeBase;
SIZE_T VerifiedRangeSize;
// Level 1 and 2 secret keys
FAST_MUTEX KeyBackoffMutex;
KPH_KEY L1Key;
KPH_KEY L2Key;
} KPH_CLIENT, *PKPH_CLIENT;
typedef struct _KPH_PARAMETERS
{
KPH_SECURITY_LEVEL SecurityLevel;
} KPH_PARAMETERS, *PKPH_PARAMETERS;
// main
extern ULONG KphFeatures;
extern KPH_PARAMETERS KphParameters;
NTSTATUS KpiGetFeatures(
__out PULONG Features,
__in KPROCESSOR_MODE AccessMode
);
// devctrl
__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH KphDispatchDeviceControl;
NTSTATUS KphDispatchDeviceControl(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP Irp
);
// dynimp
VOID KphDynamicImport(
VOID
);
PVOID KphGetSystemRoutineAddress(
__in PWSTR SystemRoutineName
);
// object
PHANDLE_TABLE KphReferenceProcessHandleTable(
__in PEPROCESS Process
);
VOID KphDereferenceProcessHandleTable(
__in PEPROCESS Process
);
VOID KphUnlockHandleTableEntry(
__in PHANDLE_TABLE HandleTable,
__in PHANDLE_TABLE_ENTRY HandleTableEntry
);
NTSTATUS KpiEnumerateProcessHandles(
__in HANDLE ProcessHandle,
__out_bcount(BufferLength) PVOID Buffer,
__in_opt ULONG BufferLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KphQueryNameObject(
__in PVOID Object,
__out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer,
__in ULONG BufferLength,
__out PULONG ReturnLength
);
NTSTATUS KphQueryNameFileObject(
__in PFILE_OBJECT FileObject,
__out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer,
__in ULONG BufferLength,
__out PULONG ReturnLength
);
NTSTATUS KpiQueryInformationObject(
__in HANDLE ProcessHandle,
__in HANDLE Handle,
__in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
__out_bcount(ObjectInformationLength) PVOID ObjectInformation,
__in ULONG ObjectInformationLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KpiSetInformationObject(
__in HANDLE ProcessHandle,
__in HANDLE Handle,
__in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
__in_bcount(ObjectInformationLength) PVOID ObjectInformation,
__in ULONG ObjectInformationLength,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KphOpenNamedObject(
__out PHANDLE ObjectHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE AccessMode
);
// process
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 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 KpiOpenProcessJob(
__in HANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__out PHANDLE JobHandle,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KpiTerminateProcess(
__in HANDLE ProcessHandle,
__in NTSTATUS ExitStatus,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
);
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 KpiSetInformationProcess(
__in HANDLE ProcessHandle,
__in KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass,
__in_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__in KPROCESSOR_MODE AccessMode
);
// qrydrv
NTSTATUS KpiOpenDriver(
__out PHANDLE DriverHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KpiQueryInformationDriver(
__in HANDLE DriverHandle,
__in DRIVER_INFORMATION_CLASS DriverInformationClass,
__out_bcount(DriverInformationLength) PVOID DriverInformation,
__in ULONG DriverInformationLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
);
// thread
NTSTATUS KpiOpenThread(
__out PHANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__in PCLIENT_ID ClientId,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KpiOpenThreadProcess(
__in HANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__out PHANDLE ProcessHandle,
__in KPROCESSOR_MODE AccessMode
);
ULONG KphCaptureStackBackTrace(
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__in_opt ULONG Flags,
__out_ecount(FramesToCapture) PVOID *BackTrace,
__out_opt PULONG BackTraceHash
);
NTSTATUS KphCaptureStackBackTraceThread(
__in PETHREAD Thread,
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__out_ecount(FramesToCapture) PVOID *BackTrace,
__out_opt PULONG CapturedFrames,
__out_opt PULONG BackTraceHash,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KpiCaptureStackBackTraceThread(
__in HANDLE ThreadHandle,
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__out_ecount(FramesToCapture) PVOID *BackTrace,
__out_opt PULONG CapturedFrames,
__out_opt PULONG BackTraceHash,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KpiQueryInformationThread(
__in HANDLE ThreadHandle,
__in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
__out_bcount(ProcessInformationLength) PVOID ThreadInformation,
__in ULONG ThreadInformationLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
);
NTSTATUS KpiSetInformationThread(
__in HANDLE ThreadHandle,
__in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
__in_bcount(ThreadInformationLength) PVOID ThreadInformation,
__in ULONG ThreadInformationLength,
__in KPROCESSOR_MODE AccessMode
);
// util
VOID KphFreeCapturedUnicodeString(
__in PUNICODE_STRING CapturedUnicodeString
);
NTSTATUS KphCaptureUnicodeString(
__in PUNICODE_STRING UnicodeString,
__out PUNICODE_STRING CapturedUnicodeString
);
NTSTATUS KphEnumerateSystemModules(
__out PRTL_PROCESS_MODULES *Modules
);
NTSTATUS KphValidateAddressForSystemModules(
__in PVOID Address,
__in SIZE_T Length
);
NTSTATUS KphGetProcessMappedFileName(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__out PUNICODE_STRING *FileName
);
// verify
NTSTATUS KphHashFile(
__in PUNICODE_STRING FileName,
__out PVOID *Hash,
__out PULONG HashSize
);
NTSTATUS KphVerifyFile(
__in PUNICODE_STRING FileName,
__in_bcount(SignatureSize) PUCHAR Signature,
__in ULONG SignatureSize
);
VOID KphVerifyClient(
__inout PKPH_CLIENT Client,
__in PVOID CodeAddress,
__in_bcount(SignatureSize) PUCHAR Signature,
__in ULONG SignatureSize
);
NTSTATUS KpiVerifyClient(
__in PVOID CodeAddress,
__in_bcount(SignatureSize) PUCHAR Signature,
__in ULONG SignatureSize,
__in PKPH_CLIENT Client
);
VOID KphGenerateKeysClient(
__inout PKPH_CLIENT Client
);
NTSTATUS KphRetrieveKeyViaApc(
__inout PKPH_CLIENT Client,
__in KPH_KEY_LEVEL KeyLevel,
__inout PIRP Irp
);
NTSTATUS KphValidateKey(
__in KPH_KEY_LEVEL RequiredKeyLevel,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
);
// vm
NTSTATUS KphCopyVirtualMemory(
__in PEPROCESS FromProcess,
__in PVOID FromAddress,
__in PEPROCESS ToProcess,
__in PVOID ToAddress,
__in SIZE_T BufferLength,
__in KPROCESSOR_MODE AccessMode,
__out PSIZE_T ReturnLength
);
NTSTATUS KpiReadVirtualMemoryUnsafe(
__in_opt HANDLE ProcessHandle,
__in PVOID BaseAddress,
__out_bcount(BufferSize) PVOID Buffer,
__in SIZE_T BufferSize,
__out_opt PSIZE_T NumberOfBytesRead,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
);
#endif

View File

@@ -0,0 +1,351 @@
#ifndef NTFILL_H
#define NTFILL_H
extern ULONG KphDynNtVersion;
extern ULONG KphDynObDecodeShift;
extern ULONG KphDynObAttributesShift;
// EX
typedef struct _EX_PUSH_LOCK_WAIT_BLOCK *PEX_PUSH_LOCK_WAIT_BLOCK;
NTKERNELAPI
VOID
FASTCALL
ExfUnblockPushLock(
__inout PEX_PUSH_LOCK PushLock,
__inout_opt PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
);
typedef struct _HANDLE_TABLE_ENTRY
{
union
{
PVOID Object;
ULONG ObAttributes;
ULONG_PTR Value;
};
union
{
ACCESS_MASK GrantedAccess;
LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
typedef struct _HANDLE_TABLE HANDLE_TABLE, *PHANDLE_TABLE;
typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK_61)(
__inout PHANDLE_TABLE_ENTRY HandleTableEntry,
__in HANDLE Handle,
__in PVOID Context
);
// since WIN8
typedef BOOLEAN (NTAPI *PEX_ENUM_HANDLE_CALLBACK)(
__in PHANDLE_TABLE HandleTable,
__inout PHANDLE_TABLE_ENTRY HandleTableEntry,
__in HANDLE Handle,
__in PVOID Context
);
NTKERNELAPI
BOOLEAN
NTAPI
ExEnumHandleTable(
__in PHANDLE_TABLE HandleTable,
__in PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,
__inout PVOID Context,
__out_opt PHANDLE Handle
);
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
__in SYSTEM_INFORMATION_CLASS SystemInformationClass,
__out_bcount_opt(SystemInformationLength) PVOID SystemInformation,
__in ULONG SystemInformationLength,
__out_opt PULONG ReturnLength
);
// IO
extern POBJECT_TYPE *IoDriverObjectType;
// KE
typedef enum _KAPC_ENVIRONMENT
{
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT, *PKAPC_ENVIRONMENT;
typedef VOID (NTAPI *PKNORMAL_ROUTINE)(
__in PVOID NormalContext,
__in PVOID SystemArgument1,
__in PVOID SystemArgument2
);
typedef VOID KKERNEL_ROUTINE(
__in PRKAPC Apc,
__inout PKNORMAL_ROUTINE *NormalRoutine,
__inout PVOID *NormalContext,
__inout PVOID *SystemArgument1,
__inout PVOID *SystemArgument2
);
typedef KKERNEL_ROUTINE (NTAPI *PKKERNEL_ROUTINE);
typedef VOID (NTAPI *PKRUNDOWN_ROUTINE)(
__in PRKAPC Apc
);
NTKERNELAPI
VOID
NTAPI
KeInitializeApc(
__out PRKAPC Apc,
__in PRKTHREAD Thread,
__in KAPC_ENVIRONMENT Environment,
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine,
__in_opt PKNORMAL_ROUTINE NormalRoutine,
__in_opt KPROCESSOR_MODE ProcessorMode,
__in_opt PVOID NormalContext
);
NTKERNELAPI
BOOLEAN
NTAPI
KeInsertQueueApc(
__inout PRKAPC Apc,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2,
__in KPRIORITY Increment
);
// MM
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQueryVirtualMemory(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__in MEMORY_INFORMATION_CLASS MemoryInformationClass,
__out_bcount(MemoryInformationLength) PVOID MemoryInformation,
__in SIZE_T MemoryInformationLength,
__out_opt PSIZE_T ReturnLength
);
// OB
// These definitions are no longer correct, but they produce correct results.
#define OBJ_PROTECT_CLOSE 0x00000001
#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)
// This attribute is now stored in the GrantedAccess field.
#define ObpAccessProtectCloseBit 0x2000000
#define ObpDecodeGrantedAccess(Access) \
((Access) & ~ObpAccessProtectCloseBit)
FORCEINLINE PVOID ObpDecodeObject(PVOID Object)
{
#ifdef _M_X64
if (KphDynNtVersion >= PHNT_WIN8)
{
if (KphDynObDecodeShift != -1)
return (PVOID)(((LONG_PTR)Object >> KphDynObDecodeShift) & ~(ULONG_PTR)0xf);
else
return NULL;
}
else
{
return (PVOID)((ULONG_PTR)Object & ~OBJ_HANDLE_ATTRIBUTES);
}
#else
return (PVOID)((ULONG_PTR)Object & ~OBJ_HANDLE_ATTRIBUTES);
#endif
}
FORCEINLINE ULONG ObpGetHandleAttributes(PHANDLE_TABLE_ENTRY HandleTableEntry)
{
#ifdef _M_X64
if (KphDynNtVersion >= PHNT_WIN8)
{
if (KphDynObAttributesShift != -1)
return (ULONG)(HandleTableEntry->Value >> KphDynObAttributesShift) & 0x3;
else
return 0;
}
else
{
return (HandleTableEntry->ObAttributes & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) |
((HandleTableEntry->GrantedAccess & ObpAccessProtectCloseBit) ? OBJ_PROTECT_CLOSE : 0);
}
#else
return (HandleTableEntry->ObAttributes & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) |
((HandleTableEntry->GrantedAccess & ObpAccessProtectCloseBit) ? OBJ_PROTECT_CLOSE : 0);
#endif
}
typedef struct _OBJECT_CREATE_INFORMATION OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION;
// This is incorrect as of Windows 8.1, but the size of the structure is still correct.
typedef struct _OBJECT_HEADER
{
LONG PointerCount;
union
{
LONG HandleCount;
PVOID NextToFree;
};
POBJECT_TYPE Type;
UCHAR NameInfoOffset;
UCHAR HandleInfoOffset;
UCHAR QuotaInfoOffset;
UCHAR Flags;
union
{
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
PVOID QuotaBlockCharged;
};
PVOID SecurityDescriptor;
QUAD Body;
} OBJECT_HEADER, *POBJECT_HEADER;
#define OBJECT_TO_OBJECT_HEADER(Object) CONTAINING_RECORD((Object), OBJECT_HEADER, Body)
NTKERNELAPI
POBJECT_TYPE
NTAPI
ObGetObjectType(
__in PVOID Object
);
NTKERNELAPI
NTSTATUS
NTAPI
ObOpenObjectByName(
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE PreviousMode,
__in_opt PACCESS_STATE AccessState,
__in_opt ACCESS_MASK DesiredAccess,
__in PVOID ParseContext,
__out PHANDLE Handle
);
NTKERNELAPI
NTSTATUS
NTAPI
ObSetHandleAttributes(
__in HANDLE Handle,
__in POBJECT_HANDLE_FLAG_INFORMATION HandleFlags,
__in KPROCESSOR_MODE PreviousMode
);
NTKERNELAPI
NTSTATUS
ObCloseHandle(
__in HANDLE Handle,
__in KPROCESSOR_MODE PreviousMode
);
// PS
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQueryInformationProcess(
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength
);
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwSetInformationProcess(
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__in_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength
);
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQueryInformationThread(
__in HANDLE ThreadHandle,
__in THREADINFOCLASS ThreadInformationClass,
__out_bcount(ThreadInformationLength) PVOID ThreadInformation,
__in ULONG ThreadInformationLength,
__out_opt PULONG ReturnLength
);
NTKERNELAPI
NTSTATUS
NTAPI
PsLookupProcessThreadByCid(
__in PCLIENT_ID ClientId,
__out_opt PEPROCESS *Process,
__out PETHREAD *Thread
);
NTKERNELAPI
PVOID
NTAPI
PsGetThreadWin32Thread(
__in PETHREAD Thread
);
typedef struct _EJOB *PEJOB;
extern POBJECT_TYPE *PsJobType;
NTKERNELAPI
PEJOB
NTAPI
PsGetProcessJob(
__in PEPROCESS Process
);
NTKERNELAPI
NTSTATUS
NTAPI
PsAcquireProcessExitSynchronization(
__in PEPROCESS Process
);
NTKERNELAPI
VOID
NTAPI
PsReleaseProcessExitSynchronization(
__in PEPROCESS Process
);
// RTL
// Sensible limit that may or may not correspond to the actual Windows value.
#define MAX_STACK_DEPTH 256
#define RTL_WALK_USER_MODE_STACK 0x00000001
#define RTL_WALK_VALID_FLAGS 0x00000001
NTSYSAPI
ULONG
NTAPI
RtlWalkFrameChain(
__out PVOID *Callers,
__in ULONG Count,
__in ULONG Flags
);
#endif

356
KProcessHacker/main.c Normal file
View File

@@ -0,0 +1,356 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
#include <dyndata.h>
DRIVER_INITIALIZE DriverEntry;
DRIVER_UNLOAD DriverUnload;
__drv_dispatchType(IRP_MJ_CREATE) DRIVER_DISPATCH KphDispatchCreate;
__drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH KphDispatchClose;
ULONG KphpReadIntegerParameter(
__in_opt HANDLE KeyHandle,
__in PUNICODE_STRING ValueName,
__in ULONG DefaultValue
);
NTSTATUS KphpReadDriverParameters(
__in PUNICODE_STRING RegistryPath
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, DriverEntry)
#pragma alloc_text(PAGE, DriverUnload)
#pragma alloc_text(PAGE, KphpReadIntegerParameter)
#pragma alloc_text(PAGE, KphpReadDriverParameters)
#pragma alloc_text(PAGE, KpiGetFeatures)
#endif
PDRIVER_OBJECT KphDriverObject;
PDEVICE_OBJECT KphDeviceObject;
ULONG KphFeatures;
KPH_PARAMETERS KphParameters;
NTSTATUS DriverEntry(
__in PDRIVER_OBJECT DriverObject,
__in PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
UNICODE_STRING deviceName;
PDEVICE_OBJECT deviceObject;
PAGED_CODE();
KphDriverObject = DriverObject;
if (!NT_SUCCESS(status = KphDynamicDataInitialization()))
return status;
KphDynamicImport();
if (!NT_SUCCESS(status = KphpReadDriverParameters(RegistryPath)))
return status;
// Create the device.
RtlInitUnicodeString(&deviceName, KPH_DEVICE_NAME);
status = IoCreateDevice(
DriverObject,
0,
&deviceName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject
);
if (!NT_SUCCESS(status))
return status;
KphDeviceObject = deviceObject;
// Set up I/O.
DriverObject->MajorFunction[IRP_MJ_CREATE] = KphDispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KphDispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KphDispatchDeviceControl;
DriverObject->DriverUnload = DriverUnload;
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
dprintf("Driver loaded\n");
return status;
}
VOID DriverUnload(
__in PDRIVER_OBJECT DriverObject
)
{
PAGED_CODE();
IoDeleteDevice(KphDeviceObject);
dprintf("Driver unloaded\n");
}
NTSTATUS KphDispatchCreate(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stackLocation;
PFILE_OBJECT fileObject;
PIO_SECURITY_CONTEXT securityContext;
PKPH_CLIENT client;
stackLocation = IoGetCurrentIrpStackLocation(Irp);
fileObject = stackLocation->FileObject;
securityContext = stackLocation->Parameters.Create.SecurityContext;
dprintf("Client (PID %Iu) is connecting\n", PsGetCurrentProcessId());
if (KphParameters.SecurityLevel == KphSecurityPrivilegeCheck ||
KphParameters.SecurityLevel == KphSecuritySignatureAndPrivilegeCheck)
{
UCHAR requiredPrivilegesBuffer[FIELD_OFFSET(PRIVILEGE_SET, Privilege) + sizeof(LUID_AND_ATTRIBUTES)];
PPRIVILEGE_SET requiredPrivileges;
// Check for SeDebugPrivilege.
requiredPrivileges = (PPRIVILEGE_SET)requiredPrivilegesBuffer;
requiredPrivileges->PrivilegeCount = 1;
requiredPrivileges->Control = PRIVILEGE_SET_ALL_NECESSARY;
requiredPrivileges->Privilege[0].Luid.LowPart = SE_DEBUG_PRIVILEGE;
requiredPrivileges->Privilege[0].Luid.HighPart = 0;
requiredPrivileges->Privilege[0].Attributes = 0;
if (!SePrivilegeCheck(
requiredPrivileges,
&securityContext->AccessState->SubjectSecurityContext,
Irp->RequestorMode
))
{
status = STATUS_PRIVILEGE_NOT_HELD;
dprintf("Client (PID %Iu) was rejected\n", PsGetCurrentProcessId());
}
}
if (NT_SUCCESS(status))
{
client = ExAllocatePoolWithTag(PagedPool, sizeof(KPH_CLIENT), 'ChpK');
if (client)
{
memset(client, 0, sizeof(KPH_CLIENT));
ExInitializeFastMutex(&client->StateMutex);
ExInitializeFastMutex(&client->KeyBackoffMutex);
fileObject->FsContext = client;
}
else
{
dprintf("Unable to allocate memory for client (PID %Iu)\n", PsGetCurrentProcessId());
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS KphDispatchClose(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stackLocation;
PFILE_OBJECT fileObject;
PKPH_CLIENT client;
stackLocation = IoGetCurrentIrpStackLocation(Irp);
fileObject = stackLocation->FileObject;
client = fileObject->FsContext;
if (client)
{
ExFreePoolWithTag(client, 'ChpK');
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
/**
* Reads an integer (REG_DWORD) parameter from the registry.
*
* \param KeyHandle A handle to the Parameters key. If NULL, the function
* fails immediately and returns \a DefaultValue.
* \param ValueName The name of the parameter.
* \param DefaultValue The value that is returned if the function fails
* to retrieve the parameter from the registry.
*
* \return The parameter value, or \a DefaultValue if the function failed.
*/
ULONG KphpReadIntegerParameter(
__in_opt HANDLE KeyHandle,
__in PUNICODE_STRING ValueName,
__in ULONG DefaultValue
)
{
NTSTATUS status;
UCHAR buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)];
PKEY_VALUE_PARTIAL_INFORMATION info;
ULONG resultLength;
PAGED_CODE();
if (!KeyHandle)
return DefaultValue;
info = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
status = ZwQueryValueKey(
KeyHandle,
ValueName,
KeyValuePartialInformation,
info,
sizeof(buffer),
&resultLength
);
if (info->Type != REG_DWORD)
status = STATUS_OBJECT_TYPE_MISMATCH;
if (!NT_SUCCESS(status))
{
dprintf("Unable to query parameter %.*S: 0x%x\n", ValueName->Length / sizeof(WCHAR), ValueName->Buffer, status);
return DefaultValue;
}
return *(PULONG)info->Data;
}
/**
* Reads the driver parameters.
*
* \param RegistryPath The registry path of the driver.
*/
NTSTATUS KphpReadDriverParameters(
__in PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
HANDLE parametersKeyHandle;
UNICODE_STRING parametersString;
UNICODE_STRING parametersKeyName;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING valueName;
PAGED_CODE();
// Open the Parameters key.
RtlInitUnicodeString(&parametersString, L"\\Parameters");
parametersKeyName.Length = RegistryPath->Length + parametersString.Length;
parametersKeyName.MaximumLength = parametersKeyName.Length;
parametersKeyName.Buffer = ExAllocatePoolWithTag(PagedPool, parametersKeyName.MaximumLength, 'ThpK');
if (!parametersKeyName.Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
memcpy(parametersKeyName.Buffer, RegistryPath->Buffer, RegistryPath->Length);
memcpy(&parametersKeyName.Buffer[RegistryPath->Length / sizeof(WCHAR)], parametersString.Buffer, parametersString.Length);
InitializeObjectAttributes(
&objectAttributes,
&parametersKeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL
);
status = ZwOpenKey(
&parametersKeyHandle,
KEY_READ,
&objectAttributes
);
ExFreePoolWithTag(parametersKeyName.Buffer, 'ThpK');
if (!NT_SUCCESS(status))
{
dprintf("Unable to open Parameters key: 0x%x\n", status);
status = STATUS_SUCCESS;
parametersKeyHandle = NULL;
// Continue so we can set up defaults.
}
// Read in the parameters.
RtlInitUnicodeString(&valueName, L"SecurityLevel");
KphParameters.SecurityLevel = KphpReadIntegerParameter(parametersKeyHandle, &valueName, KphSecurityPrivilegeCheck);
KphReadDynamicDataParameters(parametersKeyHandle);
if (parametersKeyHandle)
ZwClose(parametersKeyHandle);
return status;
}
NTSTATUS KpiGetFeatures(
__out PULONG Features,
__in KPROCESSOR_MODE AccessMode
)
{
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(Features, sizeof(ULONG), sizeof(ULONG));
*Features = KphFeatures;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
{
*Features = KphFeatures;
}
return STATUS_SUCCESS;
}

1295
KProcessHacker/object.c Normal file

File diff suppressed because it is too large Load Diff

570
KProcessHacker/process.c Normal file
View File

@@ -0,0 +1,570 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
#include <dyndata.h>
#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;
}

238
KProcessHacker/qrydrv.c Normal file
View File

@@ -0,0 +1,238 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
VOID KphpCopyInfoUnicodeString(
__out PVOID Information,
__in_opt PUNICODE_STRING UnicodeString
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KpiOpenDriver)
#pragma alloc_text(PAGE, KpiQueryInformationDriver)
#pragma alloc_text(PAGE, KphpCopyInfoUnicodeString)
#endif
NTSTATUS KpiOpenDriver(
__out PHANDLE DriverHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in KPROCESSOR_MODE AccessMode
)
{
PAGED_CODE();
return KphOpenNamedObject(
DriverHandle,
DesiredAccess,
ObjectAttributes,
*IoDriverObjectType,
AccessMode
);
}
NTSTATUS KpiQueryInformationDriver(
__in HANDLE DriverHandle,
__in DRIVER_INFORMATION_CLASS DriverInformationClass,
__out_bcount(DriverInformationLength) PVOID DriverInformation,
__in ULONG DriverInformationLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status = STATUS_SUCCESS;
PDRIVER_OBJECT driverObject;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(DriverInformation, DriverInformationLength, 1);
if (ReturnLength)
ProbeForWrite(ReturnLength, sizeof(ULONG), 1);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
DriverHandle,
0,
*IoDriverObjectType,
AccessMode,
&driverObject,
NULL
);
if (!NT_SUCCESS(status))
return status;
__try
{
switch (DriverInformationClass)
{
// Basic information such as flags, driver base and driver size.
case DriverBasicInformation:
{
if (DriverInformationLength == sizeof(DRIVER_BASIC_INFORMATION))
{
PDRIVER_BASIC_INFORMATION basicInfo;
basicInfo = (PDRIVER_BASIC_INFORMATION)DriverInformation;
basicInfo->Flags = driverObject->Flags;
basicInfo->DriverStart = driverObject->DriverStart;
basicInfo->DriverSize = driverObject->DriverSize;
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
if (ReturnLength)
*ReturnLength = sizeof(DRIVER_BASIC_INFORMATION);
}
break;
// The name of the driver - e.g. \Driver\Null.
case DriverNameInformation:
{
if (DriverInformation)
{
/* Check buffer length. */
if (sizeof(UNICODE_STRING) + driverObject->DriverName.Length <=
DriverInformationLength)
{
KphpCopyInfoUnicodeString(
DriverInformation,
&driverObject->DriverName
);
}
else
{
status = STATUS_BUFFER_TOO_SMALL;
}
}
if (ReturnLength)
*ReturnLength = sizeof(UNICODE_STRING) + driverObject->DriverName.Length;
}
break;
// The name of the driver's service key - e.g. \REGISTRY\...
case DriverServiceKeyNameInformation:
{
if (driverObject->DriverExtension)
{
if (DriverInformation)
{
if (sizeof(UNICODE_STRING) +
driverObject->DriverExtension->ServiceKeyName.Length <=
DriverInformationLength)
{
KphpCopyInfoUnicodeString(
DriverInformation,
&driverObject->DriverExtension->ServiceKeyName
);
}
else
{
status = STATUS_BUFFER_TOO_SMALL;
}
}
if (ReturnLength)
{
*ReturnLength = sizeof(UNICODE_STRING) +
driverObject->DriverExtension->ServiceKeyName.Length;
}
}
else
{
if (DriverInformation)
{
if (sizeof(UNICODE_STRING) <= DriverInformationLength)
{
// Zero the information buffer.
KphpCopyInfoUnicodeString(
DriverInformation,
NULL
);
}
else
{
status = STATUS_BUFFER_TOO_SMALL;
}
}
if (ReturnLength)
*ReturnLength = sizeof(UNICODE_STRING);
}
}
break;
default:
{
status = STATUS_INVALID_INFO_CLASS;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
ObDereferenceObject(driverObject);
return status;
}
VOID KphpCopyInfoUnicodeString(
__out PVOID Information,
__in_opt PUNICODE_STRING UnicodeString
)
{
PUNICODE_STRING targetUnicodeString = Information;
PWCHAR targetBuffer;
PAGED_CODE();
if (UnicodeString)
{
targetBuffer = (PWCHAR)((PCHAR)Information + sizeof(UNICODE_STRING));
targetUnicodeString->Length = UnicodeString->Length;
targetUnicodeString->MaximumLength = UnicodeString->Length;
targetUnicodeString->Buffer = targetBuffer;
memcpy(targetBuffer, UnicodeString->Buffer, UnicodeString->Length);
}
else
{
targetUnicodeString->Length = 0;
targetUnicodeString->MaximumLength = 0;
targetUnicodeString->Buffer = NULL;
}
}

View File

@@ -0,0 +1,53 @@
#include <windows.h>
#define VER_COMMA 3,0,0,0
#define VER_STR "3.0\0"
#define VER_FILEVERSION VER_COMMA
#define VER_FILEVERSION_STR VER_STR
#define VER_PRODUCTVERSION VER_COMMA
#define VER_PRODUCTVERSION_STR VER_STR
#ifndef DEBUG
#define VER_DEBUG 0
#else
#define VER_DEBUG VS_FF_DEBUG
#endif
#define VER_PRIVATEBUILD 0
#define VER_PRERELEASE 0
#define VER_COMPANYNAME_STR "wj32\0"
#define VER_FILEDESCRIPTION_STR "KProcessHacker\0"
#define VER_LEGALCOPYRIGHT_STR "Licensed under the GNU GPL, v3.\0"
#define VER_ORIGINALFILENAME_STR "kprocesshacker.sys\0"
#define VER_PRODUCTNAME_STR "KProcessHacker\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_PRODUCTVERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS (VER_PRIVATEBUILD | VER_PRERELEASE | VER_DEBUG)
FILEOS VOS__WINDOWS32
FILETYPE VFT_DRV
FILESUBTYPE VFT2_DRV_SYSTEM
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VER_COMPANYNAME_STR
VALUE "FileDescription", VER_FILEDESCRIPTION_STR
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR
VALUE "ProductName", VER_PRODUCTNAME_STR
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

7
KProcessHacker/sign.cmd Normal file
View File

@@ -0,0 +1,7 @@
@echo off
set PHBASE=..
set SIGN_TIMESTAMP=1
copy bin\i386\kprocesshacker.sys bin-signed\i386\kprocesshacker.sys
copy bin\amd64\kprocesshacker.sys bin-signed\amd64\kprocesshacker.sys
call ..\build\internal\sign.cmd bin-signed\i386\kprocesshacker.sys kmcs
call ..\build\internal\sign.cmd bin-signed\amd64\kprocesshacker.sys kmcs

View File

@@ -0,0 +1,28 @@
TARGETTYPE=DRIVER
!IF !DEFINED(TARGETNAME)
TARGETNAME=kprocesshacker
!ENDIF
!IF !DEFINED(TARGETPATH)
TARGETPATH=..\bin
!ENDIF
TARGETLIBS=$(TARGETLIBS) $(DDK_LIB_PATH)\ksecdd.lib
INCLUDES=$(DDK_INC_PATH);..\include;..\..\phnt\include;..\..\phlib\include
LIBS=%BUILD%\lib
SOURCES= \
..\main.c \
..\devctrl.c \
..\dyndata.c \
..\dynimp.c \
..\object.c \
..\process.c \
..\qrydrv.c \
..\thread.c \
..\util.c \
..\verify.c \
..\vm.c \
..\resource.rc

714
KProcessHacker/thread.c Normal file
View File

@@ -0,0 +1,714 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
#include <dyndata.h>
typedef struct _CAPTURE_BACKTRACE_THREAD_CONTEXT
{
BOOLEAN Local;
KAPC Apc;
KEVENT CompletedEvent;
ULONG FramesToSkip;
ULONG FramesToCapture;
PVOID *BackTrace;
ULONG CapturedFrames;
ULONG BackTraceHash;
} CAPTURE_BACKTRACE_THREAD_CONTEXT, *PCAPTURE_BACKTRACE_THREAD_CONTEXT;
KKERNEL_ROUTINE KphpCaptureStackBackTraceThreadSpecialApc;
VOID KphpCaptureStackBackTraceThreadSpecialApc(
__in PRKAPC Apc,
__inout PKNORMAL_ROUTINE *NormalRoutine,
__inout PVOID *NormalContext,
__inout PVOID *SystemArgument1,
__inout PVOID *SystemArgument2
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KpiOpenThread)
#pragma alloc_text(PAGE, KpiOpenThreadProcess)
#pragma alloc_text(PAGE, KphCaptureStackBackTraceThread)
#pragma alloc_text(PAGE, KphpCaptureStackBackTraceThreadSpecialApc)
#pragma alloc_text(PAGE, KpiCaptureStackBackTraceThread)
#pragma alloc_text(PAGE, KpiQueryInformationThread)
#pragma alloc_text(PAGE, KpiSetInformationThread)
#endif
/**
* Opens a thread.
*
* \param ThreadHandle A variable which receives the thread handle.
* \param DesiredAccess The desired access to the thread.
* \param ClientId The identifier of a thread. \a UniqueThread must be present. If \a UniqueProcess
* is present, the process of the referenced thread will be checked against this identifier.
* \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 KpiOpenThread(
__out PHANDLE ThreadHandle,
__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;
PETHREAD thread;
KPH_KEY_LEVEL requiredKeyLevel;
HANDLE threadHandle;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(ThreadHandle, sizeof(HANDLE), sizeof(HANDLE));
ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
clientId = *ClientId;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
{
clientId = *ClientId;
}
// Use the process ID if it was specified.
if (clientId.UniqueProcess)
{
status = PsLookupProcessThreadByCid(&clientId, NULL, &thread);
}
else
{
status = PsLookupThreadByThreadId(clientId.UniqueThread, &thread);
}
if (!NT_SUCCESS(status))
return status;
requiredKeyLevel = KphKeyLevel1;
if ((DesiredAccess & KPH_THREAD_READ_ACCESS) != DesiredAccess)
requiredKeyLevel = KphKeyLevel2;
if (NT_SUCCESS(status = KphValidateKey(requiredKeyLevel, Key, Client, AccessMode)))
{
// Always open in KernelMode to skip access checks.
status = ObOpenObjectByPointer(
thread,
0,
NULL,
DesiredAccess,
*PsThreadType,
KernelMode,
&threadHandle
);
}
ObDereferenceObject(thread);
if (NT_SUCCESS(status))
{
if (AccessMode != KernelMode)
{
__try
{
*ThreadHandle = threadHandle;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
*ThreadHandle = threadHandle;
}
}
return status;
}
/**
* Opens the process of a thread.
*
* \param ThreadHandle A handle to a thread.
* \param DesiredAccess The desired access to the process.
* \param ProcessHandle A variable which receives the process handle.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiOpenThreadProcess(
__in HANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__out PHANDLE ProcessHandle,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PETHREAD thread;
PEPROCESS process;
HANDLE processHandle;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(ProcessHandle, sizeof(HANDLE), sizeof(HANDLE));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ThreadHandle,
0,
*PsThreadType,
AccessMode,
&thread,
NULL
);
if (!NT_SUCCESS(status))
return status;
process = IoThreadToProcess(thread);
status = ObOpenObjectByPointer(
process,
0,
NULL,
DesiredAccess,
*PsProcessType,
AccessMode,
&processHandle
);
ObDereferenceObject(thread);
if (NT_SUCCESS(status))
{
if (AccessMode != KernelMode)
{
__try
{
*ProcessHandle = processHandle;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
*ProcessHandle = processHandle;
}
}
return status;
}
/**
* Captures a stack trace of the current thread.
*
* \param FramesToSkip The number of frames to skip from the bottom of the stack.
* \param FramesToCapture The number of frames to capture.
* \param Flags A combination of the following:
* \li \c RTL_WALK_USER_MODE_STACK The user-mode stack will be retrieved instead of the kernel-mode
* stack.
* \param BackTrace An array in which the stack trace will be stored.
* \param BackTraceHash A variable which receives a hash of the stack trace.
*
* \return The number of frames captured.
*/
ULONG KphCaptureStackBackTrace(
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__in_opt ULONG Flags,
__out_ecount(FramesToCapture) PVOID *BackTrace,
__out_opt PULONG BackTraceHash
)
{
PVOID backTrace[MAX_STACK_DEPTH];
ULONG framesFound;
ULONG hash;
ULONG i;
// Skip the current frame (for this function).
FramesToSkip++;
// Ensure that we won't overrun the buffer.
if (FramesToCapture + FramesToSkip > MAX_STACK_DEPTH)
return 0;
// Validate the flags.
if ((Flags & RTL_WALK_VALID_FLAGS) != Flags)
return 0;
// Walk the stack.
framesFound = RtlWalkFrameChain(
backTrace,
FramesToCapture + FramesToSkip,
Flags
);
// Return nothing if we found fewer frames than we wanted to skip.
if (framesFound <= FramesToSkip)
return 0;
// Copy over the stack trace. At the same time we calculate the stack trace hash by summing the
// addresses.
for (i = 0, hash = 0; i < FramesToCapture; i++)
{
if (FramesToSkip + i >= framesFound)
break;
BackTrace[i] = backTrace[FramesToSkip + i];
hash += PtrToUlong(BackTrace[i]);
}
if (BackTraceHash)
*BackTraceHash = hash;
return i;
}
/**
* Captures the stack trace of a thread.
*
* \param Thread The thread to capture the stack trace of.
* \param FramesToSkip The number of frames to skip from the bottom of the stack.
* \param FramesToCapture The number of frames to capture.
* \param BackTrace An array in which the stack trace will be stored.
* \param CapturedFrames A variable which receives the number of frames captured.
* \param BackTraceHash A variable which receives a hash of the stack trace.
* \param AccessMode The mode in which to perform access checks.
*
* \return The number of frames captured.
*/
NTSTATUS KphCaptureStackBackTraceThread(
__in PETHREAD Thread,
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__out_ecount(FramesToCapture) PVOID *BackTrace,
__out_opt PULONG CapturedFrames,
__out_opt PULONG BackTraceHash,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status = STATUS_SUCCESS;
CAPTURE_BACKTRACE_THREAD_CONTEXT context;
ULONG backTraceSize;
PVOID *backTrace;
PAGED_CODE();
// Make sure the caller didn't request too many frames. This also restricts the amount of memory
// we will try to allocate later.
if (FramesToCapture > MAX_STACK_DEPTH)
return STATUS_INVALID_PARAMETER_3;
backTraceSize = FramesToCapture * sizeof(PVOID);
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(BackTrace, backTraceSize, sizeof(PVOID));
if (CapturedFrames)
ProbeForWrite(CapturedFrames, sizeof(ULONG), sizeof(ULONG));
if (BackTraceHash)
ProbeForWrite(BackTraceHash, sizeof(ULONG), sizeof(ULONG));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
// If the caller doesn't want to capture anything, return immediately.
if (backTraceSize == 0)
{
if (AccessMode != KernelMode)
{
__try
{
if (CapturedFrames)
*CapturedFrames = 0;
if (BackTraceHash)
*BackTraceHash = 0;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
if (CapturedFrames)
*CapturedFrames = 0;
if (BackTraceHash)
*BackTraceHash = 0;
}
return status;
}
// Allocate storage for the stack trace.
backTrace = ExAllocatePoolWithTag(NonPagedPool, backTraceSize, 'bhpK');
if (!backTrace)
return STATUS_INSUFFICIENT_RESOURCES;
// Initialize the context structure.
context.FramesToSkip = FramesToSkip;
context.FramesToCapture = FramesToCapture;
context.BackTrace = backTrace;
// Check if we're trying to get a stack trace of the current thread.
// If so, we don't need to insert an APC.
if (Thread == PsGetCurrentThread())
{
PCAPTURE_BACKTRACE_THREAD_CONTEXT contextPtr = &context;
PVOID dummy = NULL;
KIRQL oldIrql;
// Raise the IRQL to APC_LEVEL to simulate an APC environment,
// and call the APC routine directly.
context.Local = TRUE;
KeRaiseIrql(APC_LEVEL, &oldIrql);
KphpCaptureStackBackTraceThreadSpecialApc(
&context.Apc,
NULL,
NULL,
&contextPtr,
&dummy
);
KeLowerIrql(oldIrql);
}
else
{
context.Local = FALSE;
KeInitializeEvent(&context.CompletedEvent, NotificationEvent, FALSE);
KeInitializeApc(
&context.Apc,
(PKTHREAD)Thread,
OriginalApcEnvironment,
KphpCaptureStackBackTraceThreadSpecialApc,
NULL,
NULL,
KernelMode,
NULL
);
if (KeInsertQueueApc(&context.Apc, &context, NULL, 2))
{
// Wait for the APC to complete.
status = KeWaitForSingleObject(
&context.CompletedEvent,
Executive,
KernelMode,
FALSE,
NULL
);
}
else
{
status = STATUS_UNSUCCESSFUL;
}
}
if (NT_SUCCESS(status))
{
ASSERT(context.CapturedFrames <= FramesToCapture);
if (AccessMode != KernelMode)
{
__try
{
memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID));
if (CapturedFrames)
*CapturedFrames = context.CapturedFrames;
if (BackTraceHash)
*BackTraceHash = context.BackTraceHash;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
memcpy(BackTrace, backTrace, context.CapturedFrames * sizeof(PVOID));
if (CapturedFrames)
*CapturedFrames = context.CapturedFrames;
if (BackTraceHash)
*BackTraceHash = context.BackTraceHash;
}
}
ExFreePoolWithTag(backTrace, 'bhpK');
return status;
}
VOID KphpCaptureStackBackTraceThreadSpecialApc(
__in PRKAPC Apc,
__inout PKNORMAL_ROUTINE *NormalRoutine,
__inout PVOID *NormalContext,
__inout PVOID *SystemArgument1,
__inout PVOID *SystemArgument2
)
{
PCAPTURE_BACKTRACE_THREAD_CONTEXT context = *SystemArgument1;
PAGED_CODE();
context->CapturedFrames = KphCaptureStackBackTrace(
context->FramesToSkip,
context->FramesToCapture,
0,
context->BackTrace,
&context->BackTraceHash
);
if (!context->Local)
{
// Notify the originating thread that we have completed.
KeSetEvent(&context->CompletedEvent, 0, FALSE);
}
}
/**
* Captures the stack trace of a thread.
*
* \param ThreadHandle A handle to the thread to capture the stack trace of.
* \param FramesToSkip The number of frames to skip from the bottom of the stack.
* \param FramesToCapture The number of frames to capture.
* \param BackTrace An array in which the stack trace will be stored.
* \param CapturedFrames A variable which receives the number of frames captured.
* \param BackTraceHash A variable which receives a hash of the stack trace.
* \param AccessMode The mode in which to perform access checks.
*
* \return The number of frames captured.
*/
NTSTATUS KpiCaptureStackBackTraceThread(
__in HANDLE ThreadHandle,
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__out_ecount(FramesToCapture) PVOID *BackTrace,
__out_opt PULONG CapturedFrames,
__out_opt PULONG BackTraceHash,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status = STATUS_SUCCESS;
PETHREAD thread;
PAGED_CODE();
status = ObReferenceObjectByHandle(
ThreadHandle,
0,
*PsThreadType,
AccessMode,
&thread,
NULL
);
if (!NT_SUCCESS(status))
return status;
status = KphCaptureStackBackTraceThread(
thread,
FramesToSkip,
FramesToCapture,
BackTrace,
CapturedFrames,
BackTraceHash,
AccessMode
);
ObDereferenceObject(thread);
return status;
}
/**
* Queries thread information.
*
* \param ThreadHandle A handle to a thread.
* \param ThreadInformationClass The type of information to query.
* \param ThreadInformation The buffer in which the information will be stored.
* \param ThreadInformationLength The number of bytes available in \a ThreadInformation.
* \param ReturnLength A variable which receives the number of bytes required to be available in
* \a ThreadInformation.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiQueryInformationThread(
__in HANDLE ThreadHandle,
__in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
__out_bcount(ProcessInformationLength) PVOID ThreadInformation,
__in ULONG ThreadInformationLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PETHREAD thread;
ULONG returnLength;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(ThreadInformation, ThreadInformationLength, sizeof(ULONG));
if (ReturnLength)
ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ThreadHandle,
THREAD_QUERY_INFORMATION,
*PsThreadType,
AccessMode,
&thread,
NULL
);
if (!NT_SUCCESS(status))
return status;
switch (ThreadInformationClass)
{
default:
status = STATUS_INVALID_INFO_CLASS;
returnLength = 0;
break;
}
ObDereferenceObject(thread);
if (ReturnLength)
{
if (AccessMode != KernelMode)
{
__try
{
*ReturnLength = returnLength;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
NOTHING;
}
}
else
{
*ReturnLength = returnLength;
}
}
return status;
}
/**
* Sets thread information.
*
* \param ThreadHandle A handle to a thread.
* \param ThreadInformationClass The type of information to set.
* \param ThreadInformation A buffer which contains the information to set.
* \param ThreadInformationLength The number of bytes present in \a ThreadInformation.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiSetInformationThread(
__in HANDLE ThreadHandle,
__in KPH_THREAD_INFORMATION_CLASS ThreadInformationClass,
__in_bcount(ThreadInformationLength) PVOID ThreadInformation,
__in ULONG ThreadInformationLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PETHREAD thread;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForRead(ThreadInformation, ThreadInformationLength, sizeof(ULONG));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ThreadHandle,
THREAD_SET_INFORMATION,
*PsThreadType,
AccessMode,
&thread,
NULL
);
if (!NT_SUCCESS(status))
return status;
switch (ThreadInformationClass)
{
default:
status = STATUS_INVALID_INFO_CLASS;
break;
}
ObDereferenceObject(thread);
return status;
}

272
KProcessHacker/util.c Normal file
View File

@@ -0,0 +1,272 @@
/*
* KProcessHacker
*
* Copyright (C) 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KphFreeCapturedUnicodeString)
#pragma alloc_text(PAGE, KphCaptureUnicodeString)
#pragma alloc_text(PAGE, KphEnumerateSystemModules)
#pragma alloc_text(PAGE, KphValidateAddressForSystemModules)
#pragma alloc_text(PAGE, KphGetProcessMappedFileName)
#endif
VOID KphFreeCapturedUnicodeString(
__in PUNICODE_STRING CapturedUnicodeString
)
{
PAGED_CODE();
if (CapturedUnicodeString->Buffer)
ExFreePoolWithTag(CapturedUnicodeString->Buffer, 'UhpK');
}
NTSTATUS KphCaptureUnicodeString(
__in PUNICODE_STRING UnicodeString,
__out PUNICODE_STRING CapturedUnicodeString
)
{
UNICODE_STRING unicodeString;
PWCHAR userBuffer;
PAGED_CODE();
__try
{
ProbeForRead(UnicodeString, sizeof(UNICODE_STRING), sizeof(ULONG));
unicodeString.Length = UnicodeString->Length;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = NULL;
userBuffer = UnicodeString->Buffer;
ProbeForRead(userBuffer, unicodeString.Length, sizeof(WCHAR));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
if (unicodeString.Length & 1)
{
return STATUS_INVALID_PARAMETER;
}
if (unicodeString.Length != 0)
{
unicodeString.Buffer = ExAllocatePoolWithTag(
PagedPool,
unicodeString.Length,
'UhpK'
);
if (!unicodeString.Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
__try
{
memcpy(
unicodeString.Buffer,
userBuffer,
unicodeString.Length
);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KphFreeCapturedUnicodeString(&unicodeString);
return GetExceptionCode();
}
}
*CapturedUnicodeString = unicodeString;
return STATUS_SUCCESS;
}
/**
* Enumerates the modules loaded by the kernel.
*
* \param Modules A variable which receives a pointer to a structure containing information about
* the kernel modules. The structure must be freed with the tag 'ThpK'.
*/
NTSTATUS KphEnumerateSystemModules(
__out PRTL_PROCESS_MODULES *Modules
)
{
NTSTATUS status;
PVOID buffer;
ULONG bufferSize;
ULONG attempts;
PAGED_CODE();
bufferSize = 2048;
attempts = 8;
do
{
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'ThpK');
if (!buffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
status = ZwQuerySystemInformation(
SystemModuleInformation,
buffer,
bufferSize,
&bufferSize
);
if (NT_SUCCESS(status))
{
*Modules = buffer;
return status;
}
ExFreePoolWithTag(buffer, 'ThpK');
if (status != STATUS_INFO_LENGTH_MISMATCH)
{
break;
}
} while (--attempts);
return status;
}
/**
* Checks if an address range lies within a kernel module.
*
* \param Address The beginning of the address range.
* \param Length The number of bytes in the address range.
*/
NTSTATUS KphValidateAddressForSystemModules(
__in PVOID Address,
__in SIZE_T Length
)
{
NTSTATUS status;
PRTL_PROCESS_MODULES modules;
ULONG i;
BOOLEAN valid;
PAGED_CODE();
status = KphEnumerateSystemModules(&modules);
if (!NT_SUCCESS(status))
return status;
valid = FALSE;
for (i = 0; i < modules->NumberOfModules; i++)
{
if (
(ULONG_PTR)Address + Length >= (ULONG_PTR)Address &&
(ULONG_PTR)Address >= (ULONG_PTR)modules->Modules[i].ImageBase &&
(ULONG_PTR)Address + Length <= (ULONG_PTR)modules->Modules[i].ImageBase + modules->Modules[i].ImageSize
)
{
dprintf("Validated address 0x%Ix in %s\n", Address, modules->Modules[i].FullPathName);
valid = TRUE;
break;
}
}
ExFreePoolWithTag(modules, 'ThpK');
if (valid)
status = STATUS_SUCCESS;
else
status = STATUS_ACCESS_VIOLATION;
return status;
}
/**
* Gets the file name of a mapped section.
*
* \param ProcessHandle A handle to a process. The handle must have PROCESS_QUERY_INFORMATION
* access.
* \param BaseAddress The base address of the section view.
* \param Modules A variable which receives a pointer to a string containing the file name of the
* section. The structure must be freed with the tag 'ThpK'.
*/
NTSTATUS KphGetProcessMappedFileName(
__in HANDLE ProcessHandle,
__in PVOID BaseAddress,
__out PUNICODE_STRING *FileName
)
{
NTSTATUS status;
PVOID buffer;
SIZE_T bufferSize;
SIZE_T returnLength;
PAGED_CODE();
bufferSize = 0x100;
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'ThpK');
if (!buffer)
return STATUS_INSUFFICIENT_RESOURCES;
status = ZwQueryVirtualMemory(
ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
buffer,
bufferSize,
&returnLength
);
if (status == STATUS_BUFFER_OVERFLOW)
{
ExFreePoolWithTag(buffer, 'ThpK');
bufferSize = returnLength;
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'ThpK');
if (!buffer)
return STATUS_INSUFFICIENT_RESOURCES;
status = ZwQueryVirtualMemory(
ProcessHandle,
BaseAddress,
MemoryMappedFilenameInformation,
buffer,
bufferSize,
&returnLength
);
}
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(buffer, 'ThpK');
return status;
}
*FileName = buffer;
return status;
}

496
KProcessHacker/verify.c Normal file
View File

@@ -0,0 +1,496 @@
/*
* KProcessHacker
*
* Copyright (C) 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
#define FILE_BUFFER_SIZE (2 * PAGE_SIZE)
#define FILE_MAX_SIZE (32 * 1024 * 1024) // 32 MB
VOID KphpBackoffKey(
__in PKPH_CLIENT Client
);
static UCHAR KphpTrustedPublicKey[] =
{
0x45, 0x43, 0x53, 0x31, 0x20, 0x00, 0x00, 0x00, 0x5f, 0xe8, 0xab, 0xac, 0x01, 0xad, 0x6b, 0x48,
0xfd, 0x84, 0x7f, 0x43, 0x70, 0xb6, 0x57, 0xb0, 0x76, 0xe3, 0x10, 0x07, 0x19, 0xbd, 0x0e, 0xd4,
0x10, 0x5c, 0x1f, 0xfc, 0x40, 0x91, 0xb6, 0xed, 0x94, 0x37, 0x76, 0xb7, 0x86, 0x88, 0xf7, 0x34,
0x12, 0x91, 0xf6, 0x65, 0x23, 0x58, 0xc9, 0xeb, 0x2f, 0xcb, 0x96, 0x13, 0x8f, 0xca, 0x57, 0x7a,
0xd0, 0x7a, 0xbf, 0x22, 0xde, 0xd2, 0x15, 0xfc
};
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KphHashFile)
#pragma alloc_text(PAGE, KphVerifyFile)
#pragma alloc_text(PAGE, KphVerifyClient)
#pragma alloc_text(PAGE, KpiVerifyClient)
#pragma alloc_text(PAGE, KphGenerateKeysClient)
#pragma alloc_text(PAGE, KphRetrieveKeyViaApc)
#pragma alloc_text(PAGE, KphValidateKey)
#pragma alloc_text(PAGE, KphpBackoffKey)
#endif
NTSTATUS KphHashFile(
__in PUNICODE_STRING FileName,
__out PVOID *Hash,
__out PULONG HashSize
)
{
NTSTATUS status;
BCRYPT_ALG_HANDLE hashAlgHandle = NULL;
ULONG querySize;
ULONG hashObjectSize;
ULONG hashSize;
PVOID hashObject = NULL;
PVOID hash = NULL;
BCRYPT_HASH_HANDLE hashHandle = NULL;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iosb;
HANDLE fileHandle = NULL;
FILE_STANDARD_INFORMATION standardInfo;
ULONG remainingBytes;
ULONG bytesToRead;
PVOID buffer = NULL;
PAGED_CODE();
// Open the hash algorithm and allocate memory for the hash object.
if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hashAlgHandle, KPH_HASH_ALGORITHM, NULL, 0)))
goto CleanupExit;
if (!NT_SUCCESS(status = BCryptGetProperty(hashAlgHandle, BCRYPT_OBJECT_LENGTH,
(PUCHAR)&hashObjectSize, sizeof(ULONG), &querySize, 0)))
{
goto CleanupExit;
}
if (!NT_SUCCESS(status = BCryptGetProperty(hashAlgHandle, BCRYPT_HASH_LENGTH, (PUCHAR)&hashSize,
sizeof(ULONG), &querySize, 0)))
{
goto CleanupExit;
}
if (!(hashObject = ExAllocatePoolWithTag(PagedPool, hashObjectSize, 'vhpK')))
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupExit;
}
if (!(hash = ExAllocatePoolWithTag(PagedPool, hashSize, 'vhpK')))
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupExit;
}
if (!NT_SUCCESS(status = BCryptCreateHash(hashAlgHandle, &hashHandle, hashObject, hashObjectSize,
NULL, 0, 0)))
{
goto CleanupExit;
}
// Open the file and compute the hash.
InitializeObjectAttributes(&objectAttributes, FileName, OBJ_KERNEL_HANDLE, NULL, NULL);
if (!NT_SUCCESS(status = ZwCreateFile(&fileHandle, FILE_GENERIC_READ, &objectAttributes,
&iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0)))
{
goto CleanupExit;
}
if (!NT_SUCCESS(status = ZwQueryInformationFile(fileHandle, &iosb, &standardInfo,
sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation)))
{
goto CleanupExit;
}
if (standardInfo.EndOfFile.QuadPart <= 0)
{
status = STATUS_UNSUCCESSFUL;
goto CleanupExit;
}
if (standardInfo.EndOfFile.QuadPart > FILE_MAX_SIZE)
{
status = STATUS_FILE_TOO_LARGE;
goto CleanupExit;
}
if (!(buffer = ExAllocatePoolWithTag(PagedPool, FILE_BUFFER_SIZE, 'vhpK')))
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupExit;
}
remainingBytes = (ULONG)standardInfo.EndOfFile.QuadPart;
while (remainingBytes != 0)
{
bytesToRead = FILE_BUFFER_SIZE;
if (bytesToRead > remainingBytes)
bytesToRead = remainingBytes;
if (!NT_SUCCESS(status = ZwReadFile(fileHandle, NULL, NULL, NULL, &iosb, buffer, bytesToRead,
NULL, NULL)))
{
goto CleanupExit;
}
if ((ULONG)iosb.Information != bytesToRead)
{
status = STATUS_INTERNAL_ERROR;
goto CleanupExit;
}
if (!NT_SUCCESS(status = BCryptHashData(hashHandle, buffer, bytesToRead, 0)))
goto CleanupExit;
remainingBytes -= bytesToRead;
}
if (!NT_SUCCESS(status = BCryptFinishHash(hashHandle, hash, hashSize, 0)))
goto CleanupExit;
if (NT_SUCCESS(status))
{
*Hash = hash;
*HashSize = hashSize;
hash = NULL; // Don't free this in the cleanup section
}
CleanupExit:
if (buffer)
ExFreePoolWithTag(buffer, 'vhpK');
if (fileHandle)
ZwClose(fileHandle);
if (hashHandle)
BCryptDestroyHash(hashHandle);
if (hash)
ExFreePoolWithTag(hash, 'vhpK');
if (hashObject)
ExFreePoolWithTag(hashObject, 'vhpK');
if (hashAlgHandle)
BCryptCloseAlgorithmProvider(hashAlgHandle, 0);
return status;
}
NTSTATUS KphVerifyFile(
__in PUNICODE_STRING FileName,
__in_bcount(SignatureSize) PUCHAR Signature,
__in ULONG SignatureSize
)
{
NTSTATUS status;
BCRYPT_ALG_HANDLE signAlgHandle = NULL;
BCRYPT_KEY_HANDLE keyHandle = NULL;
PVOID hash = NULL;
ULONG hashSize;
PAGED_CODE();
// Import the trusted public key.
if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&signAlgHandle, KPH_SIGN_ALGORITHM, NULL, 0)))
goto CleanupExit;
if (!NT_SUCCESS(status = BCryptImportKeyPair(signAlgHandle, NULL, KPH_BLOB_PUBLIC, &keyHandle,
KphpTrustedPublicKey, sizeof(KphpTrustedPublicKey), 0)))
{
goto CleanupExit;
}
// Hash the file.
if (!NT_SUCCESS(status = KphHashFile(FileName, &hash, &hashSize)))
goto CleanupExit;
// Verify the hash.
if (!NT_SUCCESS(status = BCryptVerifySignature(keyHandle, NULL, hash, hashSize, Signature,
SignatureSize, 0)))
{
goto CleanupExit;
}
CleanupExit:
if (hash)
ExFreePoolWithTag(hash, 'vhpK');
if (keyHandle)
BCryptDestroyKey(keyHandle);
if (signAlgHandle)
BCryptCloseAlgorithmProvider(signAlgHandle, 0);
return status;
}
VOID KphVerifyClient(
__inout PKPH_CLIENT Client,
__in PVOID CodeAddress,
__in_bcount(SignatureSize) PUCHAR Signature,
__in ULONG SignatureSize
)
{
NTSTATUS status;
PUNICODE_STRING processFileName = NULL;
MEMORY_BASIC_INFORMATION memoryBasicInfo;
PUNICODE_STRING mappedFileName = NULL;
PAGED_CODE();
if (Client->VerificationPerformed)
return;
if ((ULONG_PTR)CodeAddress > (ULONG_PTR)MmHighestUserAddress)
{
status = STATUS_ACCESS_VIOLATION;
goto CleanupExit;
}
if (!NT_SUCCESS(status = SeLocateProcessImageName(PsGetCurrentProcess(), &processFileName)))
goto CleanupExit;
if (!NT_SUCCESS(status = ZwQueryVirtualMemory(NtCurrentProcess(), CodeAddress,
MemoryBasicInformation, &memoryBasicInfo, sizeof(MEMORY_BASIC_INFORMATION), NULL)))
{
goto CleanupExit;
}
if (memoryBasicInfo.Type != MEM_IMAGE || memoryBasicInfo.State != MEM_COMMIT)
{
status = STATUS_INVALID_PARAMETER;
goto CleanupExit;
}
if (!NT_SUCCESS(status = KphGetProcessMappedFileName(NtCurrentProcess(), CodeAddress,
&mappedFileName)))
{
goto CleanupExit;
}
if (!RtlEqualUnicodeString(processFileName, mappedFileName, TRUE))
{
status = STATUS_INVALID_PARAMETER;
goto CleanupExit;
}
status = KphVerifyFile(processFileName, Signature, SignatureSize);
CleanupExit:
if (mappedFileName)
ExFreePoolWithTag(mappedFileName, 'ThpK');
if (processFileName)
ExFreePool(processFileName);
ExAcquireFastMutex(&Client->StateMutex);
if (NT_SUCCESS(status))
{
Client->VerifiedProcess = PsGetCurrentProcess();
Client->VerifiedProcessId = PsGetCurrentProcessId();
Client->VerifiedRangeBase = memoryBasicInfo.BaseAddress;
Client->VerifiedRangeSize = memoryBasicInfo.RegionSize;
}
Client->VerificationStatus = status;
MemoryBarrier();
Client->VerificationSucceeded = NT_SUCCESS(status);
Client->VerificationPerformed = TRUE;
ExReleaseFastMutex(&Client->StateMutex);
}
NTSTATUS KpiVerifyClient(
__in PVOID CodeAddress,
__in_bcount(SignatureSize) PUCHAR Signature,
__in ULONG SignatureSize,
__in PKPH_CLIENT Client
)
{
PUCHAR signature;
PAGED_CODE();
__try
{
ProbeForRead(Signature, SignatureSize, 1);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
if (SignatureSize > KPH_SIGNATURE_MAX_SIZE)
return STATUS_INVALID_PARAMETER_3;
signature = ExAllocatePoolWithTag(PagedPool, SignatureSize, 'ThpK');
if (!signature)
return STATUS_INSUFFICIENT_RESOURCES;
__try
{
memcpy(signature, Signature, SignatureSize);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
ExFreePoolWithTag(signature, 'ThpK');
return GetExceptionCode();
}
KphVerifyClient(Client, CodeAddress, Signature, SignatureSize);
ExFreePoolWithTag(signature, 'ThpK');
return Client->VerificationStatus;
}
VOID KphGenerateKeysClient(
__inout PKPH_CLIENT Client
)
{
ULONGLONG interruptTime;
ULONG seed;
KPH_KEY l1Key;
KPH_KEY l2Key;
PAGED_CODE();
if (Client->KeysGenerated)
return;
interruptTime = KeQueryInterruptTime();
seed = (ULONG)(interruptTime >> 32) | (ULONG)interruptTime | PtrToUlong(Client);
l1Key = RtlRandomEx(&seed) | 0x80000000; // Make sure the key is nonzero
do
{
l2Key = RtlRandomEx(&seed) | 0x80000000;
} while (l2Key == l1Key);
ExAcquireFastMutex(&Client->StateMutex);
if (!Client->KeysGenerated)
{
Client->L1Key = l1Key;
Client->L2Key = l2Key;
MemoryBarrier();
Client->KeysGenerated = TRUE;
}
ExReleaseFastMutex(&Client->StateMutex);
}
NTSTATUS KphRetrieveKeyViaApc(
__inout PKPH_CLIENT Client,
__in KPH_KEY_LEVEL KeyLevel,
__inout PIRP Irp
)
{
PIO_APC_ROUTINE userApcRoutine;
KPH_KEY key;
PAGED_CODE();
if (!Client->VerificationSucceeded)
return STATUS_ACCESS_DENIED;
MemoryBarrier();
if (PsGetCurrentProcess() != Client->VerifiedProcess ||
PsGetCurrentProcessId() != Client->VerifiedProcessId)
{
return STATUS_ACCESS_DENIED;
}
userApcRoutine = Irp->Overlay.AsynchronousParameters.UserApcRoutine;
if (!userApcRoutine)
return STATUS_INVALID_PARAMETER;
if ((ULONG_PTR)userApcRoutine < (ULONG_PTR)Client->VerifiedRangeBase ||
(ULONG_PTR)userApcRoutine >= (ULONG_PTR)Client->VerifiedRangeBase + Client->VerifiedRangeSize)
{
return STATUS_ACCESS_DENIED;
}
KphGenerateKeysClient(Client);
switch (KeyLevel)
{
case KphKeyLevel1:
key = Client->L1Key;
break;
case KphKeyLevel2:
key = Client->L2Key;
break;
default:
return STATUS_INVALID_PARAMETER;
}
Irp->Overlay.AsynchronousParameters.UserApcContext = UlongToPtr(key);
return STATUS_SUCCESS;
}
NTSTATUS KphValidateKey(
__in KPH_KEY_LEVEL RequiredKeyLevel,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
)
{
PAGED_CODE();
if (AccessMode == KernelMode)
return STATUS_SUCCESS;
if (Key && Client->VerificationSucceeded && Client->KeysGenerated)
{
MemoryBarrier();
switch (RequiredKeyLevel)
{
case KphKeyLevel1:
if (Key == Client->L1Key || Key == Client->L2Key)
return STATUS_SUCCESS;
else
KphpBackoffKey(Client);
break;
case KphKeyLevel2:
if (Key == Client->L2Key)
return STATUS_SUCCESS;
else
KphpBackoffKey(Client);
break;
default:
return STATUS_INVALID_PARAMETER;
}
}
return STATUS_ACCESS_DENIED;
}
VOID KphpBackoffKey(
__in PKPH_CLIENT Client
)
{
LARGE_INTEGER backoffTime;
PAGED_CODE();
// Serialize to make it impossible for a single client to bypass the backoff by creating
// multiple threads.
ExAcquireFastMutex(&Client->KeyBackoffMutex);
backoffTime.QuadPart = -KPH_KEY_BACKOFF_TIME;
KeDelayExecutionThread(KernelMode, FALSE, &backoffTime);
ExReleaseFastMutex(&Client->KeyBackoffMutex);
}

449
KProcessHacker/vm.c Normal file
View File

@@ -0,0 +1,449 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
ULONG KphpGetCopyExceptionInfo(
__in PEXCEPTION_POINTERS ExceptionInfo,
__out PBOOLEAN HaveBadAddress,
__out PULONG_PTR BadAddress
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KphCopyVirtualMemory)
#pragma alloc_text(PAGE, KpiReadVirtualMemoryUnsafe)
#endif
#define KPH_STACK_COPY_BYTES 0x200
#define KPH_POOL_COPY_BYTES 0x10000
#define KPH_MAPPED_COPY_PAGES 14
#define KPH_POOL_COPY_THRESHOLD 0x3ff
ULONG KphpGetCopyExceptionInfo(
__in PEXCEPTION_POINTERS ExceptionInfo,
__out PBOOLEAN HaveBadAddress,
__out PULONG_PTR BadAddress
)
{
PEXCEPTION_RECORD exceptionRecord;
*HaveBadAddress = FALSE;
exceptionRecord = ExceptionInfo->ExceptionRecord;
if ((exceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
(exceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
(exceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
{
if (exceptionRecord->NumberParameters > 1)
{
/* We have the address. */
*HaveBadAddress = TRUE;
*BadAddress = exceptionRecord->ExceptionInformation[1];
}
}
return EXCEPTION_EXECUTE_HANDLER;
}
/**
* Copies memory from one process to another.
*
* \param FromProcess The source process.
* \param FromAddress The source address.
* \param ToProcess The target process.
* \param ToAddress The target address.
* \param BufferLength The number of bytes to copy.
* \param AccessMode The mode in which to perform access checks.
* \param ReturnLength A variable which receives the number of bytes copied.
*/
NTSTATUS KphCopyVirtualMemory(
__in PEPROCESS FromProcess,
__in PVOID FromAddress,
__in PEPROCESS ToProcess,
__in PVOID ToAddress,
__in SIZE_T BufferLength,
__in KPROCESSOR_MODE AccessMode,
__out PSIZE_T ReturnLength
)
{
UCHAR stackBuffer[KPH_STACK_COPY_BYTES];
PVOID buffer;
PFN_NUMBER mdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + KPH_MAPPED_COPY_PAGES + 1];
PMDL mdl = (PMDL)mdlBuffer;
PVOID mappedAddress;
SIZE_T mappedTotalSize;
SIZE_T blockSize;
SIZE_T stillToCopy;
KAPC_STATE apcState;
PVOID sourceAddress;
PVOID targetAddress;
BOOLEAN doMappedCopy;
BOOLEAN pagesLocked;
BOOLEAN copyingToTarget = FALSE;
BOOLEAN probing = FALSE;
BOOLEAN mapping = FALSE;
BOOLEAN haveBadAddress;
ULONG_PTR badAddress;
PAGED_CODE();
sourceAddress = FromAddress;
targetAddress = ToAddress;
// We don't check if buffer == NULL when freeing. If buffer doesn't need to be freed, set to
// stackBuffer, not NULL.
buffer = stackBuffer;
mappedTotalSize = (KPH_MAPPED_COPY_PAGES - 2) * PAGE_SIZE;
if (mappedTotalSize > BufferLength)
mappedTotalSize = BufferLength;
stillToCopy = BufferLength;
blockSize = mappedTotalSize;
while (stillToCopy)
{
// If we're at the last copy block, copy the remaining bytes instead of the whole block
// size.
if (blockSize > stillToCopy)
blockSize = stillToCopy;
// Choose the best method based on the number of bytes left to copy.
if (blockSize > KPH_POOL_COPY_THRESHOLD)
{
doMappedCopy = TRUE;
}
else
{
doMappedCopy = FALSE;
if (blockSize <= KPH_STACK_COPY_BYTES)
{
if (buffer != stackBuffer)
ExFreePoolWithTag(buffer, 'ChpK');
buffer = stackBuffer;
}
else
{
// Don't allocate the buffer if we've done so already. Note that the block size
// never increases, so this allocation will always be OK.
if (buffer == stackBuffer)
{
// Keep trying to allocate a buffer.
while (TRUE)
{
buffer = ExAllocatePoolWithTag(NonPagedPool, blockSize, 'ChpK');
// Stop trying if we got a buffer.
if (buffer)
break;
blockSize /= 2;
// Use the stack buffer if we can.
if (blockSize <= KPH_STACK_COPY_BYTES)
{
buffer = stackBuffer;
break;
}
}
}
}
}
// Reset state.
mappedAddress = NULL;
pagesLocked = FALSE;
copyingToTarget = FALSE;
KeStackAttachProcess(FromProcess, &apcState);
__try
{
// Probe only if this is the first time.
if (sourceAddress == FromAddress && AccessMode != KernelMode)
{
probing = TRUE;
ProbeForRead(sourceAddress, BufferLength, sizeof(UCHAR));
probing = FALSE;
}
if (doMappedCopy)
{
// Initialize the MDL.
MmInitializeMdl(mdl, sourceAddress, blockSize);
MmProbeAndLockPages(mdl, AccessMode, IoReadAccess);
pagesLocked = TRUE;
// Map the pages.
mappedAddress = MmMapLockedPagesSpecifyCache(
mdl,
KernelMode,
MmCached,
NULL,
FALSE,
HighPagePriority
);
if (!mappedAddress)
{
// Insufficient resources; exit.
mapping = TRUE;
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
}
else
{
memcpy(buffer, sourceAddress, blockSize);
}
KeUnstackDetachProcess(&apcState);
// Attach to the target process and copy the contents out.
KeStackAttachProcess(ToProcess, &apcState);
// Probe only if this is the first time.
if (targetAddress == ToAddress && AccessMode != KernelMode)
{
probing = TRUE;
ProbeForWrite(targetAddress, BufferLength, sizeof(UCHAR));
probing = FALSE;
}
// Copy the data.
copyingToTarget = TRUE;
if (doMappedCopy)
memcpy(targetAddress, mappedAddress, blockSize);
else
memcpy(targetAddress, buffer, blockSize);
}
__except (KphpGetCopyExceptionInfo(
GetExceptionInformation(),
&haveBadAddress,
&badAddress
))
{
KeUnstackDetachProcess(&apcState);
// If we mapped the pages, unmap them.
if (mappedAddress)
MmUnmapLockedPages(mappedAddress, mdl);
// If we locked the pages, unlock them.
if (pagesLocked)
MmUnlockPages(mdl);
// If we allocated pool storage, free it.
if (buffer != stackBuffer)
ExFreePoolWithTag(buffer, 'ChpK');
// If we failed when probing or mapping, return the error status.
if (probing || mapping)
return GetExceptionCode();
// Determine which copy failed.
if (copyingToTarget && haveBadAddress)
{
*ReturnLength = (ULONG)(badAddress - (ULONG_PTR)sourceAddress);
}
else
{
*ReturnLength = BufferLength - stillToCopy;
}
return STATUS_PARTIAL_COPY;
}
KeUnstackDetachProcess(&apcState);
if (doMappedCopy)
{
MmUnmapLockedPages(mappedAddress, mdl);
MmUnlockPages(mdl);
}
stillToCopy -= blockSize;
sourceAddress = (PVOID)((ULONG_PTR)sourceAddress + blockSize);
targetAddress = (PVOID)((ULONG_PTR)targetAddress + blockSize);
}
if (buffer != stackBuffer)
ExFreePoolWithTag(buffer, 'ChpK');
*ReturnLength = BufferLength;
return STATUS_SUCCESS;
}
/**
* Copies process or kernel memory into the current process.
*
* \param ProcessHandle A handle to a process. The handle must have PROCESS_VM_READ access. This
* parameter may be NULL if \a BaseAddress lies above the user-mode range.
* \param BaseAddress The address from which memory is to be copied.
* \param Buffer A buffer which receives the copied memory.
* \param BufferSize The number of bytes to copy.
* \param NumberOfBytesRead A variable which receives the number of bytes copied to the buffer.
* \param Key An access key. 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 KpiReadVirtualMemoryUnsafe(
__in_opt HANDLE ProcessHandle,
__in PVOID BaseAddress,
__out_bcount(BufferSize) PVOID Buffer,
__in SIZE_T BufferSize,
__out_opt PSIZE_T NumberOfBytesRead,
__in_opt KPH_KEY Key,
__in PKPH_CLIENT Client,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
SIZE_T numberOfBytesRead;
PAGED_CODE();
if (!NT_SUCCESS(status = KphValidateKey(KphKeyLevel2, Key, Client, AccessMode)))
return status;
if (AccessMode != KernelMode)
{
if (
(ULONG_PTR)BaseAddress + BufferSize < (ULONG_PTR)BaseAddress ||
(ULONG_PTR)Buffer + BufferSize < (ULONG_PTR)Buffer ||
(ULONG_PTR)Buffer + BufferSize > (ULONG_PTR)MmHighestUserAddress
)
{
return STATUS_ACCESS_VIOLATION;
}
if (NumberOfBytesRead)
{
__try
{
ProbeForWrite(NumberOfBytesRead, sizeof(SIZE_T), sizeof(SIZE_T));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
}
if (BufferSize != 0)
{
// Select the appropriate copy method.
if ((ULONG_PTR)BaseAddress + BufferSize > (ULONG_PTR)MmHighestUserAddress)
{
ULONG_PTR page;
ULONG_PTR pageEnd;
status = KphValidateAddressForSystemModules(BaseAddress, BufferSize);
if (!NT_SUCCESS(status))
return status;
// Kernel memory copy (unsafe)
page = (ULONG_PTR)BaseAddress & ~(PAGE_SIZE - 1);
pageEnd = ((ULONG_PTR)BaseAddress + BufferSize - 1) & ~(PAGE_SIZE - 1);
__try
{
// This will obviously fail if any of the pages aren't resident.
for (; page <= pageEnd; page += PAGE_SIZE)
{
if (!MmIsAddressValid((PVOID)page))
ExRaiseStatus(STATUS_ACCESS_VIOLATION);
}
memcpy(Buffer, BaseAddress, BufferSize);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
numberOfBytesRead = BufferSize;
status = STATUS_SUCCESS;
}
else
{
// User memory copy (safe)
status = ObReferenceObjectByHandle(
ProcessHandle,
PROCESS_VM_READ,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (NT_SUCCESS(status))
{
status = KphCopyVirtualMemory(
process,
BaseAddress,
PsGetCurrentProcess(),
Buffer,
BufferSize,
AccessMode,
&numberOfBytesRead
);
ObDereferenceObject(process);
}
}
}
else
{
numberOfBytesRead = 0;
status = STATUS_SUCCESS;
}
if (NumberOfBytesRead)
{
if (AccessMode != KernelMode)
{
__try
{
*NumberOfBytesRead = numberOfBytesRead;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// Don't mess with the status.
NOTHING;
}
}
else
{
*NumberOfBytesRead = numberOfBytesRead;
}
}
return status;
}