2026_졸업프로젝트
[프로젝트]UEFI DXE 바이너리 취약점 분석기 1주차 - UEFI/DXE/PE
망구입니다
2026. 2. 26. 14:33
UEFI (Unified Extensible Firmware Interface)
UEFI는 운영체제와 플랫폼 펌웨어 사이의 소프트웨어 인터페이스를 정의하는 규격.
기존의 레거시 BIOS를 대체하기 위해 등장하였으며, 하드웨어 초기화와 운영체제 부팅을 담당.
C언어 기반으로 개발이 가능하며, 모듈화 된 구조를 가지고 있어 확장성이 매우 뛰어남.
현재 진행하는 [UEFI DXE 바이너리 취약점 분석기]에서 DXE는 UEFI 부팅 과정 중 하나.
[UEFI 부팅 과정]
- SEC (Security)
- PEI (Pre-EFI Initialization)
- DXE (Driver Execution Environment)
- BDS (boot Device Selection)
- TSL (Transient System Load)
- RT (Runtime)

위 과정 중 DXE 단계에서는 메모리가 초기화 된 상태 이므로, 대부분의 하드웨어 드라이버와 런타임 서비스들이 이 때 로드되고 실행됨.
즉, 분석 대상인 바이너리들이 가장 많이 실행되는 단계이므로 취약점이 발생할 가능성도 가장 높음.
DXE (Driver Execution Environment)
- DXE Dispatcher 및 DEPEX(Dependency Expression)
- UEFI 드라이버들은 순차적으로 실행되는 것이 아니라, 조건부로 실행 됨. 각 DXE 드라이버는 자신의 PE 헤더 내부나 별도의 섹션에 DEPEX라는 데이터를 가짐.
- DEPEX에 조건을 명시하고, DXE Dispatcher는 이 조건을 만족하는 드라이버를 찾아 실행함.
- 분석기는 각 바이너리의 DEPEX 섹션을 파싱할 수 있어야 함. 만약 공격자가 악성 드라이버를 심어두면, DEPEX 조작을 통해 정상적인 보안 모듈보다 먼저 실행되게 하거나, 특정 보안 모듈의 로드를 방해하는 취약점이 존재할 수 있기 때문.
- 즉, 분석기는 드라이버 파일의 DEPEX 섹션을 들여다보고 의심스러운 권한을 가지거나 모순된 의존성을 가진 관계 등을 찾아내야 함.
2. 핸들 데이터베이스 및 프로토콜
-
- 모든 드라이버와 장치는 핸들(handle)이라는 객체로 관리되며, 이 핸들에 프로토콜(protocol)이라는 인터페이스가 부착됨.
- 프로토콜은 특정 기능을 수행하는 함수 포인터들의 집합체. 모든 프로토콜은 고유한 128비트 GUID(Globally Unique Identifier)로 식별.
- 분석기는 바이너리 내부에서 프로토콜을 등록하거나 사용하는 등 부트 서비스 함수 호출을 추적해야 함.(예 : InstallProtocolInterface, LocateProtocol)
3. 부트 서비스 및 런타임 서비스
-
- DXE 단계에서는 두 가지 형태의 시스템 서비스 테이블이 생성됨.
- Boot Services (BS) : OS가 부팅되기 전까지만 유효한 서비스. (메모리 할당, 이벤트 처리 등) OS가 로드되면 이 메모리는 회수 됨.
- Runtime Services (RT) : OS가 부팅된 이후에도 계속 살아있는 서비스. (시간 설정, 변수 저장 등)
- 런타임 서비스 관련 코드에 취약점이 있다면, OS가 실행 중일 때도 공격자가 펌웨어 레벨에서 시스템을 장악할 수 있음.
- 분석기는 해당 드라이버가 런타임 드라이버인지 식별하는 것이 중요.
- DXE 단계에서는 두 가지 형태의 시스템 서비스 테이블이 생성됨.
4. SMM (System Management Mode) 진입 준비
-
- DXE 단계는 x86 프로세서의 SMM 환경을 설정하고 관련 드라이버를 로드하는 시기이기도 함.
- SMM 드라이버는 DXE 단계에서 로드되지만, 실제로는 SMRAM이라는 격리된 메모리 영역에 복사되어 실행 됨.
- SMM 드라이버의 취약점은 OS의 모든 보안 기능을 우회할 수 있다는 것.

