Skip to content

Commit f8ca8c0

Browse files
committed
Use PE runtime function tables for finding function start addresses
1 parent dceaa9c commit f8ca8c0

File tree

6 files changed

+74
-50
lines changed

6 files changed

+74
-50
lines changed

EfiGuardDxe/PatchBootmgr.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ PatchBootManager(
300300
// Found signature; backtrack to function start
301301
// Note: pOriginalAddress is a pointer to a (function) pointer, because the original address depends on the type of boot manager we are patching.
302302
VOID **pOriginalAddress = PatchingBootmgrEfi ? &gOriginalBootmgrImgArchStartBootApplication : &gOriginalBootmgfwImgArchStartBootApplication;
303-
*pOriginalAddress = (VOID*)BacktrackToFunctionStart(Found, MAX((UINT8*)ImageBase + CodeSection->VirtualAddress, Found - 1024));
303+
*pOriginalAddress = (VOID*)BacktrackToFunctionStart((UINT8*)ImageBase, NtHeaders, Found);
304304
CONST VOID* OriginalAddress = *pOriginalAddress;
305305
if (OriginalAddress == NULL)
306306
{

EfiGuardDxe/PatchNtoskrnl.c

+6-12
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,7 @@ DisablePatchGuard(
110110
}
111111

112112
// Backtrack to function start
113-
UINT8* KeInitAmd64SpecificState = BacktrackToFunctionStart(KeInitAmd64SpecificStatePatternAddress,
114-
(UINT8*)(KeInitAmd64SpecificStatePatternAddress - StartVa));
113+
UINT8* KeInitAmd64SpecificState = BacktrackToFunctionStart(ImageBase, NtHeaders, KeInitAmd64SpecificStatePatternAddress);
115114
if (KeInitAmd64SpecificState == NULL)
116115
{
117116
PRINT_KERNEL_PATCH_MSG(L" Failed to find KeInitAmd64SpecificState%S.\r\n",
@@ -202,8 +201,7 @@ DisablePatchGuard(
202201
}
203202

204203
// Backtrack to function start
205-
UINT8* CcInitializeBcbProfiler = BacktrackToFunctionStart(CcInitializeBcbProfilerPatternAddress,
206-
(UINT8*)(CcInitializeBcbProfilerPatternAddress - StartVa));
204+
UINT8* CcInitializeBcbProfiler = BacktrackToFunctionStart(ImageBase, NtHeaders, CcInitializeBcbProfilerPatternAddress);
207205
if (CcInitializeBcbProfiler == NULL)
208206
{
209207
PRINT_KERNEL_PATCH_MSG(L" Failed to find %S%S.\r\n",
@@ -249,8 +247,7 @@ DisablePatchGuard(
249247
}
250248

251249
// Backtrack to function start
252-
ExpLicenseWatchInitWorker = BacktrackToFunctionStart(ExpLicenseWatchInitWorkerPatternAddress,
253-
(UINT8*)(ExpLicenseWatchInitWorkerPatternAddress - StartVa));
250+
ExpLicenseWatchInitWorker = BacktrackToFunctionStart(ImageBase, NtHeaders, ExpLicenseWatchInitWorkerPatternAddress);
254251
if (ExpLicenseWatchInitWorker == NULL)
255252
{
256253
PRINT_KERNEL_PATCH_MSG(L" Failed to find ExpLicenseWatchInitWorker%S.\r\n",
@@ -279,8 +276,7 @@ DisablePatchGuard(
279276
PRINT_KERNEL_PATCH_MSG(L" Found KiVerifyScopesExecute pattern at 0x%llX.\r\n", (UINTN)KiVerifyScopesExecutePatternAddress);
280277

281278
// Backtrack to function start
282-
KiVerifyScopesExecute = BacktrackToFunctionStart(KiVerifyScopesExecutePatternAddress,
283-
(UINT8*)(KiVerifyScopesExecutePatternAddress - StartVa));
279+
KiVerifyScopesExecute = BacktrackToFunctionStart(ImageBase, NtHeaders, KiVerifyScopesExecutePatternAddress);
284280
if (KiVerifyScopesExecute == NULL)
285281
{
286282
PRINT_KERNEL_PATCH_MSG(L" Failed to find KiVerifyScopesExecute.\r\n");
@@ -352,10 +348,8 @@ DisablePatchGuard(
352348
}
353349

354350
// Backtrack to function start
355-
KiMcaDeferredRecoveryServiceCallers[0] = BacktrackToFunctionStart(KiMcaDeferredRecoveryServiceCallers[0],
356-
(UINT8*)(KiMcaDeferredRecoveryServiceCallers[0] - StartVa));
357-
KiMcaDeferredRecoveryServiceCallers[1] = BacktrackToFunctionStart(KiMcaDeferredRecoveryServiceCallers[1],
358-
(UINT8*)(KiMcaDeferredRecoveryServiceCallers[1] - StartVa));
351+
KiMcaDeferredRecoveryServiceCallers[0] = BacktrackToFunctionStart(ImageBase, NtHeaders, KiMcaDeferredRecoveryServiceCallers[0]);
352+
KiMcaDeferredRecoveryServiceCallers[1] = BacktrackToFunctionStart(ImageBase, NtHeaders, KiMcaDeferredRecoveryServiceCallers[1]);
359353
if (KiMcaDeferredRecoveryServiceCallers[0] == NULL || KiMcaDeferredRecoveryServiceCallers[1] == NULL)
360354
{
361355
PRINT_KERNEL_PATCH_MSG(L" Failed to find KiMcaDeferredRecoveryService callers.\r\n");

EfiGuardDxe/PatchWinload.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ PatchImgpValidateImageHash(
191191
}
192192

193193
// Backtrack to function start
194-
CONST UINT8* ImgpValidateImageHash = BacktrackToFunctionStart(AndMinusFortyOneAddress, CodeStartVa);
194+
CONST UINT8* ImgpValidateImageHash = BacktrackToFunctionStart(ImageBase, NtHeaders, AndMinusFortyOneAddress);
195195
if (ImgpValidateImageHash == NULL)
196196
{
197197
Print(L" Failed to find %S!ImgpValidateImageHash%S.\r\n",
@@ -329,7 +329,7 @@ PatchImgpFilterValidationFailure(
329329
}
330330

331331
// Backtrack to function start
332-
CONST UINT8* ImgpFilterValidationFailure = BacktrackToFunctionStart(LeaIntegrityFailureAddress, LeaIntegrityFailureAddress - Length);
332+
CONST UINT8* ImgpFilterValidationFailure = BacktrackToFunctionStart(ImageBase, NtHeaders, LeaIntegrityFailureAddress);
333333
if (ImgpFilterValidationFailure == NULL)
334334
{
335335
Print(L" Failed to find %S!ImgpFilterValidationFailure%S.\r\n",
@@ -380,7 +380,7 @@ FindOslFwpKernelSetupPhase1(
380380
if (!EFI_ERROR(Status))
381381
{
382382
// Found signature; backtrack to function start
383-
*OslFwpKernelSetupPhase1Address = BacktrackToFunctionStart(Found, Found - 0x400);
383+
*OslFwpKernelSetupPhase1Address = BacktrackToFunctionStart(ImageBase, NtHeaders, Found);
384384
if (*OslFwpKernelSetupPhase1Address != NULL)
385385
{
386386
Print(L"\r\nFound OslFwpKernelSetupPhase1 at 0x%llX.\r\n", (UINTN)(*OslFwpKernelSetupPhase1Address));
@@ -479,7 +479,7 @@ FindOslFwpKernelSetupPhase1(
479479
return EFI_NOT_FOUND;
480480
}
481481

482-
CONST UINT8* EfipGetRsdt = BacktrackToFunctionStart(LeaEfiAcpiTableGuidAddress, LeaEfiAcpiTableGuidAddress - Length);
482+
CONST UINT8* EfipGetRsdt = BacktrackToFunctionStart(ImageBase, NtHeaders, LeaEfiAcpiTableGuidAddress);
483483
if (EfipGetRsdt == NULL)
484484
{
485485
Print(L" Failed to find EfipGetRsdt.\r\n");
@@ -516,7 +516,7 @@ FindOslFwpKernelSetupPhase1(
516516
OperandAddress == (UINTN)EfipGetRsdt)
517517
{
518518
// Calculate the distance from the start of the function to the instruction. OslFwpKernelSetupPhase1 will always have the shortest distance
519-
CONST UINTN StartOfFunction = (UINTN)BacktrackToFunctionStart((UINT8*)InstructionAddress, (UINT8*)InstructionAddress - Length);
519+
CONST UINTN StartOfFunction = (UINTN)BacktrackToFunctionStart(ImageBase, NtHeaders, (UINT8*)InstructionAddress);
520520
CONST UINTN Distance = InstructionAddress - StartOfFunction;
521521
if (Distance < ShortestDistanceToCall)
522522
{

EfiGuardDxe/pe.h

+17
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ typedef EFI_IMAGE_EXPORT_DIRECTORY *PEFI_IMAGE_EXPORT_DIRECTORY;
4040
#define VS_VERSION_INFO 1
4141
#define VS_FF_DEBUG (0x00000001L)
4242

43+
#define RUNTIME_FUNCTION_INDIRECT 0x1
44+
4345
#define IMAGE32(NtHeaders) ((NtHeaders)->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC)
4446
#define IMAGE64(NtHeaders) ((NtHeaders)->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC)
4547

@@ -172,6 +174,21 @@ typedef struct _VS_VERSIONINFO
172174
} VS_VERSIONINFO, *PVS_VERSIONINFO;
173175

174176

177+
//
178+
// Function table entry data
179+
//
180+
typedef struct _RUNTIME_FUNCTION
181+
{
182+
UINT32 BeginAddress;
183+
UINT32 EndAddress;
184+
union
185+
{
186+
UINT32 UnwindInfoAddress;
187+
UINT32 UnwindData;
188+
} u;
189+
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
190+
191+
175192
//
176193
// Function declarations
177194
//

EfiGuardDxe/util.c

+38-27
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ AppendKernelPatchMessage(
7878
VOID
7979
EFIAPI
8080
PrintKernelPatchInfo(
81+
VOID
8182
)
8283
{
8384
ASSERT(gST->ConOut != NULL);
@@ -101,6 +102,7 @@ PrintKernelPatchInfo(
101102
BOOLEAN
102103
EFIAPI
103104
WaitForKey(
105+
VOID
104106
)
105107
{
106108
// Hack: because we call this at TPL_NOTIFY in ExitBootServices, we cannot use WaitForEvent()
@@ -339,42 +341,51 @@ ZydisInit(
339341
UINT8*
340342
EFIAPI
341343
BacktrackToFunctionStart(
342-
IN CONST UINT8* StartAddress,
343-
IN CONST UINT8* LowerBound
344+
IN CONST UINT8* ImageBase,
345+
IN PEFI_IMAGE_NT_HEADERS NtHeaders,
346+
IN CONST UINT8* AddressInFunction
344347
)
345348
{
346349
// Test for null. This allows callers to do 'FindPattern(..., &Address); X = Backtrack(Address, ...)' with a single failure branch
347-
if (StartAddress == NULL)
350+
if (AddressInFunction == NULL)
351+
return NULL;
352+
if (NtHeaders->OptionalHeader.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION)
348353
return NULL;
349354

350-
ASSERT(StartAddress > LowerBound);
355+
CONST PRUNTIME_FUNCTION FunctionTable = (PRUNTIME_FUNCTION)(ImageBase + NtHeaders->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress);
356+
CONST UINT32 FunctionTableSize = NtHeaders->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
357+
if (FunctionTableSize == 0)
358+
return NULL;
351359

352-
UINT8 *Address;
353-
BOOLEAN Found = FALSE;
354-
for (Address = (UINT8*)StartAddress; Address >= LowerBound; --Address)
360+
// Do a binary search until we find the function that contains our address
361+
CONST UINT32 RelativeAddress = (UINT32)(AddressInFunction - ImageBase);
362+
PRUNTIME_FUNCTION FunctionEntry = NULL;
363+
INT32 Low = 0;
364+
INT32 High = (FunctionTableSize / sizeof(RUNTIME_FUNCTION)) - 1;
365+
366+
while (High >= Low)
355367
{
356-
if ((*(Address - 1) == 0xCC || // Previous byte is int 3 padding, or
357-
(*(Address - 2) == 0x90 && *(Address - 1) == 0x90) || // Previous 2 bytes are nop padding, or
358-
(*(Address - 4) == 0x00 && *(Address - 3) == 0x00 && // Previous 4+ bytes are 00 padding (rare, only happens at start of a section), or
359-
*(Address - 2) == 0x00 && *(Address - 1) == 0x00) ||
360-
(*(Address - 1) == 0xC3 && *(Address - 3) != 0x8D) // Previous byte is 'ret', or
361-
#if defined(MDE_CPU_IA32) || defined(_M_IX86)
362-
|| (*(Address - 3) == 0xC2 && *(Address - 1) == 0x00) // Previous 3 bytes are 'ret XX' (x86)
363-
#endif
364-
)
365-
&& // *and*
366-
(*Address == 0x40 || *Address == 0x55 || // Current byte is either 'push [ebp|ebx|rbp|rbx]', 'mov REG, XX' or 'sub REG, XX'
367-
(Address < StartAddress && *Address == 0x44 && *(Address + 1) == 0x89) ||
368-
(Address < StartAddress && *Address == 0x48 && *(Address + 1) == 0x83) ||
369-
(Address < StartAddress && *Address == 0x48 && *(Address + 1) == 0x89) ||
370-
(Address < StartAddress && *Address == 0x48 && *(Address + 1) == 0x8B) ||
371-
(Address < StartAddress && *Address == 0x49 && *(Address + 1) == 0x89) ||
372-
(Address < StartAddress && *Address == 0x4C && *(Address + 1) == 0x8B)))
373-
{
374-
Found = TRUE;
368+
CONST INT32 Middle = (Low + High) >> 1;
369+
FunctionEntry = &FunctionTable[Middle];
370+
371+
if (RelativeAddress < FunctionEntry->BeginAddress)
372+
High = Middle - 1;
373+
else if (RelativeAddress >= FunctionEntry->EndAddress)
374+
Low = Middle + 1;
375+
else
375376
break;
377+
}
378+
379+
if (High >= Low)
380+
{
381+
// If the function entry specifies indirection, get the address of the master function entry
382+
if ((FunctionEntry->u.UnwindData & RUNTIME_FUNCTION_INDIRECT) != 0)
383+
{
384+
FunctionEntry = (PRUNTIME_FUNCTION)(FunctionEntry->u.UnwindData + ImageBase - 1);
376385
}
386+
387+
return (UINT8*)ImageBase + FunctionEntry->BeginAddress;
377388
}
378389

379-
return Found ? Address : NULL;
390+
return NULL;
380391
}

EfiGuardDxe/util.h

+7-5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ AppendKernelPatchMessage(
4242
VOID
4343
EFIAPI
4444
PrintKernelPatchInfo(
45+
VOID
4546
);
4647

4748
//
@@ -51,6 +52,7 @@ PrintKernelPatchInfo(
5152
BOOLEAN
5253
EFIAPI
5354
WaitForKey(
55+
VOID
5456
);
5557

5658
//
@@ -108,13 +110,13 @@ ZydisInit(
108110
);
109111

110112
//
111-
// Finds the start of a function given an address within it, scanning downwards.
112-
// Returns NULL if StartAddress is NULL (this simplifies error checking logic in calling functions).
113-
// Returns NULL if LowerBound is reached and no function boundary was found.
113+
// Finds the start of a function given an address within it.
114+
// Returns NULL if AddressInFunction is NULL (this simplifies error checking logic in calling functions).
114115
//
115116
UINT8*
116117
EFIAPI
117118
BacktrackToFunctionStart(
118-
IN CONST UINT8* StartAddress,
119-
IN CONST UINT8* LowerBound
119+
IN CONST UINT8* ImageBase,
120+
IN PEFI_IMAGE_NT_HEADERS NtHeaders,
121+
IN CONST UINT8* AddressInFunction
120122
);

0 commit comments

Comments
 (0)