SEH란?
Windows 운영체제에서 제공하는 예외 처리 시스템
소스코드에서 __try, __except, __finally 키워드로 구현 가능
SEH 예제 실습 #1
위에 파일로 진행
이 실행파일은 의도적으로 메모리 Access Viloation 예외를 발생 시킨 후에 SEH를 이용해서 해당 예외를 처리 후 PEB의 정보를 이용해 안티 디버깅 코드 추가 -> 디버거와 일반 실행이 다르도록 구현
일반 실행 시 위처럼 뜨지만 올리디버거로 열고 F9로 실행을 시키면
예외가 발생되면서 디버거 실행이 중지된다.
401019 주소의 MOV DWORD PTR DS:[EAX], 1 명령어는 예외 발생을 위해 추가된 코드로 EAX 레지스터의 값은 0인데 여기 메모리 주소 0에 값에 1을 입력하라는 의미가 된다.
근데 메모리 주소 0은 할당 되지 않았는데 쓰려고 시도해서 에러가 발생한 것
예외 발생시에는 아래 경고 문구가 뜬다
"Access violation when writing to [00000000] - use Shift +F7/F8/F9 to pass exception to program"
메모리 0번지에서 쓰기 예외가 발생했으니 shift어쩌구 써서 예외를 프로그램에 넘겨라
그래서 시키는 대로 shift+f9를 하면 위와같이 뜬다
이걸로 일반실행이랑 디버거 실행이랑 다른걸 알 수 있음 -> 예외 처리하는 방식이 다른것 ---> SEH를 이용한 안티 디버깅 기법
이애하려면 운영체제의 exception과 예외 처리기(SEH)구조에 대해 알아야됨
OS의 예외처리 방법
1. 일반 실행의 경우 예외 처리 방법
운영체제는 프로세스 실행 중에 예외 발생 시 프로세스에게 처리를 맡김
프로세스 코드에 예외 처리 구현시 -> 해당 예외를 처리 후 계속 프로세스 실행
구현 안되어 있을 시 -> 기본 예외 처리기 동작으로 프로세스 종료
2. 디버깅 실행의 경우 예외 처리 방법
디버깅 중에 디버기 프로세스에 예외 발생 시 운영체제는 우선적으로 디버거에게 예외를 넘겨서 처리하도록 함
디버거는 디버기에 대한 거의 모든 소유권 有 -> 디버기의 실행 및 종료 제어, 디버기 프로세스 내부의 가상 메모리 및 레지스터에 대한 읽기 및 쓰기 권한, 디버기 내부 발생된 exception 처리할 책임 有
2번같은 에외로 디버거 실행 중지 시 조치를 취해야 디버깅이 가능
조치 방법1) 예외 직접 수정: 코드, 레지스터, 메모리
문제가 발생한 코드, 메모리, 레지스터를 직접 수정
조치 방법2)예외를 디버기에게 넘겨서 처리
디버기 내부에 이미 SEH가 존재해 처리가 가능하다면 exception 통지를 디버기한테 줘서 자체 해결하도록 함 -> 1번 일반 실행 경우랑 동일하게 처리됨(아까 실습에서 한 shift어쩌구한게 이거임)
조치 방법3) 운영체제 기본 예외 처리기
디버기랑 디버거에서 처리 못하거나 처리안하려고 하면 그냥 운영체제의 기본 예외 처리기에서 처리한다.
디버기 프로세스 종료되면서 디버깅이 중지됨
EXCEPTION
대표적인 예외 다섯개
EXCEPTION_ACCESS_VIOLATION (C0000005) | 존재하지 않거나 접급 권한이 없는 메모리 영역에 대해 접근 시도 |
EXCEPTION_BREAKPOINT (80000003) | 실행 코드에 BP 설치 시 CPU가 그 주소 실행하려할 때 방생 |
EXCEPTION_ILLEGAL_INSTRUCTION (C000001D) | CPU가 해석할 수 없는 명령어를 만날 대 |
EXCEPTION_INT_DIVIDE_BY_ZERO (C0000094) | 정수 나눗셈에서 분모가 0인 경우 |
EXCEPTION_SINGLE_STEP (80000004) | 명령어 하나를 실행하고 멈추기 위해 사용 |
SEH 상세 설명
SEH 체인
첫번째 예외 처리기에서 해당 예외를 처리하지 못하면 처리될 때까지 다음 예외 처리기로 예외를 넘겨준다
기술적으로 SEH는 _EXCEPTION_REGISTRATION_RECORD 구조체 연결 리스트의 형태
함수 정의
EXCEPTION_DISPOSITION _except_handler
(
EXCEPTION_RECORD *pRecord,
EXCEPTION_REGISTRATION_RECORD *pFrame,
CONTEXT *pContext,
PVOID pValue
);
4개 파라미터를 받아서 EXCEPTION_DISPOSITION이라는 열거형을 리턴한다. (시스템에 의해 호출되는 콜백함수)
TEB.NtTib.ExceptionList
프로세스의 SEH체인에 접근하려면 TEB구조체의 NtTib 멤버를 따라가면 된다
TEB.NtTib.ExceptionList 멤버는 TEB 구조체에서 가장 첫 멤버
TEB.NtTib.ExceptionList = FS:[0] |
으로 구할 수 있음(46장 참고..)
SEH설치 방법
C언어에서 SEH설치하려면 __try, __exception, __finally 키워드를 사용해 구현 가능함
PUSH @MyHandler ; 예외 처리기
PUSH DWORD PTR FS:[0] ; Head of SEH Linked List
MOV DWORD PTR FS:[0], ESP ; 연결 리스트 추가
SEH를 설치한다 = 기존의 SEH 체인에 자신의 에외 처리기를 추가시킨다
SEH 예제 실습 #2
아까 그 파일 다시 실행 시키자
일단 401000까지 실행시키고
401005까지 실행시키면서 FS:[0]값을 확인해보면 SEH 체인의 시작 주소를 알 수 있다.
코드 정보창이라 확인하면 FS[0] = [00383000] = 0019FF64로 나온다
난 0019FF64가 SEH 체인의 시작 주소이다
스택창에서 해당 주소를 확인하면 첫번째 EXCEPTION_REGISTRATION_RECORD 구조체가 나온다
두번째 EXCEPTION_REGISTRATION_RECORD 구조체 확인을 위해 0019FFE4주소로 가보았다.
Next멤버가 FFFFFFFF니까 이게 SEH체인의 마지막인걸 알 수 있다
SEH 추가
401005주소 명령어 실행하고 스택을 보면 _EXCEPTION_REGISTRATION_RECORD 구조체가 생성된다.
그록나서 40100C 명령어도 수행하면 스택 장에 새로운 SEH가 생성되었다는 주석이 생긴다
이렇게 되면 새로운 예외 처리기가 SEH 체인에 추가된거다
view-SEH chain에서 확인할수도 있음
욜케..
예외 발생
401019주소하면 예외가 발생했었다
지금은 디버깅 중이니까 예외 처리 우선순위에 따라 제어가 디버거에게 넘어가는데 예외를 다시 디버기에게 돌려주기 위해 40105A에 BP를 설치하고 shift+f9로 실행한다.
이제 예외처리기를 디버깅 할 수 있음
예외 처리기 디버깅
40105A 예외 처리기에는 디버거 탐지 코드가 존재
첫 줄에서 ESP+C가 예외 처리기의 pContext 파라미터 값을 의미해서 이 값을 ESI 레지스터에 입력하는 것이다.
그러고 나서 EAX 레지스터에 PEB 구조체의 시작 주소를 넣는다
이후에 PEB.BeingDebugged멤버 1바이트를 1과 비교한다.
1이랑 같으면 점프를 안하는 데 즉 일반 실행이면 401076으로 점프하고 디버깅이 실행이면 40106A 주소의 명령어를 실행한다.
현재 프로세스가 디버깅 중이면 40106A 명령어로 오는데 pContext->Eip의 값을 401023으로 변경하는 명령어다
예외 처리기가 종료 시 예외가 발생한 스레드는 401023주소의 코드를 실행한다. 이 주소에서 Debugger Detected 메시지를 출력하는 것이다.
(디버깅의 편의를 위해 401023에 BP를 설치해 두었다.)
이후 줄에서는 pContext->Eip값을 변경하였으므로 예외 처리기 종료코드(401080)으로 점프한다. 마지막으로 리턴값인 EAX를 0으로 세팅하고 예외 처리기는 리턴한다. 리턴 값 0의 의미는 EXCEPTION_CONTINUE_EXECUTION으로 예외를 잘 처리했읜 해당 스레드를 실행시키라는 의미이다.
RETN 명령까지 실행되면 ntdll.dll 모듈의 코드 영역으로 리턴된다.
SEH제거
40104D주소까지 오면 스택에는 앞에서 입력한EXCEPTION)REGISTRATION_RECORD 구조체가 있는데 이건 SEH에서 제일 먼저 실행되는 에외 처리기이다.
올리디버거 옵션 설정
- 디버거는 자신의 exception을 디버거에 먼저 보내 저리한다
:이걸로 일반 실행과 디버깅 실행이 달라지는 것 -> 자동으로 디버기에게 돌려주는 옵션 有
[options] - [Debugging options] - [Exceptions] - [Ignore memory access violations in KERNEL32 선택]
6가지 항목이 있는데 위에서 부터 5개가 위에 적어둔 5개 예외다
각 체크 버튼에 체크하면 디버거에서 예외를 무시하고 디버기에게 돌려준다.
마지막에 All FPU exceptions은 부동 소수점 처리를 빠르게 하기 위한 장치. FPU 명령어를 처리하다가 예외 발생 시 디버기에서 처리하는 옵션임
'공부해요 > 리버싱_핵심원리' 카테고리의 다른 글
50. 안티 디버깅 (0) | 2025.01.12 |
---|---|
49. IA-32 Instruction (0) | 2025.01.12 |
47. PEB (1) | 2025.01.02 |
46. TEB (1) | 2025.01.02 |
45. TLS 콜백 함수 (0) | 2025.01.02 |