DOS Header


Microsoft 는 PE 파일 포멧을 만들때 당시에 널리 사용되던 DOS 파일에 대한 하위 호환성을 고려해서 만들었습니다.
그 결과로 PE header 의 제일 앞부분에는 기존 DOS EXE header 를 확장시킨 IMAGE_DOS_HEADER 구조체가 존재합니다.

typedef struct _IMAGE_DOS_HEADER {     
    WORD   e_magic;          // DOS signature : 4D5A ("MZ")
    WORD   e_cblp;                    
    WORD   e_cp;                      
    WORD   e_crlc;                    
    WORD   e_cparhdr;                 
    WORD   e_minalloc;                
    WORD   e_maxalloc;                
    WORD   e_ss;                      
    WORD   e_sp;                      
    WORD   e_csum;                    
    WORD   e_ip;                      
    WORD   e_cs;                      
    WORD   e_lfarlc;                  
    WORD   e_ovno;                    
    WORD   e_res[4];                  
    WORD   e_oemid;                   
    WORD   e_oeminfo;                 
    WORD   e_res2[10];                  
    LONG   e_lfanew;         // offset to NT header 
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

* 출처 : Microsoft 의 Visual C++ 에서 제공하는 winnt.h


<IMAGE_DOS_HEADER>

IMAGE_DOS_HEADER 구조체의 크기는 40h 입니다.
이 구조체에서 꼭 알아둬야 할 중요한 멤버는 e_magic 과 e_lfanew 입니다.

  • e_magic : DOS signature (4D5A => ASCII 값 "MZ")
  • e_lfanew : NT header 의 옵셋을 표시 (가변적인 값을 가짐)

모든 PE 파일은 시작 부분(e_magic)에 DOS signature ("MZ") 가 존재하고,
e_lfanew 값이 가리키는 위치에 NT header 구조체가 존재해야 합니다.
(NT header 구조체의 이름은 IMAGE_NT_HEADERS 이며 나중에 소개됩니다.)


* 'MZ' 는 Microsoft 에서 DOS 실행파일을 설계한 Mark Zbikowski 라는 사람의 이니셜입니다.
  (출처 : http://en.wikipedia.org/wiki/Mark_Zbikowski)

notepad.exe 를 hex editor 로 열어서 IMAGE_DOS_HEADER 구조체를 확인해 보겠습니다.


과연 PE 스펙에 맞게 파일 시작 2 byte 는 4D5A 이며, e_lfanew 값은 000000E0 입니다. (E0000000 이 아닙니다.)
(참고 : Intel 계열 PC 는 자료를 역순으로 저장합니다. 이것을 Little Endian 표기법이라고 합니다.)

시험삼아 이 값들을 변경한 후 저장해서 실행해 보세요.
정상 실행되지 않을 것입니다. (PE 스펙에 따라서 더 이상 PE 파일이 아니거든요.)



DOS Stub


DOS Header 밑에는 DOS Stub 이 존재합니다.
DOS Stub 의 존재여부는 옵션이며 크기도 일정하지 않습니다. 
(DOS Stub 은 없어도 파일 실행에는 문제가 없습니다.)

DOS Stub 은 코드와 데이타의 혼합으로 이루어져 있으며, 아래 그림에 notepad.exe 의 DOS Stub 이 나타나 있습니다.


위 그림에서 붉은색으로 표시된 부분은 16 bit 어셈블리 명령어 입니다.
32 bit 윈도우즈에서는 이쪽 명령어가 실행되지 않습니다. (PE 파일로 인식하기 때문에 아예 이쪽 코드를 무시하지요.) 

DOS 환경에서 실행하거나, DOS 용 디버거 (debug.exe) 를 이용해서 실행할 수 있습니다.
(DOS EXE 파일로 인식합니다. 이들은 PE 파일 포멧을 모르니까요...)

콘솔 윈도우(cmd.exe)를 띄워서 아래와 같이 명령을 입력합니다.

C:\WINDOWS>debug notepad.exe
-u
0D1E:0000 0E        PUSH    CS
0D1E:0001 1F        POP     DS
0D1E:0002 BA0E00    MOV     DX,000E   ; DX = 0E : "This program cannot be run in DOS mode"
0D1E:0005 B409      MOV     AH,09
0D1E:0007 CD21      INT     21        ; AH = 09 : WriteString()
0D1E:0009 B8014C    MOV     AX,4C01
0D1E:000C CD21      INT     21        ; AX = 4C01 : Exit()

코드는 매우 간단합니다. 문자열을 출력하고 종료해버리지요.

즉, notepad.exe 는 32 bit 용 PE 파일이지만, MS-DOS 호환 모드를 가지고 있어서
DOS 환경에서 실행하면 "This program cannot be run in DOS mode" 문자열을 출력하고 종료합니다.

이 특성을 잘 이용하면 하나의 실행(EXE) 파일에 DOS 와 Windows 에서 모두 실행 가능한 파일을 만들 수도 있습니다.
실제로 세계적인 보안업체 McAfee 에서 무료로 배포했던 scan.exe 라는 파일이 이와 같은 특징을 가지고 있었습니다.
(DOS 환경에서는 16 bit DOS 용 코드가, Windows 환경에서는 32 bit Windows 코드가 각각 실행됨.)

앞에서 말씀드린대로 DOS Stub 은 옵션이기 때문에, 개발 도구에서 지원해 줘야 합니다.
(VB, VC++, Delphi 등은 DOS Stub 을 기본 지원합니다.)


(continue)

  1. 베리굿 2009.11.09 16:39 신고 댓글주소 | 수정 | 삭제 | 댓글

    으앗.. 어렵다

    • ReverseCore 2009.11.09 16:47 신고 댓글주소 | 수정 | 삭제

      베리굿님, 안녕하세요. ^^
      처음 보시는 분에게는 위 내용이 어려울 수 있습니다.
      제 블로그를 읽어보신 후, PE 로 검색해서 다른 분들의 글들을 계속 읽다 보시면... 조금 쉬워지실 거에요~

  2. graythief 2010.02.17 21:08 신고 댓글주소 | 수정 | 삭제 | 댓글

    dos stub 코드부분을 조금 봤는데요
    ------------------제가 나름 정리하고 있는 내용-----------------------------
    notepad.exe 16비트 어셈블리 부분은
    윈도우로 실행시킬 경우엔 e_lfanew 값이 가르키는 곳을 인식을 하여 옮겨가지만
    16비트 도스 환경에서 실행할 때는 PE 포맷을 인식하지 못함으로 e_lfanew 값을 인식 하지 못하고 바로 stub 코드로 넘어가게 됩니다
    그리고 나서 “This program cannot be run in Dos mode” 뿌리고 종료할 것 입니다
    다른 운영체제에선 실행조차 되지 않겠지요

    이런 식으로 이해해도 될런지요 역시나.. 제 생각이 옳다 그르다 잡아주는 사람이 없어서 올립니다 ㅜㅜ

    • reversecore 2010.02.18 12:38 신고 댓글주소 | 수정 | 삭제

      graythief님, 안녕하세요.

      적어주신 내용이 맞습니다.

      쫌만 더 정확히 설명드리면 DOS 운영체제에서 인식하는 MZ 헤더와 Windows 운영체제의 PE 헤더내에 있는 MZ 헤더와 다릅니다. PE 헤더내의 MZ 헤더가 DOS 의 MZ 헤더를 포함하면서 더 확장시킨 개념이지요.

      그리고 DOS 운영체제에서는 e_cs, e_ip 멤버로 EntryPoint 가 결정됩니다. 그냥 참고로 알아두시면 됩니다. ^^

      감사합니다.

  3. 멈비 2010.03.11 16:13 신고 댓글주소 | 수정 | 삭제 | 댓글

    구조체의 크기를 표현할 때 h 단위를 쓰셨는데 어떤 단위인가요??

  4. SeHwa 2010.04.18 21:04 신고 댓글주소 | 수정 | 삭제 | 댓글

    기술 문서 작성중에 의문점이 있는 부분이 하나 있어서 이렇게 질문을 드립니다.
    DOS stub 코드는 저도 debug 에서 역어셈블링한 결과로 "This cannot ~" 문자열의
    앞에 있는 기계어가 DOS stub 코드라는 것을 알 수 있었는데, "This ~" 문자열이 끝나는 부분부터
    0xE0 (위에서 예로 드신 notepad.exe 에서의 IMAGE_NT_HEADERS 의 시작 어드레스) 까지의
    내용은 어떤 부분인가요?

    Windows 시스템 실행파일의 구조와 원리 라는 책에서는 이 부분을 "도스용 코드" 라고
    적어놓았는데, 이 부분을 마찬가지로 debug 에서 역어셈블링하니 의미있는 코드가
    출력되지 않았습니다. 그래서 아마 이 부분은 코드가 아닌 것 같다는 생각을 했습니다.
    (설마 IA-32 코드가 아닐 리는 없다고 생각했습니다.)

    이 부분은 어떤 역할을 하는 무엇인지 알 수 있을까요?
    기술 문서 작성이 급해서 이렇게 여기에 질문을 합니다. 감사합니다.
    (옛날부터 이 블로그 내용을 자주 참조하게 되는데(검색하면 항상 나옴) 너무 잘 보고 있습니다.)

    • Elephunk 2010.04.19 00:02 신고 댓글주소 | 수정 | 삭제

      @@SeHwa 님 제가 이해를 잘못한것 같습니다 죄송합니다

    • 빕뱟뱟 2010.04.19 10:35 신고 댓글주소 | 수정 | 삭제

      IMAGE_DOS_HEADER 스텁 뒤에 보면
      push cs
      pop ds
      mov dx, 000e
      mov ah, 09
      int 21
      mov ax, 4c01
      int 21
      가 있는데
      위 코드가 의미있는 코드임.
      ds랑 cs 싱크 맞추고
      dx에 인자 000e 넣고 (코드 000e 옵셋부터 This program cannot... 문자열)
      ax에 interrupt 번호 9번 넣고( stdout :: http://spike.scu.edu.au/~barry/interrupts.html )
      int 21(system call)
      이때 this program.. 문자열 출력.
      그담에 ax에 4c01 넣고 (4c == exit program)
      al에 01이 들어가는 이유는 리턴밸류 1 즉 return true;
      다시 int 21로 system call. program 종료.

    • 빕뱟뱟 2010.04.19 10:56 신고 댓글주소 | 수정 | 삭제

      헉 SeHwa의 질문내용을 잘못읽었군영.
      문자열 뒤로부터 IMAGE_NT_HEADERS까지는 뭐하는 부분인지 모르겠는데 코드는 아닐겁니다.

      MOV AX, 4C01
      INT 21
      여기서 프로그램이 종료되기 때문이죠.

      결론은 모르겠다 이군요. 뻘답 달아서 쏴리여 ㅋㅋ

    • SeHwa 2010.04.19 12:33 신고 댓글주소 | 수정 | 삭제

      답변을 달아주신 것만으로도 감사합니다.
      하지만 아쉽게도 관련이 없는 내용이었네요.

    • reversecore 2010.04.19 23:20 신고 댓글주소 | 수정 | 삭제

      SeHwa님, 안녕하세요.

      문의하신 내용은 위 DOS Stub 그림에서 80h ~ DFh 영역이 과연 무엇이냐 하는 것이지요?

      일단 굉장히 참신한 질문이네요~ 저 또한 당연히 생각하고 무심코 넘어간 부분입니다.

      확실한건 저 데이타를 참조하는 부분은 없기 때문에 프로그램 실행에 아무런 영향을 끼치지 않는다는 것입니다. 저부분을 밀어버려도 DOS/Windows 환경에서 실행에 아무런 영향이 없습니다.

      보통 VC++ 계열에서 저러한 형태의 데이타가 나타나고요, Delphi/VB 등에서는 나타나지 않네요. 아마 특정 개발도구의 특성이라고 보여집니다. PE 파일을 생성하기 위해서 임시 데이타를 써놓지 않았나 싶어요. (이건 어디까지나 저의 추측입니다.)

      답변 달아주신 Elephunk님과 빕뱟뱟님께도 감사드립니다.

      감사합니다.

    • SeHwa 2010.04.20 00:24 신고 댓글주소 | 수정 | 삭제

      하루만에 답변을 이렇게 빨리 주셔서 정말 감사합니다..^^
      저도 저 부분이 일반적인 EXE 프로그램 실행에 관계가
      없다는 것은 이미 0으로 채워보고 알고 있었습니다.

      다만 제가 의문인 것은 Microsoft 에서 제공하는
      PE and COFF 문서의 처음 부분을 보았는데, 그곳에
      MS-DOS 2.0 Stub Program and Relocation Table
      그리고 Unused 가 나온 뒤 PE Header가 있었습니다.

      여기서 혹시 제가 말한 부분이
      Relocation Table + Unused 부분이 아닌가 싶어
      얘기를 드렸습니다. 그리고 Relocation Table 이
      PE에서 말하는 기준 재배치 테이블은 아닌것 같고
      MS-DOS 시절에도 재배치 테이블이라는 것이 존재
      했다면 그것 같기도 하고.. 그러면 쓸모없는 내용은
      맞겠지만 저 부분을 설명을 할때는
      MS-DOS Relocation Table + Unused 라고
      설명해야할 것 같아 그렇기도 하고 그 외에도
      이것저것 의문이 많아 질문을 드렸습니다.

      (여러모로 귀찮게 하는것 같아서 너무 죄송합니다.
      아무에게도 답변을 들을 수 없어서 최후의 보루가
      reversecore 님이 되었네요..)

    • SeHwa 2010.04.20 00:30 신고 댓글주소 | 수정 | 삭제

      아.. 지금 막 메일을 받아보았습니다.
      친절하신 답변 정말로 감사합니다..

      그런데 설명하신 것이 제가 질문드린 것의 논점에서 약간
      벗어난게 안타깝긴 하지만.. 그 부분에서만 다시
      답변을 주시면 정말 감사하겠습니다..

      제가 질문한 것은 그냥 어떠한 "사실 확인" 이 목적인데
      앞의 IMAGE_DOS_HEADER 에서 e_magic / e_lfanew
      멤버를 제외하고는 Windows 에서 쓸모없는 것들인데,
      만약 MS-DOS Loader 에서 이 PE 파일을 로딩한다면
      어떻게 정확히 DOS Stub 코드 부분부터 실행하느냐가
      제가 궁금해하는 것이었습니다.
      IMAGE_DOS_HEADER 는 MS-DOS EXE 포맷과 비교하니
      뒤에 몇개 멤버가 더 있더군요. DOS Stub 코드는
      그 멤버들의 값 뒷부분에 위치하는데, 이 부분부터
      DOS가 실행하느냐가 궁금합니다.
      (만약 한다면 어떻게 하는지도 궁금하고요..
      메일로 문의드렸다시피 IMAGE_DOS_HEADER 의
      ip와 cs 멤버가 중요할 것 같은데, 그걸 대부분 다
      0으로 채운다고 하니 더욱 DOS에서 어떻게 DOS Stub
      코드 부분을 EP로 인식하고 실행하는지가 궁금하네요.)

      제가 어디에서 잘못 이해하고 있는지도 모르겠네요.
      질문을 받아주셔서 정말 감사합니다.

    • reversecore 2010.04.20 02:36 신고 댓글주소 | 수정 | 삭제

      SeHwa님, 안녕하세요.

      제가 답변을 달자마자 읽어 주셨군요. ^^

      "만약 MS-DOS Loader 에서 이 PE 파일을 로딩한다면
      어떻게 정확히 DOS Stub 코드 부분부터 실행하느냐~"
      => 아마 이 내용을 가장 궁금해 하시는것 같습니다.

      PE 파일의 IMAGE_DOS_HEADER(크기 40h) 는 DOS 의 MZ 파일 헤더(크기 20h)에다가 추가적으로 20h 크기만큼 확장시킨 형태입니다.

      따라서 DOS 운영체제에서 PE 파일을 실행한다면 MZ Loader 는 자기가 보고자 하는 정보(IMAGE_DOS_HEADER 의 상위 20h)만 보는 것이지요. 실행에 필요한 정보("MZ", cs, ip, ss, etc)는 상위 20h 내에 다 들어있으니까요. -> MZ Loader 는 IMAGE_DOS_HEADER 의 하위 20h 크기를 인식하지 못합니다. 그냥 상위 20h 가 전부라고 생각하는 것이지요.

      그리고 cs, ip 를 해석하면 정확히 DOS Stub 내의 코드 시작위치(EP)를 가리키게 됩니다. 그래서 실행이 가능한 것이지요.

      * 위의 MS-DOS Relocation Table 설명은 잘 봤습니다. 저도 그 문서를 본 적이 있지만 전혀 기억나지 않는군요. 덕분에 좋은 공부가 되었습니다. ^^

      감사합니다.

    • SeHwa 2010.04.20 07:23 신고 댓글주소 | 수정 | 삭제

      아... 역시 그렇군요.
      그러면 IMAGE_DOS_HEADER 를 e_magic/e_lfanew 를
      제외하고 모두 0으로 만들면 MS-DOS에서 실행이 안되겠군요.
      감사합니다.

      그리고 MS-DOS 시절에 EXE 포맷에 RelocationTable
      가 있었던건지 아니면 PE에서 추가된 것인지 그걸
      잘 모르겠네요..
      http://www.delorie.com/djgpp/doc/exe/
      에 보면 그런게 보이진 않는데.. MS-DOS를 잘 몰라서..


      귀찮은 질문 다 받아주셔서 정말 감사합니다..^^

    • reversecore 2010.04.21 00:37 신고 댓글주소 | 수정 | 삭제

      SeHwa님, 안녕하세요.

      MS-DOS 의 EXE 에는 relocation 테이블은 필요없을것 같습니다.

      왜냐하면 멀티 테스킹이 안되기 때문에 무조건 한번에 하나의 프로그램만 실행가능하며, 가상메모리 개념이 없기 때문이지요.

      그리고 저는 질문을 받아서 같이 생각하는 것이 좋답니다. ^^

      많이 질문해 주세요~

      감사합니다.

    • SeHwa 2010.04.21 00:47 신고 댓글주소 | 수정 | 삭제

      질문을 귀찮게 생각 안하시고 그렇게 생각해주시니
      감사할 따름입니다.
      (사실 하루종일 틈틈이 모니터링 하고 있습니다.)

      제 생각도 MS-DOS 에 재배치 개념이 있어야 할 이유가
      없기 때문에 이건 PE 의 재배치 테이블이 아닌가 싶은데...
      Visual Studio 의 경우는 이 부분이 그냥
      tmp(쓰레기값)인게 맞는것 같기도 하네요.
      하나의 프로그램을 코드를 조금씩 바꿔가면서 계속
      빌드하여 이 부분의 값을 확인해보면
      미묘하게 조금씩 바뀌는 경향을 보이네요.

      그런데 가장 이상한건 특정한 코드에 따라서
      이 부분의 값이 그 특정한 값으로 결정될 때도
      있다는 것이 이상하네요...
      (이건 그렇지만 PE 스펙과 관련있는 내용이 아니라
      Visual Studio 내에서의 특성인것 같으니
      원래의 PE 포맷에선 Unused 부분이라고 봐도 될까요?)

    • reversecore 2010.04.22 01:06 신고 댓글주소 | 수정 | 삭제

      SeHwa님, 안녕하세요.

      DOS Stub 영역은 PE 파일의 실행에 상관없는 영역인건 확실합니다. ^^

      유난히 VC++ 로 작성된 PE 파일에서 많이 보이기 때문에 어렴풋이 VC++ 에서 생성하는 (그러나 실행에는 전혀 상관없는) 임시 데이타가 아닐까 추측만 해보는 것이죠...

      감사합니다.

  5. 호동왕자 2010.11.29 13:49 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕 하세요.

    PE Haeder에 관심이 있어 가지구요..

    마지막에 이해가 안되는 부분이 생겨서 말이죠..

    문자열을 출력 하고 바로 종료 한다고 하셨는데요.

    이 내용이 무슨 내용인지 잘 이해가 안됩니다..

    아니면 제가 앞의 내용을 이해를 잘 못하고 있어서

    그런건가요?

    • reversecore 2010.11.30 10:53 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      네, 32bit PE 파일을 16bit DOS 운영체제 환경에서 실행하면...
      DOS 운영체제는 PE 파일 내부의 IMAGE_DOS_HEADER (16bit 버전) 헤더만을 읽어들여서 실행시킨다는 뜻입니다. 일종의... DOS 운영체제 속이기라고 보시면 됩니다. ^^

      사실은 PE 파일이지만 DOS 가 봤을때 16 BIT EXE 파일포멧에 맞거든요. 그래서 DOS Stub 의 내용(문자열 출력)만 실행하고 종료한다는 뜻입니다.

      감사합니다.

    • 호동왕자 2010.11.30 13:59 신고 댓글주소 | 수정 | 삭제

      감사합니다.

      한번 더 보고 다른 자료들도

      찾아 보면서 보면서 툴로 또 보니까

      이제 이해가 가네요 ^^ 좋은 정보

      정말 감사 합니다.

  6. Manwon 2010.12.10 17:05 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요! 항상 좋은글 감사히 보고있습니다.

    공부하는중에 궁금한점이 생겨서 댓글달아봅니다..

    대부분의 설명은 이해가 되는데, 어떻게 hex editor에 나온부분중

    파랗거나 빨갛게 표시해놓은 부분이 Dosstub이다, IMAGE_FILE_HEADER이다 라고 판단 할수있는건지 이해가 잘 안가네요

    도와주세요~~ㅜ

    • reversecore 2010.12.16 15:36 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      파일의 첫 2 byte "MZ" 이고 3C 옵셋에서 값을 읽어 따라 갔을때 "PE\0\0" 가 나오면 PE 파일의 가능성이 높습니다.

      PE 스펙에 따르면 IMAGE_DOS_HEADER 의 크기는 40 이므로 DOSStub 은 40 ~ PE시그니처직전 까지이구요... PE 시그니처 바로 뒤부터 IMAGE_FILE_HEADER 가 바로 시작되는 것을 알고 있기 때문에 저렇게 찾아 갈 수 있는 것입니다.

      모든건 PE 스펙에 정의된 구조체와 구조체 멤버의 의미대로 해석해서 따라갈 수 있는 것입니다.

      IMAGE_FILE_HEADER 는 바로 이어서 설명되므로 그걸 읽어보시면 조금씩 감을 잡으실 수 있으실 겁니다.

      감사합니다.

  7. blackwolf 2011.03.15 12:28 신고 댓글주소 | 수정 | 삭제 | 댓글

    좋은 글 잘 봤습니다. ^^;

  8. 있나없나 2011.04.21 19:46 신고 댓글주소 | 수정 | 삭제 | 댓글

    오늘 처음으로 이 블로그를 친구를 통해 소개받았는데 매우 좋네요!

    아참. 그리고 덧글들도 읽어보니 도움되는 내용이 꽤나 많네요.
    그런데 중간에 Dos Stub 와 IMAGE_NT_HEADER 사이의 내용에 대해 덧글이 길게 늘어지더군요.

    이에 대해 언급을 한 다른 게시글이 있어서 링크 남겨드립니다
    http://cafe.naver.com/xlsvba.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=1007&social=1
    네이버 카페에 '수학쟁이' 님께서 쓰신 PE구조 강좌 내용중 [개발자 TIP] 라는 부분에 언급되어 있더군요. 경찰쪽에서 쓴다고 하네요.

    그럼 다른글도 계속 보면서 열심히 공부할께요~
    감사합니다.

    • reversecore 2011.05.03 01:03 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      방문 감사합니다. 올려주신 정보는 꼭 읽어보도록 하겠습니다. (까페 가입을 해야 하는 것인가요?)

      감사합니다.

  9. Synaps 2012.05.04 15:07 신고 댓글주소 | 수정 | 삭제 | 댓글

    좋은글 잘 읽고 있습니다. ^^

  10. 코딩하는 경제학도 2017.02.03 09:25 댓글주소 | 수정 | 삭제 | 댓글

    관리자의 승인을 기다리고 있는 댓글입니다





티스토리 툴바