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 |