simple exception hooking detection

This commit is contained in:
donnaskiez 2024-07-13 15:43:50 +10:00
parent 08b059bee9
commit 0b729a6fa0
4 changed files with 63 additions and 20 deletions

View file

@ -9,6 +9,8 @@ open source anti cheat (lol) which I made for fun.
- NMI stackwalking via isr iretq
- APC stackwalking via RtlCaptureStackBackTrace
- DPC stackwalking via RtlCaptureStackBackTrace
- Return address exception hooking detection
- Chained .data pointer detection (iffy)
- Handle stripping via obj callbacks
- Process handle table enumeration
- System module device object verification
@ -27,7 +29,7 @@ open source anti cheat (lol) which I made for fun.
# architecuture
For an overview of the architecture, see architecture.md.
- todo!
# planned features

View file

@ -260,7 +260,7 @@ IrpQueueAllocateDeferredPacket(_In_ PVOID Buffer, _In_ UINT32 BufferSize)
return report;
}
#define MAX_DEFERRED_REPORTS_COUNT 100
#define MAX_DEFERRED_REPORTS_COUNT 256
STATIC
VOID

View file

@ -497,6 +497,14 @@ end:
return STATUS_SUCCESS;
}
FORCEINLINE
STATIC
BOOLEAN
IsUserModeAddress(_In_ UINT64 Rip)
{
return Rip <= WINDOWS_USERMODE_MAX_ADDRESS ? TRUE : FALSE;
}
NTSTATUS
HandleValidateDriversIOCTL()
{
@ -638,7 +646,8 @@ ReportMissingCidTableEntry(_In_ PNMI_CONTEXT Context)
STATIC
VOID
ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context)
ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context,
_In_ UINT32 ReportSubCode)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UINT32 packet_size =
@ -650,7 +659,7 @@ ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context)
if (!report)
return;
INIT_REPORT_PACKET(report, REPORT_NMI_CALLBACK_FAILURE, 0);
INIT_REPORT_PACKET(report, REPORT_NMI_CALLBACK_FAILURE, ReportSubCode);
report->kthread_address = Context->kthread;
report->invalid_rip = Context->interrupted_rip;
@ -667,6 +676,34 @@ ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context)
IrpQueueSchedulePacket(report, packet_size);
}
#define INSTRUCTION_UD2_BYTE_1 0x0F
#define INSTRUCTION_UD2_BYTE_2 0x0B
#define INSTRUCTION_INT3_BYTE_1 0xCC
STATIC
BOOLEAN
DoesRetInstructionCauseException(_In_ UINT64 ReturnAddress)
{
/* UD2 instruction is 2 bytes*/
UCHAR opcodes[2] = {0};
/* we deal with um later */
if (IsUserModeAddress(ReturnAddress))
return FALSE;
RtlCopyMemory(&opcodes, ReturnAddress, sizeof(opcodes));
if (opcodes[0] == INSTRUCTION_UD2_BYTE_1 &&
opcodes[1] == INSTRUCTION_UD2_BYTE_2)
return TRUE;
if (opcodes[0] == INSTRUCTION_INT3_BYTE_1)
return TRUE;
DEBUG_VERBOSE("Ret address instruction doesnt throw exception");
return FALSE;
}
/*
* todo: i think we should split this function up into each analysis i.e one for
* the interrupted rip, one for the cid etc.
@ -718,16 +755,19 @@ AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules)
* PsGetNextProcess ?
*/
if (!DoesThreadHaveValidCidEntry(NmiContext[core].kthread)) {
if (!DoesThreadHaveValidCidEntry(NmiContext[core].kthread))
ReportMissingCidTableEntry(&NmiContext[core]);
}
if (NmiContext[core].user_thread)
continue;
if (DoesRetInstructionCauseException(NmiContext[core].interrupted_rip))
ReportInvalidRipFoundDuringNmi(
&NmiContext[core], REPORT_SUBTYPE_EXCEPTION_THROWING_RET);
if (IsInstructionPointerInInvalidRegion(
NmiContext[core].interrupted_rip, SystemModules))
ReportInvalidRipFoundDuringNmi(&NmiContext[core]);
ReportInvalidRipFoundDuringNmi(&NmiContext[core], 0);
}
return STATUS_SUCCESS;
@ -749,14 +789,6 @@ GetIsrMachineFrame(_In_ TASK_STATE_SEGMENT_64* TaskStateSegment)
return TaskStateSegment->Ist3 - sizeof(MACHINE_FRAME);
}
FORCEINLINE
STATIC
BOOLEAN
IsUserModeAddress(_In_ UINT64 Rip)
{
return Rip <= WINDOWS_USERMODE_MAX_ADDRESS ? TRUE : FALSE;
}
STATIC BOOLEAN
NmiCallback(_Inout_opt_ PVOID Context, _In_ BOOLEAN Handled)
{
@ -1266,7 +1298,9 @@ CheckForDpcCompletion(_In_ PDPC_CONTEXT Context)
STATIC
VOID
ReportDpcStackwalkViolation(_In_ PDPC_CONTEXT Context, _In_ UINT64 Frame)
ReportDpcStackwalkViolation(_In_ PDPC_CONTEXT Context,
_In_ UINT64 Frame,
_In_ UINT32 ReportSubtype)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UINT32 packet_size =
@ -1278,7 +1312,7 @@ ReportDpcStackwalkViolation(_In_ PDPC_CONTEXT Context, _In_ UINT64 Frame)
if (!report)
return;
INIT_REPORT_PACKET(report, REPORT_DPC_STACKWALK, 0);
INIT_REPORT_PACKET(report, REPORT_DPC_STACKWALK, ReportSubtype);
report->kthread_address = PsGetCurrentThread();
report->invalid_rip = Frame;
@ -1306,11 +1340,17 @@ ValidateDpcStackFrame(_In_ PDPC_CONTEXT Context, _In_ PSYSTEM_MODULES Modules)
NTSTATUS status = STATUS_UNSUCCESSFUL;
BOOLEAN flag = FALSE;
/* With regards to this, lets only check the interrupted rip */
if (DoesRetInstructionCauseException(Context->stack_frame[0]))
ReportDpcStackwalkViolation(Context,
Context->stack_frame[0],
REPORT_SUBTYPE_EXCEPTION_THROWING_RET);
for (UINT32 frame = 0; frame < Context->frames_captured; frame++) {
UINT64 rip = Context->stack_frame[frame];
if (IsInstructionPointerInInvalidRegion(rip, Modules))
ReportDpcStackwalkViolation(Context, rip);
ReportDpcStackwalkViolation(Context, rip, 0);
}
}

View file

@ -19,6 +19,7 @@
#define REPORT_SUBTYPE_NO_BACKING_MODULE 0x0
#define REPORT_SUBTYPE_INVALID_DISPATCH 0x1
#define REPORT_SUBTYPE_EXCEPTION_THROWING_RET 0x2
#define PACKET_TYPE_REPORT 0x0
#define PACKET_TYPE_HEARTBEAT 0x1