본문 바로가기

system

SEH(Structured Exception Handler)

SEH(Structured Exception Handler) 는 windows 운영체제의 예외 처리 메커니즘이다.

 

보통 예외가 발생하면 windows 에서는 어플리케이션을 강제로 종료한다. 하지만 C, C++ 과 같은 컴파일러에서 프로그래머에게 __try, __except, catch, throw 와 같은 예외 처리에 대해 컨트롤 할 수 있도록 제공하고 있다. 하지만 온전히 컴파일러의 힘만으로는 힘들고.. 결국은 windows 운영체제에서 이러한 예외를 처리해주어야 한다.

예외는 하드웨어 예외와 소프트웨어 예외로 나뉘는데, 주로 zero divide, Acess violation, integer overflow 와 같은 예외가 하드웨어 예외에 해당한다. 소프트웨어 예외는 개발자가 작성한 프로그램과 운영체제 모두 의미한다.

 

 SEH 기법의 본래의 목적은 예외 처리이지만, 공격자의 입장에서 SEH 기법은 windows 운영체제의 메모리 기법 중 하나인 GS 를 우회하는데 이용하기도 한다.

 

 

typedef struct _EXCEPTION_REGISTRATION_RECORD {

struct _EXCEPTION_REGISTRATION_RECORD *Next;

PEXCEPTION_ROUTINE Handler

} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

위 구조체는 SEH Chain 을 구성하는 구조체 프로토 타입으로, 시작은 FS:[0] 을 가리키고 있다.

*Next 에는 이전에 설치되어져 있던 EXCEPTION_REGISTRATION_RECORD 구조체의 포인터가 저장되어 있고, Handler 에는 Exception Handler의 주소 값이 저장되어 있다.

 

처리 과정을 보자면,

가장 먼저 예외가 발생하면 FS:[0] 에 위치한 Exception Handler 포인터 값을 호출한다. 이 때 호출한 함수가 1(ExceptionContinueSearch)을 리턴할 때 마다 *Next 값을 참조해 그 이전에 설치된 _EXCEPTION_REGISTRATION_RECORD 구조체의Exception Handdler를 호출하는 식으로 SEH Chain을 따라간다.

이 때, *Next 값이 0xFFFFFFFF 이 되면 kernel32!_except_handler 가 호출되면서 커널 영역의 예외처리 루틴을 수행한다.

 

 

Exception Handler의 프로토 타입을 살펴보자.

 

EXCEPTION_DISPOSITION __cdecl_except_handler (

struct _EXCEPTION_RECORD *ExceptionRecord,

void *EstablisherFrame,

struct _CONTEXT *ContextRecord,

void *DispatcherContext

);

위의 Exception Handler가 리턴하는 값이 1(ExceptionContinueSearch)이 아닐 경우 windows는 더 이상의 Exception Handler를 요청하지 않고 unwind를 진행한다.

 

Exception Handler의 리턴 값인 EXCEPTION_DISPOSITION 은 아래와 같다.

Typedef enum _EXCEPTION_DISPOSITION {

ExceptionContinueExecution,    // 0

ExceptionContinueSearch,        // 1

ExceptionNestedException,       // 2

ExceptionCollidedUnwind,        // 3

} EXCEPTION_DISPOSITION;

 

※ unwind

: 이 작업은 에러가 발생했던 함수들에 대하여 __final 또는 C++ 소멸자 호출과 같은 에러가 발생했던 함수들의 쓰레기 내용을 제거할 수 있는 기회를 제공하는 메커니즘이다.

 

 

다음은 각각의 파라미터들에 대해 보자.

 

첫 번째 파라미터 값인 EXCEPTION_RECORD 는 아래와 같은 구조체로 구성되어 있다.

Typedef struct _EXCEPTION_RECORD {

DWORD    Exceptioncode;    // Exception 종류를 나타내는 코드 값 => NTSTATUS.H 참조

DWORD    ExceptionFlags;    // 예외상황 플래그 => 이 값이 0일 경우 예외 상황을 뜻한다.

Struct    _EXCEPTION_RECORD *ExceptionRecord;    // 다른 처리되지 않은 예외 상황을 위한 ExceptionRecord 포인터 값

PVOID    ExceptionAddress;    // 예외가 발생한 코드의 위치 값

DWORD    NumberParameters;    

UINT_PTR Exceptionlnformation[EXCEPTION_MAXIMUM_PARAMETERS];    

} EXCEPTION_RECORD;

 

두 번째 파라미터 값인 EstablisherFrame은 꽤 중요한 역할을 하는 값으로 이 값은 SEH를 설치할 때 코드에서 설치한 ESP을 저장하고 있다.

 

세 번째 파라미터 값인 ContextRecord는 예외가 발생할 당시의 Register 값들이 저장되어 있다.

 

 

다시 한번 예외 처리 절차를 설명하자면,

예외 발생 시 인터럽트가 발생하게 된다. 인터럽트 루틴에서는 예외 발생 당시의 레지스터 값들을 저장해두며, 이후 KiDispatchException() 함수를 호출한다. 이 함수는 다시 트랩 프레임(저장시켜둔 레지스터값)을 써준 뒤 ntdll 의 KiUserExceptionDispatcher 함수를 실행하게 된다.

 

'system' 카테고리의 다른 글

[how2heap translation] fastbin_dup.c  (0) 2017.01.10
MP3 CD Converter Exploit  (0) 2016.11.22
Use-After-Free 취약점  (0) 2016.11.08
Back(` `) , Single(' '), Double(" ") Quotes, Blackslash(\)  (0) 2016.11.04
stdin 임시버퍼  (0) 2016.10.11