├── README.md
├── Xpolicy.sln
├── Xpolicy
├── Xpolicy.vcxproj
├── Xpolicy.vcxproj.filters
├── Xpolicy.vcxproj.user
├── bin
│ ├── Xpolicy32.exe
│ ├── Xpolicy32.pdb
│ ├── Xpolicy64.exe
│ └── Xpolicy64.pdb
└── xpolicy.c
└── src
├── KeVerifyContextIpForUserCet.c
├── KeVerifyContextRecord.c
├── KeVerifyContextXStateCetU.c
├── KiVerifyContextIpForUserCet.c
├── KiVerifyContextRecord.c
├── KiVerifyContextXStateCetUEnabled.c
├── NtSetInformationProcess.c
└── RtlVerifyUserUnwindTarget.c
/README.md:
--------------------------------------------------------------------------------
1 | # Windows Control Flow Enforcement Technology (CET) Research Repository
2 |
3 | A collection of tools, source code, and papers researching Windows' implementation of CET.
4 |
5 | Associated blog post: https://www.windows-internals.com/cet-on-windows
6 |
7 | (c) By Yarden Shafir (@yarden_shafir) & Alex Ionescu (@aionescu)
8 |
--------------------------------------------------------------------------------
/Xpolicy.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.852
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Xpolicy", "Xpolicy\Xpolicy.vcxproj", "{A82A784A-5EFB-4E1C-BE43-83169D135076}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Debug|x64.ActiveCfg = Debug|x64
17 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Debug|x64.Build.0 = Debug|x64
18 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Debug|x86.ActiveCfg = Debug|Win32
19 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Debug|x86.Build.0 = Debug|Win32
20 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Release|x64.ActiveCfg = Release|x64
21 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Release|x64.Build.0 = Release|x64
22 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Release|x86.ActiveCfg = Release|Win32
23 | {A82A784A-5EFB-4E1C-BE43-83169D135076}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {29038274-320C-46F5-8173-5B13D1F41515}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/Xpolicy/Xpolicy.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 15.0
23 | {A82A784A-5EFB-4E1C-BE43-83169D135076}
24 | Xpolicy
25 | 10.0.19525.0
26 |
27 |
28 |
29 | Application
30 | true
31 | v141
32 | MultiByte
33 |
34 |
35 | Application
36 | false
37 | v141
38 | true
39 | MultiByte
40 |
41 |
42 | Application
43 | true
44 | v141
45 | MultiByte
46 |
47 |
48 | Application
49 | false
50 | v141
51 | true
52 | MultiByte
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Level3
76 | Disabled
77 | true
78 | true
79 |
80 |
81 | "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.23.28105\lib\x86"
82 |
83 |
84 |
85 |
86 | Level3
87 | Disabled
88 | true
89 | true
90 |
91 |
92 | "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.23.28105\lib\x64"
93 |
94 |
95 |
96 |
97 | Level3
98 | MaxSpeed
99 | true
100 | true
101 | true
102 | true
103 |
104 |
105 | true
106 | true
107 |
108 |
109 |
110 |
111 | Level3
112 | MaxSpeed
113 | true
114 | true
115 | true
116 | true
117 |
118 |
119 | true
120 | true
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/Xpolicy/Xpolicy.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Xpolicy/Xpolicy.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Xpolicy/bin/Xpolicy32.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yardenshafir/cet-research/f97cfb131165cb524671dc9ff0dbd8dcedfbf2d1/Xpolicy/bin/Xpolicy32.exe
--------------------------------------------------------------------------------
/Xpolicy/bin/Xpolicy32.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yardenshafir/cet-research/f97cfb131165cb524671dc9ff0dbd8dcedfbf2d1/Xpolicy/bin/Xpolicy32.pdb
--------------------------------------------------------------------------------
/Xpolicy/bin/Xpolicy64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yardenshafir/cet-research/f97cfb131165cb524671dc9ff0dbd8dcedfbf2d1/Xpolicy/bin/Xpolicy64.exe
--------------------------------------------------------------------------------
/Xpolicy/bin/Xpolicy64.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yardenshafir/cet-research/f97cfb131165cb524671dc9ff0dbd8dcedfbf2d1/Xpolicy/bin/Xpolicy64.pdb
--------------------------------------------------------------------------------
/Xpolicy/xpolicy.c:
--------------------------------------------------------------------------------
1 | #define UNICODE
2 | #include
3 | #include
4 | #include
5 |
6 | typedef struct _XSAVE_CPU_INFO
7 | {
8 | /* 0x0000 */ unsigned char Processor;
9 | /* 0x0002 */ unsigned short Family;
10 | /* 0x0004 */ unsigned short Model;
11 | /* 0x0006 */ unsigned short Stepping;
12 | /* 0x0008 */ unsigned short ExtendedModel;
13 | /* 0x000c */ unsigned long ExtendedFamily;
14 | /* 0x0010 */ unsigned __int64 MicrocodeVersion;
15 | /* 0x0018 */ unsigned long Reserved;
16 | /* 0x001c */ long __PADDING__[1];
17 | } XSAVE_CPU_INFO, *PXSAVE_CPU_INFO; /* size: 0x0020 */
18 |
19 | typedef struct _XSAVE_CPU_ERRATA
20 | {
21 | /* 0x0000 */ unsigned long NumberOfErrata;
22 | /* 0x0008 */ struct _XSAVE_CPU_INFO Errata[1];
23 | } XSAVE_CPU_ERRATA, *PXSAVE_CPU_ERRATA; /* size: 0x0028 */
24 |
25 | typedef struct _XSAVE_SUPPORTED_CPU
26 | {
27 | /* 0x0000 */ struct _XSAVE_CPU_INFO CpuInfo;
28 | union
29 | {
30 | /* 0x0020 */ struct XSAVE_CPU_ERRATA* CpuErrata;
31 | /* 0x0020 */ unsigned __int64 Unused;
32 | }; /* size: 0x0008 */
33 | } XSAVE_SUPPORTED_CPU, *PXSAVE_SUPPORTED_CPU; /* size: 0x0028 */
34 |
35 | typedef struct _XSAVE_VENDOR
36 | {
37 | /* 0x0000 */ unsigned long VendorId[3];
38 | /* 0x0010 */ struct _XSAVE_SUPPORTED_CPU SupportedCpu;
39 | } XSAVE_VENDOR, *PXSAVE_VENDOR; /* size: 0x0038 */
40 |
41 | typedef struct _XSAVE_VENDORS
42 | {
43 | /* 0x0000 */ unsigned long NumberOfVendors;
44 | /* 0x0008 */ struct _XSAVE_VENDOR Vendor[1];
45 | } XSAVE_VENDORS, *PXSAVE_VENDORS; /* size: 0x0040 */
46 |
47 | typedef struct _XSAVE_FEATURE
48 | {
49 | /* 0x0000 */ unsigned long FeatureId;
50 | union
51 | {
52 | /* 0x0008 */ struct _XSAVE_VENDORS* Vendors;
53 | /* 0x0008 */ unsigned __int64 Unused;
54 | }; /* size: 0x0008 */
55 | } XSAVE_FEATURE, *PXSAVE_FEATURE; /* size: 0x0010 */
56 |
57 | typedef struct _XSAVE_POLICY
58 | {
59 | /* 0x0000 */ unsigned long Version;
60 | /* 0x0004 */ unsigned long Size;
61 | /* 0x0008 */ unsigned long Flags;
62 | /* 0x000c */ unsigned long MaxSaveAreaLength;
63 | /* 0x0010 */ unsigned __int64 FeatureBitmask;
64 | /* 0x0018 */ unsigned long NumberOfFeatures;
65 | /* 0x0020 */ struct _XSAVE_FEATURE Features[1];
66 | } XSAVE_POLICY, *PXSAVE_POLICY; /* size: 0x0030 */
67 |
68 | PCHAR featureName[] =
69 | {
70 | "XSTATE_LEGACY_FLOATING_POINT",
71 | "XSTATE_LEGACY_SSE",
72 | "XSTATE_AVX",
73 | "XSTATE_MPX_BNDREGS",
74 | "XSTATE_MPX_BNDCSR",
75 | "XSTATE_AVX512_KMASK",
76 | "XSTATE_AVX512_ZMM_H",
77 | "XSTATE_AVX512_ZMM",
78 | "XSTATE_IPT",
79 | "XSTATE_PKRU",
80 | "XSTATE_UNKNOWN",
81 | "XSTATE_CET_U",
82 | };
83 |
84 | int main()
85 | {
86 | UINT res;
87 | WCHAR filePath[MAX_PATH];
88 | HMODULE handle;
89 | HRSRC resource;
90 | HGLOBAL hResource;
91 | PXSAVE_POLICY policy;
92 | PXSAVE_VENDORS xsaveVendors;
93 | PXSAVE_CPU_INFO cpuInfo;
94 | PXSAVE_CPU_ERRATA cpuErrata;
95 |
96 | //
97 | // Build the path and load hwpolicy.sys
98 | //
99 | res = GetSystemDirectory(filePath, sizeof(filePath));
100 | if (res == 0)
101 | {
102 | printf("Failed to get system directory. Error: %d\n", GetLastError());
103 | return 1;
104 | }
105 | wcscat_s(filePath, _countof(filePath), L"\\drivers\\hwpolicy.sys");
106 | handle = LoadLibraryEx(filePath, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
107 | if (handle == NULL)
108 | {
109 | printf("Failed loading hwpolicy.sys. Error: %d\n", GetLastError());
110 | return 1;
111 | }
112 |
113 | //
114 | // Load the resource that contains the errata information
115 | //
116 | resource = FindResource(handle, MAKEINTRESOURCE(1), MAKEINTRESOURCE(101));
117 | if (resource == NULL)
118 | {
119 | printf("Failed finding resource. Error: %d\n", GetLastError());
120 | return 1;
121 | }
122 | hResource = LoadResource(handle, resource);
123 | if (hResource == NULL)
124 | {
125 | printf("Failed loading resource. Error: %d\n", GetLastError());
126 | return 1;
127 | }
128 |
129 | policy = (PXSAVE_POLICY)LockResource(hResource);
130 |
131 | printf("Xsave policy: %p\n", policy);
132 | printf("Version: 0x%x\n", policy->Version);
133 | printf("Size: 0x%x\n", policy->Size);
134 | printf("Flags: 0x%x\n", policy->Flags);
135 | printf("MaxSaveAreaLength: 0x%x\n", policy->MaxSaveAreaLength);
136 | printf("FeatureBitmask: 0x%llx\n", policy->FeatureBitmask);
137 | printf("NumberOfFeatures: %d\n", policy->NumberOfFeatures);
138 |
139 | printf("\nFeatures:\n");
140 | for (ULONG i = 0; i < policy->NumberOfFeatures; i++)
141 | {
142 | printf("\tFeatureId: %d (%s)\n",
143 | policy->Features[i].FeatureId,
144 | policy->Features[i].FeatureId < _countof(featureName) ?
145 | featureName[policy->Features[i].FeatureId] :
146 | "UNKNOWN");
147 |
148 | if (policy->Features[i].Unused == 0)
149 | {
150 | continue;
151 | }
152 |
153 | printf("\tVendors:\n");
154 | xsaveVendors = (PXSAVE_VENDORS)(policy->Features[i].Unused + (ULONG_PTR)policy);
155 | printf("\tNumber of vendors: %d\n", xsaveVendors->NumberOfVendors);
156 | for (ULONG j = 0; j < xsaveVendors->NumberOfVendors; j++)
157 | {
158 | printf("\t\tVendor Id: %s\n", (PCHAR)xsaveVendors->Vendor[j].VendorId);
159 | printf("\t\tCpu Info:\n");
160 | cpuInfo = &xsaveVendors->Vendor[j].SupportedCpu.CpuInfo;
161 | printf("\t\t\tProcessor: %x\n", cpuInfo->Processor);
162 | printf("\t\t\tFamily: %x\n", cpuInfo->Family);
163 | printf("\t\t\tModel: %x\n", cpuInfo->Model);
164 | printf("\t\t\tStepping: %x\n", cpuInfo->Stepping);
165 | printf("\t\t\tExtended model: %x\n", cpuInfo->ExtendedModel);
166 | printf("\t\t\tExtended family: %x\n", cpuInfo->ExtendedFamily);
167 | printf("\t\t\tMicrocode version: %llx\n", cpuInfo->MicrocodeVersion);
168 | printf("\n");
169 |
170 | if (xsaveVendors->Vendor[j].SupportedCpu.Unused == 0)
171 | {
172 | continue;
173 | }
174 |
175 | printf("\t\tCpu Errata:\n");
176 | cpuErrata = (PXSAVE_CPU_ERRATA)(xsaveVendors->Vendor[j].SupportedCpu.Unused + (ULONG_PTR)policy);
177 | printf("\t\tNumber of errata: %d\n", cpuErrata->NumberOfErrata);
178 |
179 | for (ULONG n = 0; n NumberOfErrata; n++)
180 | {
181 | cpuInfo = &cpuErrata->Errata[n];
182 | printf("\t\t\tProcessor: %x\n", cpuInfo->Processor);
183 | printf("\t\t\tFamily: %x\n", cpuInfo->Family);
184 | printf("\t\t\tModel: %x\n", cpuInfo->Model);
185 | printf("\t\t\tStepping: %x\n", cpuInfo->Stepping);
186 | printf("\t\t\tExtended model: %x\n", cpuInfo->ExtendedModel);
187 | printf("\t\t\tExtended family: %x\n", cpuInfo->ExtendedFamily);
188 | printf("\t\t\tMicrocode version: %llx\n", cpuInfo->MicrocodeVersion);
189 | printf("\n");
190 | }
191 |
192 | printf("\n");
193 | }
194 | }
195 |
196 | UnlockResource(hResource);
197 | FreeLibrary(handle);
198 |
199 | return 0;
200 | }
--------------------------------------------------------------------------------
/src/KeVerifyContextIpForUserCet.c:
--------------------------------------------------------------------------------
1 | NTSTATUS
2 | KeVerifyContextIpForUserCet (
3 | _In_ PETHREAD Thread,
4 | _In_ PCONTEXT Context,
5 | _In_ PKCONTINUE_TYPE ContinueType,
6 | _Inout_ PULONG_PTR ShadowStack
7 | )
8 | {
9 | PEPROCESS process;
10 | NTSTATUS status;
11 | BOOLEAN cetRelaxedMode;
12 | ULONG64 userRip;
13 | BOOLEAN notRelaxed;
14 | KCONTINUE_TYPE continueType;
15 |
16 | //
17 | // No need to do anything if shadow stack is not enabled
18 | //
19 | if (!Thread->Tcb.CetShadowStack)
20 | {
21 | return STATUS_SUCCESS;
22 | }
23 |
24 | //
25 | // No need to do anything if UserCetSetContextIpValidation is not
26 | // set in this process or if Rip is not being modified
27 | //
28 | process = Thread->Tcb.ApcState.Process;
29 | if (!(process->MitigationFlags2Values.UserCetSetContextIpValidation) ||
30 | !(BooleanFlagOn(Context->ContextFlags, CONTEXT_CONTROL)))
31 | {
32 | return STATUS_SUCCESS;
33 | }
34 |
35 | //
36 | // Verify the new Rip target
37 | //
38 | cetRelaxedMode = process->MitigationFlags2Values.UserCetSetContextIpValidationRelaxedMode & 1 != 0;
39 | status = KiVerifyContextIpForUserCet(Thread, Context, ContinueType, cetRelaxedMode, ShadowStack);
40 |
41 | //
42 | // Log failure if needed and fake success
43 | //
44 | if ( status == STATUS_SET_CONTEXT_DENIED )
45 | {
46 | userRip = Context->Rip;
47 | continueType = *ContinueType;
48 | notRelaxed = CetRelaxedMode ^ 1;
49 | if (!(process->MitigationFlags2Values.AuditUserCetSetContextIpValidation))
50 | {
51 | KiLogUserCetSetContextIpValidationFailure(2, continueType, userRip, notRelaxed);
52 | return status;
53 | }
54 | KiLogUserCetSetContextIpValidationFailure(1, continueType, userRip, notRelaxed);
55 | }
56 | return status;
57 | }
--------------------------------------------------------------------------------
/src/KeVerifyContextRecord.c:
--------------------------------------------------------------------------------
1 | NTSTATUS
2 | KeVerifyContextRecord (
3 | _In_ PKTHREAD TargetThread,
4 | _In_ PCONTEXT ContextFrame
5 | _In_ PKCONTINUE_ARGUMENT ContinueArgument,
6 | _Outptr_ PULONG_PTR ShadowStack
7 | )
8 | {
9 | PKPROCESS targetProcess;
10 | ULONG_PTR userStack;
11 | PTEB userTeb;
12 | PEWOW64PROCESS wow64Process;
13 | USHORT wowMachine;
14 |
15 | targetProcess = TargetThread->Process;
16 | if (targetProcess->CheckStackExtents != FALSE)
17 | {
18 | if (BooleanFlagOn(ContextFrame->ContextFlags, CONTEXT_CONTROL))
19 | {
20 | userStack = ContextFrame->Rsp;
21 | userTeb = TargetThread->Teb;
22 |
23 | //
24 | // Get the stack limits from the process' TEB and
25 | // check if the new stack pointer is inside the native stack
26 | //
27 | if (!RtlGuardIsValidStackPointer(userStack, userTeb))
28 | {
29 | //
30 | // New stack pointer is not inside the native stack.
31 | // Check if this is a wow64 process, and if it is
32 | // check if the new stack pointer is inside the wow64 stack.
33 | //
34 | wowMachine = PsWow64GetProcessMachine(targetProcess);
35 | if ((wowMachine != IMAGE_FILE_MACHINE_I386) &&
36 | (wowMachine != IMAGE_FILE_MACHINE_ARMNT))
37 | {
38 | return STATUS_INVALID_PARAMETER;
39 | }
40 |
41 | if ((userStack >= (_4GB - 1)) ||
42 | !(RtlGuardIsValidWow64StackPointer(userStack, userTeb)))
43 | {
44 | return STATUS_INVALID_PARAMETER;
45 | }
46 |
47 | //
48 | // Call KiVerifyContextRecord to validate the new values of CS and RIP
49 | //
50 | status = KiVerifyContextRecord(TargetThread,
51 | ContextFrame,
52 | ContinueArgument,
53 | ShadowStack);
54 | }
55 | }
56 | }
57 |
58 | //
59 | // If this is a non-wow64 process trying to set its CS to something
60 | // other than KGDT64_R3_CODE, force it to be KGDT64_R3_CODE.
61 | //
62 | if ((BooleanFlagOn(ContextFrame->ContextFlags, CONTEXT_CONTROL)) &&
63 | (PsWow64GetProcessMachine(targetProcess) != IMAGE_FILE_MACHINE_I386))
64 | {
65 | ContextFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
66 | }
67 |
68 | return STATUS_SUCCESS;
69 | }
--------------------------------------------------------------------------------
/src/KeVerifyContextXStateCetU.c:
--------------------------------------------------------------------------------
1 | NTSTATUS
2 | KeVerifyContextXStateCetU (
3 | _In_ PKTHREAD Thread,
4 | _In_ PCONTEXT ContextRecord,
5 | _Outptr_ PULONG_PTR ShadowStack
6 | )
7 | {
8 | PXSAVE_CET_U_FORMAT cetData;
9 | PXSAVE_AREA_HEADER xsaveData;
10 | NTSTATUS status;
11 |
12 | if (!BooleanFlagOn(Context->Context.ContextFlags, CONTEXT_XSTATE))
13 | {
14 | return STATUS_SUCCESS;
15 | }
16 |
17 | //
18 | // Get the address of the CET state from the supplied context
19 | //
20 | cetData = (PXSAVE_CET_U_FORMAT)RtlLocateExtendedFeature2((PCONTEXT_EX)(Context + 1),
21 | XSTATE_CET_U,
22 | &SharedUserData.XState,
23 | NULL);
24 | if (cetData == NULL)
25 | {
26 | return STATUS_SUCCESS;
27 | }
28 |
29 | *ShadowStack = __readmsr(MSR_IA32_PL3_SSP);
30 |
31 | //
32 | // Check if the context contains values for CET registers.
33 | // If it doesn't, it means CET registers will not be set, and
34 | // will disable CET if it was previously enabled.
35 | //
36 | xsaveData = (PXSAVE_AREA_HEADER)RTL_CONTEXT_CHUNK(Context, XState);
37 |
38 | if (Thread->CetUserShadowStack != FALSE)
39 | {
40 | if (!BooleanFlagOn(xsaveData->Mask, XSTATE_MASK_CET_U))
41 | {
42 | //
43 | // If the thread has CET enabled but the new context doesn't have
44 | // CET registers in it, set the CET registers in the context to
45 | // the current CET values.
46 | //
47 | SetFlag(xsaveData->Mask, XSTATE_MASK_CET_U);
48 | cetData->Ia32CetUMsr = MSR_IA32_CET_SHSTK_EN;
49 | cetData->Ia32Pl3SspMsr = *ShadowStack;
50 | return STATUS_SUCCESS;
51 | }
52 |
53 | //
54 | // Verify that the new Ssp value is inside the shadow stack
55 | //
56 | status = KiVerifyContextXStateCetUEnabled(cetData, *ShadowStack);
57 | if (NT_SUCCESS(status))
58 | {
59 | return STATUS_SUCCESS;
60 | }
61 |
62 | return status;
63 | }
64 |
65 | //
66 | // If the thread doesn't have CET enabled and the new context doesn't
67 | // have CET registers, or the CET mask is set but the CET registers
68 | // don't hold any value, allow because the CET state will not change.
69 | //
70 | if (!(BooleanFlagOn(xsaveData->Mask, XSTATE_MASK_CET_U)) ||
71 | ((cetData->Ia32CetUMsr == 0) &&
72 | (cetData->Ia32Pl3SspMsr == NULL)))
73 | {
74 | return STATUS_SUCCESS;
75 | }
76 |
77 | return STATUS_SET_CONTEXT_DENIED;
78 | }
--------------------------------------------------------------------------------
/src/KiVerifyContextIpForUserCet.c:
--------------------------------------------------------------------------------
1 | struct _UNWIND_STATE
2 | {
3 | PVOID ImageBase;
4 | PIMAGE_LOAD_CONFIG_DIRECTORY64 LoadConfig;
5 | BOOLEAN CheckedLoadConfig;
6 | } UNWIND_STATE, *PUNWIND_STATE;
7 |
8 |
9 | NTSTATUS
10 | KiVerifyContextIpForUserCet (
11 | _In_ PETHREAD Thread,
12 | _In_ PCONTEXT Context,
13 | _In_ PKCONTINUE_TYPE ContinueType,
14 | _In_ BOOLEAN RelaxedMode,
15 | _Inout_ ULONG_PTR ShadowStack
16 | )
17 | {
18 | ULONG64 userRip;
19 | ULONG_PTR shadowStack;
20 | UKCONTINUE_TYPE continueType;
21 | NTSTATUS status;
22 | BOOLEAN loadConfigChecked;
23 | UNWIND_STATE unwindState;
24 |
25 | //
26 | // Deny if the target Rip a kernel address or below 0x10000
27 | //
28 | userRip = Context->Rip;
29 | if ((userRip >= MM_USER_PROBE_ADDRESS) ||
30 | (userRip < MM_ALLOCATION_GRANULARITY))
31 | {
32 | return STATUS_SET_CONTEXT_DENIED;
33 | }
34 | //
35 | // Ignore if target Rip is the previous address in user space
36 | // (such as the initial thread start address)
37 | //
38 | trapFrame = PspGetBaseTrapFrame(Thread);
39 | if (userRip == trapFrame->Rip)
40 | {
41 | return STATUS_SUCCESS;
42 | }
43 |
44 | shadowStack = *ShadowStack;
45 | continueType = *ContinueType;
46 | if (continueType == KCONTINUE_LONGJUMP)
47 | {
48 | return RtlVerifyUserUnwindTarget(userRip, KCONTINUE_LONGJUMP, 0);
49 | }
50 | else if ((continueType != KCONTINUE_UNWIND) &&
51 | (continueType != KCONTINUE_RESUME) &&
52 | (continueType != KCONTINUE_SET))
53 | {
54 | return STATUS_INVALID_PARAMETER;
55 | }
56 | //
57 | // Get address of shadow stack if one was not provided by caller.
58 | // If no shadow stack exists, allow any Rip.
59 | //
60 | if (shadowStack == NULL)
61 | {
62 | shadowStack = __readmsr(MSR_IA32_PL3_SSP);
63 | if (shadowStack == NULL)
64 | {
65 | return STATUS_SUCCESS;
66 | }
67 | }
68 |
69 | if ((continueType == KCONTINUE_UNWIND) &&
70 | (userRip == PsNtdllExports.RtlUserThreadStart))
71 | {
72 | *ContinueType = KCONTINUE_RESUME;
73 | continueType = KCONTINUE_RESUME;
74 | }
75 | RtlZeroMemory(&unwindState, sizeof(unwindState));
76 | if (continueType == KCONTINUE_UNWIND)
77 | {
78 | status = RtlVerifyUserUnwindTarget(userRip, KCONTINUE_UNWIND, &unwindState);
79 | if (NT_SUCCESS(status))
80 | {
81 | return status;
82 | }
83 | }
84 | //
85 | // This code will run when RelaxedMode is enabled and continueType is
86 | // either KCONTINUE_SET or KCONTINUE_UNWIND if RtlVerifyUserUnwindTarget failed
87 | //
88 | if ((RelaxedMode) && (*ContinueType != KCONTINUE_RESUME))
89 | {
90 | if (!unwindState.CheckedLoadConfig)
91 | {
92 | status = RtlGetImageBaseAndLoadConfig(userRip, &unwindState.ImageBase, &unwindState.LoadConfig);
93 | loadConfigChecked = NT_SUCCESS(status) ? 1: unwindState.CheckedLoadConfig;
94 | unwindState.CheckedLoadConfig = loadConfigChecked;
95 | }
96 | if ( loadConfigChecked )
97 | {
98 | if ( unwindState.ImageBase )
99 | {
100 | //
101 | // Check if there is a EhContinuationTable in the LoadConfig.
102 | // If it exists it would be after the XFG data
103 | // This code is actually just a "probe" to see if there is a point in checking the EhCont flag,
104 | // and will throw STATUS_ACCESS_VIOLATION if it fails.
105 | //
106 | __try
107 | {
108 | ProbeForRead(unwindState.LoadConfig,
109 | offsetof(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardEHContinuationCount),
110 | RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardEHContinuationCount));
111 |
112 | //
113 | // So this code is meant as a "whitelist" for older binaries that don't fully support CET.
114 | // There are some FPs with processes using NtSetContextThread to targets that CET does not expect.
115 | // For newer processes that were compiled recently with the correct flags,
116 | // this will create an EX_CONTINUATION_TABLE that will contain those targets.
117 | // But for older processes Windows supports "relaxed mode" CET.
118 | // If "relaxed mode" is set for the process, any module that does not have an EX_CONTINUATION_TABLE
119 | // will be allowed to set the context to any address.
120 | //
121 | if ((unwindState.LoadConfig) &&
122 | (unwindState.LoadConfig->Size >= RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY64, GuardEHContinuationCount)) &&
123 | ((unwindState.LoadConfig->GuardFlags & IMAGE_GUARD_EH_CONTINUATION_TABLE_PRESENT) != 0))
124 | {
125 | goto CheckAddressInShadowStack;
126 | }
127 | }
128 | __except
129 | {
130 | goto CheckAddressInShadowStack;
131 | }
132 | return STATUS_SUCCESS;
133 | }
134 | return STATUS_SUCCESS;
135 | }
136 | }
137 | CheckAddressInShadowStack:
138 | //
139 | // Iterate over shadow stack and check if target Rip is in it.
140 | // If thread is terminating, only try to find the target Rip
141 | // in the current page of the shadow stack.
142 | //
143 | __try
144 | {
145 | do
146 | {
147 | if (*shadowStack == userRip)
148 | {
149 | *ShadowStack = shadowStack + sizeof(userRip);
150 | return STATUS_SUCCESS;
151 | }
152 | shadowStack += sizeof(userRip);
153 | } while (!(PAGE_ALIGNED(shadowStack)) || !(Thread->Terminated));
154 |
155 | return STATUS_THREAD_IS_TERMINATING;
156 | }
157 | __except
158 | {
159 | return STATUS_SET_CONTEXT_DENIED;
160 | }
161 | }
--------------------------------------------------------------------------------
/src/KiVerifyContextRecord.c:
--------------------------------------------------------------------------------
1 | NTSTATUS
2 | KiVerifyContextRecord (
3 | _In_ PKTHREAD TargetThread,
4 | _In_ PCONTEXT ContextFrame
5 | _In_ PKCONTINUE_ARGUMENT ContinueArgument,
6 | _Outptr_ PULONG_PTR ShadowStack
7 | )
8 | {
9 | PKPROCESS process;
10 |
11 | process = Thread->Tcb.Process;
12 |
13 | //
14 | // If control registers (RIP/RSP) aren't beind modified, no checks to do
15 | //
16 | if (!BooleanFlagOn(ContextFrame->ContextFlags, CONTEXT_CONTROL))
17 | {
18 | return STATUS_SUCCESS;
19 | }
20 |
21 | //
22 | // If this is a non-wow64 process trying to set CS to a value other than KGDT64_R3_CODE,
23 | // Or this is a pico process trying to set CS to a value other than KGDT64_R3_CODE or
24 | // KGDT64_R3_CMCODE, Force CS to be KGDT64_R3_CODE.
25 | //
26 | if ((PsWow64GetProcessMachine(process) != IMAGE_FILE_MACHINE_I386) &&
27 | ((process->PicoContext == NULL) ||
28 | (ContextFrame->SegCs != (KGDT64_R3_CMCODE | RPL_MASK))))
29 | {
30 | ContextFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
31 | }
32 |
33 | //
34 | // New context structure is not supported
35 | //
36 | if (!ARGUMENT_PRESENT(ContinueArgument))
37 | {
38 | return STATUS_SUCCESS;
39 | }
40 |
41 | //
42 | // Verify new RIP value in the shadow stack
43 | //
44 | status = KeVerifyContextIpForUserCet(TargetThread,
45 | ContextFrame,
46 | ContinueArgument,
47 | ShadowStack);
48 | if (NT_SUCCESS(status))
49 | {
50 | return STATUS_SUCCESS;
51 | }
52 |
53 | return status;
54 | }
55 |
--------------------------------------------------------------------------------
/src/KiVerifyContextXStateCetUEnabled.c:
--------------------------------------------------------------------------------
1 | NTSTATUS
2 | KiVerifyContextXStateCetUEnabled (
3 | _In_ PXSAVE_CET_U_FORMAT CetData,
4 | _In_ ULONG_PTR ShadowStack
5 | )
6 | {
7 | MEMORY_REGION_INFORMATION regionInfo;
8 | ULONG_PTR shadowStackEnd;
9 | ULONG_PTR newShadowStack;
10 |
11 | //
12 | // If the value for the MSR mask is not 1 (CET enabled), deny the new context
13 | //
14 | if (CetData->Ia32CetUMsr != MSR_IA32_CET_SHSTK_EN)
15 | {
16 | return STATUS_SET_CONTEXT_DENIED;
17 | }
18 |
19 | //
20 | // Deny the context if the new Ssp value is not 8-byte aligned
21 | //
22 | newShadowStack = CetData->Ia32Pl3SspMsr;
23 | if ((newShadowStack & 7) != 0)
24 | {
25 | return STATUS_SET_CONTEXT_DENIED;
26 | }
27 |
28 | //
29 | // Check if the new Ssp is lower than the current Ssp,
30 | // so it will point to uninitialized memory
31 | //
32 | if (newShadowStack < ShadowStack)
33 | {
34 | return STATUS_SET_CONTEXT_DENIED;
35 | }
36 |
37 | //
38 | // Get the end address of the shadow stack
39 | //
40 | ZwQueryVirtualMemory(NtCurrentProcess(),
41 | ShadowStack,
42 | MemoryRegionInformation,
43 | ®ionInfo,
44 | sizeof(regionInfo),
45 | NULL);
46 | shadowStackEnd = MemoryInformation.AllocationBase +
47 | MemoryInformation.RegionSize -
48 | PAGE_SIZE;
49 |
50 | //
51 | // Check if the new Ssp is higher than the end address of
52 | // the shadiw stack, so outside the stack bounds
53 | //
54 | if (newShadowStack >= shadowStackEnd)
55 | {
56 | return STATUS_SET_CONTEXT_DENIED;
57 | }
58 |
59 | return STATUS_SUCCESS;
60 | }
--------------------------------------------------------------------------------
/src/NtSetInformationProcess.c:
--------------------------------------------------------------------------------
1 | NTSTATUS
2 | NTAPI
3 | NtSetInformationProcess (
4 | _In_ HANDLE ProcessHandle,
5 | _In_ PROCESSINFOCLASS ProcessInformationClass,
6 | _In_ PVOID ProcessInformation,
7 | _In_ ULONG ProcessInformationLength
8 | )
9 | {
10 | PROCESS_DYNAMIC_EH_CONTINUATION_TARGETS_INFORMATION targetInfo;
11 | ULONG targetsSize;
12 | PPROCESS_DYNAMIC_EH_CONTINUATION_TARGET targetsArray;
13 | PPROCESS_DYNAMIC_EH_CONTINUATION_TARGET ehTargets;
14 | PEPROCESS targetProcess;
15 | NTSTATUS status;
16 | KPROCESSOR_MODE previousMode = ExGetPreviousMode();
17 | ULONG i;
18 | ULONG targetsProcessed;
19 |
20 | //
21 | // Handle the dynamic exception handlers information class
22 | //
23 | if (ProcessInformationClass == ProcessDynamicEHContinuationTargets)
24 | {
25 | //
26 | // Validate the data is the right size
27 | //
28 | if (ProcessInformationLength != sizeof(targetInfo))
29 | {
30 | return STATUS_INFO_LENGTH_MISMATCH;
31 | }
32 |
33 | //
34 | // Make a local copy of the data to avoid races
35 | //
36 | __try
37 | {
38 | targetInfo = *(PPROCESS_DYNAMIC_EH_CONTINUATION_TARGETS_INFORMATION)ProcessInformation;
39 | }
40 | __except (EXCEPTION_EXECUTE_HANDLER)
41 | {
42 | return GetExceptionCode();
43 | }
44 |
45 | //
46 | // Check how many targets there are
47 | //
48 | targetsSize = sizeof(PROCESS_DYNAMIC_EH_CONTINUATION_TARGET) *
49 | targetInfo.NumberOfTargets;
50 | if (targetsSize == 0)
51 | {
52 | return STATUS_INVALID_PARAMETER;
53 | }
54 |
55 | //
56 | // Make sure there are targets
57 | //
58 | targetsArray = targetInfo.Targets;
59 | if (targetsArray == NULL)
60 | {
61 | return STATUS_INVALID_PARAMETER;
62 | }
63 |
64 | //
65 | // Probe that the targets are all in writeable UM memory
66 | //
67 | __try
68 | {
69 | ProbeForWrite(targetsArray, targetsSize, 8);
70 | }
71 | __except (EXCEPTION_EXECUTE_HANDLER)
72 | {
73 | return GetExceptionCode();
74 | }
75 |
76 | //
77 | // These fields aren't used yet
78 | //
79 | if ((targetInfo.Reserved != 0) || (targetInfo.Reserved2 != 0))
80 | {
81 | return STATUS_INVALID_PARAMETER;
82 | }
83 |
84 | //
85 | // Only user-mode code should be setting dynamic EH targets
86 | //
87 | if (previousMode != UserMode)
88 | {
89 | return STATUS_ACCESS_DENIED;
90 | }
91 |
92 | //
93 | // Make sure the caller has a full process write handle to the target
94 | //
95 | targetProcess = NULL;
96 | status = ObReferenceObjectByHandle(ProcessHandle,
97 | GENERIC_WRITE & ~SYNCHRONIZE,
98 | (POBJECT_TYPE)PsProcessType,
99 | UserMode,
100 | (PVOID*)&targetProcess,
101 | NULL);
102 | if (!NT_SUCCESS(status))
103 | {
104 | goto Cleanup;
105 | }
106 |
107 | //
108 | // Don't allow the current process to add targets to itself
109 | //
110 | if (targetProcess == PsGetCurrentProcess())
111 | {
112 | status = STATUS_ACCESS_DENIED;
113 | goto Cleanup;
114 | }
115 |
116 | //
117 | // Don't allow setting EH handlers if the target process doesn't have CET
118 | //
119 | if (targetProcess->MitigationFlags2Values.CetUserShadowStacks == FALSE)
120 | {
121 | status = STATUS_NOT_SUPPORTED;
122 | goto Cleanup;
123 | }
124 |
125 | //
126 | // Allocate a kernel copy of the targets
127 | //
128 | ehTargets = ExAllocatePoolWithQuotaTag(PagedPool |
129 | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
130 | targetsSize,
131 | 'NHED');
132 | if (ehTargets == NULL)
133 | {
134 | status = STATUS_NO_MEMORY;
135 | goto Cleanup;
136 | }
137 |
138 | //
139 | // Copy them in the array
140 | //
141 | RtlCopyMemory(ehTargets, targetsArray, targetsSize);
142 |
143 | //
144 | // Process each target in the array
145 | //
146 | targetsProcessed = 0;
147 | status = PspProcessDynamicEHContinuationTargets(targetProcess,
148 | ehTargets,
149 | targetInfo.NumberOfTargets,
150 | &targetsProcessed);
151 |
152 | //
153 | // Write out the flags back in the original user buffer, which will
154 | // basically fill set DYNAMIC_EH_CONTINUATION_TARGET_PROCESSED so the
155 | // caller knows what wasn't processed
156 | //
157 | __try
158 | {
159 | for (i = 0; i < targetsProcessed; i++)
160 | {
161 | targetsArray[i].Flags = ehTargets[i].Flags;
162 | }
163 | }
164 | __except (EXCEPTION_EXECUTE_HANDLER)
165 | {
166 | status = GetExceptionCode();
167 | }
168 |
169 | Cleanup:
170 | //
171 | // Dereference the target process if needed
172 | //
173 | if (targetProcess != NULL)
174 | {
175 | ObDereferenceObject(targetProcess);
176 | }
177 |
178 | //
179 | // Free the EH target array if needed
180 | //
181 | if (ehTargets != NULL)
182 | {
183 | ExFreePoolWithTag(ehTargets, 'NHED');
184 | }
185 | }
186 |
187 | //
188 | // Return back to caller
189 | //
190 | return status;
191 | }
--------------------------------------------------------------------------------
/src/RtlVerifyUserUnwindTarget.c:
--------------------------------------------------------------------------------
1 | INT
2 | RtlpTargetCompare (
3 | _In_opt_ PVOID Context,
4 | _In_ PCVOID Key,
5 | _In_ PCVOID Datum
6 | )
7 | {
8 | ULONG_PTR rva1;
9 | ULONG_PTR rva2;
10 | UNREFERENCED_PARAMETER(Context);
11 |
12 | //
13 | // Return if the compared RVA comes before (-1), after (+1), or identical (0)
14 | //
15 | rva1 = *(PULONG_PTR)Key;
16 | rva2 = *(PULONG_PTR)Datum;
17 | return (INT)(rva1 - rva2);
18 | }
19 |
20 | NTSTATUS
21 | RtlVerifyUserUnwindTarget (
22 | _In_ PVOID TargetRip,
23 | _In_ KCONTINUE_TYPE ContinueType
24 | )
25 | {
26 | PIMAGE_LOAD_CONFIG_DIRECTORY64 loadConfig;
27 | INVERTED_FUNCTION_TABLE_ENTRY userFunctionTable;
28 | ULONGLONG imageSize;
29 | NTSTATUS status;
30 | ULONG guardFlags;
31 | SIZE_T configSize;
32 | PVOID table;
33 | ULONGLONG count;
34 | ULONG rva;
35 | SIZE_T metaSize;
36 | BOOLEAN found;
37 | PVOID entry;
38 |
39 | //
40 | // First, do a quick lookup in the user function table, which should almost always work
41 | //
42 | found = RtlpLookupUserFunctionTableInverted(TargetRip, &userFunctionTable);
43 | if (found == FALSE)
44 | {
45 | //
46 | // This module might not have any exception/unwind data, so do a slow VAD lookup instead
47 | //
48 | status = MmGetImageBase(TargetRip, &userFunctionTable.ImageBase, &imageSize);
49 | if (!NT_SUCCESS(status))
50 | {
51 | //
52 | // There does not appear to be a valid module loaded at this address.
53 | // The only other possibility is that this is JIT, which we'll handle at the end.
54 | //
55 | userFunctionTable.ImageBase = NULL;
56 | }
57 | else
58 | {
59 | //
60 | // The VAD lookup can theoretically return a >= 4GB-sized module. This is not expected
61 | // and not supported for actual PE images.
62 | //
63 | if (imageSize >= MAXULONG)
64 | {
65 | return STATUS_INTEGER_OVERFLOW;
66 | }
67 | //
68 | // To simplify the code, capture the size in the same structure that the user function
69 | // table lookup would've returned.
70 | //
71 | userFunctionTable.SizeOfImage = (ULONG)imageSize;
72 | }
73 | }
74 |
75 | //
76 | // Did we find a loaded module at this address?
77 | //
78 | if (userFunctionTable.ImageBase != NULL)
79 | {
80 | //
81 | // We're going to touch user-mode data, so enter an exception handler context
82 | //
83 | __try
84 | {
85 | //
86 | // Kind of an arbitrary probe of 64 bytes, since the call below will call
87 | // RtlImageNtHeaderEx which does a proper probe of the whole header already.
88 | //
89 | ProbeForRead(userFunctionTable.ImageBase, 64, 1);
90 |
91 | //
92 | // Get the Image Load Config Directory. Note that this is a user-mode pointer
93 | //
94 | loadConfig = LdrImageDirectoryEntryToLoadConfig(userFunctionTable.ImageBase);
95 |
96 | //
97 | // For longjmp, use the longjump table, otherwise, for unwind, use the dynamic
98 | // exception handler continuation table.
99 | //
100 | if (ContinueType == KCONTINUE_LONGJUMP)
101 | {
102 | guardFlags = IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT;
103 | configSize = RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY64,
104 | GuardLongJumpTargetTable);
105 | }
106 | else
107 | {
108 | guardFlags = IMAGE_GUARD_EH_CONTINUATION_TABLE_PRESENT;
109 | configSize = RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY64,
110 | GuardEHContinuationTable);
111 | }
112 | //
113 | // Probe the configuration directory, as LdrImageDirectoryEntryToLoadConfig only
114 | // probes the first 4 bytes to account for the "Size" field.
115 | //
116 | // This probe will also raise if loadConfig is NULL (unless this is NTVDM on 32-bit).
117 | //
118 | ProbeForRead(loadConfig, configSize, 1);
119 |
120 | //
121 | // Make sure there's a load configuration directory, that it's large enough to have
122 | // one of the two tables we care about, and that the guard flags indicate that the
123 | // table we care about is actually present.
124 | //
125 | if ((loadConfig == NULL) ||
126 | (loadConfig->Size < configSize) ||
127 | !(guardFlags & loadConfig->GuardFlags))
128 | {
129 | //
130 | // We return success here, because this means that the binary is not compatible
131 | // with CET. As such, for compatibility, allow this jump target.
132 | //
133 | return STATUS_SUCCESS;
134 | }
135 | }
136 | __except (EXCEPTION_EXECUTE_HANDLER)
137 | {
138 | //
139 | // Something's wrong with the user address space, bail out
140 | //
141 | return GetExceptionCode();
142 | }
143 |
144 | //
145 | // Use the correct table and count (longjmp vs. unwind)
146 | //
147 | if (ContinueType == KCONTINUE_LONGJUMP)
148 | {
149 | table = (PVOID)loadConfig->GuardLongJumpTargetTable;
150 | count = loadConfig->GuardLongJumpTargetCount;
151 | }
152 | else
153 | {
154 | table = (PVOID)loadConfig->GuardEHContinuationTable;
155 | count = loadConfig->GuardEHContinuationCount;
156 | }
157 |
158 | //
159 | // More than 4 billion entries are not allowed
160 | //
161 | if (count >= MAXULONG)
162 | {
163 | return STATUS_INTEGER_OVERFLOW;
164 | }
165 |
166 | //
167 | // If the table is empty, then there can't be any valid targets in this image...
168 | //
169 | if (count != 0)
170 | {
171 | //
172 | // PE Images are always <= 4GB, so compute the 32-bit RVA
173 | //
174 | rva = (ULONG)((ULONG_PTR)TargetRip - (ULONG_PTR)userFunctionTable.ImageBase);
175 |
176 | //
177 | // The guard tables can have n-bytes of metadata, indicated by the upper nibble
178 | //
179 | metaSize = loadConfig->GuardFlags >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT;
180 |
181 | //
182 | // Search through the guard table for this RVA
183 | //
184 | entry = bsearch_s(&rva, table, count, metaSize + sizeof(rva), RtlpTargetCompare, NULL);
185 | if (entry != NULL)
186 | {
187 | //
188 | // The entry was found, so this is a valid target
189 | //
190 | return STATUS_SUCCESS;
191 | }
192 | }
193 | }
194 |
195 | //
196 | // Either there's no valid image mapped at this address, or there is, but its relevant guard
197 | // table does not contain the target RIP requested (as a reminder, if there's no table, then
198 | // the target _is_ allowed, for compatibility reasons).
199 | //
200 | // In this case, for exception unwinding (and obviously not longjmp), check if there is a
201 | // JIT-ted (dynamic) exception handler continuation target registered at this target.
202 | //
203 | if (ContinueType == KCONTINUE_UNWIND)
204 | {
205 | found = RtlpFindDynamicEHContinuationTarget(TargetRip);
206 | if (found != FALSE)
207 | {
208 | return STATUS_SUCCESS;
209 | }
210 | }
211 |
212 | //
213 | // Otherwise, we either didn't find a dynamic handler, or this wasn't an unwind to begin with,
214 | // so fail the request.
215 | //
216 | return STATUS_SET_CONTEXT_DENIED;
217 | }
218 |
--------------------------------------------------------------------------------