IMAGE_NT_HEADERS - IMAGE_OPTIONAL_HEADER32


PE header 구조체 중에서 가장 크기가 큰 IMAGE_OPTIONAL_HEADER32 입니다.
(64 bit PE 파일의 경우 IMAGE_OPTIONAL_HEADER64 구조체를 사용합니다.)

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

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


IMAGE_OPTIONAL_HEADER32 구조체에서 주목해야 할 멤버들은 아래와 같습니다.
이 값들 역시 파일 실행에 필수적인 값들이라서 잘 못 세팅되면 파일이 정상 실행 되지 않습니다.


#1. Magic
IMAGE_OPTIONAL_HEADER32 인 경우 10Bh, IMAGE_OPTIONAL_HEADER64 인 경우 20Bh 값을 가지게 됩니다.


#2. AddressOfEntryPoint
EP(Entry Point) 의 RVA(Relative Virtual Address) 값을 가지고 있습니다.


#3. ImageBase
프로세스의 가상 메모리는 0 ~ FFFFFFFFh 범위입니다. (32 bit 의 경우)
ImageBase 는 이렇게 광활한 메모리내에서 PE 파일이 로딩(매핑)되는 시작 주소를 나타냅니다.

EXE, DLL 파일은 user memory 영역인 0 ~ 7FFFFFFFh 범위에 위치하고,
SYS 파일은 kernel memory 영역인 80000000h ~ FFFFFFFFh 범위에 위치합니다.


일반적으로 개발 도구(VB/VC++/Delphi)들이 만들어내는 EXE 파일의 ImageBase 값은 00400000h 이고,
DLL 파일의 ImageBase 값은 01000000h 입니다. (물론 다른 값도 가능합니다.)

PE loader 는 PE 파일을 실행시키기 위해 프로세스를 생성하고 파일을 메모리에 로딩(매핑) 시킨 후
EIP 레지스터 값을 ImageBase + AddressOfEntryPoint 값으로 세팅합니다.


#4. SectionAlignment, FileAlignment
PE 파일은 섹션으로 나뉘어져 있는데 파일에서 섹션의 최소단위를 나타내는 것이 FileAlignment 이고
메모리에서 섹션의 최소단위를 나타내는 것이 SectionAlignment 입니다.
(하나의 파일에서 FileAlignment 와 SectionAlignment 의 값은 같을 수도 있고 틀릴 수도 있습니다.)

따라서 파일/메모리의 섹션 크기는 반드시 각각 FileAlignment/SectionAlignment 의 배수가 되어야 합니다.


#5. SizeOfImage
PE 파일이 메모리에 로딩되었을 때 가상 메모리에서 PE Image 가 차지하는 크기를 나타냅니다.
일반적으로 파일의 크기와 메모리에 로딩된 크기는 다릅니다. 
(각 섹션의 로딩 위치와 메모리 점유 크기는 나중에 소개할 Section Header 에 정의 되어 있습니다.)


#6. SizeOfHeader
PE header 의 전체 크기를 나타냅니다.
이 값 역시 FileAlignment 의 배수 이어야 합니다.

파일 시작에서 SizeOfHeader 옵셋만큼 떨어진 위치에 첫번째 섹션이 위치합니다.


#7. Subsystem
1 : Driver file (*.sys)
2 : GUI (Graphic User Interface) 파일 -> notepad.exe 와 같은 윈도우 기반 어플리케이션
3 : CUI (Console User Interface) 파일 -> cmd.exe 와 같은 콘솔 기반 어플리케이션


#8. NumberOfRvaAndSizes
마지막 멤버인 DataDirectory 배열의 갯수

구조체 정의에 분명히 배열 갯수가 IMAGE_NUMBEROF_DIRECTORY_ENTRIES (16) 이라고 명시 되어 있지만,
PE loader 는 NumberOfRvaAndSizes 의 값을 보고 배열의 크기를 인식합니다.



#9. DataDirectory
IMAGE_DATA_DIRECTORY 구조체의 배열로써, 배열의 각 항목마다 정의된 값을 가지게 됩니다.

아래에 각 배열 항목을 나열하였습니다.

DataDirectory[0] = EXPORT Directory        
DataDirectory[1] = IMPORT Directory        
DataDirectory[2] = RESOURCE Directory      
DataDirectory[3] = EXCEPTION Directory     
DataDirectory[4] = SECURITY Directory      
DataDirectory[5] = BASERELOC Directory     
DataDirectory[6] = DEBUG Directory         
DataDirectory[7] = COPYRIGHT Directory     
DataDirectory[8] = GLOBALPTR Directory     
DataDirectory[9] = TLS Directory           
DataDirectory[A] = LOAD_CONFIG Directory   
DataDirectory[B] = BOUND_IMPORT Directory  
DataDirectory[C] = IAT Directory           
DataDirectory[D] = DELAY_IMPORT Directory  
DataDirectory[E] = COM_DESCRIPTOR Directory
DataDirectory[F] = Reserved Directory       


여기서 말하는 Directory 란 그냥 어떤 구조체의 배열이라고 생각하시면 됩니다.

빨간색으로 표시한 EXPORT, IMPORT, RESOURCE, TLS Directory 를 눈여겨 보시기 바랍니다.
특히 IMPORT 와 EXPORT Directory 구조는 PE header 에서 매우 중요하기 때문에 나중에 따로 설명하도록 하겠습니다.

나머지는 크게 중요하지 않다고 보시면 됩니다.


이제 실제로 notepad.exe 의 IMAGE_OPTIONAL_HEADER32 를 확인해 보겠습니다.


구조체 멤버별 값과 설명은 아래와 같습니다.

[ IMAGE_OPTIONAL_HEADER ] - notepad.exe

 offset   value   description
-------------------------------------------------------------------------------
000000F8     010B magic
000000FA       07 major linker version
000000FB       0A minor linker version
000000FC 00007800 size of code
00000100 00008C00 size of initialized data
00000104 00000000 size of uninitialized data
00000108 0000739D address of entry point
0000010C 00001000 base of code
00000110 00009000 base of data
00000114 01000000 image base
00000118 00001000 section alignment
0000011C 00000200 file alignment
00000120     0005 major OS version
00000122     0001 minor OS version
00000124     0005 major image version
00000126     0001 minor image version
00000128     0004 major subsystem version
0000012A     0000 minor subsystem version
0000012C 00000000 win32 version value
00000130 00014000 size of image
00000134 00000400 size of headers
00000138 000126CE checksum
0000013C     0002 subsystem
0000013E     8000 DLL characteristics
00000140 00040000 size of stack reserve
00000144 00011000 size of stack commit
00000148 00100000 size of heap reserve
0000014C 00001000 size of heap commit
00000150 00000000 loader flags
00000154 00000010 number of directories
00000158 00000000 RVA  of EXPORT Directory
0000015C 00000000 size of EXPORT Directory
00000160 00007604 RVA  of IMPORT Directory
00000164 000000C8 size of IMPORT Directory
00000168 0000B000 RVA  of RESOURCE Directory
0000016C 00008304 size of RESOURCE Directory
00000170 00000000 RVA  of EXCEPTION Directory
00000174 00000000 size of EXCEPTION Directory
00000178 00000000 RVA  of SECURITY Directory
0000017C 00000000 size of SECURITY Directory
00000180 00000000 RVA  of BASERELOC Directory
00000184 00000000 size of BASERELOC Directory
00000188 00001350 RVA  of DEBUG Directory
0000018C 0000001C size of DEBUG Directory
00000190 00000000 RVA  of COPYRIGHT Directory
00000194 00000000 size of COPYRIGHT Directory
00000198 00000000 RVA  of GLOBALPTR Directory
0000019C 00000000 size of GLOBALPTR Directory
000001A0 00000000 RVA  of TLS Directory
000001A4 00000000 size of TLS Directory
000001A8 000018A8 RVA  of LOAD_CONFIG Directory
000001AC 00000040 size of LOAD_CONFIG Directory
000001B0 00000250 RVA  of BOUND_IMPORT Directory
000001B4 000000D0 size of BOUND_IMPORT Directory
000001B8 00001000 RVA  of IAT Directory
000001BC 00000348 size of IAT Directory
000001C0 00000000 RVA  of DELAY_IMPORT Directory
000001C4 00000000 size of DELAY_IMPORT Directory
000001C8 00000000 RVA  of COM_DESCRIPTOR Directory
000001CC 00000000 size of COM_DESCRIPTOR Directory
000001D0 00000000 RVA  of Reserved Directory
000001D4 00000000 size of Reserved Directory


여기까지 NT Header 의 설명을 마치고 다음에 Section Header 에 대해서 살펴보도록 하겠습니다.

