본문 바로가기

Programming

C++ SEH Handling

익셉션 핸들링기능 자체는 OS 에서 지원하지만 프로그래밍 문법적으로 쉽게 지원하는 언어는 C++ 이다.  물론 C 언어로도 익셉션 핸들링이 가능하긴한데... 일단 여기서는 C++ 컴파일러가 사용하는 Structured Exception Handling 방식을 정리하고자 한다.  Vectored Exception Handling 방식은 여기서 논외다... 일단 결과만 정리해서 말하자면 SEH 핸들링 방식을 100% 분석하진 못했다.  매우 복잡하지만 정리하면 다음과 같다.


0. 익셉션 핸들러의 구조

익셉션 핸들러의 구조는 2개의 포인터로 구성된 구조체의 선형 연결리스트이다. 

typedef struct _EXCEPTION_REGISTRATION_RECORD { 
    struct _EXCEPTION_REGISTRATION_RECORD *Next; 
    PEXCEPTION_ROUTINE Handler; 
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

 이 구조체들은 모두 스택에 박혀있고 구조체의 첫번째 멤버가 다음 구조체의 포인터를 의미하며 두번쩨 멤버가 핸들러함수의 주소를 가지고있다.  이 연결리스트의 시작은 FS:0 이 가리키고있다.  익셉션이 발생하면 익셉션핸들링을 하는 kernel32.dll 의 함수는 FS:0 에 들어있는 시작위치부터 참조해서 핸들러들을 하나하나 따라가다가 해당 익셉션을 처리할 의무가 있는놈을 호출하게 되는식이다.


1. 익셉션 핸들러등록

try-catch 문을 쓰거나 except, finally 등의 문을 사용하는 함수에 진입하면 다음과같이 익셉션 핸들러를 fs:0 에 등록한다.

.text:002B14C1 mov     ebp, esp

.text:002B14C3 push    0FFFFFFFFh

.text:002B14C5 push    offset __ehhandler$_wmain

.text:002B14CA mov     eax, large fs:0

.text:002B14D0 push    eax

...

.text:002B14F8 lea     eax, [ebp+var_C]

.text:002B14FB mov     large fs:0, eax

그림으로 보면 아래와 같다

여기서는 __ehhandler$_wmain 이라는 핸들러함수를 등록하는 과정인데, 일단 저 함수 자체는 실제 catch 문 블록의 시작주소는 아니다.  최종적으로 catch 문의 블록시작으로 뛰기위한 wrapper 함수인데... 문제는 이 wrapper 의 내부가 분석이 안된다는점이다... 익셉션이 발생하면 디버거로 step by step 따라갈수가 없어서 분석이 힘들다.

아래와같이 두개의 익셉션 핸들러 함수가 있을때 func 가 wmain 내부에서 호출되는 경우 순서는 FS:0 에서 시작해서 func 의 익셉션 핸들러가 먼저 위치하고 그다음 wmain 의 익셉션 핸들러가 오는... 그이후에는 디폴트 시스템익셉션핸들러가 위치하는 식으로 이루어져있다.  여기서 이 익셉션 핸들러의 구조체들이 우선순위가 높을수록 SP 와 가깝다.  그래서 BOF 로 SEH Overwriting 이 가능한것 같다.

익셉션 발생시 FS:0 부터 핸들러들을 뒤지는과정을 분석해보려했는데 쉽지않다. 기본적으로 아래의 msvcr 라이브러리에 다음함수가 처리하는듯하다.


내부적으로 kernel32 의 RaiseException 을 호출하여 익셉션을 발생시키는거같다.



2. 익셉션 발생
익셉션이 발생하면  FS:0 -> 핸들러1 -> 핸들러2 -> 0xFFFFFFFF 이런순서로 핸들러를 스캔한다.  0xFFFFFFFF 는 핸들러의 주소가 아니라 다음번 익셉션 구조체의 주소이다.  즉 EOF 마커인셈이다. 익셉션이 발생했을때 흐름을 따라가다보면 fs:0 을 참조하는 부분을 ntdll.dll 에서 찾을 수 있다.  여기서 실제 익셉션 핸들링 BB 의 wrapper 를 호출하는것 같다

여기서 뭔가 매우 복잡하게 돌아가서 자세히는 모르겠지만 익셉션핸들러의 리스트를 확인하여 실제 catch, except 등에서 정의된 BB 로 뛰게 해주는것 같다.

어쨋든 결론은 SEH 는 개념상 구조는 "스택상에 박혀있는 구조체의 연결리스트" 그리고 각 구조체 속에 핸들러 함수의 주소가 박혀있다 정도로 정리할수 있고, 그 구현방식은 매우매우 복잡하다는 것이다.  








'Programming' 카테고리의 다른 글

How ptrace works in Linux  (0) 2014.05.29
Exception Handling in Linux g++  (0) 2014.05.27
Debugging multi-threaded application with GDB attaching  (0) 2014.03.18
Linux DDD Debuger  (0) 2014.03.13
Intel PIN Tutorial  (1) 2014.03.06