329 lines
9.2 KiB
C
329 lines
9.2 KiB
C
|
/*
|
|||
|
|
|||
|
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;
|
|||
|
}
|