(continue)


 

  1. 뭉이 2010.01.06 18:01 신고 댓글주소 | 수정 | 삭제 | 댓글

    하나의 파일에서 FileAlegnment과 SectionAlegnment는 같을 수도 있고 다를수도 있다고 했는데..
    만약 SectionAlegnment이 FileAlegnment보다 작게 되면
    파일의 각 섹션에 있는 내용이 로드될때,... 내용이 잘리게 되는 경우는 없는지요?..

    SectionAlegnment과 FileAlegnmentr값은 누가 어떻게 결정 되나요?

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

      뭉이님, 안녕하세요.

      기발한 생각이네요~

      SectionAlignment 가 FileAlignment 보다 작은 경우는 본적이 없습니다만... 굳이 안될것 같지는 않네요.

      이 값들은 기본 단위값이기 때문에 내용이 잘리지 않도록 단위의 배수만큼 섹션 크기를 조절하면 될거 같습니다.

      이 값들은 개발 툴에서 결정되는데요...
      VC++ 에서 옵션을 못찾겠네요...

      검색을 좀 해봐야 하겠습니다.

      감사합니다.

    • 뭉이 2010.01.07 00:42 신고 댓글주소 | 수정 | 삭제

      아...SectionAlegnment는 항상 FileAlignment보다 크게 정해진다는 말씀이시죠?....

      답변 감사합니다..^^

      크게 중요한 부분은 아닌거 같은데...글을 읽다가...
      자꾸 그부분이 이상하게 궁금해서...물어봤습니다..

      이런사소한 질문에 시간내서 답변달아주셔서 정말 감사합니다..^^
      ...

    • newbie 2010.07.21 19:52 신고 댓글주소 | 수정 | 삭제

      SectionAlignment와 FileAlignment의 값이 상당히 중요한걸로 알고 있습니다. 이게 제대로 맞지 않으면 오류를 내는것 같더라구요 예전에 확인해본 결과 FileAlignment를 SectionAlignment값보다 크게 잡으면 오류가 났었던것 같아요. 아무리 커도 SectionAlignment값보다는 크지 않아야 하고 꼭 0200h의 배수가 되어야 하나 그랬던걸로 기억합니다. 오래전 기억이어서 가물가물하지만 참고가 되었으면 좋겠네요~

  2. Vi0 2010.08.04 21:23 신고 댓글주소 | 수정 | 삭제 | 댓글

    PE IMAGE란것이 무엇인가요.?
    PE파일 전체를 말하는건가요?

  3. 비빅 2010.08.04 23:11 신고 댓글주소 | 수정 | 삭제 | 댓글

    내용중에 #.5 SizeOfImage 설명이 잘 이해가 안되는데요 특히 PEImage가 무엇을 말하는지
    모르겠습니다;; 순서대로 읽어왔는데 처음보는거같은데 #.5부분만 조금 자세히
    설명 부탁드립니다 ^^;

  4. reversecore 2010.08.06 07:20 신고 댓글주소 | 수정 | 삭제 | 댓글

    Vi0 님과 비빅님께서 거의 동시에 같은 내용의 질문을 해주셨네요~ ^^

    IMAGE 라는 용어는 PE 구조를 만든 MS 에서 만들어낸 용어입니다.
    보통은 PE 파일이 메모리에 로딩된 상태에서 PE 메모리 영역 전체를 말한다고 생각하시면 거의 정확합니다.

    PE 헤더 정보중에 SizeOfImage 라는 것이 있는데 이것이 바로 PE Image 가 점유한 메모리 크기를 의미하는 것입니다. 이 크기는 당연히 파일 크기와는 다르지요.

    PE File Format 의 묘미중의 하나가 바로 파일 상태의 모습과 메모리 상태의 모습이 다르다는 것이지요.

    감사합니다.

    • 비빅 2010.08.06 13:46 신고 댓글주소 | 수정 | 삭제

      답변 감사합니다 ^^

      설명해주신대로 PE이미지가 PE메모리 영역 전체라고 하셔서 제 생각으로는 image_section_header 들의
      Virtual Size 값들을 모두 합하면 PE Image 값이
      나오는건가? 생각해보고 계산해봤는데..

      ㅋㅋ 역시 그건아니더군요;;
      Virtual Size값이 어떤 계산에 의해서 나오는지
      궁금합니다 ㅎ

    • reversecore 2010.08.09 11:27 신고 댓글주소 | 수정 | 삭제

      VirtualSize 값이요?
      그건 섹션 헤더에 나와 있구요...

      아마 SizeOfImage 값이 궁금하신것 같은데요...

      VirtualSize 를 다 합쳐도 SizeOfImage 값이 나오지 않는 이유는 SectionAlignment 때문입니다. SectionAlignment (보통 1000) 에 맞게 확장시킨후 더하시면 정확한 값이 나오게 됩니다. (예를 들면 첫번째 섹션의 VirtualSize = E00 이면 SectionAlignment 에 맞춰서 1000 으로 확장됩니다.)

      이렇게 모든 섹션의 보정된 VirtuslSize 를 다 더하고,
      마지막으로 PE 헤더 크기만큼 더하면 됩니다. (그건 그냥 쉽게 첫번째 섹션의 RVA 값을 더해주면 되지요.)

      잘 이해 안되시면 다시 질문 올려주세요~

      감사합니다.

  5. 비빅 2010.08.09 23:09 신고 댓글주소 | 수정 | 삭제 | 댓글

    아 제가 SizeOfImage를 물어본다는게 VirtualSize값을 물어봤네요;;

    알려주신대로 계산해보니 제대로 계산이 됩니다 감사합니다^^
    SectionAlignment값에 의해 확장까진 생각했는데 PE헤더 만큼 더해주는걸 생각못했네요 ㅎㅎ

  6. 호동왕자 2010.11.30 14:42 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요.

    몇일 동안 헤메다가 이제서야 여기까지 왔네요.

    보는데 궁금증이 생겨서요. 오늘도 바로 답변해 주시고 정말 감사 합니다.

    질문은 이렇습니다.

    IMAGE_OPTIONAL_HEADER

  7. 호동왕자 2010.11.30 14:46 신고 댓글주소 | 수정 | 삭제 | 댓글

    위에 글이 삭제가 안되네요. 잘못 클릭 했는데 말이죠..

    IMAGE_OPTIONAL_HEADER 을 실제적으로 확인해 보았을때

    지금 현재 스크린샷을 찍으셔서 파란칸으로 만드셨잖아요..

    그런데 처음에 0B 01 07 ~ 00 00 00 00 으로 끝나잖아요...

    그런데 제가 PEview로 확인을 해봤는데요.

    맨처음 desscription magic에서 data가 01 0B 로 시작 하는데요

    제가 지금 잘못 된건지.. 알고 싶어서요..

    • reversecore 2010.12.03 16:50 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      Endian 문제인데요...

      위 그림의 Hex Editor 에서는 0B 01 로 나오지만...
      구조체 멤버로는 WORD 타입이기 때문에
      PEView 에서 읽으면 010B 가 맞습니다.

      Intel CPU 는 Little Endian 방식을 사용하기 때문에 이처럼 값이 뒤집혀 저장됩니다.

      * Little Endian 이라고 검색을 해보시면 관련 설명이 많이 검색될 것입니다.

      감사합니다.

  8. TeamKhan 2011.04.01 18:36 신고 댓글주소 | 수정 | 삭제 | 댓글

    size of image값을 구하는방법을 설명해주셧는데
    그부분이 잘 이해가안가서요..확장시킨후 더하라는말이 무엇인가요?
    그리고 Virtual size=e00 이면 SectionAlignment 에 맞춰서 1000 으로 확장됩니다.)
    라는 말의 의미도 무슨뜼인지 잘 모르겟습니다...
    virtual size가무엇인지요 ㅠㅠ sectionAlignment 는 위글에서 설명해주셧듯이
    메모리상에서 섹션의 최소단위인건 알겟구요...ㅠ
    모든 섹션의 보정된 VirtuslSize 를 다 더하고,
    마지막으로 PE 헤더 크기만큼 더하면 됩니다. (그건 그냥 쉽게 첫번째 섹션의 RVA 값을 더해주면
    된다는말도 이해가잘안되내요...RVA가 제가 알기론 기준점이있고 그기준점에서부터 OFFSET값으로 RVA가 구성된다는것으로알고잇는데요...
    쉽게말씀드려서 sectionAlignment뺴고는 전부 무슨말씀이신지 이해가안가내요...

    • reversecore 2011.04.06 01:04 신고 댓글주소 | 수정 | 삭제

      안녕하세요. ^^

      네, 위의 설명이 좀 어려운게 사실입니다.
      먼저 개념을 이해하시면 설명이 조금 보기 편하실 거에요.

      음... 쉽게 말하자면...
      Windows 운영체제에서 메모리를 할당하는 방법에 관한 건데요...

      예전에 귤->박스->컨테이너->창고 식으로 설명을 많이 했었는데요... 잘 생각이 안나네요. ^^

      어쨌든 컴퓨터 메모리, 하드디스크 등은 기본 단위 크기로 움직인다는 것이 핵심입니다. 기본 규격이라고 보시면 됩니다. 즉 1 바이트 저장을 위해서 SectionAlignment (보통 1000) 바이트가 필요한거죠. PE 파일에서도 실제 필요한 메모리 크기는 10001 바이트라고 하면 기본 단위 규격에 맞게 11000 바이트를 잡는 것이지요.

      * VirtualSize 는 메모리 크기를 의미합니다.

      잘 이해가 안되시면 다시 질문해 주세요~

      감사합니다.

  9. reversingk 2011.05.17 13:40 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세여 ^^ 리버스코어님 강의 잘봤습니다.
    그런데 궁굼한것이 하나 생겨서 여쭈어봅니다.
    pe파일을 실행시키기 위해 프로세스를 생성하고 파일을 메모리에 로딩 (매핑)시킨후 EIP 레지스터 값을 addressofEntrypoint + imageBase 값으로 셋팅 한다는 말이 메모리에 로딩되었을때
    시작점을 말하는 것인가여?? 아니면 main()의 시작점을 말하는 것인가요?

    그리고 notepad 를 ollydbg로 열어보니 00da3689값으로 나오던데 혹시 이값이 맞나여?
    마즈면 왜 01003689가 아닌 00da3689값이 나오는지도 알고 싶습니다.

    너무 질문만 하는거 아닌지 모르겟네여 ^^;; ㅎㅎ

    • reversecore 2011.05.18 20:08 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      다른 곳에 비슷한 질문을 올려 주셨죠? ^^

      ASLR 기능 때문이구요. 아래 포스트를 참고하시면 되겠습니다.

      http://www.reversecore.com/69

      감사합니다.

  10. EP 2011.06.04 15:43 신고 댓글주소 | 수정 | 삭제 | 댓글

    EP(Entry Point)가 메모리에 로딩되는 위치를 말하는 건가요 ?

    RVA로 표현된다면
    739D니까

    메모리에 올라갈경우 100739D가 되는건가요 ?

    EntryPoint 개념을 잘 모르겠네요 ..

    • reversecore 2011.06.06 21:32 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      EP(EntryPoint) 란 사용자 프로그램에서 최초로 실행되는 코드 주소를 말합니다. PE Header 에 RVA 로 명시되어 있지요.

      그리고 PE 파일이 로딩 되는 기준 주소를 ImageBase 라고 하지요.

      따라서 EP 의 VA = ImageBase(1000000) + RVA of EP(739D) = 100739D 가 됩니다.

      감사합니다.

  11. SmartSnake 2011.10.27 17:30 신고 댓글주소 | 수정 | 삭제 | 댓글

    pe파일에서.. rva랑 offset이랑 이해가 잘안되여..

    rva는 기준주소로부터의 상대주소값을 말하고
    file offset은 하드디스크상에 있을때의 메모리 주소를말하는거지요?
    파일이 메모리에 올라오면 RVA로 되는거구요..맞나요?!!

    • reversecore 2013.02.25 21:51 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      네, 맞습니다.

      PE 파일이 하드디스크의 파일로 존재할 때와 메모리의 PE Image 로서 존재할 때의 모습(형태)가 달라지기 때문에...

      각각의 옵셋을 얘기할 때 file offset(RAW), RVA 라고 얘기합니다.

      감사합니다.

  12. 2013.02.25 20:29 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2013.02.25 21:49 신고 댓글주소 | 수정 | 삭제

      안녕하세요. 반갑습니다~ ^^

      winhack은 안써봤습니다.

      질문의 요지는 Windows OS 의 핵심 개념중의 하나인 "모든 프로세스에게는 4GB 크기의 가상메모리(VirtualMemory)가 할당된다" 는 개념인것 같습니다.

      따라서 1.exe 프로세스의 ImageBase 는 40000h 이고, 2.exe 프로세스의 ImageBase 도 40000h 이 될 수 있는 것입니다. 물리적 메모리는 완전 다르지만 각 프로세스는 자신이 4GB 크기의 물리메모리를 가지고 있다고 착각하는 것이죠.

      이 개념을 사용하기 때문에 (일반적으로는) 다른 프로세스의 메모리에 접근할 수 없습니다.

      감사합니다.

  13. Null0 2014.07.01 12:56 신고 댓글주소 | 수정 | 삭제 | 댓글

    이론 상으로는 file alignment가 1이여도 정상적인 실행이 되야하는데 자꾸 오류가 뜨네요
    위에 댓글다신 분 말씀처럼 0x200의 배수여야만 하는건가요?




Introduction


Windows 운영체제의 PE(Portable Executable) File Format 에 대해서 아주 상세히 공부해 보도록 하겠습니다.

PE format 을 공부하면서 Windows 운영체제의 가장 핵심적인 부분인
Process, Memory, DLL 등에 대한 내용을 같이 정리할 수 있습니다.



PE(Portable Executable) File Format


PE 파일의 종류는 아래와 같습니다.

  • 실행 파일 계열 : EXE, SCR
  • 라이브러리 계열 : DLL, OCX
  • 드라이버 계열 : SYS
  • 오브젝트 파일 계열 : OBJ

엄밀히 얘기하면 OBJ(오브젝트) 파일을 제외한 모든 파일들은 실행 가능한 파일 입니다.

DLL, SYS 파일등은 쉘(Explorer.exe) 에서 직접 실행 할 수는 없지만,
다른 형태의 방법(디버거, 서비스, 기타)을 이용하여 실행이 가능한 파일들입니다.

* PE 공식 스펙 에는 컴파일 결과물인 OBJ(오브젝트) 파일도 PE 파일로 간주합니다.
  하지만 OBJ 파일 자체로는 어떠한 형태의 실행도 불가능하므로 리버싱에서 관심을 가질 필요는 없습니다.


간단한 설명을 위해서 노트패드(notepad.exe) 파일을 hex editor 를 이용해서 열어보겠습니다.


<Fig. 1>

<Fig. 1> 은 notepad.exe 파일의 시작 부분이며, PE 파일의 헤더 (PE header) 부분입니다.

바로 이 PE header 에 notepad.exe 파일이 실행되기 위해 필요한 모든 정보가 적혀있습니다.

어떻게 메모리에 적재되고, 어디서부터 실행되어야 하며, 실행에 필요한 DLL 들은 어떤것들이 있고,
필요한 stack/heap 메모리의 크기를 얼마로 할지 등등...


수 많은 정보들이 PE header 에 구조체 형식으로 저장되어 있습니다.

즉, PE File Format 을 공부한다는 것은 PE header 구조체를 공부한다는 것과 같은 말입니다.



Basic Structure


일반적인 PE 파일의 기본 구조입니다. (notepad.exe)


<Fig. 2>

<Fig. 2> 는 notepad.exe 파일이 메모리에 적재(loading 또는 mapping)될 때의 모습을 나타낸 그림입니다.
많은 내용을 함축하고 있는데요, 하나씩 살펴보겠습니다.


  • DOS header 부터 Section header 까지를 PE Header, 그 밑의 Section 들을 합쳐서 PE Body 라고 합니다.

  • 파일에서는 offset 으로, 메모리에서는 VA(Virtual Address) 로 위치를 표현합니다.

  • 파일이 메모리에 로딩되면 모양이 달라집니다. (Section 의 크기, 위치 등)

  • 파일의 내용은 보통 코드(".text" 섹션), 데이타(".data" 섹션), 리소스(".rsrc") 섹션에 나뉘어서 저장됩니다.
    반드시 그런것은 아니며 개발도구(VB/VC++/Delphi/etc)와 빌드 옵션에 따라서
    섹션의 이름, 크기, 개수, 저장내용 등은 틀려집니다. 중요한 것은 섹션이 나뉘어서 저장 된다는 것입니다.

  • Section Header 에 각 Section 에 대한 파일/메모리에서의 크기, 위치, 속성 등이 정의 되어 있습니다.

  • PE Header 의 끝부분과 각 Section 들의 끝에는 NULL padding 이라고 불리우는 영역이 존재합니다.
    컴퓨터에서 파일, 메모리, 네트워크 패킷 등을 처리할 때 효율을 높이기 위해 최소 기본 단위 개념을 사용하는데,
    PE 파일에도 같은 개념이 적용된 것입니다.

  • 파일/메모리에서 섹션의 시작위치는 각 파일/메모리의 최소 기본 단위의 배수에 해당하는 위치여야 하고,
    빈 공간은 NULL 로 채워버립니다. (<Fig. 2> 를 보면 각 섹션의 시작이 이쁘게 딱딱 끊어지는 걸 볼 수 있습니다.)



VA & RVA



VA (Virtual Address) 는 프로세스 가상 메모리의 절대 주소를 말하며,
RVA (Relative Virtual Address) 는 어느 기준위치(ImageBase) 에서부터의 상대 주소를 말합니다.

VA 와 RVA 의 관계는 아래 식과 같습니다.

RVA + ImageBase = VA

PE header 내의 많은 정보는 RVA 형태로 된 것들이 많습니다.
그 이유는 PE 파일(주로 DLL)이 프로세스 가상 메모리의 특정 위치에 로딩되는 순간
이미 그 위치에 다른 PE 파일(DLL) 이 로딩 되어 있을 수 있습니다.

그럴때는 재배치(Relocation) 과정을 통해서 비어 있는 다른 위치에 로딩되어야 하는데,
만약 PE header 정보들이 VA (Virtual Address - 절대주소) 로 되어 있다면 정상적인 엑세스가 이루어지지 않을것입니다.

정보들이 RVA (Relative Virtual Address - 상대주소) 로 되어 있으면 Relocation 이 발생해도
기준위치에 대한 상대주소는 변하지 않기 때문에 아무런 문제없이 원하는 정보에 엑세스 할 수 있을 것입니다.




이어지는 강좌에서 PE Header 구조체를 하나씩 상세히 살펴보도록 하겠습니다.



(continue)



    이전 댓글 더보기
  1. 장황제 2010.01.21 21:48 신고 댓글주소 | 수정 | 삭제 | 댓글

    와..읽으면서..웃음이 나옵니다..ㅋㅋ

    이해가 퐉왔습니다~;;

    정말 개념만 보면 쉬운거였네요..ㅋㅋ

    이해하기 쉽게 알려주셔서 감사합니다~

    • ReverseCore 2010.01.21 22:47 신고 댓글주소 | 수정 | 삭제

      장황제님, 안녕하세요.

      이해가 잘 되셨다니 다행입니다.

      사실 저렇게 귤을 보관하면 검색(찾기)이 편리해 집니다.
      "몇번 창고 몇번 박스내의 몇번 귤" 하면 되니까요.

      즉, 대량의 데이타를 보관할 때 묶어서 보관하면 정리도 잘되고 검색도 쉬워지는 장점이 있지요.

      감사합니다.

  2. 장황제 2010.01.30 10:45 신고 댓글주소 | 수정 | 삭제 | 댓글

    이번에는 패딩사이즈질문좀 드릴께요..ㅠ

    파일에 패딩하고 메모리에 로드되었을때의 패딩 크기가 알고싶어서요.

    저는 만약 sectionalignment에서 ox00001000이면 4096바이트의 배수가 되는거고.

    filealignment가 ox00002000 이면 8192바이트의 배수가 된다고 생각을 했는데....

    제가 잘못생각하고 있는건가요??

    • reversecore 2010.01.31 23:19 신고 댓글주소 | 수정 | 삭제

      장황제님, 안녕하세요.

      패딩크기 말씀이시죠?

      실제 코드(데이타) 의 크기를 각 alignment 에 맞게 확장시켜 주기 때문에 그때 그때 달라집니다.

      그리고 각 섹션의 크기는 장황제님 말씀대로 입니다.

      보통 file alignment 는 0x200, 0x400, 0x1000 이 많이 쓰입니다.
      Section Alignment 는 0x1000 이 많이 쓰이구요.

      감사합니다.

  3. tanosi 2010.04.06 01:02 신고 댓글주소 | 수정 | 삭제 | 댓글

    머리속에 '파밧' 하고 개념이 잘 서는 포스팅을 보고 놀랐습니다.
    출간 준비중 이라고 하시니 기대됩니다~~
    최근 나온 리버스엔지니어링 역분석 구조와 원리 라는책을 보고 PE 에서 딱 막혔는데, 다시한번 달리게 되었습니다.
    많은 도움이 되고 있습니다~~

  4. 할라 2010.04.15 22:29 신고 댓글주소 | 수정 | 삭제 | 댓글

    리버싱 입문에 들어서는 할라라고 합니다^^;;
    요즘 리버싱이 재밌어지려고 하는 찰라에
    많은걸 학습해야 하는걸 깨닫고 관련된 자료를 찾아다니는 찰나에
    이런 좋은 글을 발견했네요 ㅎㅎ
    괜찮으시면 이 글을 토대로 제가 따로 글을 올려놓고 싶은데
    괜찮나요?^^;

    아무튼 여기서 많은 걸 배워갑니다^^

    • reversecore 2010.04.15 23:42 신고 댓글주소 | 수정 | 삭제

      할라님, 안녕하세요. ^^

      저의 저작권 정책만 지켜주시면
      여기서 배우신 내용을 가지고 본인의 글을 쓰시는건
      아무런 문제가 없습니다.

      감사합니다.

  5. 할라 2010.04.19 16:39 신고 댓글주소 | 수정 | 삭제 | 댓글

    음.. 저기 VA/RVA 부분에서요
    VA와 RVA에 관계를 보시면
    RVA + ImageBase = VA라고 되어있는데요
    +가 맞나요??? -아닌가요???

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

      + 가 맞습니다.

      RVA 의 의미가 Relative Virtual Address (to ImageBase) 이거든요.

      따라서 VA = RVA + ImageBase 이고,
      RVA = VA - ImageBase 이지요.

      감사합니다.

  6. kvirus 2010.05.27 10:39 신고 댓글주소 | 수정 | 삭제 | 댓글

    책으로 출판하시는군요...
    꼭 소장하겠습니다!!!
    기대되네요 :)

  7. MyKim 2010.06.17 23:47 신고 댓글주소 | 수정 | 삭제 | 댓글

    이렇게 체계적으로 잘 정리된 내용이라니 ...
    참 좋은 내용를 제공해 주셔서 감사합니다.
    또한 박수와 격려를 보내드립니다.
    그런데 언제 책이 출간되나요 ?
    빨리 사서 공부해야 겠습니다....

    Have reverence for God, and obey his commands, because this is all that man was created for. (Ecclesia 12:13)

  8. 박두현 2010.08.09 19:02 신고 댓글주소 | 수정 | 삭제 | 댓글

    Fig2. 그림을 사용하고 싶습니다.
    프로그램 실행에 관해서 블로깅을 하고 있는데
    Fig2 그림이 너무나 필요합니다.
    물론 출처는 남기겠습니다.

    • reversecore 2010.08.10 12:43 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      가져가시기 전에 먼저 문의해 주셔서 감사합니다. ^^

      이글의 제일 처음 댓글을 보시면 showtime 님께서 동일한 문의를 주셨는데요.

      그쪽에 제가 답변 달아놓은 대로만 지켜주시면 사용하실 수 있도록 해드리겠습니다.

      감사합니다.

  9. maruhun 2010.08.11 23:55 신고 댓글주소 | 수정 | 삭제 | 댓글

    pe구조에 궁금한점있어서 문의드립니다.

    인터넷으로찾아보았지만 시원한 대답이 없어서 이렇게문의 드립니다.

    페이징 파일이란 가상메모리라고 알고있습니다. 그럼 페이지는 CPU가 엑세스하는 메모리인가요?

    그리고 공부를 하다보니깐 처음에 CPU가 메모리를 vitual address를 발생시킨다고 하는데

    cpu는 가상주소밖에 모르는건가요?

    실제 주소 번지(가상주소)=이미지 로드 시작 번지+RVA 인데

    실제 주소 번지가(가상주소)<----메모리 주소이면 실제주소는 어딘지..

    VAS,MMF,VMM 등 찾아보고 연습장에 내용이랑 찾아놓았지만 연결이 되지를 않습니다..

    간단하게 연결만 시켜주셨으면 감사하겠습니다..

    • reversecore 2010.08.15 22:48 신고 댓글주소 | 수정 | 삭제

      CPU 는 하드웨어이고 컴퓨터의 두뇌 역할이지요.
      따라서 메모리를 직접 access 할 수 있습니다.

      내부적으로 치열하게 물리메모리<->가상메모리 변환을 통해 정확한 주소에 접근할 수 있습니다.

      물리주소에 접근하는걸 궁금하시는게 아니라면...

      위의 설명은 이렇게 이해하시면 됩니다.

      PE 파일이 실행되면 프로세스가 생성되는데,
      프로세스는 4 GB 의 가상메모리를 할당 받고 그중 하위 2GB 를 유저영역으로 사용할 수 있습니다.

      이 프로세스 가상 메모리 주소를 표시할 때 VA 로 나타냅니다.

      음... 어떻게 이해가 되시는지요...

      궁금하신 내용은 또 질문 올려주세요~

      감사합니다.

  10. pe공부 2011.01.01 10:52 신고 댓글주소 | 수정 | 삭제 | 댓글

    리버스코어님 제가 PE포맷에 대해서 공부하다가 의문점이 생겨서 질문드립니다.
    ------------------------------------------------------------------------------------------------
    현재 PE파일포맷형식이 A라고 하면(현재 PE구조)
    PE파일포맷형식을 다른 형식인 B로 만들어도 되는가?(물론,프로그램이 실행해야할 필수정보는 있다.)
    WIN32 OS들은 모두 B형식을 인식할수있다고 치자

    • reversecore 2011.01.03 01:04 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      네, 말씀하신 대로 가능합니다.

      packer 류의 프로그램이 바로 정확하게 그 일을 수행하고 있지요.

      감사합니다.

  11. 금빛 star 2011.04.27 11:54 신고 댓글주소 | 수정 | 삭제 | 댓글

    좋은 자료에 감사드립니다.
    근데. 가상메모리->물리메모리변환은 누가 해주는가요? OS인가요?
    무식한넘. 감히 질문합니다요.

  12. wjsdlfdhkd 2011.05.01 20:14 신고 댓글주소 | 수정 | 삭제 | 댓글

    wjsdlfdhkd@nate.com 네이트온친추좀 해주세요 ㅠ

  13. PE 2011.05.18 11:43 신고 댓글주소 | 수정 | 삭제 | 댓글

    제가 PE관련하여 발표를 하게 되었습니다.

    ppt자료에 그림을 쓰고 싶습니다.

    사용해도 되는지 먼저 문의 드립니다.

  14. cropin 2011.05.30 01:28 신고 댓글주소 | 수정 | 삭제 | 댓글

    제가 리버싱입문한지 얼마안되서 죄송한데
    offset과 address의 차이점이 무엇인지 설명해주실수 있나요?...

    • reversecore 2011.06.03 22:22 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      둘 다 거의 같은 개념인데요... 보통 리버싱에서는 아래와 같이 사용됩니다.

      offset 은 어떤 기준점에서 떨어진 거리(변위)를 나타내며 주로 파일내의 위치를 표현할 때 사용합니다. (상대 거리를 나타낼 때 주로 사용됨)

      address 는 주로 메모리의 절대 주소를 나타낼 때 사용됩니다. (절대 주소를 나타낼 때 주로 사용됨)

      감사합니다.

  15. 금강이 2011.09.19 16:43 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 요즘은 하루하루 방문해서 조금씩 공부하고 있습니다. ㅎㅎ
    사내에서 스터디 같은걸 하는데 PE 관련 교육을 하려고 합니다. 그림2번 PPT 파일에 사용해도 될지요?

  16. 2012.02.03 11:06 신고 댓글주소 | 수정 | 삭제 | 댓글

    조금 햇갈리는게 있는데 각 널패딩은 하위주소(그림에서 윗부분)에 종속되는건가요? 그러니까 각각의 헤더 및 섹션들의 널패딩은 그 아래부분인건가요?

  17. 라마르틴 2013.02.26 17:00 신고 댓글주소 | 수정 | 삭제 | 댓글

    dllinjection 먼저 읽고 pe구조 몰라서 읽었는데 역시 깔끔합니다. 그동안 몰랐던 것들이 머릿속에서 깔끔하게 정리되는 느낌.

  18. 이정민 2014.10.27 12:20 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요.
    글을 읽다가 궁금한 점이 하나 생겨서 여쭤봅니다.
    RVA 와 VA에 대해서
    DLL파일 같은 경우 이미 그 위치에 다른 파일이있다면
    재배치 과정을 통해 다른 위치로 로딩되어야 하는데
    VA주소로 되어있다면 정상적인 접근이 안된다는게 이해가 안되는데요,

    첫번째로는
    각 프로세스마다 독립된 가상메모리를 사용하는데
    어떻게 메모리 주소가 겹칠 수 있는지를 모르겠고,

    두번째로는
    RVA 주소든 VA 주소든 하나의 고유한 메모리 주소인데
    VA주소로는 왜 접근이 안되는건지 모르겠습니다.

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

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

  20. 바이낸 2018.02.09 11:51 댓글주소 | 수정 | 삭제 | 댓글

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



