'공부합시다'에 해당되는 글 70건

  1. 2009.07.06 [WinAPI] BMP 파일에서 특정색상을 투명하게 출력하기
  2. 2009.07.03 [소켓프로그래밍] winsock.h 중복 재정의 에러, error C2011: 'fd_set' : 'struct' type redefinition
  3. 2009.06.10 [ARM] elf32-littlearm.lds 파일 분석
  4. 2009.06.10 [프로그래밍 일반] 링커 스크립트
  5. 2009.06.10 [ARM] Boot Code(Startup Code)
  6. 2009.06.04 [ARM7] LCD 사용자 문자 출력
  7. 2009.05.28 [ARM7] H-JTAG와 Wiggler형 JTAG 인터페이스 OK-JTAG의 사용법
  8. 2009.05.26 [프로그램] GNU ARM 을 설치하자.
  9. 2009.05.26 [프로그램] cygwin 설치법
  10. 2009.05.20 [프로그래밍 일반] PE 파일 분석 - 헤더분석 2
2009. 7. 6. 12:31

[WinAPI] BMP 파일에서 특정색상을 투명하게 출력하기

BOOL TransparentBlt(
  HDC
hdcDest,                                 //그려질 영역의 DC 핸들.
  int nXOriginDest,                              //그려질영역의 x 좌표.
  int nYOriginDest,                              //그려질영역의 y 좌표.
  int nWidthDest,                               //그려질 영역의 넓이
  int hHeightDest,                              //그려질 영역의 높이
  HDC hdcSrc,                                  //그릴 소스 DC 핸들  
  int nXOriginSrc,                               //그릴 소스의 x 좌표.
  int nYOriginSrc,                               //그릴 소스의 y 좌표.
  int nWidthSrc,                                 //그릴 소스의 넓이
  int nHeightSrc,                                //그릴 소스의 높이.
  UINT crTransparent                                 //비트맵에서 제외할 색상.RGB( ?, ?, ? )
);

 

HEADER :  #include <wingdi.h>

LINK       : Msimg32.lib , Msimg32.dll

Return Values

If the function succeeds, the return value is TRUE.

If the function fails, the return value is FALSE

 

  CDC* pDC = CDC::FromHandle( lpDrawItemStruct->hDC );
  CDC MemDC;

  MemDC.CreateCompatibleDC( pDC );

  CRect rcItem( &(lpDrawItemStruct->rcItem) );

  CBitmap bmpIcon;

  bmpIcon.LoadBitmap( IDB_YAHOO_COMBO_IMAGE );

  CBitmap* pOldbmp = (CBitmap*)MemDC.SelectObject( &bmpIcon );

  ::TransparentBlt( pDC->m_hDC,
   rcItem.left, rcItem.top, 16, 16,
   MemDC.m_hDC, 0, 0, 16, 16,
   RGB( 255, 255, 255 ) );


'공부합시다 > WinAPI, MFC' 카테고리의 다른 글

[MFC] 프로세스 접근 권한 얻기  (0) 2009.10.06
[MFC] 리스트 컨트롤 폰트 바꾸기  (0) 2009.09.30
2009. 7. 3. 10:38

[소켓프로그래밍] winsock.h 중복 재정의 에러, error C2011: 'fd_set' : 'struct' type redefinition

원인
windows.h 가 구버전의 winsock.h 를 포함하고 있기 때문이다.

해결방법
#include <windows.h > 문장 위에 아래 문장을 추가해 준다.
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI
#endif
2009. 6. 10. 21:11

[ARM] elf32-littlearm.lds 파일 분석

링커 스크립트인 elf32-littlearm.lds 파일에 대한 분석이다.

링커 스크립트는 링커에게 코드배치를 이렇게 하라고 설명하는 파일이며, 더 자세한 내용은 다음의 포스트를 참조하자.



/* elf32-littlearm.lds 파일 */

//이 부분은 arm-linux-ld가 만들어 낼 최종 결과 파일의 포맷을 나타낸다.
//즉, little endian 포맷의 파일을 생성할 것인지,
//big endian 포맷의 파일을 생성할 것인지를 결정하는 역할을 한다.

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

//이 부분은 최종 결과 파일이 동작할 CPU의 architecture를 나타낸다.
//즉, 이 파일은 ARM CPU 상에서 동작한다는 의미이다.

OUTPUT_ARCH(arm)

//최종 결과 파일의 시작 지점을 나타낸다.
//즉, 여기서 파일의 시작 지점은 reset_handler가 된다.
//reset_handler는 Cstartup.s 파일에 정의되어 있다.

ENTRY(reset_handler)

//SECTIONS{ ... }부분은 링커(arm-linux-ld)가 입력 파일들의 섹션들을
//결과 파일의 어떤 섹션들로 위치시킬지를 결정하는 역할을 한다.

SECTIONS
{
    .text : {          //프로그램의 코드영역
        _stext = .;      //코드영역의 시작주소를 makefile로 부터 가져온다.
        *(.text)        //모든 입력 파일들(*)의 .text 섹션을 결과 파일의 .text 섹션에 위치시킨다.
        *(.rodata)      //readonly 파일들을 .text 섹션에 위치시킨다.
        *(.rodata*)    //그외 모든 데이터들을 .text 섹션에 위치시킨다.
        . = ALIGN(4);  //현재의 위치에서 4 바이트 단위로 놓겠다는 의미이다.
                                 // 즉, 4바이트 단위로 메모리를 정렬한다.

         _etext = . ;    //코드영역의 끝을 현재 위치포인트로 하겠다는 의미이다.
    }

    //데이터 영역
     //코드영역 시작주소부터 코드영역의 크기만큼 더한다음 주소 즉, 코드영역 다음 주소
    .data : AT ( ADDR (.text) + SIZEOF (.text) ) { 
        _sdata = .;
        *(.vectors)    //모든 입력 파일의 벡터 테이블을 데이터 영역에 포함
        *(.data)       //모든 입력 파일의 초기화 된 데이터를 데이터 영역에 포함
        _edata = .;
    }

     //bss 영역
     //noload = 적재하지 않는다.
    .bss (NOLOAD) : {
        . = ALIGN(4);
        _sbss = .;
        *(.bss)         //모든 입력 파일의 bss영역을 출력파일의 bss영역에 포함한다.
        _ebss = .;
    }
}
end = .;