[그림 설명]
- Device Handle
- "모든 드라이버와 장치는 핸들(Handle)이라는 객체로 관리"
- 핸들이 서로 연결된 리스트(Linked List) 형태, 즉 '핸들 데이터베이스'를 의미
2. GUID Interface
-
- "모든 프로토콜은 고유한 128비트 GUID로 식별"
- 핸들에 프로토콜을 붙일 때, 그냥 붙이는 게 아니라 이 GUID를 꼬리표처럼 달아서 붙임.
3. Protocol Interface
-
- "특정 기능을 수행하는 함수 포인터들의 집합체"
- 실제 기능을 수행하는 코드가 담긴 곳
4. Instance Data
-
- 드라이버가 사용하는 Private Data
- 취약점 분석 시 공격자가 이곳의 데이터를 오버플로우 시키려 시도하는 경우가 많으므로, 분석 관점에서도 매우 중요
PE (Portable Executable) 헤더
[용어 정리]
- PE 포맷
- ' 규격'
- 윈도우 운영체제나 UEFI 환경에서 실행 가능한 파일을 어떻게 만들고 저장해야 하는지 정의한 것.
- 윈도우 운영체제에서 실행 파일(.exe), 객체 코드(.obj), 라이브러리 파일(.dll) 등을 위해 사용되는 파일 형식.
2. PE 파일
-
- '실체'
- PE 포맷이라는 규격에 맞춰서 실제로 만들어진 파일.
- .efi 파일, .exe 파일, .dll 파일 등이 모두 PE 파일.
3. PE 헤더
-
- '정보'
- PE 파일의 가장 앞부분에 위치하며, 이 파일이 어떻게 실행되어야 하는지 알려주는 요약 정보.
- 코드나 데이터가 어디에 있는지 알려주는 지도 역할을 함.
이번 프로젝트에서 PE를 공부해야 하는 가장 큰 이유는 UEFI 이미지(.efi 파일)가 PE 포맷을 따르기 때문.
- '바이너리 취약점 분석기'가 UEFI 바이너리를 분석하려면 먼저 파일을 읽어서 메모리 구조를 파악해야 함.
- 이 때 PE 헤더를 파싱해야만 실제 실행 코드와 데이터가 어디 있는지 알 수 있음.

[구조체 (Structures)]
- IMAGE_DOS_HEADER
- 파일의 가장 첫 부분(64 bytes)
- 이 파일이 PE 파일임을 알리는 식별자 역할
- 핵심 필드
- e_magic : 매직 넘버. 반드시 MZ(0x5A4D)여야 함.
- e_lfanew : 실제 NT Header가 위치한 파일 오프셋을 가리킴.
2. MS-DOS stub
-
- "This program cannot be run in DOS mode" 라는 문자열이 들어있는 잉여 영역.
3. IMAGE_NT_HEADERS
-
- 3개의 하위 구조체로 구성됨.
- Sginature : 값이 PE\0\0(0x00004550)인지 확인해야 함. (PE파일이라는 증명)
- File Header (IMAGE_FILE_HEADER)
- Machine : CPU 아키텍쳐 정보. UEFI는 주로 0x8664 값을 가짐
- NumberOfSections : 섹션의 개수
- SizeOfOptinalHeader : 바로 뒤에 이어질 Optional Header의 크기를 알려줌
- Optional Header (IMAGE_OPTIONAL_HEADER)
- AddressOfEntryPoint : 프로그램이 처음 시작되는 코드의 즈소.
- ImageBase : 메모리에 로드될 떄의 기준 주소
- SectionAlignmnet & FileAlignment : 메모리상의 섹션 간격과 파일상의 섹션 간격. 이 둘이 다르기 때문에 주소 변환 (RVA <-> RAW) 공식이 필요.
- DataDirectory : 익스포트, 임포트, 재배치 테이블 등 주요 데이터의 위치 배열. 악성 코드는 이 곳을 조작하여 특정 함수를 숨기기도 함.
- 3개의 하위 구조체로 구성됨.
4. IMAGE _SECTION_HEADER
-
- 각 섹션에 대한 정보를 담은 구조체 배열
- Name : 섹션 이름 (예: .text, .data, .rdata)
- VirtualAddress (RVA) : 메모리에 로드 되었을 때의 시작 주소
- PointerToRawData : 파일상의 시작 위치
- SizeOfRawData : 파일상에서의 크기
- Characteristics : 속성
- 각 섹션에 대한 정보를 담은 구조체 배열