#0. prologue

분야를 막론하고 기술자(특히 엔지니어)들은 자신만의 작업환경과 손에 익은 도구(장비)가 있습니다.

기술자(특히 엔지니어) 란 특정 업무를 위해서 필요한 도구를 아주 능숙하게 다룰 줄 아는 사람들입니다.

같은 도구를 사용하더라도 기술자의 능력에 따라서 전혀 다른 결과를 보여줍니다.
(더 나아가서 필요한 도구를 직접 만들어 내기도 합니다.)

또한 기술자들은 각자 자신만의 도구를 가지고 있으며
한번 손에 익힌 도구를 되도록 오래 쓰고 왠만해서는 바꾸려 하지 않습니다.
(바꿀 때도 될 수 있으면 같은 회사의 후속 제품으로 바꾸려고 하지요.)

남의 도구, 남의 작업환경에서 일을 하면 아무래도 불편하다고 생각합니다.

즉, 자신만의 작업환경과 자신만의 도구가 갖춰져야 그 기술자의 진정한 실력이 100% 발휘 된다고 할 수 있습니다.



#1. Reverse Code Engineer (Reverser)

리버서들은 어떨까요?

리버서도 역시 IT 엔지니어 범주에 들어가기 때문에 위에서 언급한 일반적인 기술자의 성향과 다를 바가 없습니다.