2009. 6. 10. 20:34

[프로그래밍 일반] 링커 스크립트

일반적으로 대부분의 컴파일러는 다음의 과정을 거쳐 소스파일을 실행파일로 컴파일한다.


간단하게 얘기하면 소스 파일 -> 컴파일러 -> 오브젝트 파일 ->링커 -> 실행파일 -> 로더 -> 실행 의 순으로 진행된다고 볼 수 있다.

여기서 링크과정을 살펴보면 그림에는 없지만, 모든 링크 과정에는 링커 스크립트가 조정을 한다. 오늘은 이 링커 스크립트에 대해 알아보자.

링커 스크립트

링커 스트립트는 링커 명령 언어로 쓰여진다.

링커는 항상 링커 스크립트를 사용한다. 직접 제공하지 않으면 링커는 링커 실행파일에 컴파일된 기본 스크립트를 사용한다.

링커 스크립트의 주목적은 입력파일의 섹션이 어떻게 출력파일로 대응하는지와 출력파일의 메모리 상태를 어떻게 조정하는지를 지정하는 것이다. 대부분의 링커 스크립트는 이것으로 충분하다.

링커 스크립트 형식

링커 스크립트는 일련의 명령어로 이루어져 있다. 각 명령어는 키워드로 뒤에 아규먼트를 가지거나, 심볼에 할당될 수 있다. ';'으로 각 명령어를 구분한다. 공백은 일반적으로 무시된다.

파일이나 형식 이름과 같은 문자열은 보통 직접 입력한다. 파일명이 보통 파일명을 구분하는 ',' 같은 문자를 포함한다면 파일명을 쌍따옴표 안에 두어야 한다. 파일명에 쌍따옴표를 사용할 수는 없다.

링커 스크립트는 C와 같이 '/*' 와 '*/'로 둘러싸인 주석을 포함할 수 있다.

링커 스크립트의 간단한 예

많은 링커 스크립트는 매우 간단하다.

가장 간단한 링커 스크립트는 'SECTIONS'이라는 단 하나의 명령어를 가진다. 'SECTIONS' 명령어는 강력한 명령어이며, 'SECTIONS' 명령어는 출력파일의 메모리 구조를 기술한다.

다음의 예는 프로그램이 코드, 초기화된 자료, 초기화되지 않은 자료로만 이루어진다고 가정했을 때의 링커 스크립트이다. 또한, 입력파일에도 이 섹션들만이 나온다고 가정한 스크립트이다. 그리고, 코드가 주소 0x10000에서 로드되고 자료는 0x8000000에서 시작한다고 가정하자.

SECTIONS
{
    . = 0x10000;
    .text : {*(.text)}
    . = 0x8000000;
    .data : {*(.data)}
    .bss : {*(.bss)}
}

SECTIONS 명령어는 다음에 오는 대괄호로 묶인 일련의 심볼 할당과 출력 섹션 명이 나온다.

특별한 변수인 '.' 은 항상 위치 카운터를 저장한다. '.'에 값을 대입하면 위치 카운터가 변경된다. 그래서 출력 섹션에 공백을 만들 수 있다. 위치 카운터는 절대로 뒤로 움직이지 않는다.

.text : {*(.text)} 줄을 살펴보면, 출력 섹션 '.text'가 정의될 때 현재 위치 카운터의 값으로 주소를 정한다. 현재 위치 카운터의 값은 윗줄에서 0x10000이므로 '.text' 섹션의 주소는 0x10000이 된다. '.text' 섹션은 어셈을 공부했다면 많이 본 코드 영역을 의미한다.

마찬가지로 아래쪽에 데이터 영역과 bss 영역의 주소를 정해주는 문장을 볼 수 있다.



링커 스크립트에 대한 더 자세한 문법을 알고 싶다면 아래의 페이지를 참조하자. 위의 내용은 아래 페이지의 도입부를 간략하게 정리한 것이다.

http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html
2009. 6. 10. 11:36

[ARM] Boot Code(Startup Code)

1. Reset (장치를 켜거나 리셋)
2. 0번지로 점프
3. reset handler
4. low level init 호출(하드웨어 초기화)
5. remap
6. 메모리 초기화
    6-1. Stack 영역 초기화
    6-2. Data 영역 초기화
    6-3. BSS 영역 초기화
//여기까지 boot code(or startup code)

7. main 호출
2009. 6. 4. 15:13

[ARM7] LCD 사용자 문자 출력

텍스트형 LCD 모듈에서 사용자 정의 문자를 보내는 방법에 대해 알아보자.

보통 기본적으로 텍스트형 LCD 모듈은 LCD 콘트롤러를 내장하고 있어서, 이것의 내부에 있는 CG ROM안에는 192개(일본어형) 또는 240개(유럽형)의 기본 문자 폰트를 가지고 있어서 사용자가 화면에 표시하고자 하는 문자 코드를 DD RAM에 써주기만 하면 이것에 해당하는 폰트가 자동으로 CG ROM에서 찾아져서 화면에 디스플레이 된다.

