Burrows Code Blog

July 18, 2010

Determining If Your Process is Being Debugged

Filed under: Kernel Modules, Reverse Engineering — Tags: , , , , — burrowscode @ 9:12 pm

There are numerous situations when you may want to find out if your process is being debugged. The most notable is if you are writing a protection scheme for some software. As the ability to detect debugging can be an instrumental part of your protection. Note that the methods I am going to show you are not perfect and can be circumvented using a variety of techniques.

The first thing you can do is use the trivial IsDebuggerPresent(). This function works by checking the processes program environment block (PEB) as there is a member variable called BeingDebugged which will be set to TRUE if the process is being debugged. Thus it is simple to bypass this method by simply changing this value in the PEB.

Another way that you can check for debugging is by testing bit 8 of the FLAGS register. This is the trap flag and if set to 1 this means that the program is single stepping, a very good indication of debugging. You can accomplish this with a piece of code like this.

BOOL Is_Being_Debugged(VOID)
{
	USHORT flags;
	__asm__ (
		"pushf\n"
		"pop %0\n"
		: "=r" (flags)
		:
		: "%eax"
	);
	return ((( flags >> 8 ) & 1 ) == 1 );
}

The easiest way to circumvent this is to watch for a similar piece of code in the debugger and simply change the return value (eax). You also should be able to unset the trap flag, insert a software break point after the code, and then set the flag again.

A third way to detect when a program is being debugged involves the use of a kernel mode driver. Here we hook the system call NtCreateDebugObject(), which is used when a debugger initial attaches to a program. By intercepting this call we are able to determine when a process is attempting to debug something. I’ve included sample source for that as well.

// NtCreateDebugObject Hook
// Creative Commons - Aaron Burrow

#include "ntddk.h"

#define NTCDO_INDEX 0x37

#pragma pack(1)

typedef struct _Service_Descriptor_Entry {
	unsigned int* service_table_base;
	unsigned int* service_counter_table_base;
	unsigned int number_of_services;
	unsigned char* param_table_base;
} Service_Descriptor_Entry, *PService_Descriptor_Entry;

NTSTATUS DriverUnload(PDRIVER_OBJECT driver_object);

/* Native API */

NTSTATUS (*NtCreateDebugObject)(OUT PHANDLE, IN ACCESS_MASK,
	IN POBJECT_ATTRIBUTES, IN BOOLEAN);

NTSTATUS Hooked_Nt_CDO(OUT PHANDLE DebugObject, IN ULONG AccessRequired,
	IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN KillProcessOnExit);

extern PService_Descriptor_Entry KeServiceDescriptorTable; // SSDT
PMDL mdl; // SSDT mdl

NTSTATUS DriverEntry(PDRIVER_OBJECT driver_object, PUNICODE_STRING registry_path)
{
	unsigned int* addressable_service_table_base;

	driver_object->DriverUnload = DriverUnload;

	if (!(mdl = IoAllocateMdl(KeServiceDescriptorTable->service_table_base, KeServiceDescriptorTable->number_of_services * sizeof(unsigned int*), FALSE, TRUE, NULL)))
		return STATUS_UNSUCCESSFUL;

	MmBuildMdlForNonPagedPool(mdl);

	mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;

	// Causes a bug check on failure
	addressable_service_table_base = (PService_Descriptor_Entry)MmMapLockedPages(mdl, KernelMode);

	NtCreateDebugObject = ((PVOID*)addressable_service_table_base)[NTCDO_INDEX];
	addressable_service_table_base[NTCDO_INDEX] = (unsigned int)Hooked_Nt_CDO;
	return STATUS_SUCCESS;
}

NTSTATUS DriverUnload(PDRIVER_OBJECT driver_object)
{
	((unsigned int*)mdl->MappedSystemVa)[NTCDO_INDEX] = (unsigned int)NtCreateDebugObject;
	MmUnmapLockedPages((PVOID)mdl->MappedSystemVa, mdl);
	IoFreeMdl(mdl);
	return STATUS_SUCCESS;
}

