Herhangi bir basit problem, hakkında yeterince toplantı yapılarak, çözümsüz hale getirilebilir.

Anonim

Portable Executable Dosya Formatı

1. Giriş

    Microsoft, DOS işletim sisteminde çalıştırılabilen dosya formatı olarak MZ formatını kullanıyordu.[1] MZ formatı koruma mekanizması olmayan 8086 mimarisi için tasarlanmıştı ve korumalı modun gereksinim duyduğu özelliklere sahip değildi. Bu nedenle Microsoft 16 bit Windows sistemleriyle birlikte çalıştırılabilir dosya formatını NE (New Executable), 32 bit Windows sistemleriyle de PE (Portable Executable) ismiyle yenilemiştir. Bugün 32 bit ve 64 bit Windows sistemlerinde çalıştırılabilen dosya formatı olarak PE formatı kullanılmaktadır. PE formatı UNIX türevi sistemlerde kullanılan ELF (Executable and Linkable Format) formatı gibi, işlemciden bağımsız olan ve bölümlerden (sections) oluşan bir formattır. Microsoft .NET ortamıyla birlikte PE formatı üzerinde bu ortamın gereksinimlerinin karşılanması için çeşitli eklentiler de yapmıştır. PE formatının orijinali 32 bittir. Fakat daha sonraları 64 bit sistemler için bu formatın 64 bit versiyonu da oluşturulmuştur. PE formatının 64 bitlik bu versiyonuna PE+ da denilmektedir.

    Microsoft zaman içerisinde, çalıştırılabilen dosya formatıyla birlikte amaç dosya (object file) formatını da yeniledi. DOS zamanlarında kullandığı OMF (Object Module Format) formatını Windows 3.X sistemlerinde eklentiler yaparak kullanmıştı. 32 bit Windows sistemlerinde ise COFF (Common Object File Format) ismiyle bunu tümden değiştirdi. PE formatı ile COFF formatı birbirine çok benzemektedir. Yalnızca bölümlemede ve bölümlerin bazı alanlarında farklılıklar vardır. Bu nedenle çoğu kez teknik dokümanlarda çalıştırılabilen dosya formatı ile amaç dosya formatı PE/COFF biçiminde bir arada ele alınmaktadır

    PE formatında da pek çok dosya formatında olduğu gibi başlık kısımları vardır. Başlık kısımları yüklenecek imajın metadata bilgilerini tutar. PE formatının genel yapısı şöyledir:

Makaledeki anlatımları bir program örneğiyle somutlaştırmak istedik. Bu amaçla oluşturulan program C Programlama Dili kullanılarak prosedürel teknikle yazılmıştır. Örnek programımız PE dosyasını Windows'un bellek tabanlı dosya tekniği ile açmaktadır. Yani tüm PE imajı belleğe aktarılıp sanki dosya belli bir adresten itibaren bellekte bulunuyormuş gibi ele alınmıştır. Örnek programda karmaşıklıktan kaçınmak için oldukça sade bir yapı kullanıldığını belirtelim. Okuyucu programı kendi isteklerine göre kolayca değiştirebilir. Programın komut satırından kullanımı şöyledir:

pedisp <dosyanın yol ifadesi>

Örnek programın başlangıç kısmını aşağıda veriyoruz:

HANDLE g_hFile;
HANDLE g_hFileMapping;
LPVOID g_pImageAddr;
/* ... */

/* Function Definitions */

int _tmain(int argc, TCHAR *argv[])
{
    if (argc != 2)
        ExitUsr(_TEXT("Wrong number of arguments!"), EXIT_SUCCESS);

    g_hFile = CreateFile(
        argv[1],
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        OPEN_EXISTING,
        NULL);
      if (g_hFile == INVALID_HANDLE_VALUE)
            ExitSys(TEXT("CreateFile"), EXIT_FAILURE);
   
      g_hFileMapping = CreateFileMapping(
          g_hFile,
          NULL,
          PAGE_READONLY,
          0,
          0,
          NULL);
      if (g_hFileMapping == NULL)
          ExitSys(TEXT("CreateFileMapping"), EXIT_FAILURE);
   
      g_pImageAddr = MapViewOfFile(
          g_hFileMapping,
          FILE_MAP_READ,
          0,
          0,
          0);
      if (g_pImageAddr == NULL)
          ExitSys(TEXT("MapViewOfFile"), EXIT_FAILURE);
       
      /* .... */
}

Gördüğünüz gibi komut satırı argümanıyla alınan yol ifadesine ilişkin dosya CreateFile fonksiyonuyla açılmış ve daha sonra CreateFileMapping ve MapViewOfFile fonksiyonlarıyla bellek tabanlı hale getirilmiştir. Dosyanın belleğe yüklenme adresi g_pImageAddr isimli global göstericide saklanmıştır. Programımızda ExitUsr fonksiyonu hata mesajını, ExitSys fonksiyonu ise GetLastError ile elde ettiği hata koduna ilişkin yazıyı ekrana basarak programı sonlandırmaktadır. Ayrıca programın TEXT makroları ve <tchar.h> içerisindeki standart C makroları kullanılarak UNICODE uyumlu bir biçimde yazıldığına da dikkat ediniz.

Makale içerisindeki örneklerde kullanmak üzere küçük bir .exe dosya oluşturacağız. Bu dosyanın kaynak kodları şöyledir:

/* Sample.c */

#include <stdio.h>

int Add(int a, int b)
{
    return a + b;
}

int Multiply(int a, int b)
{
    return a + b;
}

int main(void)
{
    printf("%d\n", Add(10, 20));
    printf("%d\n", Multiply(10, 20));

    return 0;
}

Bu programı aşağıdaki gibi derleyerek 32 bit PE formatı oluşturabilirsiniz:

cl /Fe:Sample32.exe Sample.c

Elde edilen Sample32.exe dosyasının başlangıç kısmı aşağıdaki gibidir:



PE formatının başlık kısmını Microsoft'un dumpbin.exe programıyla da inceleyebilirsiniz. Bunun için dumpbin aşağıdaki gibi çalıştırılabilir:

dumpbin /HEADERS Sample32.exe

Sample32.exe için dumpbin proıgramından elde edilen çıktı şöyledir:

Microsoft (R) COFF/PE Dumper Version 11.00.50727.1
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file Sample32.exe

PE signature found

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES
             14C machine (x86)
               4 number of sections
        50574C1E time date stamp Mon Sep 17 19:13:18 2012
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
             102 characteristics
                   Executable
                   32 bit word machine

OPTIONAL HEADER VALUES
             10B magic # (PE32)
           10.00 linker version
            6C00 size of code
            5C00 size of initialized data
               0 size of uninitialized data
            12A2 entry point (004012A2)
            1000 base of code
            8000 base of data
          400000 image base (00400000 to 0040EFFF)
            1000 section alignment
             200 file alignment
            5.01 operating system version
            0.00 image version
            5.01 subsystem version
               0 Win32 version
            F000 size of image
             400 size of headers
               0 checksum
               3 subsystem (Windows CUI)
            8140 DLL characteristics
                   Dynamic base
                   NX compatible
                   Terminal Server Aware
          100000 size of stack reserve
            1000 size of stack commit
          100000 size of heap reserve
            1000 size of heap commit
               0 loader flags
              10 number of directories
               0 [       0] RVA [size] of Export Directory
            9CA4 [      28] RVA [size] of Import Directory
               0 [       0] RVA [size] of Resource Directory
               0 [       0] RVA [size] of Exception Directory
               0 [       0] RVA [size] of Certificates Directory
            E000 [     6E4] RVA [size] of Base Relocation Directory
               0 [       0] RVA [size] of Debug Directory
               0 [       0] RVA [size] of Architecture Directory
               0 [       0] RVA [size] of Global Pointer Directory
               0 [       0] RVA [size] of Thread Storage Directory
            9980 [      40] RVA [size] of Load Configuration Directory
               0 [       0] RVA [size] of Bound Import Directory
            8000 [     100] RVA [size] of Import Address Table Directory
               0 [       0] RVA [size] of Delay Import Directory
               0 [       0] RVA [size] of COM Descriptor Directory
               0 [       0] RVA [size] of Reserved Directory

