You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
9.2 KiB
328 lines
9.2 KiB
/*
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
File name:
|
|
|
|
hotpatch.c
|
|
|
|
Author:
|
|
|
|
Adrian Marinescu (adrmarin) Nov 20 2001
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
*/
|
|
|
|
#include "exp.h"
|
|
#pragma hdrstop
|
|
|
|
NTSTATUS
|
|
ExpSyncRenameFiles(
|
|
IN HANDLE FileHandle1,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock1,
|
|
IN PFILE_RENAME_INFORMATION RenameInformation1,
|
|
IN ULONG RenameInformationLength1,
|
|
IN HANDLE FileHandle2,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock2,
|
|
IN PFILE_RENAME_INFORMATION RenameInformation2,
|
|
IN ULONG RenameInformationLength2
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE,ExApplyCodePatch)
|
|
#pragma alloc_text(PAGE,ExpSyncRenameFiles)
|
|
|
|
#endif
|
|
|
|
#define EXP_MAX_HOTPATCH_INFO_SIZE PAGE_SIZE
|
|
|
|
//
|
|
// Privileged flags define the operations where we require privilege check
|
|
//
|
|
|
|
#define FLG_HOTPATCH_PRIVILEGED_FLAGS (FLG_HOTPATCH_KERNEL | FLG_HOTPATCH_RELOAD_NTDLL | FLG_HOTPATCH_RENAME_INFO | FLG_HOTPATCH_MAP_ATOMIC_SWAP)
|
|
|
|
//
|
|
// Exclusive flags define the flags that cannot be used in combinations with other flags
|
|
//
|
|
|
|
#define FLG_HOTPATCH_EXCLUSIVE_FLAGS (FLG_HOTPATCH_RELOAD_NTDLL | FLG_HOTPATCH_RENAME_INFO | FLG_HOTPATCH_MAP_ATOMIC_SWAP)
|
|
|
|
volatile LONG ExHotpSyncRenameSequence = 0;
|
|
|
|
|
|
NTSTATUS
|
|
ExpSyncRenameFiles(
|
|
IN HANDLE FileHandle1,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock1,
|
|
IN PFILE_RENAME_INFORMATION RenameInformation1,
|
|
IN ULONG RenameInformationLength1,
|
|
IN HANDLE FileHandle2,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock2,
|
|
IN PFILE_RENAME_INFORMATION RenameInformation2,
|
|
IN ULONG RenameInformationLength2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This service changes the provided information about a specified file. The
|
|
information that is changed is determined by the FileInformationClass that
|
|
is specified. The new information is taken from the FileInformation buffer.
|
|
|
|
Arguments:
|
|
|
|
FileHandle1 - Supplies a first handle to the file to be renamed.
|
|
|
|
IoStatusBlock1 - Address of the caller's I/O status block.
|
|
|
|
FileInformation1 - Supplies the new name for the first file.
|
|
|
|
RenameInformationLength1 - Supplies the lengtd of the rename information buffer
|
|
|
|
FileHandle2 - Supplies a second handle to the file to be renamed.
|
|
|
|
IoStatusBlock2 - Address of the second caller's I/O status block.
|
|
|
|
FileInformation2 - Supplies the new name for the second file.
|
|
|
|
RenameInformationLength2 - Supplies the lengtd of the rename information buffer
|
|
|
|
Return Value:
|
|
|
|
The status returned is the final completion status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LONG CapturedSeqNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
CapturedSeqNumber = ExHotpSyncRenameSequence;
|
|
|
|
if ((CapturedSeqNumber & 1)
|
|
||
|
|
InterlockedCompareExchange(&ExHotpSyncRenameSequence, CapturedSeqNumber + 1, CapturedSeqNumber) != CapturedSeqNumber) {
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Status = NtSetInformationFile( FileHandle1,
|
|
IoStatusBlock1,
|
|
RenameInformation1,
|
|
RenameInformationLength1,
|
|
FileRenameInformation);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
Status = NtSetInformationFile( FileHandle2,
|
|
IoStatusBlock2,
|
|
RenameInformation2,
|
|
RenameInformationLength2,
|
|
FileRenameInformation);
|
|
}
|
|
|
|
InterlockedIncrement(&ExHotpSyncRenameSequence);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ExApplyCodePatch (
|
|
IN PVOID PatchInfoPtr,
|
|
IN SIZE_T PatchSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is handling the common tasks to both user-mode
|
|
and kernel-mode patching
|
|
|
|
Arguments:
|
|
|
|
PatchInfoPtr - Pointer to PSYSTEM_HOTPATCH_CODE_INFORMATION structure
|
|
describing the patch. The pointer is user-mode.
|
|
|
|
PatchSize - the size of the PatchInfoPtr buffer passed in
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSYSTEM_HOTPATCH_CODE_INFORMATION PatchInfo;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
|
|
//
|
|
// Allocate a temporary non-paged buffer to capture the used information
|
|
// Restrict the size of the data to be patched to EXP_MAX_HOTPATCH_INFO_SIZE
|
|
// The buffer must be non-paged because the information is accessed at DPC of higher level
|
|
//
|
|
|
|
if ( (PatchSize > (sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION) + EXP_MAX_HOTPATCH_INFO_SIZE))
|
|
||
|
|
(PatchSize < sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION)) ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PatchInfo = ExAllocatePoolWithQuotaTag (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
|
|
PatchSize,
|
|
'PtoH');
|
|
|
|
if (PatchInfo == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
PreviousMode = KeGetPreviousMode ();
|
|
|
|
try {
|
|
|
|
//
|
|
// Get previous processor mode and probe output argument if necessary.
|
|
//
|
|
|
|
if (PreviousMode != KernelMode) {
|
|
|
|
ProbeForRead (PatchInfoPtr, PatchSize, sizeof(ULONG_PTR));
|
|
}
|
|
|
|
RtlCopyMemory (PatchInfo, PatchInfoPtr, PatchSize);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
Status = GetExceptionCode ();
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ExFreePool (PatchInfo);
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (PatchInfo->Flags & FLG_HOTPATCH_PRIVILEGED_FLAGS) {
|
|
|
|
if (!SeSinglePrivilegeCheck (SeDebugPrivilege, PreviousMode)
|
|
||
|
|
!SeSinglePrivilegeCheck (SeLoadDriverPrivilege, PreviousMode)) {
|
|
|
|
ExFreePool (PatchInfo);
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
if (PatchInfo->Flags & FLG_HOTPATCH_EXCLUSIVE_FLAGS) {
|
|
|
|
//
|
|
// Special hotpatch operation
|
|
//
|
|
|
|
if (PatchInfo->Flags & FLG_HOTPATCH_RELOAD_NTDLL) {
|
|
|
|
Status = PsLocateSystemDll( TRUE );
|
|
|
|
} else if (PatchInfo->Flags & FLG_HOTPATCH_RENAME_INFO) {
|
|
|
|
//
|
|
// The io routine is expected to perform the parameter check
|
|
//
|
|
|
|
Status = ExpSyncRenameFiles( PatchInfo->RenameInfo.FileHandle1,
|
|
PatchInfo->RenameInfo.IoStatusBlock1,
|
|
PatchInfo->RenameInfo.RenameInformation1,
|
|
PatchInfo->RenameInfo.RenameInformationLength1,
|
|
PatchInfo->RenameInfo.FileHandle2,
|
|
PatchInfo->RenameInfo.IoStatusBlock2,
|
|
PatchInfo->RenameInfo.RenameInformation2,
|
|
PatchInfo->RenameInfo.RenameInformationLength2);
|
|
|
|
} else if (PatchInfo->Flags & FLG_HOTPATCH_MAP_ATOMIC_SWAP) {
|
|
|
|
Status = ObSwapObjectNames( PatchInfo->AtomicSwap.ParentDirectory,
|
|
PatchInfo->AtomicSwap.ObjectHandle1,
|
|
PatchInfo->AtomicSwap.ObjectHandle2,
|
|
0);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Regular patch operation which can be in either kernel mode or user mode
|
|
//
|
|
|
|
if (PatchInfo->Flags & FLG_HOTPATCH_KERNEL) {
|
|
|
|
//
|
|
// Kernel-mode patch
|
|
//
|
|
|
|
if ( (PatchInfo->InfoSize != PatchSize)
|
|
||
|
|
!(PatchInfo->Flags & FLG_HOTPATCH_NAME_INFO)
|
|
||
|
|
(PatchInfo->KernelInfo.NameOffset >= PatchSize)
|
|
||
|
|
(PatchInfo->KernelInfo.NameLength >= PatchSize)
|
|
||
|
|
((ULONG)(PatchInfo->KernelInfo.NameOffset + PatchInfo->KernelInfo.NameLength) > PatchSize)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
Status = MmHotPatchRoutine (PatchInfo);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// User-mode patch
|
|
//
|
|
// No privilege check is required for the user mode patching
|
|
// as it can only be done to the current process
|
|
//
|
|
|
|
//
|
|
// Lock the user buffer. This function also performs the
|
|
// validation of the patch address to be USER
|
|
//
|
|
|
|
if ((PatchSize < sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION))
|
|
||
|
|
(PatchInfo->InfoSize != PatchSize)
|
|
||
|
|
((PatchInfo->CodeInfo.DescriptorsCount - 1) >
|
|
(PatchSize - sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION))/sizeof(HOTPATCH_HOOK_DESCRIPTOR))) {
|
|
|
|
ExFreePool (PatchInfo);
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = MmLockAndCopyMemory(PatchInfo, PreviousMode);
|
|
}
|
|
}
|
|
|
|
ExFreePool (PatchInfo);
|
|
|
|
return Status;
|
|
}
|