Reverse engineering core principle: SEH

SEH

SEH is an exception handling mechanism provided by the Windows operating system, which is realized by using __try, __except, and __finally keywords in the program source code. Mainly used in anti-debugging.

Note:
SEH has a different structure than try.catch exception handling in C++. In terms of time, compared with C++’s try and catch exception handling, Microsoft first created the SEH mechanism, and then carried it into VC++. So SEH is an exception handling mechanism subordinate to VC ++ development tools and Windows operating system.

Exception handling during debugging

If an exception occurs inside the debugged process:
OS will first throw the exception to the debugging process. The debugger has almost all the permissions of the debugged. It can not only run and terminate the debugged, but also has the read and write permissions of the virtual memory and registers of the debugged process. In particular, all exceptions (errors) that occur inside the debuggee are handled by the debugger. Therefore, all exceptions (errors) that occur during debugging must be managed by the debugger first (the SEH of the debugged is pushed to the debugger according to the priority order). Like this, when an exception occurs in the debugged, the debugger will suspend the operation, and some measures must be taken to handle the exception, and continue debugging after completion.

  • Several processing methods are often used when encountering exceptions:
  1. Modify exceptions directly: code, registers, memory
  2. Throw the exception to the debugee for handling
  3. OS default exception handling mechanism

Exceptions defined by the operating system

EXCEPTION_DATATYPE_MISALIGNMENT (0X80000002)
EXCEPTION_BREAKPOINT (0X80000003)
EXCEPTION_SINGLE_STEP (0x80000004)
EXCEPTION_ACCESS_VIOLATION (0xC0000005)
EXCEPTION_IN_PAGE_ERROR (0xC0000006)
EXCEPTION_ILLEGAL_INSTRUCTION (0xC000001D)
EXCEPTION_NONCONTINUABLE_EXCEPTION (0xC0000025)
EXCEPTION_INVALID_DISPOSITION (0xC0000026)
EXCEPTION_ARRAY_BOUNDS_EXCEEDED (0xC000008C)
EXCEPTION_FLT_DENORMAL_OPERAND (0xC000008D)
EXCEPTION_FLT_DIVIDE_BYZERO (0xC000008E)
EXCEPTION_FLT_INEXACT_RESULT (0xC000008F)
EXCEPTION_FLT_INVALID_OPERATION (0xC0000090)
EXCEPTION_FLT_OVERFLOW (0xC0000091)
EXCEPTION_FLT_STACK_CHECK (0xC0000092)
EXCEPTION_FLT_UNDERFLOW (0xC0000093)
EXCEPTION_INT_DIVIDE_BY_ZERO (0xC0000094)
EXCEPTION_INT_OVERFLOW (0xC0000095)
EXCEPTION_PRIV_INSTRUCTION (0xC0000096)
EXCEPTION_STACK_OVERFLOW (0xC00000FD)
  • common exception

EXCEPTION_ACCESS_VIOLATION(C0000005): illegal access exception, trying to access a memory area that does not exist or has no access rights.

EXCEPTION_BREAKPOINT(80000003): After the running code is set with a breakpoint, when the CPU tries to execute the instruction at the address, an EXCEPTION_BREAKPOINT exception will occur. The principle is that setting a breakpoint will modify the instruction at this place to 0xCC, but Ollydbg will not display the actual instruction of the temporary breakpoint. You can use the PE Tools tool to dump the process memory and open the file with Hex Editor to see It’s really an order.

EXCEPTION_ILLEGAL_INSTRUCTION(C000001D): This exception is thrown when the CPU encounters an instruction that cannot be parsed.

EXCEPTION_INT_DIVIDE_BY_ZERO(C0000094): If the denominator is zero in the division operation, a division by zero exception will occur. The situation that may occur in the program is that the denominator is a variable, and the variable becomes 0 at a certain moment, and an exception will occur when the division operation is performed.

EXCEPTION_SINGLE_STEP(80000004): After setting the eighth bit TF (Trap Flag trap flag) of the EFLAGS register to 1, the CPU will enter the single-step working mode. After each instruction is executed, it will pause and throw an exception.

SEH chain

SEH exists in the form of a chain, a linked list composed of _EXCEPTION_REGISTRATION_RECORD structure
If the associated exception is not handled by the first exception handler, it is passed to the next exception handler until it is handled.

  • SEH structure