그러나 한글과 같이 정의되어 있지 않은 문자를 출력하려면, 사용자가 문자를 정의해서 사용해야 하는데, 이와 같이 사용자 정의문자를 지정하는데 사용하는 메모리가 CG RAM이며, 이 CG RAM에는 5x7 도트를 사용하는 경우에 최대 8문자까지 정의할 수 있고, 5x10도트의 경우에는 최대 4문자까지 정의하여 사용할 수 있다.

<5x8에서 '가'를 그린 모습>


LCD 모듈의 CG RAM에 사용자 정의문자를 작성해 넣는 방법은 다음과 같다.

1. 가로가 5칸이므로 한바이트로 표현이 가능하다. 따라서 한줄을 한바이트로 정의하며, 총 8줄이므로 한글자는 8바이트가 된다.

2. 토트를 검게 표시하고 싶은 부분을 1로 하고 공백부분은 0으로 한다. 일반적으로 8번째 줄은 커서를 표시하기 위해 비우기도 한다.

3. 위의 '가'란 문자를 정의한다고 하면, 5칸으므로 8비트(1바이트)로 표시할 때 상위 3비트는 필요가 없다. 그리고 검게 표시된 부분은 2번째 비트이다. 따라서 이것을 2진수로 나타내면 xxx00010(x는 don't care)이고, 16진수로 나타내면 0x02가 된다.(don't care를 0이라 둔다면)

4. 이런식으로 8줄을 모두 정의한다.

5. 8줄을 묶어주기 위해 배열을 선언하는게 편하다. 소스로 나타내면 다음과 같을 것이다.

unsigned char font[8] = {0x02, 0x1A, 0x0A, 0x0A, 0x0B, 0x0A, 0x0A, 0x02};

이를 활용하여 한가인이라는 문자를 출력하는 프로그램을 작성하였다. (소스 일부임)

int main()
{
  InitLCD();
  SetFont();
  SendToLCD(10x80); //set ddram
  SendToLCD(00x03);  //하트
  SendToLCD(00x00);  //한
  SendToLCD(00x01);  //가
  SendToLCD(00x02);  //인
  SendToLCD(00x03);

  return 0;
}

void InitLCD()
{
  //LCD 제어 시작
  *PIO_PER = DBUS|RS|RW|EN|BL;        //병렬 입출력 활성화
  *PIO_OER = DBUS|RS|RW|EN|BL;       //  출력 활성화

  SendToLCD(10x3c);  //function set
  SendToLCD(10x06);  //Entry mode
  SendToLCD(10x1C);  //cursor shift
  SendToLCD(10x0C);  //Display On
  SendToLCD(10x01);  //Clear Display
  return;
}


void SetFont()
{
  int i;
  unsigned char font[] = {0x0a, 0x1e, 0x020x1b, 0x1a, 0x020x100x1e,  //한
                0x020x1a, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x02,   //가
                0x090x150x150x150x090x000x080x0f,  //인
                0x000x0a, 0x1b, 0x1f, 0x1f, 0x0e, 0x040x00};   //하트

  SendToLCD(10x40);
  for(i=0; i<32; ++i)
  {
    SendToLCD(0, font[i]);
  }
  return;
}



결과 화면이다.


2009. 5. 28. 18:18