NTSTATUS Hooked_Nt_CDO(OUT PHANDLE DebugObject, IN ULONG AccessRequired,
	IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN KillProcessOnExit)
{
	DbgPrint("Attempting to Debug!\n");
	return NtCreateDebugObject(DebugObject, AccessRequired, ObjectAttributes, KillProcessOnExit);
}

The most trivial way to defeat this method is to use an SSDT hook detection scheme.

Until later.

June 23, 2010

IDT Hooking/Unhooking Module

Filed under: Kernel Modules — Tags: , , , , — burrowscode @ 11:31 am

Been busy with work and such lately, but I took the time to put this code together. This implementation in no way represents a new concept as IDT (Interrupt Descriptor Table) hooking has been around for quite some time, but the code is clean and is a good example for those of you who are unfamiliar with the practice.

The IDT is a table that has entries referencing service routines associated with interrupts. A system can have up too 0xFF (256) such interrupts and routines, but in general most systems use less. The IDT is reponsible for handling software interrupts, hardware interrupts, and exceptions. This means that the IDT handles issues ranging from a device polling for attention to a divide by zero exception. Here are two articles to check out for more information about the IDT, Bran’s Kernel Dev on IDT and IDT Wiki.

The code is fairly straightforward, but here is a brief run down of how it works. First we attain a descriptor that will give us a virtual address referencing the IDT. We also save the current state of the IDT so that we can restore it at a later time (Note that the assembly instruction sidt simply loads a pointer to the IDT into a memory location). From here we are free to set hooks using hook_function and then remove hooks using unhook_function. Hooks are applied using a small section of assembly instructions which masks interrupts, applies the hooks, and then unmasks interrupts. There is also a function called enumerate_idt_entries that will output interrupt numbers and associated addresses for the service routines. When the driver is stopped it will also attempt to restore the IDT to the way it was when the driver first loaded.

Here is the code with some example usage.

// IDT Hooking/Unhooking Module
// Creative Commons - Aaron Burrow
#include "ntddk.h"

#pragma pack(1)

// Took these structures from Bran's Kernel Dev Tutorial
typedef struct _idt_entry
{
	USHORT base_lo;
	USHORT sel;
	UCHAR always0;
	UCHAR flags;
	USHORT base_hi;
} idt_entry, *pidt_entry;

typedef struct _idt_ptr
{
	USHORT limit;
	ULONG base;
} idt_ptr, *pidt_ptr;

VOID driver_unload(IN DRIVER_OBJECT* driver_object);
PVOID make_idt_writable(PMDL* mdl);
NTSTATUS save_original_idt(VOID);
VOID enumerate_idt_entries(VOID);
NTSTATUS hook_interrupt(PVOID writable_idt, ULONG interrupt_number, PVOID replacement_address, PVOID* old_address);
NTSTATUS unhook_interrupt(PVOID writable_idt, ULONG interrupt_number);
VOID cleanup_idt_memory(PVOID writable_idt, PMDL* mdl);

// Global Variables
// a prefix of '_' indicates a global variable that shouldn't be directly modified
pidt_entry _original_idt_vectors;
PVOID reg_call0x70;
PVOID reg_call0x71;

// Example Routines use in Hooks
__declspec(naked) hook_call0x70()
{
	__asm {
		jmp reg_call0x70;
	};
}

__declspec(naked) hook_call0x71()
{
	__asm {
		jmp reg_call0x71;
	};
}

NTSTATUS DriverEntry(IN DRIVER_OBJECT* driver_object, IN UNICODE_STRING* registry_path)
{
	PMDL mdl = NULL;
	PVOID old_address, writable_idt;
	driver_object->DriverUnload = driver_unload;
	
	if ((writable_idt = make_idt_writable(&mdl))) {
		reg_call0x70 = ((ULONG)_original_idt_vectors[0x70].base_hi << 16) | _original_idt_vectors[0x70].base_lo;
		reg_call0x71 = ((ULONG)_original_idt_vectors[0x71].base_hi << 16) | _original_idt_vectors[0x71].base_lo;
		enumerate_idt_entries();
		if (hook_interrupt(writable_idt, 0x70, (PVOID)&hook_call0x70, &old_address) == STATUS_SUCCESS) {
			if (hook_interrupt(writable_idt, 0x71, (PVOID)&hook_call0x71, &old_address) == STATUS_SUCCESS) {
				enumerate_idt_entries();
				unhook_interrupt(writable_idt, 0x70);
				unhook_interrupt(writable_idt, 0x71);
				cleanup_idt_memory(writable_idt, &mdl);
				return STATUS_SUCCESS;
			}
		}
	}
	return STATUS_UNSUCCESSFUL;
}

