Bilim, organize edilmiş bilgidir.

Herbert Spencer

Portable Executable Dosya Formatında CLI Metadata Tablolarının Organizasyonu

     .NET, Mono ve Rotor gibi CLI (Common Language Infrastructure) standartlarına uygun ortamlardaki assembly dosyaları PE (Portable Executable) dosya formatını kullanmaktadır. PE dosya formatı Microsoft’un 32 ve 64 bit Windows sistemlerinde kullandığı genel amaçlı çalıştırılabilir (executable) bir formattır. Bu format tıpkı UNIX/Linux sistemlerinde kullanılan ELF (Executable and Linkable Format) gibi bölümlerden (sections) oluşur. Bölümlerin içerisinde programın yüklenmesi ve çalıştırılabilmesi için gerekli bilgiler vardır.

    PE dosya formatında önemli 3 başlık (header) kısmı bulunmaktadır.[1] IMAGE_DOS_HEADER, programın DOS’ta çalıştırılması durumunda ekrana bir hata mesajı çıkartacak küçük DOS programının MZ başlığıdır ve PE dosyasının hemen başında bulunur. Bu başlığı sırasıyla IMAGE_FILE_HEADER ve IMAGE_OPTIONAL_HEADER başlıkları izler. IMAGE_FILE_HEADER birincil önemdeki yükleme bilgilerini, IMAGE_OPTIONAL_HADER ise ikincil önemdeki yükleme bilgilerini tutmaktadır.[2] PE
dosya formatının genel görünümü aşağıdaki gibidir:



Makale içerisindeki anlatımları desteklemek amacıyla aşağıdaki örnek bir C#
programından faydalanacağız:

// Sample.cs

namespace CSD
{
    class App
    {
        public static void Main()
        {
            Sample s = new Sample(100);

            System.Console.WriteLine(s.Val);
        }
    }

    class Sample
    {
        private int m_val;

        public Sample(int val)
        {
            m_val = val;
        }

        public int Val
        {
            get { return m_val; }
            set { m_val = value; }
        }
    }
}

Bu programı aşağıdaki gibi derleyebilirsiniz:

csc Sample.cs

Bu işlemden
Sample.exe dosyasını elde edeceksiniz.   

    PE dosya formatı hem doğal kod içeren (unmanaged) .EXE ve .DLL dosyaları için  hem de arakod içeren (managed) .NET assembly dosyaları için ortak kullanılan bir formattır. Bir .EXE ya da .DLL dosyasının doğal kod içeren bir dosya mı, yoksa arakod içeren bir .NET assembly dosyası mı olduğunu anlayabilmek için PE dosya formatının CLI başlığına sahip olup olmadığına bakmak gerekir. Eğer PE dosya formatı içerisinde bir CLI başlığı (buna CLR başlığı da denir) varsa bu dosya bir .NET assembly dosyasıdır. Dolayısıyla böyle bir dosyanın yüklenebilmesi için bir VES (Virtual Execution System) alt sisteminin bulunuyor olması gerekir.[3]

   
    PE dosya formatında bir CLI başlığının bulunup bulunmadığı IMAGE_OPTIONAL_HEADER başlığının sonundaki 16 elemanlı IMAGE_DATA_DIRECTORY dizisinin 15'inci elemanına (yani 14'üncü indeksteki elemanına) bakılarak belirlenir. Eğer dosyada bir CLI başlığı varsa bu eleman bu başlığın yerini ve uzunluğunu tutar. CLI
başlığı aşağıdaki elemanlara sahip olan bir yapı ile temsil edilmektedir:

typedef struct IMAGE_COR20_HEADER
{
    DWORD                   cb;
    WORD                    MajorRuntimeVersion;
    WORD                    MinorRuntimeVersion;

    IMAGE_DATA_DIRECTORY    MetaData;
    DWORD                   Flags;

    union {
        DWORD               EntryPointToken;
        DWORD               EntryPointRVA;
    } DUMMYUNIONNAME;

    IMAGE_DATA_DIRECTORY    Resources;
    IMAGE_DATA_DIRECTORY    StrongNameSignature;

    IMAGE_DATA_DIRECTORY    CodeManagerTable;
    IMAGE_DATA_DIRECTORY    VTableFixups;
    IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;

    IMAGE_DATA_DIRECTORY    ManagedNativeHeader;

} IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;

