Update functions to match build 20226

This commit is contained in:
Yarden Shafir
2020-10-05 21:32:31 +03:00
parent 328283ec0b
commit f97cfb1311
2 changed files with 146 additions and 74 deletions
+17 -6
View File
@@ -8,6 +8,10 @@ KeVerifyContextIpForUserCet (
{
PEPROCESS process;
NTSTATUS status;
BOOLEAN cetRelaxedMode;
ULONG64 userRip;
BOOLEAN notRelaxed;
KCONTINUE_TYPE continueType;
//
// No need to do anything if shadow stack is not enabled
@@ -31,16 +35,23 @@ KeVerifyContextIpForUserCet (
//
// Verify the new Rip target
//
status = KiVerifyContextIpForUserCet(Thread, Context, ContinueType, ShadowStack);
cetRelaxedMode = process->MitigationFlags2Values.UserCetSetContextIpValidationRelaxedMode & 1 != 0;
status = KiVerifyContextIpForUserCet(Thread, Context, ContinueType, cetRelaxedMode, ShadowStack);
//
// Audit failure if requested and fake success
// Log failure if needed and fake success
//
if ((status == STATUS_SET_CONTEXT_DENIED) &&
(process->MitigationFlags2Values.AuditUserCetSetContextIpValidation))
if ( status == STATUS_SET_CONTEXT_DENIED )
{
KiLogUserCetSetContextIpValidationAudit(*ContinueType);
status = STATUS_SUCCESS;
userRip = Context->Rip;
continueType = *ContinueType;
notRelaxed = CetRelaxedMode ^ 1;
if (!(process->MitigationFlags2Values.AuditUserCetSetContextIpValidation))
{
KiLogUserCetSetContextIpValidationFailure(2, continueType, userRip, notRelaxed);
return status;
}
KiLogUserCetSetContextIpValidationFailure(1, continueType, userRip, notRelaxed);
}
return status;
}
+129 -68
View File
@@ -1,16 +1,26 @@
struct _UNWIND_STATE
{
PVOID ImageBase;
PIMAGE_LOAD_CONFIG_DIRECTORY64 LoadConfig;
BOOLEAN CheckedLoadConfig;
} UNWIND_STATE, *PUNWIND_STATE;
NTSTATUS
KiVerifyContextIpForUserCet (
_In_ PETHREAD Thread,
_In_ PCONTEXT Context,
_In_ PKCONTINUE_TYPE ContinueType,
_Inout_ PULONG_PTR ShadowStack
_In_ PETHREAD Thread,
_In_ PCONTEXT Context,
_In_ PKCONTINUE_TYPE ContinueType,
_In_ BOOLEAN RelaxedMode,
_Inout_ ULONG_PTR ShadowStack
)
{
ULONG64 userRip;
PKSTACK_CONTROL stackControl;
ULONG_PTR shadowStack;
KCONTINUE_TYPE continueType;
PKTRAP_FRAME trapFrame;
UKCONTINUE_TYPE continueType;
NTSTATUS status;
BOOLEAN loadConfigChecked;
UNWIND_STATE unwindState;
//
// Deny if the target Rip a kernel address or below 0x10000
@@ -21,7 +31,6 @@ KiVerifyContextIpForUserCet (
{
return STATUS_SET_CONTEXT_DENIED;
}
//
// Ignore if target Rip is the previous address in user space
// (such as the initial thread start address)
@@ -32,69 +41,121 @@ KiVerifyContextIpForUserCet (
return STATUS_SUCCESS;
}
//
// Handle Rip validation for each KCONTINUE_TYPE
//
shadowStack = *ShadowStack;
continueType = *ContinueType;
switch (continueType)
if (continueType == KCONTINUE_LONGJUMP)
{
return RtlVerifyUserUnwindTarget(userRip, KCONTINUE_LONGJUMP, 0);
}
else if ((continueType != KCONTINUE_UNWIND) &&
(continueType != KCONTINUE_RESUME) &&
(continueType != KCONTINUE_SET))
{
case KCONTINUE_UNWIND:
case KCONTINUE_RESUME:
case KCONTINUE_SET:
//
// Get address of shadow stack if one was not provided by caller.
// If no shadow stack exists, allow any Rip.
//
if (shadowStack == NULL)
{
shadowStack = __readmsr(MSR_IA32_PL3_SSP);
if (shadowStack == NULL)
{
return STATUS_SUCCESS;
}
}
//
// Iterate over shadow stack and check if target Rip is in it.
// If thread is terminating, only try to find the target Rip
// in the current page of the shadow stack.
//
__try
{
do
{
shadowStack += sizeof(userRip);
if (*shadowStack == userRip)
{
*ShadowStack = shadowStack + sizeof(userRip);
return STATUS_SUCCESS;
}
} while (!(PAGE_ALIGNED(shadowStack)) || !(Thread->Terminated));
return STATUS_THREAD_IS_TERMINATING;
}
//
// If target Rip was not found and this is an unwind, try to verify
// Rip in the exception table unwind.
//
__except (EXCEPTION_EXECUTE_HANDLER)
{
if (continueType == KCONTINUE_UNWIND)
{
return RtlVerifyUserUnwindTarget(userRip, KCONTINUE_UNWIND);
}
return STATUS_SET_CONTEXT_DENIED;
}
//
// If this is a long jump, try to verify Rip in the longjmp table.
//
case KCONTINUE_LONGJUMP:
return RtlVerifyUserUnwindTarget(userRip, KCONTINUE_LONGJUMP);
default:
return STATUS_INVALID_PARAMETER;
}
//
// Get address of shadow stack if one was not provided by caller.
// If no shadow stack exists, allow any Rip.
//
if (shadowStack == NULL)
{
shadowStack = __readmsr(MSR_IA32_PL3_SSP);
if (shadowStack == NULL)
{
return STATUS_SUCCESS;
}
}
if ((continueType == KCONTINUE_UNWIND) &&
(userRip == PsNtdllExports.RtlUserThreadStart))
{
*ContinueType = KCONTINUE_RESUME;
continueType = KCONTINUE_RESUME;
}
RtlZeroMemory(&unwindState, sizeof(unwindState));
if (continueType == KCONTINUE_UNWIND)
{
status = RtlVerifyUserUnwindTarget(userRip, KCONTINUE_UNWIND, &unwindState);
if (NT_SUCCESS(status))
{
return status;
}
}
//
// This code will run when RelaxedMode is enabled and continueType is
// either KCONTINUE_SET or KCONTINUE_UNWIND if RtlVerifyUserUnwindTarget failed
//
if ((RelaxedMode) && (*ContinueType != KCONTINUE_RESUME))
{
if (!unwindState.CheckedLoadConfig)
{
status = RtlGetImageBaseAndLoadConfig(userRip, &unwindState.ImageBase, &unwindState.LoadConfig);
loadConfigChecked = NT_SUCCESS(status) ? 1: unwindState.CheckedLoadConfig;
unwindState.CheckedLoadConfig = loadConfigChecked;
}
if ( loadConfigChecked )
{
if ( unwindState.ImageBase )
{
//
// Check if there is a EhContinuationTable in the LoadConfig.
// If it exists it would be after the XFG data
// This code is actually just a "probe" to see if there is a point in checking the EhCont flag,
// and will throw STATUS_ACCESS_VIOLATION if it fails.
//
__try
{
ProbeForRead(unwindState.LoadConfig,
offsetof(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardEHContinuationCount),
RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardEHContinuationCount));
//
// So this code is meant as a "whitelist" for older binaries that don't fully support CET.
// There are some FPs with processes using NtSetContextThread to targets that CET does not expect.
// For newer processes that were compiled recently with the correct flags,
// this will create an EX_CONTINUATION_TABLE that will contain those targets.
// But for older processes Windows supports "relaxed mode" CET.
// If "relaxed mode" is set for the process, any module that does not have an EX_CONTINUATION_TABLE
// will be allowed to set the context to any address.
//
if ((unwindState.LoadConfig) &&
(unwindState.LoadConfig->Size >= RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardEHContinuationCount)) &&
((unwindState.LoadConfig->GuardFlags & IMAGE_GUARD_EH_CONTINUATION_TABLE_PRESENT) != 0))
{
goto CheckAddressInShadowStack;
}
}
__except
{
goto CheckAddressInShadowStack;
}
return STATUS_SUCCESS;
}
return STATUS_SUCCESS;
}
}
CheckAddressInShadowStack:
//
// Iterate over shadow stack and check if target Rip is in it.
// If thread is terminating, only try to find the target Rip
// in the current page of the shadow stack.
//
__try
{
do
{
if (*shadowStack == userRip)
{
*ShadowStack = shadowStack + sizeof(userRip);
return STATUS_SUCCESS;
}
shadowStack += sizeof(userRip);
} while (!(PAGE_ALIGNED(shadowStack)) || !(Thread->Terminated));
return STATUS_THREAD_IS_TERMINATING;
}
__except
{
return STATUS_SET_CONTEXT_DENIED;
}
}