리버싱을 하기 위한 도구의 종류만도 수십 가지가 넘으며, 각 종류별로 다양한 제품들이 존재합니다.
또한 IT 분야의 특성상 계속해서 새로운 도구가 개발되고 있습니다.

도구의 종류만도 아래와 같이 매우 많습니다. (언급하지 못한 것도 많을 것입니다.)

disassembler
debugger - PE, script, etc
development tool - assembly, C/C++, etc
editor(viewer) - text, hex, resource, retistry, string, PE, etc
monitoring tool - process, file, registry, network, message, etc
memory dump
classifier
calculator - hex, binary
compare tool - text, hex
packer/unpacker
encoder/decoder
virtual machine
decompiler - VB, Delphi, etc
emulator
...



#2. 좋은 분석 도구 선택의 5 가지 기준

아래에 ReverseCore 만의 도구 선택 기준(가이드)을 제시합니다. (참고만 하세요~)


첫째, 도구 개수를 최소화시킨다.

남들이 쓴다고 해서 기능을 알지도 못하는 도구를 잔뜩 가지고 있어봐야 도움이 되지 않습니다.
자신에게 필요한 도구만을 각 종류별로 하나씩만 사용하는 것이 좋습니다.

자신의 실력에 맞는 것만을 고르고 차츰 하나씩 늘려나가시면 됩니다.