SECTION HEADER #1
   .text name
    6BDA virtual size
    1000 virtual address (00401000 to 00407BD9)
    6C00 size of raw data
     400 file pointer to raw data (00000400 to 00006FFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60000020 flags
         Code
         Execute Read

SECTION HEADER #2
  .rdata name
    2262 virtual size
    8000 virtual address (00408000 to 0040A261)
    2400 size of raw data
    7000 file pointer to raw data (00007000 to 000093FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         Read Only

SECTION HEADER #3
   .data name
    2BAC virtual size
    B000 virtual address (0040B000 to 0040DBAB)
     E00 size of raw data
    9400 file pointer to raw data (00009400 to 0000A1FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000040 flags
         Initialized Data
         Read Write

SECTION HEADER #4
  .reloc name
     B96 virtual size
    E000 virtual address (0040E000 to 0040EB95)
     C00 size of raw data
    A200 file pointer to raw data (0000A200 to 0000ADFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
42000040 flags
         Initialized Data
         Discardable
         Read Only

  Summary

        3000 .data
        3000 .rdata
        1000 .reloc
        7000 .text

64 bit PE dosyası oluşturabilmek için ise 64 bit derleme yapabilen cl.exe'yi aşağıdaki gibi çalıştırabilirsiniz: (Maalesef Microsoft’un 32 bit ve 64 bit derleyicileri aynı isimli olarak birbirinden farklı dizinlerde bulunuyorlar. Bu nedenle komutu uygulamadan önce arama yolunda 64 bit cl.exe'nin bulunduğundan emin olmalısınız.)

cl /F2: Sample64.exe Sample.c

Elde edilen Sample64.exe dosyasının  baş kısmı şöyledir:

dumpbin programı ile Sample64.exe programının başlık kısımları şöyle elde edilebilir:

dumpbin /HEADERS Sample64.exe

Elde edilen çıktı da şöyledir:

Microsoft (R) COFF/PE Dumper Version 11.00.50727.1
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file Sample64.exe

PE signature found

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES
            8664 machine (x64)
               7 number of sections
        5048BFBF time date stamp Thu Sep 06 18:22:39 2012
               0 file pointer to symbol table
               0 number of symbols
              F0 size of optional header
              22 characteristics
                   Executable
                   Application can handle large (>2GB) addresses

OPTIONAL HEADER VALUES
             20B magic # (PE32+)
           10.00 linker version
            4400 size of code
            3800 size of initialized data
               0 size of uninitialized data
            1230 entry point (0000000140001230)
            1000 base of code
       140000000 image base (0000000140000000 to 000000014000DFFF)
            1000 section alignment
             200 file alignment
            5.02 operating system version
            0.00 image version
            5.02 subsystem version
               0 Win32 version
            E000 size of image
             400 size of headers
            A126 checksum
               3 subsystem (Windows CUI)
            8140 DLL characteristics
                   Dynamic base
                   NX compatible
                   Terminal Server Aware
          100000 size of stack reserve
            1000 size of stack commit
          100000 size of heap reserve
            1000 size of heap commit
               0 loader flags
              10 number of directories
               0 [       0] RVA [size] of Export Directory
            B000 [      3C] RVA [size] of Import Directory
            C000 [     1B4] RVA [size] of Resource Directory
            A000 [     270] RVA [size] of Exception Directory
               0 [       0] RVA [size] of Certificates Directory
            D000 [      34] RVA [size] of Base Relocation Directory
            6770 [      1C] RVA [size] of Debug Directory
               0 [       0] RVA [size] of Architecture Directory
               0 [       0] RVA [size] of Global Pointer Directory
               0 [       0] RVA [size] of Thread Storage Directory
               0 [       0] RVA [size] of Load Configuration Directory
               0 [       0] RVA [size] of Bound Import Directory
            B2E8 [     2A8] RVA [size] of Import Address Table Directory
               0 [       0] RVA [size] of Delay Import Directory
               0 [       0] RVA [size] of COM Descriptor Directory
               0 [       0] RVA [size] of Reserved Directory

SECTION HEADER #1
   .text name
    43E0 virtual size
    1000 virtual address (0000000140001000 to 00000001400053DF)
    4400 size of raw data
     400 file pointer to raw data (00000400 to 000047FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60000020 flags
         Code
         Execute Read

SECTION HEADER #2
  .rdata name
    209C virtual size
    6000 virtual address (0000000140006000 to 000000014000809B)
    2200 size of raw data
    4800 file pointer to raw data (00004800 to 000069FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         Read Only

  Debug Directories

        Time Type       Size      RVA  Pointer
    -------- ------ -------- -------- --------
    5048BFBF cv           43 0000736C     5B6C    Format: RSDS, {FD553AC1-48F8-43B4-9D23-51C6762FBE5C}, 2, D:\Study\C\Sample64\x64\Debug\Sample64.pdb

SECTION HEADER #3
   .data name
     770 virtual size
    9000 virtual address (0000000140009000 to 000000014000976F)
     200 size of raw data
    6A00 file pointer to raw data (00006A00 to 00006BFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000040 flags
         Initialized Data
         Read Write

SECTION HEADER #4
  .pdata name
     3D8 virtual size
    A000 virtual address (000000014000A000 to 000000014000A3D7)
     400 size of raw data
    6C00 file pointer to raw data (00006C00 to 00006FFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         Read Only

SECTION HEADER #5
  .idata name
     A8F virtual size
    B000 virtual address (000000014000B000 to 000000014000BA8E)
     C00 size of raw data
    7000 file pointer to raw data (00007000 to 00007BFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000040 flags
         Initialized Data
         Read Write

SECTION HEADER #6
   .rsrc name
     1B4 virtual size
    C000 virtual address (000000014000C000 to 000000014000C1B3)
     200 size of raw data
    7C00 file pointer to raw data (00007C00 to 00007DFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         Read Only

SECTION HEADER #7
  .reloc name
     104 virtual size
    D000 virtual address (000000014000D000 to 000000014000D103)
     200 size of raw data
    7E00 file pointer to raw data (00007E00 to 00007FFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
42000040 flags
         Initialized Data
         Discardable
         Read Only

  Summary

        1000 .data
        1000 .idata
        1000 .pdata
        3000 .rdata
        1000 .reloc
        1000 .rsrc
        5000 .text


32 bit ve 64 bit PE formatlarının çok küçük kısımları birbirlerinden farklıdır. Aksi belirtilmediği sürece bu iki formatı aynıymış gibi ele alabilirsiniz.

1.2. RVA (Relative Virtual Address) Kavramı

    PE formatı bölümlerden (sections) oluşur. Dosya yükleyici tarafından belleğe yüklenirken bu bölümler yeniden sıralanıp belirli değerlerin katlarına hizalanmaktadır. Ayrıca işletim sistemi PE formatını hemen belleğin başından itibaren yüklemek zorunda da değildir. PE formatının yükleme adresi yine formatın kendi içerisinde bulundurulmaktadır. İşte RVA (Relative Virtual Address) PE formatı yüklendikten sonra format içerisindeki belli bir byte'ın yükleme adresinden itibaren göreli uzaklığını belirten bir kavramdır. Yani biz belli bir byte'ın RVA'sını biliyorsak bu değere formatın yüklendiği adresi eklediğimizde ilgili byte'ın gerçek doğrusal adresini (linear address) elde etmiş oluruz. Örneğin format içerisinde belli bir bölgenin adresi bize RVA olarak verilmiş olsun. Bunun anlamı şudur: Bu program işletim sistemi tarafından yüklendiğinde ilgili bölge yükleme adresinden itibaren o kadar byte uzaklıkta olacaktır.

PE formatı içerisinde bazı özel bölümlerin yerleri hep RVA biçiminde verilmiştir. Burada unutulmaması gereken nokta şudur: İlgili byte'ın RVA'sı ile o byte'ın dosya offset'i aynı olmak zorunda değildir. Çünkü yükleme sırasında birtakım ayarlamalar ve hizalamalar yapılmaktadır. Bu nedenle imajı belleğe yüklemeden doğrudan dosya üzerinde  işlem yapacaksak ilgili byte’ın RVA'sını dosya offset'ine dönüştürmemiz gerekir. Pekiyi bir RVA verildiğinde biz onun dosya offset'ini nasıl elde edebiliriz? İşte verilen RVA bir bölüm  içerisindedir ve PE formatının kendi içerisinde her bölümün hangi RVA'dan ve dosya offset'inden başladığı belirtilmektedir. O halde bir RVA'nın dosya offset’ine dönüştürülmesi için şunlar yapılabilir:

1) İlgili RVA'nın hangi bölüm içerisinde olduğu bulunur. (Yukarıda da belirtildiği gibi bölümlerin başlangıç RVA'ları ve uzunlukları, ayrıca onların dosyanın hangi offset'inden başladığı bilgisi PE formatının içerisinde yazmaktadır.)

2) İlgili byte'ın RVA'sı onun içinde bulunduğu bölümün başlangıç RVA'sından çıkartılır. Böylece dosya offset'i bulunacak olan byte'ın ilgili bölümün hangi offset'inde olduğu hesaplanır. Nihayet hesaplanan bu değere ilgili bölümün dosya offset'i eklenir.

2. Portable Executable Dosya Formatının Başlık Kısımları

    Her dosya formatında olduğu gibi PE formatı da dosya içerisindeki çeşitli bilgilerin niceliğini ve niteliğini tutan başlık kısımlarına sahiptir. PE formatı, MS-DOS ortamında çalıştırılmak istendiğinde ekrana küçük bir mesaj yazısının çıkartılmasına yarayan bir DOS başlığına sahiptir. Bu başlık DOS uyumunu korumak için hala formatta bulundurulmaktadır ve PE formatının kendisi ile doğrudan ilişkili değildir. MS-DOS başlığını PE dosyasının asıl başlığı olan PE Dosya Başlığı (PE File Header) izler. PE Dosya Başlığından sonra da sırasıyla PE Ek Başlığı (PE Optional Header) ve Bölüm Başlıkları (section headers) gelmektedir. Bu başlık kısımları dosya içerisinde belirli yerlerde bulunurlar. Başlık kısımları çalıştırılabilen dosyanın yükleme bilgilerine ilişkin metadata bilgilerini içermektedir. Şimdi formatın başlık kısımlarını tek tek inceleyelim.

2.1 MS-DOS Başlığı ve MS-DOS Mesaj Programı

    MS-DOS başlığı, PE formatı yanlışlıkla DOS ortamında çalıştırılırsa ekrana “This program cannot be run in DOS mode” gibi bir uyarı yazısını ekrana yazdıran basit bir DOS programı içermektedir. Bu programın bulundurulması gelenekseldir ve artık DOS sistemlerinin büyük ölçüde ortadan kalktığı bu günlerde önemli bir işleve sahip değildir. Acak MS-DOS başlığı hala PE dosya formatının hemen başında bulunmak zorundadır. Microsoft'un <winnt.h> başlık dosyası içerisinde MS-DOS başlığı IMAGE_DOS_HEADER isimli yapıyla ifade edilmiştir:

typedef struct _IMAGE_DOS_HEADER {        // DOS .EXE header
    WORD   e_magic;                       // Magic number
    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;

Buradaki MS-DOS başlık programı (MS-DOS Stub) Microsoft bağlayıcıları (link.exe) ile /STUB seçeneği kullanılarak değiştirilebilmektedir. Yani biz bu başlık kısmına başka bir DOS programı yerleştirebiliriz. Yapının e_magic elemanı formatın teşhisi için kullanılabilecek sihirli sayıyı içermektedir. Buradaki değer 'M' ve 'Z' karakterlerinin ASCII tablosundaki sayısal karşılıklarından (0x4D, 0x5A) oluşturulmuştur. Zaten DOS'un EXE dosya formatına MZ formatı denilmesinin nedeni de dosyanın bu iki sihirli byte ile başlamasındandır. Yapının diğer elemanları gerçek modda (real mode) DOS programlarının yüklenmesi için gereken bilgileri içermektedir. Örneğin yapının e_cs, e_ip, e_ss gibi elemanları Intel 8086 işlemcisindeki başlangıç yazmaç değerlerini barındırır. Ancak biz bu makalede MZ başlığının ayrıntılarına girmeyeceğiz.

Örnek programımızda dosyanın MS-DOS başlığına sahip olup olmadığı aşağıdaki gibi test edilmiştir:

g_pDosHeader = (IMAGE_DOS_HEADER *) g_pImageAddr;

if (g_pDosHeader->e_magic != MZ_MAGIC)           /* MZ_MAGIC = 0x5A4D */
    ExitUsr(TEXT("Invalid PE file!"), EXIT_FAILURE);

 2.2 PE Dosya Başlığı (Image File Header)

    Image File Header, PE formatına ilişkin birincil derecede önemli parametrik bilgileri tutmaktadır. Bu başlık <winnt.h> dosyası içerisindeki IMAGE_FILE_HEADER yapısıyla temsil edilmiştir. Yapının elemanları aşağıdaki gibidir:

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 başlığı MS-DOS başlığından hemen sonra gelmek zorunda değildir. Bu başlığın yeri MS-DOS başlığının sonundaki e_lfanew elemanında (dosyanın 0x3C offsetinde) belirtilmektedir.  (Başka bir deyişle bu başlık dosyada MS-DOS başlığının e_lfanew elemanıyla belirtilen offset'indedir. Dosya bellek tabanlı olarak açıldığında başlığın yerini belirlemek amacıyla dosyanın yüklenme adresine e_lfanew elemanındaki değeri toplamamız gerekir. Ancak burada önemli bir ayrıntıyı açıklamak istiyoruz: Dosyanın e_lfanew elemanının belirttiği offset'te hemen IMAGE_FILE_HEADER yapısı bulunmamaktadır. Bu yapı bu offset'ten 4 byte ileridedir. e_lfanew offsetinde (0x3C offsetinde) önce P ve E harflerinin ASCII tablosundaki sayısal karşılıkları (sırasıyla 0x50 ve 0x45 byte’ları) ve sonra da iki tane sıfır byte’ı bulunur. Bu byte’lar dosyanın geçerliliğini sınamak amacıyla kullanılan sihirli bir sayı (magic number) işlevini görürler. Programcı dosyanın PE formatına ilişkin olup olmadığını buradan anlayabilir. Örnek programımızda başlığın yeri şöyle hesaplanmıştır:

LPVOID g_pImageAddr;
IMAGE_DOS_HEADER *g_pDosHeader;
IMAGE_FILE_HEADER *g_pImageFileHeader;
DWORD *g_pMagic;
...
g_pMagic = (DWORD *) ((BYTE *) g_pImageAddr + g_pDosHeader->e_lfanew);
if (*g_pMagic != 0x00004550)       /* PE\0\0 */
    ExitUsr(TEXT("Invalid PE file!"), EXIT_FAILURE);

g_pImageFileHeader = (IMAGE_FILE_HEADER *) (g_pMagic + 1);

Burada g_pImageAddr PE dosyasının yüklenme adresini g_pDosHeader MSDOS başlığının yerini gösteriyor. Şimdi IMAGE_FILE_HEADER yapısının elemanlarının elemanlarını inceleyelim:

WORD Machine:  Bu alan hedef makinanın (yani PE dosyasının yükleneceği makinanın) türünü belirtir. Bu alandaki değerler şunlardan biri olabilir:

Sembolik Sabit İsmi

Değeri

Anlam

IMAGE_FILE_MACHINE_UNKNOWN

0x0000

Herhangi bir makine

IMAGE_FILE_MACHINE_AM33

0x1d30

Matsushita AM33

IMAGE_FILE_MACHINE_AMD64

0x8664

x64

IMAGE_FILE_MACHINE_ARM

0x1c00

ARM little endian

IMAGE_FILE_MACHINE_ARMV7

0x1c40

ARMv7 (or daha yukarı)

IMAGE_FILE_MACHINE_EBC

0xebc0

EFI byte code

IMAGE_FILE_MACHINE_I386

0x14c0

Intel 386 ya da bununla uyumlu daha ileri model

IMAGE_FILE_MACHINE_IA64

0x2000

Intel Itanium işlemci ailesi

IMAGE_FILE_MACHINE_M32R

0x9041

MitsubishiM32R little endian

IMAGE_FILE_MACHINE_MIPS16

0x2660

MIPS16

IMAGE_FILE_MACHINE_MIPSFPU

0x3660

MIPS with FPU

IMAGE_FILE_MACHINE_MIPSFPU16

0x4660

MIPS16 with FPU

IMAGE_FILE_MACHINE_POWERPC

0x1f00

Power PC little endian

IMAGE_FILE_MACHINE_POWERPCFP

0x1f10

floating point ünitesi olan Power PC

IMAGE_FILE_MACHINE_R4000

0x1660

MIPS little endian

IMAGE_FILE_MACHINE_SH3

0x1a20

Hitachi SH3

IMAGE_FILE_MACHINE_SH3DSP

0x1a30

Hitachi SH3 DSP

IMAGE_FILE_MACHINE_SH4

0x1a60

Hitachi SH4

IMAGE_FILE_MACHINE_SH5

0x1a80

Hitachi SH5

IMAGE_FILE_MACHINE_THUMB

0x1c20

ARM ya da Thumb (“interworking”)

IMAGE_FILE_MACHINE_WCEMIPSV2

0x1690

MIPS little-endian WCE v2


Tipik olarak masaüstü bilgisayarlarımızda kullandığımız Intel tabanlı işlemciler için üretilmiş 32 bit PE formatlarında buradaki değer IMAGE_FILE_MACHINE_I386 (0x14C0), 64 bit PE formatlarında bu değer IMAGE_FILE_MACHINE_AMD64 (0x8664) biçimindedir.

WORD NumberOfSections: Bu alanda bölüm tablosunun (section table) eleman sayısı bulunur. Bölüm tablosunun ne işe yaradığı ve ne amaçla bulundurulduğu ileride ele alınmaktadır.

DWORD TimeDateStamp: Bu alanda PE dosyasının yaratıldığı tarih bilgisi 01/01/1970’ten geçen saniye sayısı olarak tutulmaktadır. Örneğin Sample32.exe dosyasında bu alanda bulunan bilgi şöyledir:



Buradaki 0x50574C1E değerinin tarih ve zaman karşılığı 17/09/2012 19:13:18 biçimindedir.

DWORD PointerToSymbolTable: Bu alan yalnızca COFF formatı için anlamlıdır. COFF dosyalarının bu alanında ilişkin sembol tablosunun dosya offset’i bulunmaktadır. Çalıştırılabilen dosyalarda bu alanın bir anlamı yoktur; sıfır bulunur.

DWORD NumberOfSymbols: Bu alan da yalnızca COFF formatı için anlamlıdır. COFF dosyalarının bu alanında sembol tablosundaki eleman sayısı bulunmaktadır. Çalıştırılabilen dosyalarda bu alanın bir anlamı yoktur; sıfır bulunur.

WORD  SizeOfOptionalHeader: Bu alanda IMAGE_FILE_HEADER başlığından hemen sonra bulunan IMAGE_OPTIONAL_HEADER başlığının byte cinsinden uzunluğu bulunmaktadır. 

WORD Characteristics: PE formatına ilişkin bazı önemli bilgiler bu alanda bitsel olarak kodlanmıştır. Her bir bit belli bir özelliği belirtmektedir. Bu alanda kullanılabilecek bitler ve bunların anlamları aşağıdaki tabloda listelenmiştir:

Bayrak 

Değer

Anlamı

IMAGE_FILE_RELOCS_STRIPPED

0x0001

İmaj bir yeniden yüklenme (relocation) bilgisi içermemektedir. Bu nedenle dosya yalnızca belirlenen adresten itibaren yüklenebilir. Eğer bu adres yükleme için uygun değilse dosya yüklenemez.

 

IMAGE_FILE_EXECUTABLE_IMAGE

0x0002

Bu bayrak set edilmişse dosya çalıştırılabeilen bir dosyadır, set edilmemişse COFF formatlı bir amaç dosyadır.

IMAGE_FILE_LARGE_ADDRESS_

AWARE

0x0020

PE formatı 2GB’den fazla adres alanı kullanmaktadır.

IMAGE_FILE_32BIT_MACHINE 

 

0x0100

Dosyanın çalıştırılacağı makinada 32 bit bir işlemci kullanılmaktadır.

IMAGE_FILE_REMOVABLE_RUN_

FROM_SWAP

0x0400

Eğer dosya çıkarılabilir (removable) bir aygıt üzerindeyse aygıtı swap amacıyla kullanılmaz, bunun için yeni bir swap dosyası yaratılarak o kullanılır.

IMAGE_FILE_NET_RUN_FROM_SWAP

0x0800

Eğer dosya ağdaki bir aygıt üzerindeyse aygıt swap amacıyla kullanılmaz, bunun için yeni bir swap dosyası yaratılır ve o kullanılır.

IMAGE_FILE_SYSTEM

0x1000

İlgili imaj bir sistem dosyasına ilişkindir, normal bir kullanıcı uygulaması değildir.

IMAGE_FILE_DLL

0x2000

İmaj dosyası bir DLL (Dynamic Link Library) dosyasıdır.

IMAGE_FILE_UP_SYSTEM_ONLY

0x4000

Dosya tek işlemcili bir makinada çalıştırılmalıdır.


Yukarıdaki listede yalnızca çalıştırılabilen PE formatları ile ilgili bayraklar belirtilmiştir. Aslında bu alandaki bayrakların sayısı daha fazladır. Bunun için PE formatının Microsoft tarafından yayınlanmış orijinal dokümanlarına başvurabilirsiniz.[2] Örneğin, Sample32.exe dosyasında bu alanda bulunan bilgi aşağıdaki gibidir:




Başlıktaki 0x0102 değeri (0000 0001 0000 0010) IMAGE_FILE_EXECUTABLE_IMAGE ve IMAGE_FILE_32BIT_MACHINE  bayraklarının set edildiği anlamına gelmektedir.

2.3 PE Ek Başlığı (Image Optional Header)

     Bu bölüm COFF amaç dosya formatında yoktur. Yalnızca çalıştırılabilen dosya formatında vardır. Image Optional Header bölümü -isminin tersine- isteğe bağlı bulundurulan bir bölüm değildir. Çalıştırılabilen her imajda bu bölüm bulunmak zorundadır. Image Optional Header temel olarak yükleme işlemi için gereken bilgileri bulundurmaktadır. Bu bölüm <winnt.h> dosyası içerisinde aşağıdaki gibi bildirilmiştir:

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

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;

typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   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;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

Görüldüğü gibi 32 bit PE formatıyla 64 bit PE formatının IMAGE_OPTIONAL_HEADER bölümlerinin formatlarında farklılıklar vardır. Bu farklılık içeriğe ilişkin değil alanların uzunluklarına ilişkindir. 64 bit PE formatında bazı alanların 8 byte uzunluk ta olduğuna (ULONGLONG) dikkat ediniz.

#ifdef _WIN64

typedef IMAGE_OPTIONAL_HEADER64             IMAGE_OPTIONAL_HEADER;
typedef PIMAGE_OPTIONAL_HEADER64            PIMAGE_OPTIONAL_HEADER;
#define IMAGE_NT_OPTIONAL_HDR_MAGIC         IMAGE_NT_OPTIONAL_HDR64_MAGIC
#else
typedef IMAGE_OPTIONAL_HEADER32             IMAGE_OPTIONAL_HEADER;
typedef PIMAGE_OPTIONAL_HEADER32            PIMAGE_OPTIONAL_HEADER;
#define IMAGE_NT_OPTIONAL_HDR_MAGIC         IMAGE_NT_OPTIONAL_HDR32_MAGIC
#endif

disppe isimli örnek programımızda IMAGE_OPTIONAL_HEADER yapısı şöyle elde edilmiştir:

if (*(WORD *)(g_pImageFileHeader + 1) == 0x010B) {

    g_flag32 = TRUE;
    g_pImageOptionalHeader32 = (IMAGE_OPTIONAL_HEADER *) (g_pImageFileHeader + 1);
}
else if (*(WORD *)(g_pImageFileHeader + 1) == 0x020B) {
    g_flag32 = FALSE;
    g_pImageOptionalHeader32 = (IMAGE_OPTIONAL_HEADER *) (g_pImageFileHeader + 1);
}
else
    ExitUsr(TEXT("Invalid PE file!"), EXIT_FAILURE);

Örneğimizde g_ImageFileHeader isimli gösterici IMAGE_FILE_HEADER isimli yapı türündendir. Bu göstericiyi 1 artırdığımızda elde edilen adresin sayısal bileşeninin bu yapı kadar artacağına dikkat ediniz. IMAGE_OPTIONAL_HEADER yapısı IMAGE_FILE_HEADER yapısının hemen ardından gelmektedir.

IMAGE_OPTIONAL_HEADER başlığı kendi içerisinde üç bölümden oluşmaktadır.

1) Standart Bölüm: Bu bölüm COFF formatını kullanan bütün sistemlerde aynı biçimde bulunmaktadır. Bu bölümdeki bilgiler platform bağımsızdır.

2) Windows'a Özgü Bölüm: Bu bölümde Windows İşletim Sistemine özgü çeşitli bilgiler bulunmaktadır.

3) Veri Dizini Bölümü: Bu bölümde işletim sistemi tarafından çeşitli aşamalarda kullanılan bazı bilgiler bulunmaktadır.

IMAGE_OPTIONAL_HEADER başlığındaki bu üç bölümün başlığa göre offset değerleri ve uzunlukları aşağıdaki tabloda belirtilmektedir:

Bölüm

Offset

Uzunluk (P32E / PE64)

Standart Bölüm

0 / 0

28 / 24

Windows’a Özgü Bölüm

28 / 24

68 / 88

Veri Dizini Bölümü

96 / 112

Değişken Uzunlukta


Şimdi IMAGE_OPTIONAL_HEADER başlığının standart bölümüne ilişkin elemanları tek tek açıklayalım:


WORD magic: Bu alanda 0x010B ya da 0x020B değeri bulunur. Bu değer imajın 32 bit mi yoksa 64 bit mi olduğunu belirtmektedir. 64 bit PE dosyasının 64 bit adres alanını kullanma yeteneği vardır. Fakat dosya en fazla 2 GB uzunlukta olabilir. Örneğin Sampl32.exe dosyasının Magic alanı şöyledir:



Sample64.exe dosyasının Magic alanı da şöyledir:



BYTE MajorLinkerVersion: Burada imajın oluşturulduğu bağlayıcının büyük numarası (majör numarası) bulunur.

BYTE MinorLinkerVersion: Bu alanda da imajın oluşturulduğu bağlayıcının minör numarası bulunmaktadır.

DWORD SizeOfCode: Burada imaj içerisindeki tüm kod bölümlerinin (text bölümlerinin) toplam uzunluğu bulunmaktadır. PE formatında genellikle .text isimli bir tane kod bölümü vardır. Eğer birden fazla kod bölümü varsa buaradaki değer tüm bu kod bölümlerinin toplamını verir.

DWORD SizeOfInitializedData: Bu alanda ilkdeğer verilmiş statik verilerin (C’deki ilkdeğer verilmiş static yerel değişkenlerin ve global değişkenlerin ve string ifadelerinin) bulunduğu bölümlerin toplam uzunluğu belirtilmektedir. Normal olarak ilkdeğer verilmiş statik veriler .data isimli bölümde bulunur. Eğer statik veriler birden fazla bölümde bulunuyorsa bu alanda tüm bu bölümlerin toplam uzunlukları tutulur.

DWORD SizeOfUninitializedData: Bu alanda ilkdeğer verilmemiş statik verilerin (C’deki ilkdeğer verilmemiş global ve static yerel değişkenlerin) bulunduğu bölümlerin toplam uzunluğu belirtilmektedir. Normal olarak ilkdeğer verilmiş statik veriler .bss isimli bölümde bulunur. Eğer statik veriler birden fazla bölümde bulunuyorsa bu alanda tüm bu bölümlerin toplam uzunlukları tutulmaktadır.

DWORD AddressOfEntryPoint: Bu alanda eğer format çalıştırılabilen bir dosyaya ilişkinse programın başlangıç noktasının (entry point) adresi, eğer format bir DLL dosyasına ilişkinse DLL'in başlangıç fonksiyonunun (yani DllMain fonksiyonunu çağıran fonksiyonun) adresi, yok eğer format bir aygıt sürücüsüne ilişkinse aygıt sürücüsünün başlangıç fonksiyonunun adresi bulunur. Buradaki adres RVA biçimindedir ve tipik olarak .text bölümünde bir yer belirtir.

DWORD BaseOfCode: Bu alanda programın kod bölümünün (.text isimli bölümün) başlangıç RVA’sı tutulmaktadır. Kod bölümü tipik olarak data bölümünden (.data isimli bölümden) daha önce bulunur. Microsoft bağlayıcıları ile oluşturulan çalıştırılabilir dosyalarda bu alanda genellikle 0x00001000 değeri (yani programın yükleme adresinden itibaren 4096 byte ileride) bulunmaktadır.

DWORD BaseOfData: Bu alan 32 bit PE formatında vardır. 64 bit PE formatında yoktur. 32 bit PE formatında bu alanda programın static verilerinin bulunduğu bölümün (.data isimli bölümün) başlangıç RVA’sı bulunmaktadır.

Şimde de IMAGE_OPTIONAL_HEADER başlığının Windows’a özgü oluşturulmuş olan alanlarını inceleyelim.

DWORD/ULONGLONG ImageBase: Bu alanda PE formatının yükleneceği doğrusal adres belirtilmektedir. PE formatının belleğin neresinden itibaren yükleneceği aslında bağlayıcı (linker) tarafından belirlenerek PE formatının bu alanına yazılmaktadır. Microsoft bağlayıcıları 32 bit .exe dosyaları için varsayılan yükleme adresini 4MB (0x00400000) olarak, 32 bit dll dosyaları için 256MB (0x10000000) olarak, 64 bit .exe dosyaları için 4GB + 1GB (0x0000000140000000) olarak ve 64 bit .dll dosyaları için ise  4GB + 2GB (0x0000000180000000) olarak almaktadır. Ancak bu değerler bağlayıcı ayarlarıyla değiştirilebilir. Tabii burada belirtilen adresler yalnızca tercih edilen (preferred) adreslerdir. İşletim sistemi PE formatını tercih edilen adres yerine başka bir adrese de yükleyebilir. Örneğin bir programın kullandığı tüm .dll dosyalarının aynı adresten itibaren yüklenemeyeceği açıktır. Bu durumda işletim sistemi DLL dosyasına ilişkin PE imajını başka bir bölgeye yükleyebilir. Şüphesiz bu yüklemenin yapılabilmesi için dosya içerisinde yer değiştirme (relocation) bilgilerinin bulunuyor olması gerekir. Çünkü PE formatında üretilen kodlar imajın o adrese yükleneceği fikriyle oluşturulmuştur. Eğer imaj başka bir adrese yüklenirse kod üzerindeki bazı adreslerin  değiştirilmesi gerekmektedir. Bu değişiklik yer değiştirme (relocation) bilgisi ile yapılmaktadır. Ayrıca bu alanın uzunluğunun 32 bit PE formatıyla 64 bit PE formatı arasında farklılık gösterdiğine dikkat ediniz. 64 bit sistemlerde bellek alanı teorik olarak 264 = 16 exabyte uzunluğundadır.[3] Örneğin Sample32.exe programı için ImageBase alanı şöyledir:



Sampl64.exe programında ImageBase alanının 8 byte uzunlukta olduğuna dikkat ediniz:



DWORD SectionAlignment: İşletim sistemi PE dosyasındaki bölümleri belli bir değerin katlarına göre hizalayarak belleğe yüklemektedir. Burada bölüm hizalamasının hangi değerin katlarına göre yapılacağı bilgisi vardır. 32 bit ve 64 bit PE formatında tipik olarak 4K’lık (0x00001000) bir hizalama kullanılmaktadır. Yani bir bölüm yüklendikten sonra onu izleyen bölüm sonraki ilk 4K'nın katından itibaren yüklenir. 4K’nın Intel mimarisinde sayfa (page) uzunluğu kadar olduğunu anımsayınız.

DWORD FileAlignment: Bölümler bağlayıcı tarafından dosyaya belli değerin katlarında olacak biçimde yerleştirilirler. Bu alanda bölümlerin PE dosyası içerisinde hangi değerin katlarına göre hizalanacağı bilgisi vardır. Microsoft bağlayıcıları varsayılan durumda bu değeri 512 olarak almaktadır. (Ayrıca, Microsoft dokümanları eğer bölüm hizalaması ilgili mimarinin sayfa uzunluğundan daha küçük olması durumunda sayfa hizalamasının dosya hizalaması ile aynı değerde olması gerektiğini söylemektedir.)

WORD MajorOperatingSystemVersion, MinorOperatingSystemVersion: Bu iki alanda sırasıyla imajı yükleyecek işletim sisteminin büyük ve küçük versiyon numaraları bulundurulur. Tabi buradaki değerler yükleme gereksinimini karşılayacak minimum değerlerdir. Örneğin Sample32.exe dosyasında bu alanda yazılan versiyon numarası 5.01 (Windows 2000) biçimindedir.


WORD MajorImageVersion, MinorImageVersion: Bu iki alanda PE formatının büyük ve küçük versiyon numaralarının tutulması öngörülmüştür. Fakat Microsoft bağlayıcıları bu alana hem 32 bit hem de 64 bit PE formatında sıfır yerleştirmektedir.

WORD MajorSubsystemVersion, MinorSubsystemVersion: Bu alanda işletim ssisteminde kullanılan altsistem numarasının majör ve minör değerleri bulunmaktadır. Normal olarak buradaki değerin işletim sisteminin versiyon numarası ile (Örneğimizde 5.01) aynı olması beklenir.

DWORD Win32VersionValue: Bu alan ileride kullanılmak üzere ayrılmıştır (reserved) ve sıfır değeri bulunur.

DWORD SizeOfHeaders: Yukarıda da belirtildiği gibi PE formatı IMAGE_DOS_HEADER, IMAGE_FILE_HEADER ve IMAGE_OPTIONAL_HEADER isimli üç başlığa sahiptir. Ayrıca bu başlıkların sonunda bölüm başlıkları (section headers) da bulunmaktadır. İşte SizeofHeader alanında bu üç başlığın ve bölüm başlıklarının toplam uzunluğu yazılıdır. Buradaki değer RVA belirtmez, doğrudan dosya offset’i belirtir ve dosya hizalama değerine (FileAlignment alanında belirtilen değere) yukarıdan yuvarlanmış durumdadır.

Sample32.exe dosyasındaki SizeofHeaders alanı aşağıda görülmektedir:

Sample64.exe dosyasındaki SizeofHeaders alanı da şöyledir:

Gördüğünüz gibi yukarıdaki iki örnekte de dosya başlıkları dosyanın 0x00000400 offset’indedir.

DWORD CheckSum: Bu alanda dosyadaki tüm byte’lara ilişkin checksum değeri bulunur. Ancak bu CheckSum değeri yalnızca kritik sistem dosyaları ve aygıt sürücülerinin yüklenmesi sırasında kontrol edilmektedir. Microsoft'un bağlayıcıları normal derleme işlemi sırasında .exe ve .dll dosyaları için CheckSum hesaplamazlar ve bu alana sıfır değerini yerleştirirler. 

DWORD CheckSum: Bu alanda dosyadaki tüm byte’lara ilişkin checksum değeri bulunur. Ancak bu CheckSum değeri yalnızca kritik sistem dosyaları ve aygıt sürücülerinin yüklenmesi sırasında kontrol edilmektedir. Microsoft'un bağlayıcıları normal derleme işlemi sırasında .exe ve .dll dosyaları için CheckSum hesaplamazlar ve bu alana sıfır değerini yerleştirirler. 

WORD Subsystem: Bu alan ilgili PE dosyasının Windows'un hangi alt sistemini kullanma potansiyelinde olduğunu belirtmektedir. Örneğin uygulama eğer bir konsol uygulaması ise yükleyici programı yükledikten sonra otomatik olarak bir konsol penceresini kendisi açmaktadır. Benzer biçimde bir dosyanın aygıt sürücü dosyası olup olmadığı, mobil cihazlar için oluşturulup oluşturulmadığı gibi bilgiler de bu alanın yorumlanmasıyla elde edilir. Bu alandaki değerler şunlardan biri olabilir:

Sembolik Sabit İsmi

Değeri

Anlamı

IMAGE_SUBSYSTEM_UNKNOWN

0

Bilinmeyen bir altsistem

IMAGE_SUBSYSTEM_NATIVE

1

Aygıt sürücüler ve doğal (native) Windows uygulamaları

IMAGE_SUBSYSTEM_WINDOWS_GUI

2

Windows GUI uygulaması

IMAGE_SUBSYSTEM_WINDOWS_CUI

3

Windows Console uygulaması

IMAGE_SUBSYSTEM_POSIX_CUI

7

POSIX altsistemi

IMAGE_SUBSYSTEM_WINDOWS_CE_GUI

9

Windows CE

IMAGE_SUBSYSTEM_EFI_APPLICATION

10

Extensible Firmware Interface (EFI) uygulaması

IMAGE_SUBSYSTEM_EFI_BOOT_

SERVICE_DRIVER

11

Boot edebilen Extensible Firmware Interface (EFI) sürücüsü

IMAGE_SUBSYSTEM_EFI_RUNTIME_

DRIVER

12

Extensible Firmware Interface (EFI) sürücüsü

IMAGE_SUBSYSTEM_EFI_ROM

13

Extensible Firmware Interface (EFI) ROM imajı

IMAGE_SUBSYSTEM_XBOX

14

XBOX uygulaması


Örneğin Sample32.exe programında Subsystem alanı aşağıdaki gibidir:




Bu durumda Sample32.exe programı bir konsol uygulamasıdır. Konsol programları yüklendiğinde Windows işletim sistemi otomatik olarak bir konsol penceresi yaratmaktadır.

WORD DllCharacteristics: Bu alanda DLL’ler için onların bazı özellikleri hakkında bilgiler bulunmaktadır. Bu alandaki değerler bitsel düzeyde anlamlıdır. Yani her bit belli bir özelliğin etkin olup olmadığını belirtir. Dolayısıyla aşağıdaki tablonun birden fazla elemanı set edilmiş olabilir:

Sembolik Sabit İsmi

Değeri

Anlamı

IMAGE_DLL_CHARACTERISTICS_

DYNAMIC_BASE

0x0040

Dosya yeniden yüklenme (relocation) bilgisine sahiptir. Dolayısıyla adres alanının herhangi bir yerine yüklenebilir.

IMAGE_DLL_CHARACTERISTICS_

FORCE_INTEGRITY

0x0080

Kod bütünlüğü yükleme sırasında kontrol edilecektir.

IMAGE_DLL_CHARACTERISTICS_

NX_COMPAT

0x0100

Dosya NX uyumludur.

IMAGE_DLLCHARACTERISTICS_

NO_ISOLATION

0x0200

Yalıtma tanınmaktadır fakat imaj yalıtılmamıştır.

IMAGE_DLLCHARACTERISTICS_ NO_SEH 

0x0400

Bu imajdaki kodlarda Structured Exception Handling (SE) kullanılmamaktadır. Dolayısıyla kodda hiçbir exception fonksiyonu çağrılmayacaktır.

IMAGE_DLLCHARACTERISTICS_ NO_BIND

0x0800

Bu imaj bağlanmamalıdır.

 

0x1000

Bu değer kullanılmamaktadır. Bu bitte 0 bulunur.

IMAGE_DLLCHARACTERISTICS_

WDM_DRIVER

0x2000

Dosya bir WDM aygıt sürücüsüdür.

IMAGE_DLLCHARACTERISTICS_

TERMINAL_SERVER_AWARE

0x8000

Terminal sunucusu tanınmaktadır.


PE
formatının eski versiyonlarında 0x0001, 0x0002, 0x0004, 0x0008 bayrakları DLL’in giriş fonksiyonunun (DllMain fonksiyonunu çağıran fonksiyonun) ne durumlarda çağrılacağını belirlemekte kullanılıyordu. Microsoft daha sonra bu bayrakları tamamen kaldırmıştır. Bu bayrakların eski anlamları şöyleydi:


0x0001: DLL adres alanına ilk kez yüklendiğinde çağır.

0x0002: Bir thread sonlandığında çağır.

0x0004: Bir thread çalışmaya başladığında çağır.

0x0008: Bir thread sonlandığında çağır.

Burada bir anımsatma yapmak istiyoruz. Windows’ta .exe uzantılı dosyalar da sanki birer DLL’miş gibi LoadLibrary ya da LoadLibraryEx fonksiyonlarıyla yüklenebilmektedir. Dolayısıyla DllCharacteristics alanındaki bitler .exe dosyalar için de anlamlıdır.

DWORD/ULONGLONG SizeOfStackReserve: Bu alanda prosesin ana thread’inin kullanacağı stack miktarı belirtilir. Microsoft bağlayıcıları varsayılan durumda hem 32 bit hem de 64 bit çalıştırılabilen PE dosyaları için buraya 1MB (0x00010000) değerini yazmaktadır. (Tabi programcı bağlayıcı ayarlarıyla bunu değiştirebilmir.) Buradaki değer aynı zamanda thread’ler CreateThread API fonksiyonuyla yaratılırken stack miktarı belirtilmezse yaratılacak thread için tahsis edilecek stack miktarının belirlenmesinde de etkili olmaktadır. Ancak burada belirtilen stack’in hepsi commit edilmez. İlk başta commit edilecek miktar formatın izleyen alanında belirtilmektedir. Örneğin Sample32.exe programında bu alan aşağıdaki gibidir:



64 bit PE dosyalarında bu alanın 8 byte uzunluğunda olduğuna dikkat ediniz.

DWORD/ULONGLONG SizeOfStackCommit: Bu alanda başlangıçta commit edilecek stack miktarı belirtilmektedir. Tabi eğer stack prosesin çalışması boyunca bu alanda belirtilenden daha fazla büyürse commit işlemi yine çalışma zamanı sırasında otomatik olarak yapılmaya devam edecektir. Microsoft bağlayıcıları varsayılan durumda hem 32 bit hem de 64 bit çalıştırılabilen PE dosyaları için buraya 4096 (0x00001000) değerini yazmaktadır. 4096 byte'ın 1 sayfaya karşılık geldiğini anımsayınız. Örneğin Sample32.exe programında bu alan aşağıdaki gibidir:

64 bit PE dosyalarında bu alanın 8 byte uzunluğunda olduğuna dikkat ediniz.

DWORD/ULONGLONG SizeOfHeapReserve: Bu alanda da prosesin varsayılan heap alanı için başlangıçta tahsis edilecek miktar belirtilmektedir. Ancak burada belirtilen miktar prosesin default heap alanının en büyük uzunluğu değildir. Başlangıçta sayfa tablosu yoluyla tahsis edilecek alandır. Prosesin default heap'i tahsisatlar yapıldığında otomatik olarak büyütülmektedir. Microsoft bağlayıcıları varsayılan durumda hem 32 bit hem de 64 bit çalıştırılabilen PE dosyaları için buraya 1MB (0x00010000) değerini yazmaktadır. Örneğin Sample32.exe programındca bu alan aşağıdaki gibidir:



DWORD/ULONGLONG SizeOfHeapCommit: Bu alanda da tıpkı SizeOfStackCommit alanında olduğu gibi başlangıçta commit edilecek stack miktarı belirtilmektedir. Tabi eğer heap prosesin çalışması boyunca bu alanda belirtilenden daha fazla büyüdüğünde commit işlemi yine çalışma zamanı sırasında otomatik olarak yapılmaya devam edecektir. Microsoft bağlayıcıları varsayılan durumda hem 32 bit hem de 64 bit çalıştırılabilen PE dosyaları için buraya 4096 (0x00001000) değerini yazmaktadır. 4096 byte'ın 1 sayfaya karşılık geldiğini anımsayınız. Örneğin Sample32.exe programında bu alan aşağıdaki gibidir:



DWORD LoaderFlags: Bu alan gelecekte kullanılmak üzere ayrılmıştır. Şimdilik bu alan içerisinde sıfır değeri bulunmak zorundadır.

DWORD NumberOfRvaAndSizes: Bu alanda IMAGE_OPTIONAL_HEADER bölümünün sonunda bulunan veri dizininde (data directory) kaç elemanın bulunduğu bilgisi vardır. Veri dizini IMAGE_DATA_DIRECTORY türüyle temsil edilen bir yapı dizisi biçimindedir. Tipik olarak 16 elemanlı olacak biçimde yaratılmaktadır. Ancak daha büyük de olabilir. Dizinin son elemanı sıfırlarla doldurulmuştur. Sample32.exe programında NumberOfRvaAndSizes alanı aşağıdaki gibidir:

2.3.1 PE Formatının Veri Dizini

     IMAGE_OPTIONAL_HEADER başlığının sonunda IMAGE_DATA_DIRECTORY türünden bir yapı dizisi bulunmaktadır. PE formatındaki bu diziye veri dizini (data directory) denir. Veri dizini PE formatı için önemli olan çeşitli tabloların yerlerini ve uzunluklarını tutar. (Yukarıda da belirtildiği gibi, bu veri dizinine ilişkin dizinin ne uzunlukta olduğu IMAGE_OPTIONAL_HEADER bölümünün NumberOfRvaAndSize elemanında saklanmaktadır.) IMAGE_DATA_DIRECTORY yapısı hem 32 bit hem de 64 bit PE formatında aynı biçimdedir. Yapı dört byte RVA ve dört byte uzunluk bilgisi içeren iki elemana sahiptir:

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

Yapının VirtualAddress isimli elemanı ilgili tablonun RVA'sını, Size elemanı da uzunluğunu tutmaktadır. Biz bu aşamada veri dizinindeki tablolardan ayrıntılarıyla söz etmeyeceğiz. Bu tabloların ne amaçlarla kullanıldıkları izleyen bölümlerde ele alınmaktadır. Veri dizininin genel yapısı aşağıdaki gibidir:




Burada bir noktayı yeniden vurgulamak istiyoruz: PE formatının veri dizini tabloların kendisini tutmamaktadır, onların yerlerini ve uzunluklarını tutmaktadır:

2.4. Bölüm Başlıkları (Section Headers)

    Bölüm başlıkları PE dosyasının bölümlerine ilişkin bilgileri tutmaktadır. Bölümlerin isimleri, nereden başladıkları, ne uzunlukta oldukları gibi bilgiler bölüm başlıklarında bulunurlar. Bölüm tablosu hemen PE Ek Başlığından (Image Optional Header) sonra gelmektedir. Çalıştırılabilen PE dosyalarında bölümlerin RVA'ları bağlayıcılar tarafından PE dosyası içerisine küçükten büyüğe doğru ve ardışıl olarak yerleştirilirler. Bölüm başlıkları 40 byte uzunluğunda olan IMAGE_SECTION_HEADER yapısıyla temsil edilmektedir:

#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;

IMAGE_SECTION_HEADER yapısının elemanlarının anlamları da şöyledir:

Microsoft (R) COFF/PE Dumper Version 11.00.50727.1

Copyright (C) Microsoft Corporation.  All rights reserved.

 

 

Dump of file Sample64.exe

 

PE signature found

 

File Type: EXECUTABLE IMAGE

 

FILE HEADER VALUES

            8664 machine (x64)

               7 number of sections

        5048BFBF time date stamp Thu Sep 06 18:22:39 2012

               0 file pointer to symbol table

               0 number of symbols

              F0 size of optional header

              22 characteristics

                   Executable

                   Application can handle large (>2GB) addresses

 

OPTIONAL HEADER VALUES

             20B magic # (PE32+)

           10.00 linker version

            4400 size of code

            3800 size of initialized data

               0 size of uninitialized data

            1230 entry point (0000000140001230)

            1000 base of code

       140000000 image base (0000000140000000 to 000000014000DFFF)

            1000 section alignment

             200 file alignment

            5.02 operating system version

            0.00 image version

            5.02 subsystem version

               0 Win32 version

            E000 size of image

             400 size of headers

            A126 checksum

               3 subsystem (Windows CUI)

            8140 DLL characteristics

                   Dynamic base

                   NX compatible

                   Terminal Server Aware

          100000 size of stack reserve

            1000 size of stack commit

          100000 size of heap reserve

            1000 size of heap commit

               0 loader flags

              10 number of directories

               0 [       0] RVA [size] of Export Directory

            B000 [      3C] RVA [size] of Import Directory

            C000 [     1B4] RVA [size] of Resource Directory

            A000 [     270] RVA [size] of Exception Directory

               0 [       0] RVA [size] of Certificates Directory

            D000 [      34] RVA [size] of Base Relocation Directory

            6770 [      1C] RVA [size] of Debug Directory

               0 [       0] RVA [size] of Architecture Directory

               0 [       0] RVA [size] of Global Pointer Directory

               0 [       0] RVA [size] of Thread Storage Directory

               0 [       0] RVA [size] of Load Configuration Directory

               0 [       0] RVA [size] of Bound Import Directory

            B2E8 [     2A8] RVA [size] of Import Address Table Directory

               0 [       0] RVA [size] of Delay Import Directory

               0 [       0] RVA [size] of COM Descriptor Directory

               0 [       0] RVA [size] of Reserved Directory

 

 

SECTION HEADER #1

   .text name

    43E0 virtual size

    1000 virtual address (0000000140001000 to 00000001400053DF)

    4400 size of raw data

     400 file pointer to raw data (00000400 to 000047FF)

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

60000020 flags

         Code

         Execute Read

 

SECTION HEADER #2

  .rdata name

    209C virtual size

    6000 virtual address (0000000140006000 to 000000014000809B)

    2200 size of raw data

    4800 file pointer to raw data (00004800 to 000069FF)

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

40000040 flags

         Initialized Data

         Read Only

 

  Debug Directories

 

        Time Type       Size      RVA  Pointer

    -------- ------ -------- -------- --------

    5048BFBF cv           43 0000736C     5B6C    Format: RSDS, {FD553AC1-48F8-43B4-9D23-51C6762FBE5C}, 2, D:\Study\C\Sample64\x64\Debug\Sample64.pdb

 

SECTION HEADER #3

   .data name

     770 virtual size

    9000 virtual address (0000000140009000 to 000000014000976F)

     200 size of raw data

    6A00 file pointer to raw data (00006A00 to 00006BFF)

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

C0000040 flags

         Initialized Data

         Read Write

 

SECTION HEADER #4

  .pdata name

     3D8 virtual size

    A000 virtual address (000000014000A000 to 000000014000A3D7)

     400 size of raw data

    6C00 file pointer to raw data (00006C00 to 00006FFF)

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

40000040 flags

         Initialized Data

         Read Only

 

SECTION HEADER #5

  .idata name

     A8F virtual size

    B000 virtual address (000000014000B000 to 000000014000BA8E)

     C00 size of raw data

    7000 file pointer to raw data (00007000 to 00007BFF)

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

C0000040 flags

         Initialized Data

         Read Write

 

SECTION HEADER #6

   .rsrc name

     1B4 virtual size

    C000 virtual address (000000014000C000 to 000000014000C1B3)

     200 size of raw data

    7C00 file pointer to raw data (00007C00 to 00007DFF)

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

40000040 flags

         Initialized Data

         Read Only

 

SECTION HEADER #7

  .reloc name

     104 virtual size

    D000 virtual address (000000014000D000 to 000000014000D103)

     200 size of raw data

    7E00 file pointer to raw data (00007E00 to 00007FFF)

       0 file pointer to relocation table

       0 file pointer to line numbers

       0 number of relocations

       0 number of line numbers

42000040 flags

         Initialized Data

         Discardable

         Read Only

 

  Summary

 

        1000 .data

        1000 .idata

        1000 .pdata

        3000 .rdata

        1000 .reloc

        1000 .rsrc

        5000 .text

Haftanın Böceği Yukarı