Contents

The Abuse of Exception Handlers

The Definition

Everyone interested in programming probably has run into the too-famous “try-catch” thing. It is not something new. So, it is basically what we will talk about today. I used to say that it is always good to understand what happens behind the scenes; I mean, speaking about try-catch, it is nothing mysterious; it is kind of easy and simple to understand. But most of the time, we are talking about the surface of this, we are not digging into the details. If you would like to get the “core” of details, it is time to stop being only on the surface of this and take a look at the structure behind it. To start, let’s define what it Exception Handling.

The definition of an exception is based on the observation that each procedure has a precondition, a set of circumstances for which it will terminate “normally”.¹ An exception handling mechanism allows the procedure to raise an exception² if this precondition is violated,¹ for example if the procedure has been called on an abnormal set of arguments. The exception handling mechanism then handles the exception.³

What is SEH (Structured Exception Handler) ?

Structured Exception Handling (SEH) is a mechanism in Windows operating systems that handles exceptions, such as errors or unexpected events, that occur during the execution of a program. SEH allows a program to respond to these exceptions in a controlled manner, ensuring that the system remains stable and providing a way to handle errors gracefully.

According to Microsoft, with SEH, you can ensure that resources, such as memory blocks and files, get released correctly if execution unexpectedly terminates. You can also handle specific problems—for example, insufficient memory—by using concise structured code that doesn’t rely on goto statements or elaborate testing of return codes.

As mentioned, SEH functions manage exceptions in a program but it can be exploited by malware to deceive disassemblers and complicate code analysis. One technique uses the FS segment register to access the Thread Environment Block (TEB), which contains a pointer to the SEH chain. The SEH chain functions like a stack, with the most recently added function executing during an exception. By manipulating this chain, malware authors can obfuscate their code, making it difficult for analysts to identify and understand malicious behavior.

A glance example of SEH

We can see how it works by getting this simple example, compiling it, and looking at IDA and x64dbg.

#include <windows.h>
#include <stdio.h>

void myFunction() {
    __try {
        int* p = NULL;
        *p = 0; 
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        printf("An exception occurred!\n");
    }
}

int main() {
    myFunction();
    return 0;
}

The graph view of IDA.

/images/exceptionHandlers/ida-seh-graph.png
IDA SEH Graph

The x64dbg view.

/images/exceptionHandlers/x64dbg-seh-disassembly.png
x64dbg SEH disassembly

A Malware Approach to SEH

This code below was created to show a way to make a simple custom exception and abuse the SEH if a debugger was detected (another straightforward trick, ya?). Looking at it with our reversing tools, we can see that magic happens. Even in the future, if you need to deal with an obfuscated or packed code, You will for sure remember the basics that you saw here :D

#include <Windows.h>
#include <stdio.h>

// Function to be executed after the exception
void RedirectedExecution()
{
	printf("\t[x] Executed after the exception\n");
}

LONG WINAPI CustomExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
	printf("[x] Exception code: 0x%X\n", pExceptionInfo->ExceptionRecord->ExceptionCode);

	// Modify the instruction pointer (EIP) to jump to the redirected execution
	pExceptionInfo->ContextRecord->Eip = (DWORD_PTR)RedirectedExecution;
	return EXCEPTION_CONTINUE_EXECUTION;
}

void CheckDebuggerAndTriggerException()
{
	if (IsDebuggerPresent())
	{
		__try
		{
			// Cause an exception to occur (divide by zero)
			int zero = 0;
			int result = 1 / zero;
		}
		__except (CustomExceptionHandler(GetExceptionInformation()))
		{
			printf("\t[-] Divide by Zero Exception handled by the __except block.\n");
		}
	}
	else
	{
		printf("\t[+] No debugger detected. Normal execution continues.\n");
	}
}

int main()
{
	CheckDebuggerAndTriggerException();
	printf("[+] Program executed successfully.\n");
	return 0;
}

In the code above we are using the GetExceptionInformation, which provides the function with the exception information structure, allowing it to both read and modify the details.

What is AddVectoredExceptionHandler?

Vectored exception handlers are an extension to structured exception handling. An application can register a function to watch or handle all exceptions for the application. Vectored handlers are not frame-based, therefore, you can add a handler that will be called regardless of where you are in a call frame. Vectored handlers are called in the order that they were added, after the debugger gets a first chance notification, but before the system begins unwinding the stack.

So, what is the difference?

