Biliyorum son yazımın ardından çok uzun zaman geçti. Bunun için hepinizden özür diliyorum. Vizeler, ödevler, RaConf derken bir blogumun olduğunu bile unuttum.
Sonunda ilk yazılarımdan biri olan Portable Executable File Format I ‘ın devam yazısını yazıyorum. Biliyorum o yazının ardından çok zaman geçti, o yüzden daha fazla uzatmadan devam ediyorum. (Not: Önceki yazıma buradan ulaşabilirsiniz.)
Section Table(Bölüm Tablosu)
Bölüm tablosu PE header ı ve raw data kısımları arasında bulunur. Bu tablo aslında her bir section’la alakalı kayıtları tutar. Daha ileri gitmeden sectionın ne olduğundan biraz bahsedelim.

P.E dosyası sectionlardan oluşur. Bu section’lar içindeki verinin nasıl bir yapıda olduğunu belirtir. Örnek vermek gerekirse code section’ında execute edilecek opcode lar bulunur. Data section’ında ilk değer verilmiş global ve statik veriler bulunur. Bss section‘ında ise ilk değer verilmemiş global ve statik veriler bulunur.
static int a = 0; // Data section
static int b; // Bss section
Farkettiğiniz gibi sadece global ve statik veriler section’larda saklanıyor. Lokal değişkenler data, bss gibi section’larda saklanmıyor. Bunun yerine programı execute ettiğimizde çalışma zamanında oluşturulup stack te depolanır ve gerektiğinde stack ten okunup gerekli işlem yapılır.
Şimdi section tablosunundaki IMAGE_SECTION_HEADER başlığını inceleyelim.
- BYTE Name [IMAGE_SIZEOF_SHORT_NAME] : Bu kısımda section ın ismi bulunuyor.
- union{DWORD PhysicalAddress, DWORD VirtualSize}Misc : Bu kısım EXE ve OBJ için farklı anlamlara geliyor. Exe için kodun veya verinin gerçek boyutunu tutuyor.
- DWORD VirtualAddress : Bu kısım EXE için anlamlıdır. Section ın nereye map edileceğinin RVA sını tutar.
- DWORD SizeOfRawData : Bu kısım section ın yuvarlanmış boyutunu tutar.
- DWORD PointerToRawData : Raw data nın adresini tutar.
- DWORD PointerToRelocations
- DWORD PointerToLinenumbers
- WORD NumberOfRelocations
- WORD NumberOfLinenumbers
- DWORD Characteristics : Section ın karakteristiğini belirler. 0x00000020 bu bölümde kodların olduğunu belirtir. 0x00000040 bu bölümde ilk değer verilmiş verilerin bulunduğunu belirtir. 0x00000080 bu bölümde ilk değer verilmemiş verilerin olduğunu belirtir. 0x20000000 bölümün executable olduğunu belirtir. 0x40000000 bölümün okunabilir olduğunu belirtir.
PE File Import
Bildiğiniz üzere yazdığımız programlarda başka dll lerdeki fonksiyonları kullanıyoruz. Bu fonksiyonları kullanabilmek için kullandığımız dll lerin kayıtlarının P.E dosya formatında bir yerde bulunması gerekiyor. Aksi halde bu fonksiyonları kullanamayız. İşte bu kayıtlar .idata section ında yer alıyor.
Loader ( P.E dosyamızı ram e map eden program) .idata section ındaki bilgileri kullanarak gerekli dll lerin ve fonksiyonların adreslerini executable image e bağlar.
.idata section ı (Import Table) IMAGE_IMPORT_DESCRIPTOR dizisi ile başlar ve execute için gerekli olan her bir dll için dizide bir eleman bulunur. Toplam kaç adet eleman olduğunu tutan bir şey olmadığından dizinin bittiği NULL karakteri ile anlaşılır.
IMAGE_IMPORT_DECRIPTOR yapısına göz atalım.
- DWORD Characteristics : Bu kısım IMAGE_IMPORT_BYNAME structure ını gösteren pointer bulunduruyor.
- DWORD TimeDateStamp : Dosya build edilirkenki tarih bulunuyor.
- DWORD ForwarderChain : Burada FirstThunk dizisinin index i bulunuyor. Bu kısım dll yönlendirme ile alakalı.
- DWORD Name : Tahmin edeceğiniz gibi dll in ismi bulunuyor burada.
- PIMAGE_THUNK_DATA FirstThunk : Bu kısımda IMAGE_THUNK_DATA union ının offset i bulunuyor. Union ı genellikle _IMAGE_IMPORT_BYNAME structure ının offseti olarak yorumlanır.
Farkedeceğiniz gibi Characteristics ve FirstThunk aynı yeri (_IMAGE_IMPORT_BYNAME) gösteriyor. FirstThunk ve Characteristics paralel olarak işliyor.