또한 중복된 기능의 도구들은 하나로 정리하는 것이 좋습니다.


둘째, 도구는 기능이 단순하고 사용방법이 편리한 것이 좋다.

실력이 늘어날 수록 사용해야 할 도구의 개수도 늘어납니다.
기능이 단순하고 인터페이스가 직관적일 수록 사용하기에 편리합니다.

여기서 기능이 단순하다는 말은 리버싱 도구치고는 단순하다는 말입니다.
(Windows 의 계산기, 메모장 수준을 생각하시면 안됩니다. ^^)

따라서 아무리 단순한 리버싱 도구를 하나 익히는 데만도 어느 정도 시간이 필요합니다.


셋째, 기능을 철저히 익힌다.

아무리 좋은 도구라도 사용할 줄 모르면 무용지물 입니다.

자신이 이미 가지고 있는 도구에서 제공되는 기능인데도,
그걸 알지 못하고 다른 도구를 찾는 분들이 많습니다.

일단 도구를 선택한 후에는 제공되는 메뉴얼을 한번 정독해보시는 것이 좋습니다.
자주 사용하는 기능의 단축키 정도는 외워 두시면 작업이 훨씬 수월합니다.
(단축키를 잘 쓰면 확실히 자타공인 전문가처럼 느껴집니다.)


넷째, 꾸준히 업데이트 한다.

리버싱도 IT 범주에 속하기 때문에 기술 발전 속도가 매우 빠릅니다.

새로운 기술에 대응하기 위해 도구들도 빠르게 변화하기 때문에
사용하는 도구의 업데이트는 매우 중요한 일입니다.

따라서 꾸준히 업데이트를 지원하는 도구를 선택하는 것이 좋습니다.


다섯째, 도구의 핵심 동작 원리를 이해한다.

도구를 더 잘 사용하기 위해서 동작 원리를 이해하는 것이 좋습니다.
더 나아가 테스트용 프로토타입을 만들 수 있다면 금상첨화 입니다.

이 부분을 간과하시는 분들이 매우 많습니다.
하지만 높은 수준의 리버싱 실력을 쌓기 위해서는 필수적인 사항입니다.

예를 들어 debugger 의 동작원리를 이해하고 있으면,
anti-debugging 기법을 잘 회피할 수 있습니다.

원리를 이해하지 못하고 도구에만 의존하면,
간단한 트릭도 해결하지 못하고 다른 도구를 찾아 나서야 합니다.
소위 말하는 "도구의 노예"가 되는 것이지요. (이것만은 꼭 경계해야 겠습니다.)



#3. epilogue

debug.exe 라는 프로그램을 아시나요?
MS-DOS 시절부터 존재하던 16-bit debugger 입니다. (XP 에도 존재합니다.)

커맨드 창에서 debug.exe 를 실행하고 '?' 명령으로 도움말을 보겠습니다.


위에 보이는 명령어가 전부입니다.

단순하지요?

제가 아는 어떤 분이 debug.exe 로 16 bit DOS 프로그램을 분석하시는 걸 본 적이 있습니다.