[ARM7] H-JTAG와 Wiggler형 JTAG 인터페이스 OK-JTAG의 사용법

 ARM 개발에 있어 사용자 프로그램의 다운로드와 프로그램의 디버깅을 할 수 있게 해주는 JTAG 에뮬레이터는 수십만원에서 수백만원에 이르는 비교적 고가의 개발장비이며, 여기에 사용되는 디버거 소프트웨어가 수백만원이 하므로 아마추어들은 사용하기가 어렵다.

 이러한 이유로 좀더 저렴하고 쉽게 사용할 수 있는 Wiggler라는 저가형 간이 JTAG 인터페이스가 있는데 이를 개발한 Macraigor사(http://www.macraigor.com)는 현재 더 이상 이러한 Wiggler JTAG 인터페이스를 지원하지 않고 있어, 여러 가지의 변형된 Wiggler 호환형 제품들만이 널리 사용되고 있다.

<Wiggler 호환형 JTAG 인터페이스>

 이러한 Wiggler 호환형의 JTAG 인터페이스를 사용하려면 하드웨어뿐만 아니라 이를 구동할 수 있는 운용 소프트웨어도 무료로 쉽게 구할 수 있어야 한다. 아마추어도 쉽게 구할 수 있는 무료 소프트웨어로 H-JTAG이란 프로그램이 널리 알려져 있다.

 H-JTAG은 Wiggler 호환의 JTAG 인터페이스와 함께 사용할 수 있는 디버그 도우미(debug agent)용의 무료 소프트웨어로서 http://www.hjtag.com 사이트에서 제공하는 H-JTAG Server 및 H-Flasher 프로그램으로 구성된다.
홈페이지에 접속해서 프로그램을 설치하자.



빨간색으로 네모친 곳을 클릭하여 H-JTAG을 다운받고 설치파일을 실행하자.




약관에 동의하고 다음


설치될 경로를 지정해주고 다음



설치가 완료되었다.


H-JTAG Server 프로그램은 내부적으로 H-Flasher 프로그램과 연계하여 ARM 소자에 내장되거나 또는 외부에 확장 인터페이스된 플래쉬 메모리를 프로그래밍하는 기능을 가진다. 이를 이용하면 SAM-BA GUI프로그램을 대신하여 사용자 프로그램을 다운로드할 수 있다.


H-JTAG Server를 실행시켜보자.


현재 장치가 연결되어 있지 않으므로 UNKNOWN이라고 표시된다. JTAG 인터페이스로 PC와 ARM을 연결하자.


사진에는 잘 보이지 않지만 JTAG에 보면 선마다 GND, TDI, TDO, TCK, TMS, VCC가 써있으며 거기에 맞게 ARM의 해당핀에 연결해주면 된다.

연결해준뒤 H-JTAG Server의 Operations-Detect Target을 해주면 장치를 읽게 되고 다음과 같이 화면이 바뀌게 된다.

<ARM CORE가 감지된 모습>

다시 H-JTAG Server에서 Setting - LPT JTAG Setting(패러럴포트이면) 메뉴를 선택하여 아래의 그림과 같이 설정한다.



H-JTAG Server에서 Setting - LPT Port Setting 메뉴를 선택하여 아래 그림과 같이 설정해준다.



Setting - Target Setting 메뉴으로 가서 아래 그림과 같이 설정한다.



Option 메뉴를 선택하여 아래 그림과 같이 설정한다.


Flasher - Start H-Flasher 메뉴를 선택하여 H-Flasher프로그램을 실행하여 아래와 같이 설정해주자.


<자신의 해당하는 모델을 선택하자>

<설정을 저장한다.>



이제 사용자 프로그램을 SAM-BA가 아닌 JTAG을 이용해서 전송해보자.

H-Flasher를 살펴보면 좌측에 Programing이란 항목이 있다.


 Type에는 Hexa 파일을 전송할건지, 바이너리 파일을 전송할건지등을 결정할 수 있다. 보낼 데이터의 타입에 맞게 설정해준다.
 Dst Addr은 Flash Base Address로 설정해준다.
 Src File에는 ...을 클릭해 소스를 선택해주자. 경로를 직접 써줘도 된다.

그리고 Type 옆에 있는 Program을 클릭하면 전송을 하게 된다.

<이로서 전송완료>


SAM-BA를 이용할 경우 TST로 10초간 리셋을 시켜줘야하는 불편함이 있지만 JTAG을 이용하는 경우 바로 전송이 가능하다는 장점이 있으므로 자주 애용하자.
2009. 5. 26. 11:38

[프로그램] GNU ARM 을 설치하자.

GNU ARM 홈페이지에 접속한다.

http://www.gnuarm.org/


홈페이지 상단 프레임에 FILES를 선택하면 GNUARM 바이너리 파일과 소스 파일들이 있다. 소스파일은 컴파일을 해줘야하니 편하게 바이너리 파일(실행파일)을 받자.


설치파일을 실행시키자.


다음을 클릭


약관에 동의하고 다음


설치할 경로를 지정해주고 다음


설치할 패키지를 선택해주고 다음(full installation 권장)


시작메뉴에 등록될 이름을 선택하고 다음(시작메뉴에 등록되지 않길 원한다면 아래 체크박스 체크)


GNU ARM Tool-chain을 Cygwin에서 사용할 것이므로, “Install Cygwin DLLs”는 선택하선 말 것. 만약 이것을 선택했다면, GNU ARM Uninstall을 실행한 후 처음부터 다시 설치한다.

이제 다음부터 나오는 대화 상자들은 계속 진행하도록 하고 “Install” 여부를 묻는 대화 상자가 나오면 “Install”을 선택한다. 인스톨을 다 마치면, “Finish”를 묻는 대화 상자가 나오는데, 그냥 “Finish”를 눌러 설치를 끝내면 된다.

제대로 설치 됐는지를 확인하기 위해 cygwin을 실행시켜보자.


$ arm-elf-gcc -v 라고 쳐서 버전이 제대로 나온다면 설치 완료.


만약 아래와 같은 화면이 나온다면,
Cygwin에  C:\cygwin\bin 안의 cywin1.dll 파일을 C:\GNUARM\bin 로 복사하고
다시 명령을 실행하면 된다.


2009. 5. 26. 10:03

[프로그램] cygwin 설치법

cygwin이란 윈도우에서 gnu 프로그램들을 사용할 수 있게 해주는 한마디로 윈도우 상에서 돌아가는 리눅스라고 할 수 있다. 오늘은 이 cygwin의 설치법에 대해 알아보자.

먼저 cygwin 홈페이지에 접속한다.



홈페이지 중간쯤에 보면 위의 그림과 같은 곳을 클릭하면 설치 파일을 받을 수 있다. 실행시키자.

다음을 누르자.

설치 파일을 다운로드 받아야 하므로 인터넷으로부터 설치를 선택하고 다음.

설치될 경로를 지정해주고 다음(디폴트는 c:\cygwin).

설치 패키지를 임시저장할 장소를 지정해주고 다음.

인터넷 설정에 맞게 선택하고(일반적으로 직접연결하면 됨) 다음.

파일을 받을 서버를 선택하고 다음(daum이 가장 빠른듯).

다운로드가 끝나면 gnu 유틸리티를 어떤걸 설치할지 묻는데 기본적으로 모두 skip으로 되있다.

+Devel을 펼쳐보면 gcc나 cvs같은 개발도구들이 있고 +Editors를 펼쳐보면 vi와 emacs같은 편집기들이 있다.
원하는 패키지가 있다면 Default를 한번 클릭하면 install로 바뀐다(몇 초정도 걸릴 수도 있다.)
인스톨할 패키지를 선택해주고 다음.


패키지를 다운 받는다. (앞에서 모두 선택했다면 1시간 정도 걸린다.)


설치가 모두 끝났다. vi와 gcc가 제대로 설치됐는지 확인해보면 끝.


윈도우의 C드라이브를 찾으려면 /cygdrive에 가면 볼수 있다.

2009. 5. 20. 09:20

[프로그래밍 일반] PE 파일 분석 - 헤더분석

원문 : kkamagui의 프로그래밍 작업실 http://kkamagui.springnote.com/pages/401262

개요

 윈도우 시스템 프로그래밍한다는 사람치고 PE 파일에 대해서 모르는 사람은 아마 거의 없을 것이다. 윈도우 실행 파일 및 DLL, 그리고 드라이버 파일까지도 PE 파일 형태를 따르고 있으니 뭘 해도 따라다니는게 이 PE(Portable Executable) 파일 포맷이니까 말이다. PE 파일 포맷은 크게 헤더, 섹션, 데이터의 세부분으로 나뉘는데 기존 DOS 시절 사용하던 COFF(Common Object File Format)과 거의 비슷한 구조를 가지며 기본 뼈대에서 확장된 듯한 형태를 가진다.

 PE 실행파일이 가지고 있는 헤더를 분석함으로써 실제 데이터가 있는 위치를 파일에서 찾고 해당 영역을 분석할 수 있다.

 PE 파일 구조에 대해서 자세히 알아보기 전에 참고할 좋은 프로그램 몇가지를 소개한다.

  • PE Explorer :  유료다. ㅡ,.ㅡ;;; 공짜 버전도 있는데 30일 한정이라서... 그렇지만 강력하다 @0@)/~
  • PE Browser : 공짜다. 하지만 역시 뭔가 부족하다는 거... 그냥 쓰기에는 괜찮다.

 

 앞으로 Relative Virtual Address(RVA)라는 용어가 많이 나올텐데, 잠깐 알아보자.

 RVA 는 실행파일이 메모리에 로드되었을 때, 그 시작 주소를 0으로 생각하고 계산하는 주소이다. 즉 RVA의 값이 0x40 이고 실행파일이 로드되었을 때, 그 시작위치가 0x1000 이라면 실제 그 영역이 메모리에 로드되었을 때 위치는 0x1040이 된다. 실행파일 시작 위치를 0으로 하는 상대적 주소라는 것만 알면 같단하다. 뒤에 설명하면서 계속 사용될 용어이므로 알아두자.