/*
	Get a virtual address that allows us to write to the IDT
	On success the caller is responsible for calling cleanup_idt_memory
	@mdl: The memory descriptor for the addresses referencing the IDT
	return: NULL on failure, address to IDT on success
*/
PVOID make_idt_writable(PMDL* mdl)
{
	idt_ptr p_idt;
	*mdl = NULL;
	
	if (save_original_idt() != STATUS_SUCCESS)
		return NULL;
	
	// Get a pointer to the IDT
	__asm {
		sidt p_idt 
	};
	
	if (!(*mdl = IoAllocateMdl((PVOID)p_idt.base, p_idt.limit, FALSE, FALSE, NULL)))
		return NULL;
	
	MmBuildMdlForNonPagedPool(*mdl);

	(*mdl)->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;

	// Causes a bug check on failure
	return MmMapLockedPages(*mdl, KernelMode);
}

/*
	Cleanup all of the resources that have been allocated.
	return: None
*/
VOID cleanup_idt_memory(PVOID writable_idt, PMDL* mdl)
{
	MmUnmapLockedPages(writable_idt, *mdl);
	
	IoFreeMdl(*mdl);
	
	ExFreePoolWithTag(_original_idt_vectors, 'idt');
	return;
}

/*
	Displays information about the current IDT
	return: None
*/
VOID enumerate_idt_entries(VOID)
{
	USHORT i;
	idt_ptr p_idt;
	pidt_entry idt_vectors;
	
	// Get a pointer to the IDT
	__asm {
		sidt p_idt 
	};
	
	idt_vectors = (pidt_entry)p_idt.base;
	
	for (i = 0; i < p_idt.limit/sizeof(idt_entry); ++i) 
		DbgPrint("Interrupt #: %X Base Lo: %.4X Base High: %.4X\n", i, idt_vectors[i].base_lo, idt_vectors[i].base_hi);
	
	return;
}

/*
	Make a copy of the original IDT so that it can be restored later on
	return: STATUS_UNSUCCESSFUL on failure, STATUS_SUCCESS on success
*/
NTSTATUS save_original_idt(VOID)
{
	idt_ptr p_idt;
	
	// Get a pointer to the IDT
	__asm {
		sidt p_idt 
	};
	
	if (!(_original_idt_vectors = ExAllocatePoolWithTag(PagedPool, p_idt.limit, 'idt')))
		return STATUS_UNSUCCESSFUL;
	
	RtlCopyMemory((PVOID)_original_idt_vectors, (PVOID)(p_idt.base), p_idt.limit);
	return STATUS_SUCCESS;
	
}

