정보공간_1

[5기 강남 김영현] PE File Format #2 본문

IT 놀이터/Elite Member Tech & Talk

[5기 강남 김영현] PE File Format #2

알 수 없는 사용자 2014. 6. 7. 05:29

PE File Format이라고 들어보셨나요?

 

이번엔 Section Header를 살펴보기 전에 NT Header Image Optional Header부터 확인해보도록 하겠습니다. PEView 상에서는 IMAGE_NT_HEADERS 하위에 IMAGE_OPTIONAL_HEADER라고 표시되고 있습니다. PE Header 구조체 중 크기가 가장 큰 만큼 우측에 출력되는 정보도 많이 있습니다.

 


Address of Entry Point Image Base가 보이시나요? Address Of Entry Point는 프로그램에서 최초로 실행되는 코드의 시작 주소(Entry Point)를 나타냅니다. 이 값을 Image Base와 더하면 0041110E가 되는데, 이를 직접 확인하기 위해 올리디버거(OllyDbg)라는 툴을 사용해보겠습니다. 이 툴은 공식사이트(http://www.ollydbg.de/)에서 무료로 다운받을 수 있습니다. 그럼 올리디버거를 실행시키고 해당 파일을 Open합니다.



올리디버거를 사용하면 프로그램의 처음 시작부터 끝까지 명령어 단위로 쫓아갈 수 있습니다. 그런데 최초로 가리키고 있는 곳이 위에서 보시다시피 0041110E입니다. 이 값은 Optional Header 내의 Address of Entry Point Image Base를 더한 값과 같습니다. 이 주소에 저장되어 있는 Opcode E9이고, Operand AD080000입니다. 이를 해석하면 친절하게도 우측에 이미 나와있는 것처럼 JMP 004119C0이 됩니다. 어디론가 Jump를 하는 코드로 프로그램을 시작하는 것입니다. 이 주소를 계속 쫓아가다 보면 각종 초기화 코드들을 실행한 다음에서야 메인 함수에 도달하게 됩니다. 이를 통해, 프로그램을 실행했을 때 메인 함수부터 실행되지 않는다는 사실을 알 수 있습니다.

 


EP(Entry Point)로부터 명령어들을 차근차근 실행하여 프로그램의 메인 함수까지 도달해 보았습니다. 현재 이 프로그램의 메인 함수는 00411550에 위치하고 있습니다. 명령어를 하나씩 실행할 때마다 우측 창에 보이는 레지스터들의 값이 변하는 모습도 확인할 수 있습니다. 올리디버거 상에서는 F2 Key Break Point를 걸 수 있고, F7 Key F8 Key Step 할 수 있습니다. F9 Key를 누르면 다음 Break Point까지 실행합니다. 하지만, 이번 포스팅의 주제는 PE File Format이기 때문에 이와 관련된 더 자세한 설명은 하지 않도록 하겠습니다.

 


다시 Optional Header로 돌아오겠습니다. Size of Image PE File이 메모리에 적재되었을 때 PE Image가 차지하는 크기를 나타내고, Size Of Header는 말 그대로 PE Header의 크기를 나타냅니다. 값을 확인해보니 PE Header 0x400의 크기를 가지고 있습니다. 그리고 아래쪽에 보시면 각종 Table들이 나열되어 있는데, 이것은 Data Directory들입니다. IMAGE_DATA_DIRECTORY 라는 구조체의 배열 항목이 나열된 것으로, Directory란 용어는 어떤 구조체의 배열이라는 뜻입니다. 차례대로 Export Table, Import Table, Resource Table… RVA Size가 보여지고 있고, 값이 없는 Directory들도 있습니다. Data Directory에 대해서는 나중에 더 자세히 살펴보도록 하겠습니다.

 

저번 포스팅에서 Section Header에 대해 간략히 언급하였습니다. Section Header는 각 Section별에 해당하는 IMAGE_SECTION_HEADER 구조체의 배열로 되어있습니다. PEView를 보시면 현재 프로그램은 .textbss, .text, .rdata, .data, .idata, .rsrc, .reloc 라는 Section들을 가지고 있는 것 같습니다.

 


IMAGE_SECTION_HEADER를 하나씩 눌러봅시다. 가장 먼저 뜨는 정보는 “Name”으로, Section의 이름을 알려줍니다. 그 다음으로는 Virtual Size, RVA, Size of Raw Data, Pointer to Raw Data가 있습니다. 먼저 앞선 두 개의 멤버는 메모리에서 해당 Section이 얼만큼의 크기를 차지하는지, 그 시작 주소는 어디인지 나타냅니다. 반면, 뒤따라 나오는 두 개의 멤버는 파일에서 해당 Section이 얼만큼의 크기를 차지하는지, 그 시작 주소는 어디인지 나타냅니다. 또한, 저번에 살펴보았던 Image File Header와 마찬가지로 “Characteristics” 정보가 있어, 해당 Section의 특성을 bit OR 형식으로 조합하여 저장하고 있습니다. 특성에 대한 자세한 상수 값들은 winnt.h를 참고하시기 바랍니다.

 


이번엔 .text Section Header를 눌렀습니다. RVA 값이 11000인 것을 볼 수 있는데, 아까 구한 Image Base 값인 400000과 더하면 00411000이 됩니다. 이는 .text Section의 주소로, 올리디버거로 확인해보겠습니다.



정말로 .text Section 00411000부터 시작하고 있습니다. 그리고 Virtual Size 382F의 값을 가지고 있네요. 마찬가지로 올리디버거를 통해 .text Section의 마지막 주소와 처음 주소의 차이를 구해 Virtual Size가 맞는지 확인해보겠습니다.

 


스크롤 바를 쭉 내려 .text Section이 끝나는 부분, 00으로 채워지기 시작하는 부분을 발견하였습니다. 이 주소는 0041482F이고, .text Section의 시작주소인 00411000과 뺄셈을 하면 382F의 값이 나오는 것을 확인할 수 있습니다. 이런 식으로 다른 값들도 확인해 볼 수 있습니다.

 

이번 포스팅에서는 PEView OllyDbg라는 툴을 동시에 사용해서 PE File의 내용을 직접 확인해보았습니다. 이러한 툴들을 사용하여 자신이 만든 프로그램이 어떠한 과정을 통해 실행되는지 살펴보는 것도 재미있을 것 같습니다. 이것으로 이번 포스팅을 마치도록 하겠습니다. 감사합니다.

 

     해당 포스팅은 『리버싱 핵심 원리(이승원 저)를 참고하여 작성하였습니다.