뭔가를 실행시키더니 키보드를 다다다다 두들기면서 화면이 번쩍 번쩍 넘어가는데,
바로 옆에서 지켜보면서도 무슨 작업을 하는지 도저히 알 수 없었습니다.
(눈이 그분의 작업 속도를 따라가지 못한 거죠.)

처음에는 실행되는 프로그램이 debug.exe 인지 조차도 몰랐었습니다.
(전 그때 당시 이미 debug.exe 를 한달 정도 써본 경험이 있었음에도 불구하고 말이죠.)

그리고는 다 됐다면서 결과를 넘겨 주시더군요.
그때 그분이 사용하신 프로그램이 debug.exe 라는 걸 알고 충격에 휩쌓였었죠.

'저 단순한 debug.exe 만으로 이 일을 이렇게 빨리 해냈단 말인가?' 하고 말이죠.

그 이후로 제가 어떤 도구를 고를 때 나름대로의 기준을 세우게 되었습니다.

그리고 하나의 깨달음을 얻었습니다.

"평범한 도구라도 극한까지 연마하면 천하에 다시 없는 비범한 도구가 된다."

마치 무림고수(武林高手)가 수련에 수련을 거듭해서 검(劍)을 버리고
결국에는 초(草)/목(木)/죽(竹)/석(石)을 모두 검처럼 사용할 수 있듯이 말이죠.


여러분은 어떻게 생각하시나요? ^^


  1. showtime 2009.10.08 13:00 신고 댓글주소 | 수정 | 삭제 | 댓글

    잘 읽고 갑니다~ ^^ㅋ

  2. 베리굿 2009.11.07 22:20 신고 댓글주소 | 수정 | 삭제 | 댓글

    감사합니다.^^

  3. 부자봉스 2009.11.12 00:43 신고 댓글주소 | 수정 | 삭제 | 댓글

    그렇게 생각합니다 흑

  4. reverse 2011.05.24 01:06 신고 댓글주소 | 수정 | 삭제 | 댓글

    이 블로그가 정말 도움이 많이 되네요
    ~~
    꾸준히 하루에 한문제씩이라도 풀어재끼껫습니다!!

  5. 석대진 2012.09.18 17:12 신고 댓글주소 | 수정 | 삭제 | 댓글

    요즘 이상하게 리버싱이 궁금해서, 책을 선택중에 있습니다. 30일날 나온다고 하여, 그때까지 블로그를 조심씩 읽어 보려고 합니다. debug.exe하니깐 피터 노턴 아저씨의 어셈블리 책이 생각나네요.


www.google.com

tool 2009.03.06 01:17


Reverse Code Engineering 을 하는데 가장 필요한 것 2 가지만 선택하라고 한다면,
첫째는 열정 이고, 둘째는 Google 을 선택하겠습니다.

사실 무슨 일을 하던지 열정이 가장 중요하고
그 다음으로는 구체적인 작업 진행 방법에 대한 정보의 수집인데,
역시 최고의 검색엔진 google 이 가장 유용하다고 할 수 있습니다.


<Fig. 1>

국내 포탈 사이트에서 제공하는 웹 페이지 검색 기능은 좀 기초적인 수준입니다.
반면에 세계 최고의 검색 엔진 google 은 원하는 정보를 거의 정확하게 찾아줍니다.

Reverse Code Engineering 분야는 해외에서 각광받는 분야이기 때문에
초중급 리버서들에게 필요한 모든 정보들이 이미 웹 사이트에 잘 정리되어 있다고 볼 수 있습니다.
(즉, 내가 지금 하고 있는 일들은 이미 누군가가 해본 후 잘 정리까지 해뒀다고 생각하시면 됩니다.)

단 하나의 걸림돌은 외국어 (특히 영어) 인데요, 이것은 SW 엔지니어의 숙명이라고 생각합니다.
무조건 영어를 잘하기 위해서 노력하지 않으면 안됩니다.

* 요즘은 중국에서 리버싱 관련 사이트들이 굉장히 많아 졌으며, 새로운 리버싱 강국으로 떠오르고 있습니다.
  영어외에도 중국어를 잘 익혀놓는다면 리버싱에 많은 도움이 될 것입니다.


이제부터 google 을 이용해서 리버싱에 필요한 정보, 강좌, 유틸리티 등을 얻고 맘껏 공부하시기 바랍니다.

'tool' 카테고리의 다른 글

PEView.exe  (49) 2013.01.27
HxD.exe 기능 추가!  (13) 2012.06.14
리버싱 현업에서 사용되는 디버거(Debugger)들  (31) 2010.09.29
InjDll.exe – DLL Injection/Ejection 전용 도구  (112) 2010.03.15
Process Explorer - 최고의 작업 관리자  (1) 2009.05.03
www.virustotal.com  (3) 2009.03.20
www.google.com  (2) 2009.03.06
  1. ㅈㄷㄱ 2011.10.29 11:31 신고 댓글주소 | 수정 | 삭제 | 댓글

    ㅈㄷㄱ

  2. 2012.01.27 13:54 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다


HelloWorld.exex

HelloWorld.cpp




Level

초급



Content

"Hello World!" 프로그램을 디버깅 해보고 간단한 패치를 해보도록 하겠습니다.
이를 통하여 디버깅에 대한 감을 잡으실 수 있습니다.



Goal

- 기본적인 디버거 사용방법의 이해
- 간단한 Disassembly code 이해
- 간단한 프로그램 패치(patch)



Tool

Visual C++ 2008 Express Edition
OllyDbg 1.10



Hello World!

모든 C 프로그래머가 최초로 만들어 본 프로그램 Hello World! 를 최초의 디버깅 프로그램으로 결정하였습니다. SW 업계에서의 차지하는 Hello World! 의 의미, 처음 C 를 배울때의 두근거림 그리고 소스 코드의 간결함까지... 이 모든 것이 최초의 디버깅 프로그램으로써 더 할 나위 없이 딱 들어맞는군요.

자신에게 익숙한 C/C++ 개발툴을 이용하여 Hello World! 를 만들어 봅니다.
(Release 모드로 빌드하면 코드가 좀 더 간결해져서 디버깅하기 편합니다.)


<Fig. 1>




디버깅 목표


위에서 만든 HelloWorld.exe 를 실행해보면 당.연.히. "Hello World!" 메시지 박스가 출력될 것입니다. 그냥 디버깅만 하면 재미없으니까 이 문자열을 "Hello Reversing!" 으로 바꾸는걸 목표로 하겠습니다.

그러기 위해서는 먼저 HelloWorld.exe 를 디버깅하여 main() 함수내의 MessageBox() 함수 호출 코드를 찾아야 합니다.

그리고 적절히 해당 문자열 위치를 알아내어 변경시키면 되겠지요.



디버깅 시작

첨부된 HelloWorld.exe 파일을 OllyDbg.exe 로 열어보겠습니다.

* VC++ 도  소스가 있을때 어셈블리 수준의 디버깅이 가능합니다만, 일반적으로는 분석할때 소스가 없으므로 OllyDbg 같은 Win32 전문 디버거를 사용하게 됩니다.


<Fig. 2>


디버깅을 시작하기 전에 간단히
<Fig. 2> 에 보이는 OllyDbg 의 메인 화면 구성에 대해 설명드리겠습니다.

- code :
기본적으로 disassembly code 를 표시하고 각종 comment, label 을 보여주며 코드를 분석하여 loop, jump 위치 등의 정보를 표시한다.
- register : CPU register 값을 실시간으로 표시하며 특정 register 들은 수정도 가능함.
- dump : 프로세스내의 원하는 memory 주소 위치를 hex ASCII 값으로 표시하고 수정도 가능함.
- stack : ESP register 가 가리키는 프로세스 stack memory 를 실시간으로 표시하고 수정도 가능함.

디버거가 멈춘 곳은
EntryPoint(EP) 코드로써 HelloWorld.exe 의 실행 시작 위치(4011A1)입니다. 일단 EP 코드에서 눈에 띄는건 CALL 명령과 그 밑의 JMP 명령입니다.

Address     OP code        Disassembly       comment
----------------------------------------------------------------------------
004011A1    EB A6160000    CALL 0040284C     ; 0040284C (40284C 주소의 함수를 호출
)
004011A6    E9 A4FEFFFF    JMP 0040104F      ; 0040104F (40104F 주소로 점프)

- Address : 프로세스의 가상메모리(Virtual Address:VA) 내의 주소 위치
- OP code : OPeration code 의 줄임말로써 IA32(또는 x86) CPU 명령어
- Disassembly : OP code 를 보기쉽게 디스어셈 해준 코드
- comment : 디버거에서 추가한 주석 (옵션에 따라 약간씩 다르게 보임)

디스어셈 코드를 처음 보신 분들이라도 위 두줄의 코드는 그 의미가 명확합니다.

"40284C 주소의 함수를 호출(CALL)한 후 40104F 주소로 점프(JMP) 하라"

계속 디버깅을 진행해 보겠습니다.
목표는 우리가 작성한 main() 함수내의 MessageBox() 함수 호출을 확인하는 것입니다.



40284C
함수 따라가기