/*
	Applies a hook to the specified interrupt
	A point to be made is that the function employes a caching mechanism which allows the caller
		to pass NULL for writable_idt and the function will use the last non-NULL address passed
		as that parameter.  Obviously there needs to originally be a valid address passed for the
		mechanism to work.
	@writable_idt: The virtual address to the IDT
	@interrupt_number: The number/index to the interrupt to be hooked
	@replacement_address: Address to the code to be executed instead of the interrupt service routine
	@old_address: Gives the caller the former address associated with the interrupt
	return: STATUS_SUCCESSFUL on failure, STATUS_SUCCESS on success
*/
NTSTATUS hook_interrupt(PVOID writable_idt, ULONG interrupt_number, PVOID replacement_address, PVOID* old_address)
{
	idt_ptr p_idt;
	pidt_entry idt_vectors, current_idt_entry;
	static PVOID s_writable_idt;
	
	// Get a pointer to the IDT
	__asm {
		sidt p_idt 
	};
	
	if (writable_idt) {
		// Cache the last non-Null idt virtual address in s_writable_idt
		s_writable_idt = writable_idt;
	}
	
	idt_vectors = (pidt_entry)s_writable_idt;
	
	if (!writable_idt)
		return STATUS_UNSUCCESSFUL;
	
	// Check if the interrupt number is out of range
	if (interrupt_number > p_idt.limit/sizeof(idt_entry))
		return STATUS_UNSUCCESSFUL;
	
	/*
	The following assembly code is the same thing as this 
		C code, except they force the processor to ignore 
		and then acknowledge interrupts.
	idt_vectors[interrupt_number].base_lo = replacement_address & 0xFFFF;
	idt_vectors[interrupt_number].base_hi = (replacement_address & 0xFFFF0000) >> 16;
	*/
	
	// Get the address to the idt entry we are dealing with
	current_idt_entry = &(idt_vectors[interrupt_number]);
	
	// Give the caller the old address as they'll probably need it
	*old_address = ((ULONG)current_idt_entry->base_hi << 16) | current_idt_entry->base_lo;

	__asm {
		cli							// mask interrupts
		mov eax, replacement_address 
		mov ebx, current_idt_entry
		mov [ebx], ax				// .base_lo offset = 0x0
		shr eax, 16
		mov [ebx + 0x6], ax 		// .base_hi offset = 0x6
		sti							// acknowledge interrupts
	};
	return STATUS_SUCCESS;
}

/*
	Restores an interrupt to it's original state.
	Employes the same caching mechanism as unhook_interrupt, addresses
		are shared between the functions, so a valid one must be pased
		to only one or the other
	@writable_idt: Virtual address of the IDT
	@interrupt_number: Index/Number of the IDT to be unhooked
*/
NTSTATUS unhook_interrupt(PVOID writable_idt, ULONG interrupt_number)
{
	idt_ptr p_idt;
	PVOID orig_addr, temp_addr;
	
	// Get a pointer to the IDT
	__asm {
		sidt p_idt 
	};
	
	// Check if the interrupt number is out of range
	if (interrupt_number > p_idt.limit/sizeof(idt_entry))
		return STATUS_UNSUCCESSFUL;
	
	// Make sure memory has actually been allocated for _original_idt_vectors
	if (!(_original_idt_vectors))
		return STATUS_UNSUCCESSFUL;
	
	orig_addr = ((ULONG)_original_idt_vectors[interrupt_number].base_hi << 16) | _original_idt_vectors[interrupt_number].base_lo;
	return hook_interrupt(writable_idt, interrupt_number, orig_addr, &temp_addr);
}

/*
	This function will unhook all interrupts
	@driver_object: The driver object
	return: None
*/
VOID driver_unload(DRIVER_OBJECT* driver_object)
{
	PMDL mdl = NULL;
	USHORT i;
	idt_ptr p_idt;
	PVOID writable_idt;
	
	// Get a pointer to the IDT
	__asm {
		sidt p_idt 
	};
	
	if ((writable_idt = make_idt_writable(&mdl))) {
		for (i = 0; i < p_idt.limit/sizeof(idt_entry); ++i) {
			unhook_interrupt(NULL, i);
		}
		cleanup_idt_memory(writable_idt, &mdl);
	}

	return;
}

This output represents the changes in the IDT (from unhooked, to hooked, to unhooked again) that occur on my system when running that code.

Interrupt #: 70 Base Lo: D3C0 Base High: 81C4
Interrupt #: 71 Base Lo: D3CA Base High: 81C4

Interrupt #: 70 Base Lo: 1010 Base High: AC20
Interrupt #: 71 Base Lo: 1020 Base High: AC20

Interrupt #: 70 Base Lo: D3C0 Base High: 81C4
Interrupt #: 71 Base Lo: D3CA Base High: 81C4

Until later.

Theme: WordPress Classic. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.