PE 파일 포맷 전체 구조

 PE 파일 포맷은 크게 아래와 같이 구성된다.

 

 위에서 보는 것과 같이 크게는 붉은 색 부분과  푸른색 부분으로 나눌 수 있다. 붉은 색 부분은 헤더나 데이터가 위치하는 영역의 속성과 크기 등등을 나타내는 정보이고, 푸른 색 부분은 실제 데이터들이 위치하는 영역을 나타낸다.

 

  • IMAGE_DOS_HEADER : PE 파일의 처음에 위치하며 뒷부분에 DOS에서 실행했을 때,  에러 메시지(This program cannot be run in DOS mode)를 표시하는 스텁(Stub) 코드를 포함하고 있음. MAGIC Number와 다음에 오는 IMAGE_NT_HEADER의 위치를 표시
  • IMAGE_NT_HEADER : PE 파일 포맷에 대한 정보를 포함. 아래의 두 부분으로 구성 
    • IMAGE_FILE_HEADER :  Section의 수 및 속성과 같은 정보 포함
    • IMAGE_OPTIONAL_HEADER : PE 파일에 대한 속성 또는 이미지 베이스와 같은 정보 포함
      • Data Directory : 어떤 영역의 Virtual Address와 Size 정보를 포함
  • IMAGE_SECTION_HEADER : 섹션에 대한 실질적인 정보를 포함
  • Section(섹션) : 실제 데이터가 위치하는 영역

 

 각 영역에 대해 세부적으로 알아보자.

 

IMAGE_DOS_HEADER

 IMAGE_DOS_HEADER는 PE 실행파일 첫부분에 위치하며 아래와 같이 WinNT.h에 정의되어 있다.

      #define IMAGE_DOS_SIGNATURE                 0x4D5A      // MZ
      #define IMAGE_OS2_SIGNATURE                 0x4E45      // NE
      #define IMAGE_OS2_SIGNATURE_LE              0x4C45      // LE
      #define IMAGE_NT_SIGNATURE                  0x50450000  // PE00
      typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
          WORD   e_magic;                     // Magic number <MZ>
          WORD   e_cblp;                      // Bytes on last page of file
          WORD   e_cp;                        // Pages in file
          WORD   e_crlc;                      // Relocations
          WORD   e_cparhdr;                   // Size of header in paragraphs
          WORD   e_minalloc;                  // Minimum extra paragraphs needed
          WORD   e_maxalloc;                  // Maximum extra paragraphs needed
          WORD   e_ss;                        // Initial (relative) SS value
          WORD   e_sp;                        // Initial SP value
          WORD   e_csum;                      // Checksum
          WORD   e_ip;                        // Initial IP value
          WORD   e_cs;                        // Initial (relative) CS value
          WORD   e_lfarlc;                    // File address of relocation table
          WORD   e_ovno;                      // Overlay number
          WORD   e_res[4];                    // Reserved words
          WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
          WORD   e_oeminfo;                   // OEM information; e_oemid specific
          WORD   e_res2[10];                  // Reserved words
          LONG   e_lfanew;                    // File address of new exe header
        } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

  크게 주의해서 볼 부분은 실행파일인지 판단하는데 사용되는 e_magic 부분과 다음에 오는 IMAGE_NT_HEADER의 위치를 표시해 주는 e_lfanew 부분이다. 다른 부분은 크게 중요한 정보를 가지고 있지 않으니 일단 패스~


IMAGE_NT_HEADER

 IMAGE_NT_HEADER는 실제 PE 파일 포맷에 대한 정보를 포함하는 헤더로써 IMAGE_FILE_HEADER와 IMAGE_OPTIONAL_HEADER로 구성된다.

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature; <PE00>
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

 IMAGE_NT_HEADER는 위와 같이 Signature와 File 헤더, 그리고 Optional Header로 구성되어있다. Signature는 IMAGE_NT_SIGNATURE 로 <PE00>의 값을 가진다. 그럼 첫번째에 해당하는 FileHeader를 알아보자.

 

