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

View file

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

View file

@ -497,6 +497,14 @@ end:
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
FORCEINLINE
STATIC
BOOLEAN
IsUserModeAddress(_In_ UINT64 Rip)
{
return Rip <= WINDOWS_USERMODE_MAX_ADDRESS ? TRUE : FALSE;
}
NTSTATUS NTSTATUS
HandleValidateDriversIOCTL() HandleValidateDriversIOCTL()
{ {
@ -638,7 +646,8 @@ ReportMissingCidTableEntry(_In_ PNMI_CONTEXT Context)
STATIC STATIC
VOID VOID
ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context) ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context,
_In_ UINT32 ReportSubCode)
{ {
NTSTATUS status = STATUS_UNSUCCESSFUL; NTSTATUS status = STATUS_UNSUCCESSFUL;
UINT32 packet_size = UINT32 packet_size =
@ -650,7 +659,7 @@ ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context)
if (!report) if (!report)
return; 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->kthread_address = Context->kthread;
report->invalid_rip = Context->interrupted_rip; report->invalid_rip = Context->interrupted_rip;
@ -667,6 +676,34 @@ ReportInvalidRipFoundDuringNmi(_In_ PNMI_CONTEXT Context)
IrpQueueSchedulePacket(report, packet_size); 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 * todo: i think we should split this function up into each analysis i.e one for
* the interrupted rip, one for the cid etc. * the interrupted rip, one for the cid etc.
@ -718,16 +755,19 @@ AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules)
* PsGetNextProcess ? * PsGetNextProcess ?
*/ */
if (!DoesThreadHaveValidCidEntry(NmiContext[core].kthread)) { if (!DoesThreadHaveValidCidEntry(NmiContext[core].kthread))
ReportMissingCidTableEntry(&NmiContext[core]); ReportMissingCidTableEntry(&NmiContext[core]);
}
if (NmiContext[core].user_thread) if (NmiContext[core].user_thread)
continue; continue;
if (DoesRetInstructionCauseException(NmiContext[core].interrupted_rip))
ReportInvalidRipFoundDuringNmi(
&NmiContext[core], REPORT_SUBTYPE_EXCEPTION_THROWING_RET);
if (IsInstructionPointerInInvalidRegion( if (IsInstructionPointerInInvalidRegion(
NmiContext[core].interrupted_rip, SystemModules)) NmiContext[core].interrupted_rip, SystemModules))
ReportInvalidRipFoundDuringNmi(&NmiContext[core]); ReportInvalidRipFoundDuringNmi(&NmiContext[core], 0);
} }
return STATUS_SUCCESS; return STATUS_SUCCESS;
@ -749,14 +789,6 @@ GetIsrMachineFrame(_In_ TASK_STATE_SEGMENT_64* TaskStateSegment)
return TaskStateSegment->Ist3 - sizeof(MACHINE_FRAME); return TaskStateSegment->Ist3 - sizeof(MACHINE_FRAME);
} }
FORCEINLINE
STATIC
BOOLEAN
IsUserModeAddress(_In_ UINT64 Rip)
{
return Rip <= WINDOWS_USERMODE_MAX_ADDRESS ? TRUE : FALSE;
}
STATIC BOOLEAN STATIC BOOLEAN
NmiCallback(_Inout_opt_ PVOID Context, _In_ BOOLEAN Handled) NmiCallback(_Inout_opt_ PVOID Context, _In_ BOOLEAN Handled)
{ {
@ -1266,7 +1298,9 @@ CheckForDpcCompletion(_In_ PDPC_CONTEXT Context)
STATIC STATIC
VOID VOID
ReportDpcStackwalkViolation(_In_ PDPC_CONTEXT Context, _In_ UINT64 Frame) ReportDpcStackwalkViolation(_In_ PDPC_CONTEXT Context,
_In_ UINT64 Frame,
_In_ UINT32 ReportSubtype)
{ {
NTSTATUS status = STATUS_UNSUCCESSFUL; NTSTATUS status = STATUS_UNSUCCESSFUL;
UINT32 packet_size = UINT32 packet_size =
@ -1278,7 +1312,7 @@ ReportDpcStackwalkViolation(_In_ PDPC_CONTEXT Context, _In_ UINT64 Frame)
if (!report) if (!report)
return; return;
INIT_REPORT_PACKET(report, REPORT_DPC_STACKWALK, 0); INIT_REPORT_PACKET(report, REPORT_DPC_STACKWALK, ReportSubtype);
report->kthread_address = PsGetCurrentThread(); report->kthread_address = PsGetCurrentThread();
report->invalid_rip = Frame; report->invalid_rip = Frame;
@ -1306,11 +1340,17 @@ ValidateDpcStackFrame(_In_ PDPC_CONTEXT Context, _In_ PSYSTEM_MODULES Modules)
NTSTATUS status = STATUS_UNSUCCESSFUL; NTSTATUS status = STATUS_UNSUCCESSFUL;
BOOLEAN flag = FALSE; 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++) { for (UINT32 frame = 0; frame < Context->frames_captured; frame++) {
UINT64 rip = Context->stack_frame[frame]; UINT64 rip = Context->stack_frame[frame];
if (IsInstructionPointerInInvalidRegion(rip, Modules)) if (IsInstructionPointerInInvalidRegion(rip, Modules))
ReportDpcStackwalkViolation(Context, rip); ReportDpcStackwalkViolation(Context, rip, 0);
} }
} }

View file

@ -17,8 +17,9 @@
#define REPORT_SELF_DRIVER_PATCHED 160 #define REPORT_SELF_DRIVER_PATCHED 160
#define REPORT_BLACKLISTED_PCIE_DEVICE 170 #define REPORT_BLACKLISTED_PCIE_DEVICE 170
#define REPORT_SUBTYPE_NO_BACKING_MODULE 0x0 #define REPORT_SUBTYPE_NO_BACKING_MODULE 0x0
#define REPORT_SUBTYPE_INVALID_DISPATCH 0x1 #define REPORT_SUBTYPE_INVALID_DISPATCH 0x1
#define REPORT_SUBTYPE_EXCEPTION_THROWING_RET 0x2
#define PACKET_TYPE_REPORT 0x0 #define PACKET_TYPE_REPORT 0x0
#define PACKET_TYPE_HEARTBEAT 0x1 #define PACKET_TYPE_HEARTBEAT 0x1