Yapının
cb elemanı başlığın uzunluğunu, MajorRuntimeVersion ve MinorRuntimeVersion elemanları çalıştırma için gerekli olan alt sistemin (VES’nin) büyük ve küçük versiyon numaralarını tutar. Yapının MetaData elemanı ise metadata tablosunun RVA olarak edresini tutmaktadır[4].

    Örnek olarak verdiğimiz Sample.exe dosyasının başlık kısımlarını ildasm programı yardımıyla inceleyebilirsiniz. Bunun için ildasm, GUI modunda ya da /all ve /output
seçeneği ile console modda çalıştırılabilir. Örneğin:

ildasm /all /output:Sample.txt Sample.cs

Bu işlemden elde ettiğimiz
CLR başlık bilgileri şöyledir:

-
---- CLR Header:
 Header size:                        0x00000048
 Major runtime version:              0x0002
 Minor runtime version:              0x0005
 0x000020bc [0x000003b0] address [size] of Metadata Directory:       
 Flags:                              0x00000001
 Entry point token:                  0x06000001
 0x00000000 [0x00000000] address [size] of Resources Directory:      
 0x00000000 [0x00000000] address [size] of Strong Name Signature:    
 0x00000000 [0x00000000] address [size] of CodeManager Table:        
 0x00000000 [0x00000000] address [size] of VTableFixups Directory:   
 0x00000000 [0x00000000] address [size] of Export Address Table:     
 0x00000000 [0x00000000] address [size] of Precompile Header:

CLR başlık kısmının hex sistemdeki görüntüsü de şöyledir:



Biz bu makalede yalnızca
CLI ortamı bağlamında PE dosya formatındaki metadata tablo yapısı üzerinde duracağız. Bu nedenle PE dosya formatının ve CLI başlığının diğer ayrıntılarını burada ele almayacağız. Ayrıca aşağıdaki anlatımlarda artık CLI terimi yerine .NET ve VES terimi yerine de CLR terimlerini kullanacağız.

    Bir .NET programında gördüğümüz sınıflar, yapılar, arayüzler gibi tüm türler ve bu türlerin tüm elemanlarına ilişkin bilgiler derleme işlemi sırasında PE dosyasının metadata tablolarına yerleştirilmektedir. Yani adeta metada bilgileri bir .NET programını decompile edebilmek için gerekli olan tüm bilgileri içerir durumdadır. PE dosyası içerisine yerleştirilmiş bu metadata bilgileri CLR tarafından programın çalıştırılma sürecinin çeşitli aşamalarında kullanılır. Assembly dosyaları içerisindeki metadata bilgilerinin alınarak işlenmesine yansıtma (reflection) deniyor. Yansıtma işlemleri için Microsoft  tarafından pek çok sınıf tasarlamıştır. Bu sınıfların hepsi System.Reflection
isim alanında bulunmaktadır.

    PE dosya formatının metadata bölümü, ismine metadata kökü (metadata root) de denilen bir başlık kısmına sahiptir. Bu başlık kısmınının sonunda akım (stream) bilgilerinin tutulduğu bir akım başlık dizisi bulunur. Metadata başlığından (aynı zamanda akım başlık dizisinden) hemen sonra ise akımlar gelir. PE
formatının metadata organizasyonu aşağıdaki şekille gösterilebilir:


   

Metadata bölümünde toplam beş farklı akım söz konusu olabilir. Bunlar #Strings, #US, #Blob, #GUID, ve #~ isimli akımlardır. Makalenin konusu bakımından bunlardan en önemli olanı #~isimli akımdır. #~ akımı metadata tablolarını tutar. Fakat biz öncelikle Metadata başlığı (metadata root) üzerinde duralım. Metadata başlığının veri yapısı şöyledir:

Metadata Başlığı
 

Offset
(Decimal) 

