[Rev] 리버싱 · 작성: 2026-07-20 03:10:34 · 수정: 2026-07-21 23:32:17 · 조회 11
PE(Portable Executable) 포맷을 "Windows 실행 파일 규격"이라고만 알면, 왜 이 포맷이 굳이 DOS 헤더로 시작하는지, 그리고 이 구조가 정적 분석 글에서 다룬 ELF와 개념적으로 어떻게 대응되는지를 놓치게 된다.
PE 파일을 헥스 에디터로 열면 맨 앞에 MZ(매직 바이트)가 보인다 — 이건 1980년대 MS-DOS 실행 파일 헤더다. Windows는 하위 호환성을 위해, DOS에서 이 파일을 실행하면 "This program cannot be run in DOS mode" 메시지를 출력하는 작은 DOS 스텁 프로그램을 그대로 유지하면서, 그 안에 e_lfanew라는 필드로 "진짜 PE 헤더는 이 오프셋에 있다"는 포인터를 심어두는 방식으로 포맷을 확장했다.
IMAGE_NT_HEADERS): PE\0\0 시그니처로 시작, 파일 헤더(아키텍처, 섹션 개수)와 옵셔널 헤더(엔트리 포인트 RVA, 이미지 베이스, 서브시스템)를 담는다 — ELF의 ELF 헤더에 해당.text(코드), .data, .rdata(읽기전용 데이터, import 정보 포함), .rsrc(아이콘·문자열 등 리소스), .reloc(재배치 정보) — ELF의 섹션 헤더 테이블과 개념은 동일하고 이름 관례만 다르다.idata, 실제로는 .rdata에 병합되는 경우가 많음): 이 실행 파일이 어떤 DLL의 어떤 함수를 가져다 쓰는지 — 여기 나열된 함수 이름이 정적 분석 글에서 강조한 "취약점 후보를 빠르게 추정하는" 단서(예: CreateProcessA, WinExec가 보이면 이 프로그램이 외부 프로세스를 실행한다는 걸 코드를 안 봐도 알 수 있다)PE 안의 주소들은 대부분 파일 오프셋이 아니라 RVA(이미지가 메모리에 로드된 베이스 주소로부터의 상대 오프셋)로 기록되어 있다. 이건 ASLR과 정보 유출 글에서 다룬 "베이스 주소는 매번 랜덤화되지만, 그 안의 상대적인 오프셋은 항상 고정"이라는 원리와 정확히 같은 이유다 — 로더가 파일을 메모리 어디에 매핑하든, RVA에 실제 로드 베이스 주소를 더하기만 하면 항상 올바른 절대 주소가 나오도록 설계되었다.
.reloc 섹션의 정보를 이용해 코드 안의 절대주소 참조들을 실제 로드 주소에 맞게 보정pefile(파이썬 라이브러리)이나 IDA/Ghidra가 이 테이블을 자동으로 파싱해서 보여준다GetProcAddress/LoadLibrary를 런타임에 직접 호출해서 함수 주소를 그때그때 알아내는 경우가 흔한데, 이런 경우 정적 Import Table 확인만으로는 부족하고 동적 분석으로 GetProcAddress 호출 시점의 인자(함수 이름 문자열)를 직접 추적해야 한다PE를 "Windows만의 독자적인 포맷"이 아니라 **"ELF와 동일한 문제(헤더·섹션·심볼·재배치)를 DOS 호환성이라는 역사적 제약 위에서 풀어낸 구조"**로 이해하면, 왜 리버싱의 기본 개념(헤더, 섹션, import/export, 재배치)이 플랫폼을 가리지 않고 그대로 적용되는지가 같은 논리로 설명된다.