diff --git a/README.md b/README.md index 44034fd..348c4b5 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/driver/io.c b/driver/io.c index 1a5b1d7..1fc573d 100644 --- a/driver/io.c +++ b/driver/io.c @@ -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 diff --git a/driver/modules.c b/driver/modules.c index 95157bf..7be18cb 100644 --- a/driver/modules.c +++ b/driver/modules.c @@ -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); } } diff --git a/driver/types/types.h b/driver/types/types.h index d140cc2..f22a6f6 100644 --- a/driver/types/types.h +++ b/driver/types/types.h @@ -17,8 +17,9 @@ #define REPORT_SELF_DRIVER_PATCHED 160 #define REPORT_BLACKLISTED_PCIE_DEVICE 170 -#define REPORT_SUBTYPE_NO_BACKING_MODULE 0x0 -#define REPORT_SUBTYPE_INVALID_DISPATCH 0x1 +#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