Uzunluk
(Byte)

İsim

Anlamı

0

4

Signature

Sihirli Sayı: 0x424A5342.

4

2

MajorVersion

Versiyon büyük numara.

6

2

MinorVersion

Versiyon küçük numara.

8

4

Reserved

Ayrılmış alan, 0 bulunur.

12

4

Length

Versiyon yazısının byte uzunluğu. m ile temsil edelim.

16

m

Version

m byte uzunlukta UTF8 olarak kodlanmış versiyon yazısı.

16 + m

 

 

4 byte’ın katlarına tamamlamak için boşluk. Buranın offset'i olsun.

x

2

Flags

Ayrılmış alan, 0 bulunur.

x +  2

2

Streams

Kaynakların sayısı. n ile temsil edelim.

x + 4

 

StreamHeaders

n elemanlık StreamHdr yapı dizisi.

 
Sample.exe programının ildasm programı ile elde edilen metadata başlığı şöyledir:[5]

Metadata Header
    Storage Signature:
              0x424a5342 Signature
                  0x0001 Major Version
                  0x0001 Minor Version
              0x00000000 Extra Data Offset
              0x0000000c Version String Length
              'v2.0.50727' Version String
    Storage Header:
                    0x00 Flags
                  0x0005 Number of Streams
    Stream 1:
              0x0000006c Offset
              0x0000017c Size
              '#~' Name
    Stream 2:
              0x000001e8 Offset
              0x00000128 Size
              '#Strings' Name
    Stream 3:
              0x00000310 Offset
              0x00000024 Size
              '#US' Name
    Stream 4:
              0x00000334 Offset
              0x00000010 Size
              '#GUID' Name
    Stream 5:
              0x00000344 Offset
              0x0000006c Size
            '#Blob' Name

Tablonun
hex sistemdeki görüntüsü de şöyledir:



Signature 4 byte uzunluktadır ve 0x424A5342 sihirli sayısını tutar. MajorVersion ve MinorVersion
CLR ortamının versiyon numarasını belirtmektedir. Reserved alanda ise 0 bulunur. Bu alan şimdilik kullanılmıyor. Length versiyon belirten yazının UTF8 olarak byte uzunluğunu tutar. Bu alandan sonra burada belirtilen uzunlukta byte’tan oluşan UTF8 karakterleri gelmelidir.  Length alanı ile belirtilen byte sayısı 4’ün katlarına yukarıya doğru yuvarlanmıştır. Flags alanı da şimdilik kullanılmamaktadır. Bu alanda da 0 değeri bulunur. Metadata başlığının sonunda ise akım başlık dizisinin uzunluğu ve akım başlıkları bulunur.  Akım başlıkları (stream headers) her akımın yerini ve uzunluğunu tutmaktadır. Aşağıdaki veri yapısına sahiptir:
 
Akım Başlığı


Offset
(Decimal)

Uzunluk
(Byte)

İsim

Anlamı

0

4

Offset

Akımın metadata başlığından itibaren yeri.

4

4

Size

Akımın byte uzunluğu. Bu uzunluk 4’ün katları olmak zorundadır.

8

Değişken
Uzunlukta

Name

Akımın ASCII olarak null karakterle sonlandırılmış ismi. Buradaki karakter sayısı 4’ün katlarına ilişkin olmak zorundadır ve 4’ün katlarına tamamlamak için null karakterlerle doldurma yapılmıştır.İsim en fazla 32 karakter olabilir.

 
Offset alanı metadata başlığından itibaren ilgili akımın yerini, Size alanı ise akım bilgilerinin (akım başlığının değil) uzunluğunu belirtir. Name alanında akımın ismi vardır. Yukarıda da açıkladığımız gibi toplam 5 akım söz konusu olabilir. Bunlar #Strings, #US, #Blob, #GUID, ve #~ isimli akımlardır. Aynı isimli akımdan birden fazla bulunamayacağı gibi 5 akımın hepsi de bulunmak zorunda değildir. Yani bir akım boşsa buna ilişkin bir akım başlığı da yoktur. Ayrıca akım başlığının kendi uzunluğunun Name alanının uzunluğuna bağlı olduğuna (yani sabit uzunlukta olmadığına) dikkat ediniz. Tüm başlıkları ele geçirmek için Name alanında null karakter görene kadar ilerleyip null karakteri bulduğunuzda uzunluğu 4’ün katlarına tamamlamaya çalışmalısınız.