IMAGE_FILE_HEADER

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

 IMAGE_FILE_HEADER는 위와 같이 구성된다. 각 항목에 대해서 알아보면 아래와 같다.

  • Machine : CPU ID를 나타내는데, 간단히 보면 Intel 인지, MIPS 인지 등등의 정보가 들어있음
  • NumberOfSections : PE 파일에 포함된 총 섹션의 수를 나타냄
  • TimeDateStamp : 컴파일러 또는 링커가 파일을 생성한 시간. 1970년 1월 1일 GMT 기준으로 지나온 초
  • PointerToSymbolTable :  COFF 파일의 심볼 테이블의 오프셋을 나타냄. 없는 경우가 대부분
  • NumberOfSymbols : 심볼의 개수를 나타냄
  • SizeOfOptionalHeader : 뒤에 이어서 나오는 Optional Header의 크기를 나타낸다. 32Bit/64Bit에 따라서 그 크기가 다름
  • Characteristics : 파일의 특성

 

 Machine에 대한 매크로는 WinNT.h에 정의되어있는데 아래와 같다. Intel32 or Intel64가 대부분일테니까 위의 파란색만 보면 될것 같다.

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB             0x01c2
#define IMAGE_FILE_MACHINE_AM33              0x01d3
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP         0x01f1
#define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
#define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE           0x0520  // Infineon
#define IMAGE_FILE_MACHINE_CEF               0x0CEF
#define IMAGE_FILE_MACHINE_EBC               0x0EBC  // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R              0x9041  // M32R little-endian
#define IMAGE_FILE_MACHINE_CEE               0xC0EE

  다음은 Characteristics에 대한 부분인데 WinNT.h에 아래와 같이 정의되어있다. 역시나 파란색 부분만 보면 될 것 같다.

// Relocation info stripped from file.
#define IMAGE_FILE_RELOCS_STRIPPED      0x0001 
// File is executable  (i.e. no unresolved externel references).
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002
// Line nunbers stripped from file.
#define IMAGE_FILE_LINE_NUMS_STRIPPED    0x0004 
// Local symbols stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008
// Agressively trim working set 
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010 
// App can handle >2gb addresses
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020
// Bytes of machine word are reversed. 
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080
// 32 bit word machine. 
#define IMAGE_FILE_32BIT_MACHINE             0x0100 
// Debugging info stripped from file in .DBG file
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200 
// If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400 
// If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800 
// System File.
#define IMAGE_FILE_SYSTEM                    0x1000 
// File is a DLL.
#define IMAGE_FILE_DLL                       0x2000 
// File should only be run on a UP machine
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000 
// Bytes of machine word are reversed.
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000 

