PE 인스펙터: Windows EXE·DLL 뷰어
dumpbin이나 pefile이 하는 일을 브라우저 안에서 바이트 그대로 읽습니다. 맨 앞의 MS-DOS 헤더는 화석이고 실제로 의미가 있는 필드는 오프셋 0x3C의 e_lfanew뿐입니다. 거기서 "PE\0\0" 서명과 COFF 헤더가 시작됩니다. 이후 머신 종류(x86, x86-64, ARM64, ARM64EC, Itanium, RISC-V 등), 옵션 헤더가 PE32인지 PE32+인지, 파일 헤더에 DLL 특성이 있는지, 로더가 어떤 서브시스템으로 시작하는지(Windows 콘솔, Windows GUI, 네이티브, EFI 계열)를 보여줍니다. 보안 강화 줄은 ELF의 checksec에 해당하며 DllCharacteristics에서 읽어냅니다. ASLR(DYNAMIC_BASE), 고엔트로피 ASLR(64비트 이미지의 재배치 무작위성을 8비트에서 17비트 이상으로 올립니다), DEP/NX(NX_COMPAT), Control Flow Guard(GUARD_CF), 구조적 예외 처리를 껐는지(NO_SEH), 무결성 강제, 그리고 인증서 테이블이 있는지입니다. 섹션은 가상 주소와 가상·원본 크기, 해독된 특성과 함께 나열되며 쓰기와 실행이 동시에 가능한 섹션은 따로 표시합니다. 패커나 설치 프로그램, 자기 수정 코드가 아니라면 그런 섹션이 있을 이유가 거의 없기 때문입니다. 임포트는 DLL별로 묶어 함수 이름을 보여주고, 서수로 임포트되어 이름이 기록되지 않은 항목은 #서수로 표시합니다. 익스포트는 라이브러리가 스스로 밝히는 이름과 서수 기준값(1이 아닌 경우가 많습니다), 그리고 모든 익스포트 이름을 보여줍니다. 한계는 분명히 말해 둡니다. 인증서 디렉터리가 비어 있지 않다는 것은 파일이 서명을 담고 있다는 뜻일 뿐, 그 서명이 유효하거나 신뢰할 만하다는 뜻이 아닙니다. Authenticode 검증에는 인증서 체인과 타임스탬프 기관이 필요하며 여기서는 다루지 않습니다. 링크 시각도 링커가 써 넣은 필드일 뿐이라 재현 가능한 빌드에서는 상수나 해시가 들어 있습니다. 잘못된 파일은 절반만 읽지 않고 거부합니다. MZ 매직이 틀렸을 때, DOS 헤더가 PE 서명을 가리키지 않을 때, 옵션 헤더 매직을 알 수 없을 때, 섹션이 파일에 없는 데이터를 요구할 때 각각 이름 붙은 오류를 냅니다.
사용법
- 파일을 상자에 놓으세요. .exe, .dll, .sys 드라이버, .ocx 컨트롤, .efi 바이너리 모두 같은 방식으로 파싱됩니다. 확장자가 기준이 아닙니다.
- 헤더 타일에서 아키텍처, PE32와 PE32+ 구분, EXE와 DLL 구분, 서브시스템을 확인하세요. 같은 프로그램의 콘솔 빌드와 GUI 빌드는 여기서만 다릅니다.
- 보안 강화 칩을 보세요. 요즘 Windows 툴체인은 ASLR, DEP, CFG를 기본으로 켜므로 최신 바이너리에 노란 칩이 있다면 배포 전에 이유를 설명할 수 있어야 합니다.
- 임포트에서 관심 있는 기능을 찾으세요. WS2_32는 소켓, WININET이나 WINHTTP는 웹 통신, ADVAPI32는 레지스트리나 서비스, CRYPT32는 인증서를 뜻합니다.
- 섹션 표에서 쓰기와 실행이 동시에 가능한 항목을, 데이터 디렉터리에서 인증서와 디버그 항목과 CLR 런타임 헤더를 확인하세요. 마지막 것은 나머지를 읽는 방식 자체를 바꿉니다.
자주 묻는 질문
- 서명 칩이 있으면 안전한 파일인가요?
- 아닙니다. 이 칩은 인증서 데이터 디렉터리가 비어 있지 않다는 것, 즉 마지막 섹션 뒤에 Authenticode 블롭이 붙어 있다는 것만 말합니다. 그 블롭이 앞의 바이트와 일치하는지, 서명 인증서가 Windows가 신뢰하는 루트까지 이어지는지, 서명 시점에 인증서가 유효했는지, 서명자가 파일이 주장하는 그 사람인지는 전혀 말해 주지 않습니다. 자체 발급 인증서로 다시 서명하거나 서명 후 본문을 고쳐 해시가 맞지 않게 만들어도 디렉터리는 여전히 채워져 있습니다. 제대로 검증하려면 전체 체인과 폐기 정보, 보통은 신뢰할 수 있는 타임스탬프 부서명까지 필요하고 이는 네트워크 작업이라, 파일을 절대 업로드하지 않겠다고 약속한 브라우저 도구의 범위 밖입니다.
- 고엔트로피 ASLR은 일반 ASLR과 무엇이 다른가요?
- 일반 ASLR도 로드 시점에 이미지를 무작위 주소로 옮기지만 32비트 Windows에서는 실제로 무작위인 비트가 8비트 정도라 반복 시도로 뚫을 수 있습니다. HIGH_ENTROPY_VA는 이 이미지를 64비트 주소 공간 어디에 놓아도 안전하다고 로더에 알리고, 그러면 무작위 비트가 17비트 이상으로 올라가 추측이 사실상 불가능해집니다. 이 플래그는 DYNAMIC_BASE도 켜진 PE32+ 이미지에서만 의미가 있고, 프로그램이 포인터를 32비트로 잘라 쓰지 않아야 한다는 조건이 붙습니다. 링커가 이를 /HIGHENTROPYVA 뒤에 두는 이유이자, 32비트 시절 코드를 물려받은 64비트 바이너리가 일부러 꺼 두기도 하는 이유입니다.
- 임포트한 함수가 이름 대신 #12로 보이는 이유는 무엇인가요?
- 파일이 그 함수를 서수로 임포트했기 때문입니다. 썽크 배열의 각 항목은 최상위 비트가 플래그인 머신 워드입니다. 그 비트가 켜져 있으면 하위 16비트가 서수이고, 임포트하는 파일 어디에도 이름이 저장되지 않습니다. 비트가 꺼져 있으면 나머지가 힌트/이름 쌍을 가리키는 RVA라서 이름이 그대로 들어 있습니다. 서수 임포트는 해석이 조금 빨라서 예전 코드에 흔했고, 어떤 API를 부르는지 감추려는 경우에도 나타납니다. 이 도구는 추측하지 않고 원래 서수를 보여 줍니다. 서수와 이름의 대응은 익스포트하는 DLL 안에 있고 설치된 버전에 따라 달라지기 때문입니다.
- 쓰기와 실행이 모두 가능한 섹션이 보입니다. 얼마나 나쁜가요?
- 눈여겨볼 만큼 드물지만 그 자체가 증거는 아닙니다. 보통의 컴파일러와 링커는 .text를 읽기와 실행으로, .data를 읽기와 쓰기로 만들고 둘을 겹치지 않습니다. DEP가 바로 그 조합을 오류로 만들려고 존재하기 때문입니다. IMAGE_SCN_MEM_WRITE와 IMAGE_SCN_MEM_EXECUTE가 함께 켜진 섹션은 대개 실행 시점에 코드를 자기 섹션으로 풀어 놓고 그리로 뛰는 런타임 패커에서 나옵니다. UPX는 그 섹션 이름을 UPX1로 붙입니다. 오래된 설치 프로그램, 자기 수정 보호 기법, 이미지 안에 할당하는 JIT도 원인이 됩니다. 그중 어느 것도 예상하지 않았다면 실행 전에 이유를 파악하는 편이 좋습니다.
- 링크 시각이 1970년이거나 미래로 나옵니다. 파일이 깨진 건가요?
- 거의 아닙니다. TimeDateStamp는 링커가 채우는 1970년 기준 32비트 초 단위 필드인데, 요즘 툴체인은 이를 날짜로 다루지 않습니다. 재현 가능한 빌드는 같은 소스가 항상 같은 바이트를 내도록 0이나 고정 상수를 넣고, MSVC의 /Brepro는 입력의 해시를 넣기 때문에 달력상 임의의, 흔히 먼 미래의 지점에 떨어집니다. 툴체인을 추정하려면 Rich 헤더나 디버그 디렉터리가 이 필드보다 나은 근거이고, 어느 쪽도 파일이 실제로 언제 배포되었는지에 대한 증거는 아닙니다. 날짜 모양을 한 메타데이터로만 보세요.
- .NET 어셈블리인데 임포트와 진입점이 거의 비어 있습니다. 왜 그런가요?
- 관리되는 어셈블리에서는 PE 부분이 껍데기이기 때문입니다. CLR 런타임 데이터 디렉터리(14번, COM 디스크립터)가 비어 있지 않으면 실제 프로그램은 .NET 메타데이터 안의 IL이고, 네이티브 헤더는 주로 Windows 로더를 만족시키려고 존재합니다. 전형적인 C# 실행 파일은 mscoree.dll의 _CorExeMain 하나만 임포트하고, 진입점은 그리로 뛰는 두 개 명령이며, .text 섹션은 기계어보다 메타데이터가 대부분입니다. 그래서 아키텍처 칸이 Intel 386이어도 64비트로 잘 도는 프로그램일 수 있고, 섹션 크기는 프로그램 규모를 말해 주지 않습니다. 제대로 읽으려면 ildasm이나 ILSpy 같은 IL 디스어셈블러가 필요합니다.
관련 도구
Mach-O 바이너리 검사기
macOS·iOS 바이너리를 브라우저에서 확인하세요. 유니버설 파일의 아키텍처, 세그먼트와 섹션, 연결된 dylib, rpath, UUID, 코드 서명까지.
ELF 바이너리 검사기
리눅스 실행 파일·.so·.o를 브라우저에서 엽니다. 아키텍처, 필요한 공유 라이브러리, 빌드 ID, PIE·NX·RELRO 적용 여부까지.
자바 클래스 파일 분석기
컴파일된 .class 파일을 브라우저에서 바로 분석합니다. 클래스 파일 버전과 자바 릴리스, 접근 플래그, 상수 풀, 필드, 메서드, 참조 클래스를 보여줍니다.
gzip(.gz) 구조 분석기
.gz 헤더를 필드 단위로 읽고 모든 멤버를 훑은 뒤, 브라우저에서 CRC32와 실제 크기를 다시 계산해 트레일러를 검증합니다.
xz(.xz) 스트림 분석기
xz 컨테이너를 읽습니다. 스트림 헤더, 블록 헤더와 필터 체인, 인덱스와 푸터, 그리고 압축을 풀지 않고 알아낸 실제 원본 크기까지.
텍스트 인코딩 변환기
EUC-KR · Shift_JIS · Windows-1252 등 비-UTF-8 텍스트 파일을 UTF-8로 읽으세요.