#Strings Akımı

    Program içerisindeki sınıf isimleri, yapı isimleri, değişken isimleri gibi tüm isimler bu akım içerisinde saklanmaktadır. Bu akımdaki tüm isimler
UTF8 olarak kodlanmıştır ve null karakter ile sonlandırılmıştır. Akımdaki ilk string ‘\0’ karakterden oluşan boş bir string olmak zorundadır. Ayrıca bu akımın tüm içeriği anlamlı yazılardan oluşmak zorunda da değildir. Akımın çöp değerler içeren kısımları da olabilir. Yukarıda verdiğimiz örnek programın #Strings akımı aşağıdaki gibidir:



#US ve #Blob Akımları

    Program içerisinde programcı tarafından kullanılmış olan string'ler
#US akımında saklanır (#US ismi User Strings’ten geliyor). Örneğin:

System.Console.WriteLine("Merhaba Metadata");

gibi bir ifadede System, Console, WriteLine isimleri #Strings akımında, “Merhaba Metadata” yazısı da #US akımında tutulmaktadır. Bu akımdaki string'ler 16 bit UNICODE biçiminde kodlanmıştır. #Blob akımı ise program içerisindeki çeşitli binary bilgilerin tutulduğu genel bir akımdır. #US ve #Blob akımları yapı olarak birbirine benzerler. Aralarındaki tek fark #US akımının yazıları tutması #Blob
akımının ise yazı dışında kalan binary bilgileri tutmasıdır.

    #US akımındaki her string'ten önce o string'in byte uzuluğu (karakter uzunluğu değil) bulunur. Ayrıca her string'in sonunda da içerisinde 0 ya da 1 değeri olan bir sonlandırıcı vardır. String'in byte uzunluğuna bu sonlandırıcı dahildir. Bu nedenle string'in byte uzunluğu her zaman tek sayı biçimindedir. (16 bit UNICODE karakterlerin her zaman çift sayı uzunluğunda olduğunu anımsayınız) #Blob akımında da her bilgiden önce onun byte uzunluğu kodlanmıştır. String'lerin ve blob elemanlarının byte uzunlukları duruma göre 1 byte, 2 byte 3 byte ya da 4 byte kullanılarak kodlanır. Kodlama şöyledir (b
’ler bitleri temsil ediyor):

- Eğer ilk byte 0bbbbbbb ise string ya da blob elemanı bbbbbbb
ile belirtilen uzunluktadır.

- Eğer ilk byte 10bbbbbb ve ikinci byte x ise string ya da blob elemanı ((bbbbbb << 8) + x)
uzunluğundadır.[6]

- Eğer ilk byte 110bbbbb ve sonraki 3 byte sırasıyla x, y, z biçimindeyse string ya da blob elemanı ((bbbbb << 24) +( x << 16)  + (y << 8) + z)
uzunluğundadır.

Stringler'de sonlandırıcı bulunduğunu ve bu sonlandırıcının da 0 ya da 1 olabileceğini belirtmiştik. Eğer string içerisindeki herhangi bir karakterin yüksek anlamlı byte’ı sıfır değilse ya da düşük anlamlı byte’ı [0x01, 0x08], [0x0E, 0x1F] aralığındaysa ya da düşük anlamlı byte’ı 0x27, 0x2D, 0X7F değerlerinden birine ilişkinse sonlandırıcı byte 1 değerinde, değilse 0 değerindedir. Ayrıca hem #US hem de #Blob akımlarının ilk byte’ının 0 olması zorunludur. Her iki akım da çöp değerlere sahip alanlara sahip olabilir. Örnek programımıza ilişkin #US
akımı şöyledir:



Örnek programımıza ilişkin #Blob
akımı ise şöyledir:



#GUID Akımı

Bu akımda 128 bit uzunluğunda
GUID (Globally Unique Identifier) değerleri tutulur. Örnek programımıza ilişkin GUID akımı şöyledir:



#~ Akımı Ve Metadata Tabloları

    Metadata tabloları ilişkisel bir veritabanı biçiminde #~ akımında tutulmaktadır. Her tabloda sütunlar (fields) ve satırlar (records) bulunur. Tablolar tam olarak normalize edilmiş durumdadır. Yani bilgiler farklı tablolarda yinelenmez; her bilgi yalnızca tek bir tabloda bulunur. #~ akımının veri yapısı şöyledir:

#~ Akımı

Offset
(Decimal)

Uzunluk
(Byte)

İsim

Anlamı

0

4

Reserved

Ayrılmış alan, 0 değeri bulunur.

4

1

MajorVersion

Tablonun büyük versiyon numarası, 2 olmalıdır.

5

1

MinorVersion

Tablonun küçük versiyon numarası, 0 olmalıdır.

6

1

Heap Sizes

Heap akımlarının(yani #String#GUID ve #Blob akımlarının) 2 byte ile mi yoksa 4 byte ile mi indeksleneceğini belirten bit dizisi .

7

1

Reserved

Ayrılmış alan, 1 değeri bulunur.

8

8

Valid

Metadata tablolarının hangilerinin bulunduğunu belirten bit dizisi. Buradaki 1 olan bitlerin sayısı n olsun.

16

8

Sorted

Sıraya dizilmiş tablolara ilişkin bit dizisi.

24

4 * n

Rows

4 byte uzunluktaki işaretsiz tamsayılardan oluşan dizi. Bu dizi tabloların satır sayılarını belirtir.

24 + 4 * n

 

Tables

Metadata tabloları sırasıyla burada bulunur.


Örnek programımızın #~ tablosu da aşağıdaki gibidir:

 


Tablodaki
HeapSize isimli 1 byte’lık alanın bazı bitleri bazı akımları indekslemek için 2 byte mı yoksa 4 byte mı gerektiği bilgisini verir. İlgili bit 0 ise akım 2 byte ile 1 ise 4 byte ile indekslenir. Şüphesiz bu durum aynı zamanda ilgili akımın 216 'dan büyük data içerip içermediği anlamına da gelmektedir.  Bitlerin ilişkili olduğu tablolar şöyledir:

Heap Size
Bit No

Akım

0

#Strings

1

#GUID

2

#Blob


Örnek programımızın
HeapSize alanında 0x00 değerinin bulunduğunu görüyorsunuz (offset: 0x32E) . Bu durumda #Strings, #GUID ve #Blob akımlarını indekslemek için 2 byte değer kullanılacaktır.

    Bir assembly PE dosyası içerisinde metadata tablolarının tümü bulunmak zorunda değildir. Her metadata tablosunun bir numarası vardır. İşte Valid isimli alan hangi metadata tablolarının bulunup bulunmadığını belirten bir bit dizisi biçimindedir. Bu bit dizisindeki her hangi bir bitin 1 olması o bite karşı gelen metadata tablosunun #~ akımında bulunduğunu, 0 olması ise bulunmadığını belirtmektedir. #~ akımında toplam n tane tablo mevcut ise bu tablolar Tables isimli alanda numara sırasına göre art arda bulunurlar. Örnek programımızdaki Valid
dizisi şöyledir:



Bu dizilim 8 byte’lık bir tamsayı (
QWORD) değer olarak yorumlanmalıdır. Böylece elde edilen değer 0x0000000901A21557 olur. Bunun da bitsel açılımı şöyledir:

0000 0000 0000 0000 0000 0000  0000 1001 0000 0001 1010 0010 0001 0101 0101 0111

Bu durumda örnek programımıza ilişkin assembly’de bulunan tabloların numaraları şunlardır (küçükten büyüğe): 0x00 (Module),  0x01 (TypeRef), 0x02 (TypeDef), 0x04 (Field), 0x06 (Method), 0x08 (Param), 0x0A (MemberRef), 0x0C (CustomAttribute), 0x11 (StandAloneSig), 0x15 (PropertyMap), 0x017 (Property), 0x18 (MethodSemantics), 0x20 (Assembly), 0x23 (AssemblyRef
). 

    Metadata tabloları sıralı olarak bulunabilmektedir. Sorted alanı hangi tabloların sıraya dizili bir biçimde bulunduğunu belirtir.
Rows dizisi tabloların satır uzunluklarını tutan 4 byte’lık (DWORD) tamsayılardan oluşan bir dizidir. Bu dizi assembly’de bulunan tablo sayısı kadar uzunluğa sahiptir. Örnek programımızda toplam 14 tane tablo olduğundan Rows dizisi de 14 uzunluğundadır. Örnek programımızdaki Rows dizisi şöyledir :



   
Metadata tablolarının ilişkisel bir veritabanı biçiminde organize edildiğini belirtmiştik. Yani metadata bilgileri tablolardan, tablolar da sütunlardan ve satırlardan oluşur. Tablolarda hangi sütunların bulunduğu ve bu sütunların hangi türden bilgiler içerdiği (yani tabloların şemaları) tabloların türlerine bağlıdır. Tüm tabloların  şemaları ECMA 335 Common Language Infrastructure standartlarının “Metadata logical format: tables” isimli 22’inci bölümünde açıklanmıştır. Bazı tabloların bazı sütunları başka tablolara referans eden indeks numaraları içeririrler. Tablodan tabloya geçiş bu indeks numaralarıyla yapılmaktadır. Örneğin, bir sınıf bildirimi temel olarak 0x02 (TypeDef) tablosunda bir satır belirtir. 0x02 (TypeDef) tablosu ise sınıfın veri elemanları için 0x04 (Field) tablosuna,  metotları için ise 0x06 (Method) tablosuna referans eden sütunlara sahiptir. Tabloların ilk satırlarının indeks numaraları 1’dir. Örneğin, 0x02 (TypeDef) tablosunun sütun yapısı size bir fikir verebilir:

0x02 (TypeDef) Tablosu

Flags

Name

Namespace

Extends

FieldList

MethodList

...

...

...


Tablodaki Flags alanı 4 byte uzunluğunda bitsel bir alandır.Türün erişim belirleyicisi ve diğer özellikleri bu alanda kodlanmıştır. Name alanı bildirilen tür ismini, Namespace alanı ise ilgili türün hangi isim alanı içerisinde bildirildiğini tutmaktadır. Bu iki alanda #Strings akımında indeks belirten tamsayı değerler bulunur. (#Strings akımındaki isimlerin ‘\0’ karakterle sonlandırıldığını anımsayınız.) #String akımını indeksleyen değerler  #~ akım başlığındaki HeapSize alanına bağlı olarak 2 byte ya da 4 byte uzunlukta olabilir. İsim alanlarının iç içe olsa bile noktalı biçimde tek bir isim gibi tutulduğunu da belirtelim. Extends alanı sınıfın taban sınıfını belirtir. Burada 0x02 (TypeDef) ya da 0x01 (TypeRef) tablolarına indeks verilmektedir. FieldList ve MethodList alanları ise ilgili türün sahip olduğu veri elemanları (fields) ve metotları belirtir. FieldList alanında 0x04 (Field) tablosuna, MethodList alanında da 0x06 (Method
) tablosuna ilişkin indeks değerleri tutulur. Türe ilişkin tüm veri elemanları ve metotlar bu tablolardaki ardışıl satırlarda bulunurlar. 

    Tablolardaki başka tablolara referans eden indeks numaraları 2 byte ya da 4 byte uzunlukta olabilir. Bu durum ilgili tablodaki toplam satırların 216 değerini aşıp aşmamasına göre belirlenmektedir. Ayrıca indekslerin yalın (simple) ve kodlanmış (coded) olmak üzere ikiye gruba ayrıldığını söyleyelim. Yalın indeksler belli bir tabloda yalnızca bir satır numarası belirtirler. Fakat kodlanmış indeksler tablo bilgisiyle satır numarasını birlikte tutarlar. Kodlanmış indekslerin düşük anlamlı belirli sayıda bitleri - o alan için söz konusu olabilecek – ilgili tabloyu, yüksek anlamlı diğer bitleri ise o tablodaki bir satırı belirtir. Örneğimizdeki 0x02 (TypeDef) tablosunun Extends alanına dikkat ediniz. (Taban sınıf aynı assembly içerisinde bildirilmiş bir sınıf ya da o assembly’den referans edilmiş sınıf olabilir.) CLI standartları Extends alanındaki indeksin 0x01 (TypeRef),  0x02 (TypeDef) ya da 0x1B (TypeSpec) tablolarından birine ilişkin olabileceğini belirtmektedir. Extends alanı için kodlanmış indeksin düşük anlamlı 2 bitinin belirtebileceği tablolar şunlardır:

TypeDef             0
TypeRef             1
TypeSpec            2

Örnek programımızda Extends alanında 0005 değeri vardır (offset: 3BC) . Bu değeri 0000 0000 0000 0101 biçiminde bitlerine açalım. Düşük anlamlı 2 bit 01 biçimindedir ve bu bitler TypeRef tablosunu belirtir. Geri kalan bitler 0000 0000 0000 01 TypeRef tablosunda 1 numaralı satırı belirtmektedir. Burada referans edilen sınıf Object sınıfıdır.[7]


Ecma 335’te tüm alanlarda söz konusu olabilecek tabloların neler olduğu ve hangi bitlerle temsil edildiği açıklanmıştır. Daha ayrıntılı bilgi için standartlara başvurmalısınız.

    Son olarak metadata tablolarından bilgileri sınıfsal bir temsille elde etmek için .NET sınıf kütüphanesinde Reflection isim alanındaki sınıfların kullanılabileceğini bir kez daha anımsatalım. Şüphesiz bu sınıflar bilgileri PE
formatının içerisinden yukarıda açıkladaığımız saf biçimiyle elde edip sınıf nesnelerine dönüştürmektedir.



[1]
Burada Microsoft’un <WinNT.h> başlık dosyası içerisindeki yapı isimlerini kullanıyoruz.

[2]
IMAGE_OPTIONAL_HEADER başlığı isminin yaptığı çağrışımın tersine bulunmak zorunda olan bir kısımdır.

[3] VES (Virtual Execution System) terimi CLI standartlarında kullanılan genel bir kavramdır. .NET için VES kavramı CLR (Common Language Runtime)
alt sistemine karşılık gelmektedir.

[4] RVA (Relative Virtual Address) dosya offset'i değildir, PE
dosyası belleğe yüklenip çeşitli hizalamalar yapıldıktan sonra yükleme yerinden itibaren göreli offset değerini belirtmektedir.

[5] Bu makalede ele alınan tüm başlık ve akım bilgileri Micosoft .NET Framework SDK içerisinde bulunan ildasm
programıyla elde edilebilir.

[6] ECMA 335 standartlarında bu konu ele alınırken << operatörünün + operatöründen daha düşük öncelikte olduğu göz ardı edilmiş. Bu açık bir hata gibi görünüyor. Bu hatayı standardizasyon komitesine rapor edebilirsiniz.

[7]
Örneğimiz için ildasm.exe ile elde edilen çıktıyı da ekler kısmında veriyoruz (Sample-Exe-Metadata.txt). Bu çıktıları da incelemelisiniz.

Kaynaklar 

1. Lidin, S. (2006). Expert .NET 2.0 IL Assembler. New York: Apress.

2. Microsoft Portable Executable and Common Object File Format Specification-Revision 8.1. (2008). URL: download.microsoft.com/download/9/c/5/.../pecoff_v8.docx

3. ECMA. (2009). Standard ECMA-335 Common Language Infrastructure(CLI), Partions I to IV. 4th Edition. 
http://www.ecma-international.org/ adresinden elde edilmiştir. 

İlgili Dosyalar
Sample-Exe-Metadata.txt Sample.cs

Haftanın Böceği Yukarı