IMAGE_OPTIONAL_HEADER

  typedef struct _IMAGE_OPTIONAL_HEADER {
          //
          // Standard fields.
          //
          WORD    Magic;
          BYTE    MajorLinkerVersion;
          BYTE    MinorLinkerVersion;
          DWORD   SizeOfCode;
          DWORD   SizeOfInitializedData;
          DWORD   SizeOfUninitializedData;
          DWORD   AddressOfEntryPoint;
          DWORD   BaseOfCode;
          DWORD   BaseOfData;
          //
          // NT additional fields.
          //
          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;

 Optional Header는 꽤나 중요한 정보를 가지고 있다. 위에서 보면 알 수 있듯이 PE 파일의 전반적이 내용들에 대한 정보를 포함한다. 항목이 꽤나 많은데 중요한 정보만 추리면 아래와 같다.

  • Magic : Signature로 32Bit의 경우 0x10b를 가짐
  • SizeOfCode : 섹션 중에 IMAGE_SCN_CNT_CODE 속성을 가진 섹션들 전체의 합
  • SizeOfInitializedData : 섹션 중에 IMAGE_SCN_CNT_INITIALIZED_DATA 속성을 가진 섹션들 전체의 합
  • SizeOfUninitializedData : 섹션 중에 IMAGE_SCN_CNT_UNINITIALIZED_DATA 속성을 가진 섹션들 전체의 합
  • AddressOfEntryPoint : Entry Point의 주소. 실제 로더가 제일 먼저 실행할 코드의 시작점
  • BaseOfCode : 코드가 시작되는 상대 주소(RVA)
  • BaseOfData : 데이터가 시작되는 상대 주소(RVA)
  • ImageBase : 이미지가 로딩되는 메모리의 Base 주소. 일반적으로 실행파일의 경우 0x400000(4Mbyte) 위치에 로딩
  • SectionAlignment : 섹션이 정렬되는 크기. PE 파일 자체가 메모리 맵 파일이기 때문에 0x1000(4Kbyte) 보다 크거나 같아야 함
  • SizeOfImage : 모든 섹션들의 합. 이미지 실행을 위해 메모리를 할당해야 하는 총 크기
  • NumberOfRvaAndSizes : 뒤에 오는 DataDirectory의 개수. 무조건 16개
  • Data Directory : 총 16개가 있으며 각 항목은 특정 데이터에 대한 정보를 가지고 있음. 뒤에서 설명

 

 Magic은 아래와 같이 WinNT.h에 정의되어있다.

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC       0x107


IMAGE_DATA_DIRECTORY

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


 데이터 디렉토리는 위와 같은 구조로 이루어져있으며 IMAGE_OPTIONAL_HEADER에 총 16개가 있다. 각각에 Index에 대한 매크로는 WinNT.h에 아래와 같이 정의되어있다.

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

 이미지 디렉토리 정보는 굉장히 중요하다. 경우에 따라서 섹션이 합쳐질 수 있기 때문에 통합된 섹션에서 원하는 정보를 찾는 방법은 이미지 디렉토리에 포함된 정보를 이용하는 방법 밖에는 없다. 여러모로 많이 쓰이는 인덱스는 아래와 같은 역할을 한다.

  • IMAGE_DIRECTORY_ENTRY_EXPORT : Export 함수들에 대한 Export Table의 시작 위치와 크기를 나타냄
  • IMAGE_DIRECTORY_ENTRY_IMPORT : Import 함수들에 대한 Import Table의 시작 위치와 크기를 나타냄
  • IMAGE_DIRECTORY_ENTRY_RESOURCE : IMAGE_RESOURCE_DIRECTORY 구조체의 시작 위치를 나타냄
  • IMAGE_DIRECTORY_ENTRY_TLS : Thread Local Storage에 대한 포인터
  • IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG : IMAGE_LOAD_CONFIG_DIRECTORY 구조체애 대한 포인터
  • IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT : IMAGE_BOUND_IMPORT_DESCRIPTOR 구조체의 배열을 가리키는 포인터
  • IMAGE_DIRECTORY_ENTRY_IAT : Import Address Table의 시작 위치를 나타냄

 실제 위의 값중에서 변경하면 OS의 로더에 의해 로딩이 되지 않는 부분이 있는데, 붉은 색으로 표시된 IMAGE_DIRECTORY_ENTRY_TLSIMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 부분이다. 이 부분은 미리 로더가 읽어서 초기 작업을 실행하는 부분이라 이부분이 포함된 영역을 이상하게 조작하게되면 로더가 로딩에 실패하게 된다(PE파일 암호화 과제를 하면서 온몸으로 느꼈다.. ㅡ_ㅡ;;;). 조작을 하려면 신중히 해야될 듯 싶다.

 

 여기까지 IMAGE_NT_HEADER에 대해서 알아보았다. 일단 지금은 특정영역의 크기와 위치를 표시한다는 정도만 알아놓고 다음으로 넘어가자.

 

IMAGE_SECTION_HEADER

 PE 헤더의 뒷부분에 연속해서 IMAGE_SECTION_HEADER가 위치하게 된다. 섹션은 뒤에 올 코드나 데이터가 위치하는 영역에 대한 구체적인 정보를 포함하고 있으므로 굉장히 중요하다. 섹션의 개수는 앞서 IMAGE_FILE_HEADER에 포함된 NumberOfSections에서 얻을 수 있으며 해당 개수만큼 얻어오면 된다. IMAGE_SECTION_HEADER는 WinNT.h에 아래와 같이 정의되어있다.

#define IMAGE_SIZEOF_SHORT_NAME              8
typedef struct _IMAGE_SECTION_HEADER {
          BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
          union {
                  DWORD   PhysicalAddress;
                  DWORD   VirtualSize;
          } Misc;
          DWORD   VirtualAddress;
          DWORD   SizeOfRawData;
          DWORD   PointerToRawData;
          DWORD   PointerToRelocations;
          DWORD   PointerToLinenumbers;
          WORD    NumberOfRelocations;
          WORD    NumberOfLinenumbers;
          DWORD   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

  중요한 항목에 대한 의미는 아래와 같다.

  • VirtualSize : 실제 코드나 데이터 영역의 크기를 표시
  • VirtualAddress : 메모리에 로드되었을 때 RVA를 표시
  • SizeOfRawData : VirtualSize의 크기를 IMAGE_OPTIONAL_HEADER에 포함된 FileAlignment의 단위로 올림한 크기
  • PointerToRawData : 실제 섹션 데이터가 파일 내에 존재하는 오프셋. Virtual Address와 같을 수도 있고 다를 수도 있음
  • Characteristics : 섹션의 속성 표시. 자세한 것은 뒤를 참조

 위의 VirtualSize와 SizeOfRawData는 영역의 크기를 나타낸다는 공통점이 있으나 라운드 업된 크기와 실제 크기를 나타낸다는 차이가 있다. 만약 섹션의 크기를 조작했다면 위의 두부분 모두 손을 봐야 한다.

 Virtual Address와 Pointer To Raw Data의 값이 다를 수 있다고 했는데, 왜그럴까? 이것은 실행 파일의 크기를 줄이기 위해서이다. 만약 로드 되었을 때 크기가 0x2000 정도인 섹션이 있다고 하자. 그런데 이 섹션은 메모리의 값이 초기화 될 필요도 없고 값도 들어있지 않다면? 실행 시에 영역만 할당해주면 끝이라면? 이런 경우라면 굳이 이 섹션이 실행파일에서 영역을 가지고 있을 필요가 없다. 따라서 Virtual Address는 0이 아닌 값을 갖겠지만 파일 내에 위치를 의미하는 Pointer To Raw Data의 값은 0이 된다.

 즉 실제 파일 내에는 존재하지 않는 영역이 생김으로써 Virtual Address와 Pointer To Raw Data의 값이 달라질 수 있으며, 기타 다른 이유로도 충분히 다를 수 있다. 따라서 실행파일을 조작하기위해서는 Pointer To Raw Data의 값을 위주로 작업을 해야 한다.

 

 Characteristics는 해당 영역의 속성을 나타내는데, WinNT.h에 정의되어있고 아주 흥미로운 값을 가지고 있다.

//      IMAGE_SCN_TYPE_REG                   0x00000000  // Reserved.
//      IMAGE_SCN_TYPE_DSECT                 0x00000001  // Reserved.
//      IMAGE_SCN_TYPE_NOLOAD                0x00000002  // Reserved.
//      IMAGE_SCN_TYPE_GROUP                 0x00000004  // Reserved.
#define IMAGE_SCN_TYPE_NO_PAD                0x00000008  // Reserved.
//      IMAGE_SCN_TYPE_COPY                  0x00000010  // Reserved.
#define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.
#define IMAGE_SCN_LNK_OTHER                  0x00000100  // Reserved.
#define IMAGE_SCN_LNK_INFO                   0x00000200  // Section contains comments or some other type of information.
//      IMAGE_SCN_TYPE_OVER                  0x00000400  // Reserved.
#define IMAGE_SCN_LNK_REMOVE                 0x00000800  // Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT                 0x00001000  // Section contents comdat.
//                                           0x00002000  // Reserved.
//      IMAGE_SCN_MEM_PROTECTED - Obsolete   0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC          0x00004000  // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL                      0x00008000  // Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA                0x00008000
//      IMAGE_SCN_MEM_SYSHEAP  - Obsolete    0x00010000
#define IMAGE_SCN_MEM_PURGEABLE              0x00020000
#define IMAGE_SCN_MEM_16BIT                  0x00020000
#define IMAGE_SCN_MEM_LOCKED                 0x00040000
#define IMAGE_SCN_MEM_PRELOAD                0x00080000
#define IMAGE_SCN_ALIGN_1BYTES               0x00100000  //
#define IMAGE_SCN_ALIGN_2BYTES               0x00200000  //
#define IMAGE_SCN_ALIGN_4BYTES               0x00300000  //
#define IMAGE_SCN_ALIGN_8BYTES               0x00400000  //
#define IMAGE_SCN_ALIGN_16BYTES              0x00500000  // Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES              0x00600000  //
#define IMAGE_SCN_ALIGN_64BYTES              0x00700000  //
#define IMAGE_SCN_ALIGN_128BYTES             0x00800000  //
#define IMAGE_SCN_ALIGN_256BYTES             0x00900000  //
#define IMAGE_SCN_ALIGN_512BYTES             0x00A00000  //
#define IMAGE_SCN_ALIGN_1024BYTES            0x00B00000  //
#define IMAGE_SCN_ALIGN_2048BYTES            0x00C00000  //
#define IMAGE_SCN_ALIGN_4096BYTES            0x00D00000  //
#define IMAGE_SCN_ALIGN_8192BYTES            0x00E00000  //
// Unused                                    0x00F00000
#define IMAGE_SCN_ALIGN_MASK                 0x00F00000
#define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
#define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.

  중요한 플래그 별로 의미를 보면 아래와 같다.

  • IMAGE_SCN_CNT_CODE : 섹션에 코드가 포함되어있음. IMAGE_SCN_MEM_EXECUTE와 보통 같이 지정됨
  • IMAGE_SCN_CNT_INITIALIZED_DATA : 섹션이 초기화된 데이터를 포함하고 있음
  • IMAGE_SCN_CNT_UNINITIALIZED_DATA : 섹션이 초기화 되지 않은 데이터를 포함하고 있음
  • IMAGE_SCN_MEM_DISCARDABLE : 섹션이 버려질 수 있음. 한번 사용되고 필요없는 섹션들(relocation 데이터 같은 경우)이 이 속성을 가짐
  • IMAGE_SCN_MEM_SHARED : 섹션이 이 모듈을 사용하는 모든 프로세스에 의해서 공유될 수 있음을 의미
  • IMAGE_SCN_MEM_EXECUTE : 섹션이 실행 가능함
  • IMAGE_SCN_MEM_READ : 섹션이 읽기 가능함
  • IMAGE_SCN_MEM_WRITE : 섹션이 쓰기 가능함

 위 의 값을 보면 섹션에 대한 속성이 미리 정의되어있다는 것을 알 수 있다. 즉 데이터 섹션 같은 경우 IMAGE_SCN_MEM_READ/WRITE 속성을 가지고 있으리라 유추할 수 있고, 코드가 포함된 섹션의 경우 IMAGE_SCN_MEM_EXECUTE 속성을 가지고 있다고 유추할 수 있다.

 섹션의 경우 섹션 이름을 가지고 있는데, VC로 실행파일을 만들면 .text, .data, .idata와 같은 이름의 섹션들이 생긴다. 이름 그대로 코드, 데이터와 같은 정보가 포함된 섹션이라는 것을 알 수 있는데, 여기서 속지 말아야 할 것은 섹션 이름은 권장값이므로 섹션 이름으로 섹션이 포함하는 내용을 판단하면 안된다는 것이다. 특히 파일의 크기를 줄이는 릴리즈 옵션 같은 경우는 섹션들이 합쳐져서 하나의 섹션으로 존재하는 경우도 있기 때문에 섹션 이름을 이용해서 찾아서는 안되며 IMAGE_NT_HEADER에 있는 Data Directory의 값을 참조해서 찾도록 해야 한다.


실제 구현

 설명이 굉장히 길었다. 이제 실제로 이 헤더 정보를 분석하는 간단한 코드를 작성해 보자. RVA와 PointerOfRawData의 관계를 생각하면 약간 복잡한데, 이것은 추후에 다시 보도록 하고 헤더 정보만 표시해 보자.

 분석하는 클래스를 작성하여 간단히 헤더 정보를 추출하고 이를 화면에 표시하는 테스트 프로그램을 작성하였다.

  • PEAnalyzer.h : PE 파일을 분석하는 클래스의 헤더 파일
  • PEAnalyzer.cpp : PE 파일을 분석하는 클래스의 소스 파일
  • main.cpp : 실제 사용하는 예제

 

 아래는 실행 결과이다.


<IMAGE_DOS_HEADER의 값>


<IMAGE_SECTION_HEADER의 값>

 

마치면서...

 지금까지 PE 파일의 헤더 정보에 대해서 알아보았다. 다음에는 실행에 필요한 함수들과 외부로 노출된 함수들을 찾는 것에 대해서 알아보자.

2007/10/09 01:00:16 추가 

 아래는 Win CE용 실행파일 헤더를 분석한 부분이다. IMAGE_FILE_HEADER의 Machine 필드 부분이 다른 것을 알 수 있다. 


첨부