- Step Into [F7] :
하나의 OP code 실행 (CALL 명령을 만나면, 그 함수 코드 내부로 따라 들어감.)
- Step Over [F8] : 하나의 OP code 실행 (CALL 명령을 만나면, 따라 들어가지 않고 그냥 함수자체를 실행함.)
- Execute till Return [Ctrl+F9] :
함수 코드 내에서 RETN 명령어 까지 실행 (함수 탈출 목적)

F7
단축키로 함수 안으로 따라갈 수 있습니다.


<Fig. 3>


위 코드를 얼핏 보아도
Hello World! 에서는 사용된 적 없는 API 들이 호출되고 있습니다.
이곳은 분명 우리가 찾는 main() 함수가 아니겠군요.
이곳은 VC++ 에서 프로그램 실행을 위해서 추가시킨 (우리 소스코드에는 없지만) VC++ stub code 입니다. (각 컴파일러 종류/버전별로 stub code 는 틀려집니다.)
지금은 신경쓰지 말고 우리의 목표 main() 을 찾아 계속 진행해 보겠습니다.

*
주의사항 : 처음에는 Win32 API 함수들(OllyDbg comment 상에서 빨간색 API 함수 호출 부분)은 따라가지 마세요.  너무 헤멜 수 있습니다. Step over[F8] 로 넘어가세요.

4028E1
주소에 RETN 명령어가 있습니다.
(RETN
은 함수의 끝을 나타내며 함수가 호출된 명령어 바로 다음 명령어로 되돌아 갑니다.)

그곳까지 Step over [F8] 하시거나 또는 Execute till Return [Ctrl+F9]로 한방에 가봅니다.

그리고
RETN 명령어를 실행하면[F7/F8] <Fig. 2> 에서 봤던 4011A6 주소로 오게됩니다.
(C
언어에서 함수를 호출하고 리턴하면 그 다음 명령어로 오는 것을 생각하시면 됩니다.)



40104F 따라가기

4011A6
주소의 JMP 0040104F 명령을 실행해서 40104F 로 갑니다.


 <Fig. 4>


<Fig. 3> 보다 좀 더 복잡해 보이는 VC++ stub code 가 나타났습니다
실력 향상을 원하시는 분들은 이 코드에서 Step In/Over 를 사용해서 마구 헤매셔야(?) 합니다
함수 호출을 너무 깊게 따라가서 너무 힘든 상황이라고 생각되시면 아래의 ‘디버거 좀 더 능숙하게 다루기’ 내용을 참고하셔서 다시 디버깅 하시면 됩니다.

* 헤매야 하는 이유
여러분이 초보자이기 때문입니다. ^^ 각 컴파일러/버전별로 stub code 를 눈에 익혀 놓으시면실전에서는 stub code 처럼 보이는 부분은 빠르게 뛰어 넘을 수 있습니다
마치 C언어 처음 배울때 컴파일 에러를 많이 내보고, 에러 메시지를 눈에 익히는 것과 같다고 할 수 있습니다실전에서 같은 에러 메시지가 발생했을때 능숙하게 해결하기 위함이지요.



디버거 좀 더 능숙하게 다루기

- Restart [Ctrl+F2] : 다시 처음부터 디버깅 시작. (디버깅 당하는 프로세스를 종료하고 재실행 시킴.)
- Go to [Ctrl+G] : 원하는 주소를 찾아감. (코드를 확인할 때 사용. 실행되는 것은 아님.)
- Execute till Cursor [F4] : cursor 위치까지 실행함 (디버깅 하고 싶은 주소까지 바로 갈 수 있음.)
- Comment [;] : Comment 추가
- User Defined Comments : 마우스 우측 메뉴 -> Search for -> User defined Comment
- Set/Reset BreakPoint [F2] : BP 설정/해제
- Run [F9] : 실행 (BP 가 걸려있으면 그곳에서 실행이 정지됨.)

디버깅을 재실행[Ctrl+F2] 하시고 [Ctrl+G] 단축키로 40104F 주소로 갑니다
40104F 주소에 커서가 놓여져 있을텐데요, [F4] 단축키로 바로 날라갑니다
이곳(40104F) 베이스 캠프라고 부르겠습니다

베이스 캠프로 가는 또 다른 방법은 BP(Break Point) 를 설정[F2] 하고 실행[F9] 하는 것입니다디버거는 현재 실행위치서부터 프로세스를 실행하다가 BP 가 걸린 곳에서 멈추게 됩니다. (BP 없으면 그대로 계속 실행)

그럼 이번에는 [;] 단축키로 주석을 달아 볼까요?


<Fig. 5>


프로그래밍에서와 마찬가지로 디버깅에서 주석은 매우 중요한데요, 커서 위치를 잠시 다른 곳에 두고 'User Defined Comments' 메뉴를 선택하면 아래와 같이 사용자가 입력한 주석들이 표시됩니다. 


<Fig. 6>


빨간 글씨로 표시된 부분이 커서 위치입니다. 
주석 위치와 겹쳐지면 빨간 글씨만 나타납니다. 
(그래서 커서위치를 잠시 다른 곳에 두시라고 한겁니다.)

해당 주석을 더블 클릭하면 그 주소로 갈 수 있습니다
(OllyDbg 를 종료하더라도 주석은 *.udd 파일에 보관되기 때문에 다음에 다시 볼 수 있습니다.)

이어지는 강좌에서 '원하는 코드를 빨리 찾는 방법' 을 가르쳐 드릴텐데요
나중에라도 베이스 캠프에서부터 API 호출을 제외한 모든 함수 호출을 일일이 따라가[F7] 보시는걸 권장합니다