Structured exception handling (SEH) and Vectored Exception Handling (VEH) are both mechanisms in Windows for handling exceptions, but they serve different purposes:

  • SEH:

    • It is a Microsoft extension to C and C++ that allows graceful handling of exceptional situations, such as hardware faults.
    • It provides complete control over exception handling and is usable across all programming languages and machines.
    • It is typed, meaning different exception types can be caught and handled differently.
    • It uses stack unwinding to properly handle both user exceptions (C++ exceptions) and OS exceptions.
    • It has “first-chance” handling, allowing you to log or handle exceptions before unwinding destroys local variables.
    • SEH is recommended for specific scenarios where fine-grained control is needed.
  • VEH:

    • It is an extension to SEH introduced in Windows XP.
    • It allows an application to register a function to watch or handle all exceptions for the entire application.
    • Unlike SEH, VEH handlers are not frame-based, so they can be called regardless of the call frame.
    • VEH handlers have priority over SEH handlers.
    • VEH is useful for scenarios where you need to intercept exceptions globally, such as debugging or logging purposes.

In summary, SEH provides fine-grained control over exception handling, while VEH allows global exception monitoring. “It is neither better nor worst, it is just different”. (MC Marcinho - Nem Melhor Nem Pior)

Abusing AddVectoredExceptionHandler

The beginning

/images/exceptionHandlers/img-gandalf.png
Gandalf

Let’s start with some malware that abuses the VEH.

How can it be abused? Let’s take a simple example here and look inside the debugger.

#include <Windows.h>
#include <stdio.h>

LPVOID allocateMemory = NULL;
unsigned char shellCode[] = {
	0x90,							// nop
	0xB0, 0x01,						// mov al, 1
	0xC3							// ret
};


ULONG Handler(PEXCEPTION_POINTERS exception_ptr) {

	if (exception_ptr->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
		
		printf("\t[-] EXCEPTION_ACCESS_VIOLATION\n");

		// Set new EIP
		exception_ptr->ContextRecord->Eip = (DWORD)allocateMemory;
		// Enables single-step mode for the processor. 
		// In single-step mode, the processor generates a debug exception 
		// (INT 1) after executing each instruction.
		exception_ptr->ContextRecord->EFlags |= 0x100;

		return EXCEPTION_CONTINUE_EXECUTION;
	}

	return EXCEPTION_CONTINUE_SEARCH;
}

int main() {
	printf("[+] Starting VEH example...\n");
	allocateMemory = ::VirtualAlloc(
		NULL, 
		sizeof(shellCode), 
		MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		
	memcpy_s(allocateMemory, sizeof(shellCode), shellCode, sizeof(shellCode));

	// Add the handler
	::AddVectoredExceptionHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)Handler);

	// Access Violation
	int* p = nullptr;
	*p = 42;

	return 0;
}

In the code above, the dereferencing the null pointer will cause an ACCESS_VIOLATION which should be caught by our VEH. When you call the AddVectoredExceptionHandler, you need two parameters.

PVOID AddVectoredExceptionHandler(
  ULONG                       First,
  PVECTORED_EXCEPTION_HANDLER Handler
);

As in the example above, the Handler type is a PVECTORED_EXCEPTION_HANDLER

PVECTORED_EXCEPTION_HANDLER PvectoredExceptionHandler;

LONG PvectoredExceptionHandler(
 [in] _EXCEPTION_POINTERS *ExceptionInfo
)
{...}

It is a pointer to EXCEPTION_POINTERS structure which contains the ExceptionRecord and ContextRecord.

typedef struct _EXCEPTION_POINTERS {
  PEXCEPTION_RECORD ExceptionRecord;
  PCONTEXT          ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

Now we have all the intrinsic details about it, and we can understand what goes within the Handler function. I’m forcing an ACCESS_VIOLATION and using my Handler to take care of it; if it is the expected exception, I will change the EIP to my shellcode.

The Debugger View

The address of the shellcode in memory and in sequence the AddVectoredExceptionHandler function with the renamed veh_fn_handler

/images/exceptionHandlers/x64dbg-shellcode-memory.png
Shellcode in memory

When we arrive at the point where the null pointer would receive a value, we get the exception.

/images/exceptionHandlers/x64fbg-access-violation.png
ACCESS_VIOLATION on x64dbg

After that, pressing F9 will reach our breakpoint on the veh_fn_handler and once more pressing F9 we arrive at our shellcode by the EXCEPTION_SINGLE_STEP.

/images/exceptionHandlers/x64dbg-exception-message.png
Exception message

And finally :D

/images/exceptionHandlers/x64dbg-shellcode.png
Shellcode on debugger

Conclusion

The idea behind this post was to show a different approach used by some malware families to achieve their goals in a way that requires the analyst to spend some time flowing through the code to understand what is happening. With these simple examples, anyone interested in this can compile the code and take a look at the debugger and disassembler.

Reversing is mostly a case of practice! Do and repeat the process as much as you can until it starts to make sense.

I hope this post has been informative and useful to you. If you have any questions, doubts, or want to help me correct any mistakes, please feel free to contact me.

References