|
|
/*
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; }
|