(continue)

    이전 댓글 더보기
  1. 김태완 2011.02.10 15:54 신고 댓글주소 | 수정 | 삭제 | 댓글

    글 잘읽고 갑니다 ~
    책 발간 하시면 ㅎㅎ바로 구매 하겠습니다~

  2. 리버싱 2011.02.17 19:42 신고 댓글주소 | 수정 | 삭제 | 댓글

    코어님,디스어셈블러의 방식 선형스윕과 재귀순회에 대해서 질문을 드릴게요
    --------------------------------------------------------------------------------
    책에서 선형스윕은 모듈전체를 순서대로 디스어셈하고 재귀순회는 코드 흐름에 따라서 디스어셈한다고 하던데 그러면 재귀순회라면 만약 12345바이트에서 JMP 2409606080(예를 든 주소임)명령어가 있다고하면 12346 바이트는 디스어셈이 안되나요?그러면 12346부터 2409606079까지는 뭐가 들어가나요?기계어?데이터?

    • reversecore 2011.02.18 22:15 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      문의하신 그 두가지 용어는 처음 들어 봤습니다.

      대부분의 Disassember 는 Instruction 단위로 파싱해서 보여줍니다. 예를 들어 12345 주소의 JMP XXXXXXXX 명령어는 5 byte 크기를 가지므로, 그 다음 명령어는 1234A 주소에 나타나겠지요.

      만약 12345 주소부터 파싱을 하고 싶다면... 가능합니다. 다만 명령어가 깨져 보이겠지요.

      감사합니다.

  3. 피파 2011.02.27 11:37 신고 댓글주소 | 수정 | 삭제 | 댓글

    exe나 dll을 올리디버거로 열때 올리디버거의 (1)code window 칸은 코드섹션을 디어셈하는건가요?

  4. 2011.02.28 12:15 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2011.02.28 15:44 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      GDB 코드를 보면 스택 프레임도 생성되어 있는데,
      디스어셈 표현 방식이 OllyDbg 와는 좀 틀려 보입니다.

      push 로 stack 에 넣어도 되지만 [ESP+X] 형식으로 넣어도 함수 입장에서 보면 파라미터 입니다.

      보통 VC++ 에서는 PUSH 로 파라미터를 전달합니다.
      혹시 어떤 컴파일러를 쓰셨는지요?

      감사합니다.

  5. TeamKhan 2011.03.02 16:53 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 질문이잇어서 이렇게 댓글을 남기게되는데요
    베이스캠프부터 시작해서 코드를 분석하라고하시는데
    어떻게분석을하라는거죠??그냥 막막하기만한데요...;
    이부분을 분석해보라고하시는의도를 잘모르겟내요...
    글에서는 stub code를 눈에 익혀두면 다음에 디버깅할떄 뛰어넘을수잇다고
    하시는데 왜 뛰어넘는건지??그리고 어떻게 눈에 익혀야되는지 잘모르겟내요...

    • Stares 2011.03.06 17:23 신고 댓글주소 | 수정 | 삭제

      stub code는 컴파일러가 임의로 넣는 코드입니다.
      솔찍히 분석의 필요가없는 코드이기때문에 넘기는것입니다.

      분석이란.. 어셈코드가 어떻게 진행되는지 알아보는것이 분석입니다..
      그 부분을 분석이 아니라 그 부분부터 분석해 나가라는뜻일겁니다~

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

      안녕하세요.

      Stares 님, 답변 감사드려요~ 너무 잘 설명해 주셨네요~ ^^

      그리고 눈에 익히는 법은요... 간단합니다...
      눈에 익을 때까지 계속 보는 겁니다.
      누구도 예외 없죠. 지름길은 없어요.
      꾸준히 계속 디버깅을 하다보면 실력이 쌓이는 겁니다.

      그런면에서 리버싱은 시간 투자한 만큼 실력이 늘어나는 참 정직한 분야인것 같아요.

      감사합니다.

  6. 수미르 2011.03.10 15:10 신고 댓글주소 | 수정 | 삭제 | 댓글

    리버싱 제대로 공부 해 볼려고 합니다.
    시중에 책도 나와 있지만, 여기 설명이 저한테는 더 와 닿네요.
    감사한 마음으로 강의 잘 보겠습니다.

  7. reverse 2011.03.10 21:02 신고 댓글주소 | 수정 | 삭제 | 댓글

    디버깅 모드로 생성된 파일을 열면 ollydbg로 열게되면

    Module 'HelloWorld' has entry point outside the code (as specified in the PE header). Maybe this file is self-extracting or self-modifying. Please keep it in mind when setting breakpoints!

    이러한 오류가 나는데,, 뭐 때문에 그러는건가요??

    릴리스 모드로 생성된 실행파일은 아무 에러 없이 되네요..

    • reversecore 2011.03.16 23:11 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      OllyDbg 는 파일을 로딩하고 자체적으로 스캔을 쭉~ 합니다. 그때 EP(EntryPoint) 가 첫번째 코드섹션(".text" 또는 ".CODE")의 바깥쪽에 있다면 저런 에러메시지를 출력합니다.

      하지만 디버그 모드라고 해서 EP 가 코드섹션을 벗어나지는 않을 텐데요~ PEView 나 OllyDbg 에서 직접 확인 해보시기 바랍니다.

      감사합니다.

    • reverse_totorohaha 2013.10.02 10:25 신고 댓글주소 | 수정 | 삭제

      안녕하세요 저도 오늘 Visual c++로 hello world 파일을 컴파일 하여서 OllyDbg로 파일을 열었는데 위 메세지가 뜨더라구요
      컴파일 할때 Release로 컴파일 하시면 위 메세지가 뜨지 않습니다.
      컴파일 할때 debug로 컴파일 하면 debug 폴더가... 그리고 release로 컴파일 하면 release 폴더에 exe 파일이 생성되는데 release 폴더에 생성된 exe 파일을 OllyDbg로 실행하시면 됩니다.

  8. reversing 2011.04.20 19:10 신고 댓글주소 | 수정 | 삭제 | 댓글

    올리디버그의 dump window 칸엔 원하는 메모리에 있는 아스키값이나 유니코드값으로 나타나 있다고 하셨는데,저기엔 코드영역메모리와 데이터영역메모리(문자열 같은것)모두 들어있나요?
    /아니면 저곳에 모든 섹션영역의 메모리주소가 다 들어있고 ctrl+G 로 원하는 메모리로 간다는 뜻???

  9. forrok 2011.11.08 18:34 신고 댓글주소 | 수정 | 삭제 | 댓글

    디버깅의 디 자도 모르는 사람인데요

    외람된 질문이지만 이런걸 통해서 무엇을 할 수 있죠?

  10. 처음인데... 2011.11.09 09:39 신고 댓글주소 | 수정 | 삭제 | 댓글

    처음이라서 하나도 모르겠는데 뭘 공부해야 이걸 이해할 수 있죠?
    영어로 적어 논게 무슨 말인지 하나도 모르겠네요...

    • cqlee 2012.03.20 12:06 신고 댓글주소 | 수정 | 삭제

      저도 공부를 시작하는 입장인데요
      어셈블리관련서적 어려우시더라도 읽으시면 이해하게 됩니다 ^_^ 아직 잘 모르지만 알아가는 단계라 ^_^

  11. cqlee 2012.03.20 12:06 신고 댓글주소 | 수정 | 삭제 | 댓글

    위 cpp 소스코드를 컴파일 하여 생성된 .obj 파일을 cl 명령어를 이용해서 실행파일을
    만드려고 링크 에러를 잡느랴고 고생했습니다 단 한줄 위에 써 넣으면 되는 것을 ㅋㅋㅋ
    오류를 잡을땐 기분좋습니다. ㅋㅋㅋ

    고맙습니다 ^^

  12. tjdrua74 2012.04.03 15:06 신고 댓글주소 | 수정 | 삭제 | 댓글

    좋은강좌 잘 봣습니당
    이제 막 리버싱배우는데
    리버싱책보면서 같이보면 이해가 더 잘 되겠네여
    아 그리고 보면서 질문이 생겼어요
    전 집에서 visual c++2010 쓰는데 주소값출력되는게 다른가요??

    • reversecore 2012.06.14 01:27 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      빌드/실행 환경에 따라서 주소값이 약간씩 변할 수 있습니다.

      본문에 첨부된 실행 파일을 사용하시면 보시기 편하실 것입니다.

      감사합니다.

  13. rgs 2012.06.06 12:02 신고 댓글주소 | 수정 | 삭제 | 댓글

    주인장님 요새는 무슨 일을 하고계신지요?
    블로그 다음 내용이 무척 기대됩니다.

  14. 0000 2013.01.31 00:09 신고 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요.
    리버싱도
    익스플로잇처럼
    네트워크에연결되있는
    컴퓨턴를공격할수있나요?

  15. wlfanswk 2013.02.03 21:32 신고 댓글주소 | 수정 | 삭제 | 댓글

    베이스캠프의 선정 기준은 뭔가요?
    책보면서 무작정 따라서 해보고 있는데 나중에 다른 프로그램들도 분석할 때 베이스캠프를 정해서 해야할 텐데 어떠한 기준으로 베이스캠프를 지정해야 할지 잘 모르겠습니다.

    • reversecore 2013.02.08 17:19 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      좋은 질문입니다. ^^
      약간 개인마다 차이가 있고 각자의 노하우가 필요한데요...

      그 부분을 통과하지 못하면 더 이상 진행할 수 없고...
      그곳까지 가는길이 단순 반복적이라면...
      그런 곳이 바로 베이스 캠프를 차리기에 적합한 곳이라 할 수 있습니다.

      제 경우에는 주로 디코딩 루프 전/후, 안티디버깅 코드 전/후, 동적으로 어떤 값이 변경되는 코드 등에 베이스 캠프를 차립니다.

      주로 PE Protector 같이 곳곳에 안티디버깅으로 함정을 파놓은 경우에 베이스 캠프를 차린답니다.

      감사합니다.

  16. wwww 2013.02.05 01:15 신고 댓글주소 | 수정 | 삭제 | 댓글

    주소값이 설명해주신거랑 다르게 계속 바끼는데 왜이런 걸까요..

    • reversecore 2013.02.08 17:16 신고 댓글주소 | 수정 | 삭제

      안녕하세요.

      혹시 직접 빌드해서 디버깅 하셨나요?

      Windows Vista/7 에서 Visual C++ 2008/2010/2012 등으로 빌드하면 그런 증상이 나타납니다.

      첨부된 실습 예제로 다시 해보시기 바랍니다.

      감사합니다.

  17. dksjd12 2013.03.21 12:38 신고 댓글주소 | 수정 | 삭제 | 댓글

    첨부해주신 exe파일을 디버깅했는데도 주소값이 다르게 나와요.. 왜그러는 걸까요..

  18. wnrnrxla 2013.03.23 13:59 신고 댓글주소 | 수정 | 삭제 | 댓글

    제 경우에 address부분이 4011A1 이런식으로 나오지않고
    013D284E 이렇게 나오는데 왜이런건가요?
    현재 윈도우 7 64bit 사용중입니다
    예제를 다운받아서 했습니다

    • dksjd12 2013.03.27 12:39 신고 댓글주소 | 수정 | 삭제

      안녕하세요. 저도 님과 같은 현상으로 고생했는데요.
      윈도우 7에서는 저작자분께서 해주신거와 다른결과가 나오더라구요.
      실습환경을 윈7에서 하시지마시고 가상환경에서 XP를 올린 후에 XP에서 실습을 하신다면 문제없이 진행됩니다~

    • reversecore 2013.04.01 22:16 신고 댓글주소 | 수정 | 삭제

      아, 문제를 해결 하셨군요.

      Vista/7/8 OS 의 ASLR 기능 때문인데요.
      XP 에서 테스트하시거나 ASLR 기능을 제거하고 테스트 하시면 됩니다.

      감사합니다.

    • wnrnrxla 2013.04.04 16:30 신고 댓글주소 | 수정 | 삭제

      아 그렇군요
      해결됬습니다
      감사합니다 ^^

  19. ASLR 제거방법 2013.05.18 18:20 신고 댓글주소 | 수정 | 삭제 | 댓글

    ASLR제거를어떻게하나요??

  20. 방문777 2015.05.28 12:07 댓글주소 | 수정 | 삭제 | 댓글

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





티스토리 툴바