Skip to content

Instantly share code, notes, and snippets.

@Barakat
Last active June 4, 2021 14:49
Show Gist options
  • Save Barakat/89002a26937a2da353868fc5130812a5 to your computer and use it in GitHub Desktop.
Save Barakat/89002a26937a2da353868fc5130812a5 to your computer and use it in GitHub Desktop.
Windows x86 Interrupt Descriptor Table (IDT) hooking driver
//
// Windows x86 Interrupt Descriptor Table (IDT) hook test
//
// Barakat Soror (https://twitter.com/barakatsoror)
//
#include <Windows.h>
int main(void)
{
OutputDebugStringA("[*] USER: Triggering interrupt\n");
__try
{
//
// Triggers Divide-By-Zero exception
//
volatile int A = 1;
volatile int B = 0;
volatile int C = A / B;
(void)C;
}
__except (GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
}
OutputDebugStringA("[*] USER: No BSOD :D!!!\n");
return 0;
}
//
// Windows x86 Interrupt Descriptor Table (IDT) hook driver
//
// Barakat Soror (https://twitter.com/barakatsoror)
//
#include <wdm.h>
#include <intrin.h>
#ifndef _X86_
#error "Only x86 is supported"
#endif
#pragma pack(push)
#pragma pack(1)
typedef struct _INTERRUPT_DESCRIPTOR_TABLE
{
UINT16 Offset0;
UINT16 Unused0;
UINT8 Unused1;
UINT8 Unused2;
UINT16 Offset1;
} INTERRUPT_DESCRIPTOR_TABLE, *PINTERRUPT_DESCRIPTOR_TABLE;
typedef struct _INTERRUPT_DESCRIPTOR_TABLE_REGISTER
{
UINT16 Unused0;
PINTERRUPT_DESCRIPTOR_TABLE InterruptDescriptorTable;
} INTERRUPT_DESCRIPTOR_TABLE_REGISTER, *PINTERRUPT_DESCRIPTOR_TABLE_REGISTER;
#pragma pack(pop)
//
// Stub assembly function for preparing the hook
//
extern LONG InterruptServiceRoutineHookStub;
//
// The original value of the real interrupt service routine
//
LONG InterruptServiceRoutineReal;
//
// Target interrupt number we want to hook (Divide-By-Zero interrupt)
//
static const ULONG TargetInterruptNumber = 0;
//
// The actual hook
//
VOID
NTAPI
InterruptServiceRoutineHook(VOID)
{
DbgPrint("[*] InterruptServiceRoutineHook has been called!\n");
}
//
// Load the IDT register and get the address of the table
//
static
PINTERRUPT_DESCRIPTOR_TABLE
GetCurrentProcessorInterruptDescriptorTable(VOID)
{
INTERRUPT_DESCRIPTOR_TABLE_REGISTER Register;
__sidt(&Register);
return Register.InterruptDescriptorTable;
}
//
//
//
static
LONG
GetInterruptServiceRoutine(PINTERRUPT_DESCRIPTOR_TABLE Table, ULONG InterruptNumber)
{
return (LONG)((Table[InterruptNumber].Offset1 << 16) | Table[InterruptNumber].Offset0);
}
//
//
//
static
VOID
SetInterruptServiceRoutine(PINTERRUPT_DESCRIPTOR_TABLE Table, ULONG InterruptNumber, LONG Value)
{
//
// Disable interrupts on the current processor, patch the IDT and reenable interrupts
//
_disable();
//
// Read the CR0 value
//
const unsigned long cr0 = __readcr0();
//
// Set the 16th bit (write-protection) to zero
//
__writecr0(cr0 & 0xfffeffffUL);
//
// Patch the table
//
Table[InterruptNumber].Offset0 = (ULONG)Value & 0xffff;
Table[InterruptNumber].Offset1 = (ULONG)Value >> 16;
//
// Restore the processor state
//
__writecr0(cr0);
_enable();
}
//
// Driver unload routine
//
static
VOID
_Function_class_(DRIVER_UNLOAD)
DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
DbgPrint("[*] DriverEntry: Unhooking active processors IDTs\n");
//
// Iterate over each active processor and pin the current thread to that processor
//
KAFFINITY ActiveProcessors = KeQueryActiveProcessors();
KAFFINITY Affinity;
for (Affinity = 1; ActiveProcessors; Affinity <<= 1, ActiveProcessors >>= 1)
{
if (ActiveProcessors & 1)
{
KeSetSystemAffinityThread(Affinity);
const PINTERRUPT_DESCRIPTOR_TABLE Table = GetCurrentProcessorInterruptDescriptorTable();
DbgPrint("[*] Pinning thread to CPU #%lu to unhook its IDT\n", (ULONG)Affinity);
//
// Restore the value of the interrupt service routine
//
SetInterruptServiceRoutine(Table, TargetInterruptNumber, InterruptServiceRoutineReal);
}
}
KeRevertToUserAffinityThread();
DbgPrint("[*] DriverEntry: Driver has been unloaded\n");
}
//
// Driver entry point
//
NTSTATUS
NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
DbgPrint("[*] DriverEntry: Hooking active processors IDTs\n");
//
// Iterate over each active processor and pin the current thread to that processor
//
KAFFINITY ActiveProcessors = KeQueryActiveProcessors();
KAFFINITY Affinity;
for (Affinity = 1; ActiveProcessors; Affinity <<= 1, ActiveProcessors >>= 1)
{
if (ActiveProcessors & 1)
{
KeSetSystemAffinityThread(Affinity);
const PINTERRUPT_DESCRIPTOR_TABLE Table = GetCurrentProcessorInterruptDescriptorTable();
DbgPrint("[*] Pinning thread to CPU #%lu to hook its IDT\n", (ULONG)Affinity);
//
// Backup the original value of the interrupt service routine
//
InterlockedCompareExchange(&InterruptServiceRoutineReal,
GetInterruptServiceRoutine(Table, TargetInterruptNumber)
, 0);
//
// Hook the interrupt service routine
//
SetInterruptServiceRoutine(Table, TargetInterruptNumber, InterruptServiceRoutineHookStub);
}
}
KeRevertToUserAffinityThread();
DbgPrint("[*] DriverEntry: Driver has been loaded\n");
return STATUS_SUCCESS;
}
;
; Windows x86 Interrupt Descriptor Table (IDT) hook driver assembly stub
;
; Barakat Soror (https://twitter.com/barakatsoror)
;
.model flat, stdcall
extern InterruptServiceRoutineReal : dword
InterruptServiceRoutineHook proto near stdcall
public InterruptServiceRoutineHookStub
.data
InterruptServiceRoutineHookStub dword offset InterruptServiceRoutineHookStub_
.code
InterruptServiceRoutineHookStub_ proc
; Store registers
pushad
pushfd
push fs
push ds
push es
; Setup segement selectors for the kernel, backup cx first
push cx
mov cx, 30h
mov fs, cx
mov cx, 23h
mov ds, cx
mov es, cx
pop cx
; Call our hook
call InterruptServiceRoutineHook
; Restore everything
pop fs
pop ds
pop es
popfd
popad
; Jump to the read routine
jmp [InterruptServiceRoutineReal]
InterruptServiceRoutineHookStub_ endp
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment