Portable Executable Dosya Formatında CLI MetadataTabloları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:

// 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; }
}
}
}
Makale içerisindeki anlatımları desteklemek amacıyla aşağıdaki örnek bir C# programından faydalanacağız:
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 | Uzunluk | İ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 x 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 | Uzunluk | İ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 | 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 | Uzunluk | İ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 | 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. Standard ECMA-335 Common Language Infrastructure(CLI), Partions I to IV. 4th Edition. (2009). http://www.ecma-international.org/ adresinden elde edilmiştir.