typedef struct _EXCEPTION_REGISTRATION_RECORD
{<!-- -->
// The linked list ends with a structure whose Next member is FFFFFFFF, indicating the last node of the linked list
PEXCEPTION_REGISTRATION_RECORD Next;
// Handler: exception handling function
PEXCEPTION_DISPOSITION Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

// The exception is passed along the chain until it is handled by an exception handler

Graphic representation

  • exception handler
    This function is a callback function, which is called by the system, and the system will provide the parameters required by the function
    It can be seen from the definition that this function requires four parameters, and the return value is a enum type
EXCEPTION_DISPOSITION_except_handler
(
EXCEPTION_RECORD *pRecord,
EXCEPTION_REGISTRATIOIN_RECORD *pFrame,
CONTEXT *pContext,
PVOID pValue
);

The first parameter

typedef struct _EXCEPTION_RECORD {<!-- -->
DWORD ExceptionCode; //Exception code, used to indicate the exception type
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD * ExceptionRecord;
PVOID ExceptionAddress; //The code address where the exception occurred
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_AMXIMUM_PARAMETERS];//15
} EXCEPTION_RECORD, *PEXCEPTION_RECORD

The third parameter

struct CONTEXT
{<!-- -->
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];//512
}

return value

typedef enum_EXCEPTION_DISPOSITION
{<!-- -->
ExceptionContinueExecution = 0, //Continue to execute exception code
ExceptionContinueSearch = 1, //run the next exception handler
ExceptionNestedException = 2, //used inside the OS
ExceptionCollidedUnwind = 3 //used internally by the OS
} EXCEPTION_DISPOSITION;

The exception handler will return ExceptionContinueExecution(0) after processing the exception, and continue running from the code where the exception occurred. If the exception cannot be handled, return ExceptionContinueSearch(1), and send a field to the next exception handler in the SEH chain.

  • Methods to access the SEH chain of a process

Through the NtTib member of the TEB structure
TEB.NtTib.ExceptionList = FS:[0]

  • Install SEH

In C language, SEH can be added to code by using __try, __except, __finally keywords.

compilation

PUSH @MyHandler
PUSH DWORD PTR FS:[0]
MOV DWORD PTR FS:[0], ESP

FS:[0]The address of TEB is stored here, the first member of TEB is the _NT_TIB structure, _NT_TIB< /strong> The first member of the structure is a structure pointer of type _EXCEPTION_REGISTRATION_RECORD, which is the first address of the SEH chain

  • TEB structure
// Reference given by MSDN
typedef struct _TEB {<!-- -->
  PVOID Reserved1[12];
  PPEB ProcessEnvironmentBlock;
  PVOID Reserved2[399];
  BYTE Reserved3[1952];
  PVOID TlsSlots[64];
  BYTE Reserved4[8];
  PVOID Reserved5[26];
  PVOID ReservedForOle;
  PVOID Reserved6[4];
  PVOID TlsExpansionSlots;
} TEB, *PTEB;

What WinDbg sees

 + 0x000 NtTib : _NT_TIB
...
 + 0x030 ProcessEnvironmentBlock : Ptr32_PEB

These are two important members, ProcessEnvironmentBlock is the address of PEB

NtTib structure:

typedef struct _NT_TIB //sizeof 1ch
{<!-- -->
 00h struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; //SEH chain entry
 04h PVOID StackBase; //Stack base address
 08h PVOID StackLimit; //Stack size
 0ch PVOID SubSystemTib;
       union {<!-- -->
           PVOID FiberData;
 10h DWORD Version;
       };
 14h PVOID ArbitraryUserPointer;
 18h struct _NT_TIB *Self; //The linear address of the NT_TIB structure itself
}NT_TIB;
 
typedef NT_TIB *PNT_TIB;

Other

source of learning

  • When an exception occurs in the process, if the SEH is not processed or the registered SEH does not exist. At this time, the kernel32!UnhandledExceptionFIlter() API of the system will be called.
  • This API will run the last exception handler of the system – Top Level Exception Filter or Last Exception Filter (the usual behavior is to pop up an error message box and terminate the process).
  • kernel32!UnhandledExceptionFilter() called ntdll!QueryInformationProcess(ProcessDebugPort). to determine whether the process is being debugged. If debugging is in progress, the exception is passed to the debugger. Otherwise the system exception handler terminates the process.
  • The last exception handler of the system can be modified by kernel32!SetUnhandledExceptionFilter(). The function definition is as follows:
//Returns the address of the previous Top Level Exception Filter for easy recovery
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
__in LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
  • The function definition of Top Level Exception Filter:
typedef struct _EXCEPTION_POINTERS{<!-- -->
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

LONG TopLevelExceptionFilter(
PEXCEPTION_POINTER pExcept;
);
syntaxbug.com © 2021 All Rights Reserved.