Yukarıdaki resimde gördüğünüz gibi FirstThunk‘ın gösterdiği IAT, loader tarafından tekrardan yazılıyor. Peki bunun nedeni ne?
Loader dizi içerisindeki pointerları gezerek her bir fonksiyonun adresini bulur, daha sonra _IMAGE_IMPORT_BYNAME pointerlarının yerine yazar.
PE File Export
Biz normalde başka dll lerin içerisindeki fonksiyonları kullanıyoruz(import), export bunun tam tersi bizim dışarıya kullanılabilecek bir fonksiyon vermemiz denebilir. Genellikle oluşturduğumuz programlarda export kısmı boş kalır, bu yüzden .edata section ı göremeyiz. Ben developer olmadığımdan bu kısımda fazla durmayacağım. Sadece IMAGE_EXPORT_DIRECTORY formatını verip geçeceğim.
- DWORD Characteristics
- DWORD TimeDateStamp
- WORD MajorVersion
- WORD MinorVersion
- DWORD Name
- DWORD Base
- DWORD NumberOfFunctions
- DWORD NumberOfNames
- *PDWORD AddressOfFunctions**
- *PDWORD AddressOfNames**
- *PWORD AddressOfNameOrdinals**
PE File Resources
Bu kısımda resim, dosya, program(bazı malware ler bunu kullanır)… gibi veriler saklanabilir. Dosya hiyerarşisi bulunur(linux a benzer).
- DWORD Characteristics
- DWORD TimeDateStamp
- WORD MajorVersion
- WORD MinorVersion
- WORD NumberOfNamedEntries
- WORD NumberOfIdEntries
- _IMAGE_RESOURCE_DIRECTORY_ENTRY DirectorEntries[]_

Relocation
Linker exe dosyasını oluşturduğunda, bu dosyanın ram de nereye map edileceğini varsayıp P.E dosyasına yazıyor. Peki herhangi bir nedenden dolayı linker ın karar verdiği adrese map edilemezse ne olacak?
İşte burada relocation devreye giriyor. Loader relocation bilgisini kullanarak dosya içerisindeki adresleri düzelterek sorunsuz bir şekilde çalışmasını sağlıyor.
Örnek vermek gerekirse: Elimizde bir exe dosyası var ve linker bu dosyanın base adresini 0x1000 olarak varsaymış. Dosya içerisinde 0x1234 adresinde ulaşmamız gereken bir stringimiz var. Eğer bizim dosyamız 0x1000 adresine map edilirse hiç bir sıkıntımız yok. Fakat dosyamız 0x2000 adresine map edildi. Eğer stringimize ulaşmaya çalışırsak ulaşamayız çünkü bizim stringimizin adresi dosya içerisinde 0x1234 adresinde gözüküyor ama şu anki durumda 0x2234 adresinde. İşte loader, relocation bilgisini kullanarak aradaki bu farkı düzeltip adres konusunda sıkıntı olmamasını sağlıyor.
Her relocation bilgisi IMAGE_BASE_RELOCATION structure ı ile başlıyor. Bu structure a göz atalım.
- DWORD VirtualAddress : Bu kısımda relocation ın başlangıç RVA sı tutuluyor.
- DWORD SizeOfBlock
Portable Executable File Format genel anlamda bu şekilde. Eğer hatam var ise lütfen yorumda belirtin, en kısa sürede düzeltmeye çalışacağım. Daha fazla bekletmemek için bu yazıyı acele bir şekilde yazdım, daha sonra gerekli gördüğüm yerleri düzeltebilir yada yeniden yazabilirim. Şimdilik görüşmek üzere 🙂
Be First to Comment