PE dosya formatı nedir
?
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~
PE dosya formatı Windows işletim sisteminin kullandığı
çalıştırılabilir,icra edilebilir dosya formatıdır. PE, Portable Executable’ın
kısaltılmış halidir.
Bu yazının amacı Windows işletim sistemi altında çalışan PE
formatının yapısını anlamaktır.
PE Genel Yapısı
~~~~~~~~~~~~~~~~~~~~~~~~
PE dosya formatının yapısı aşağıdaki resimde görülmektedir.
~~~~~~~~~~~~~~~~~~~~~~~~
PE dosya formatının yapısı aşağıdaki resimde görülmektedir.
PE dosyasının belleğe maplenişi(haritalanması) aşağıdaki
resimde görülmektedir.
Disk üzerindeki sectionlar
belleğe 4K şeklinde hizalanırlar. PE
dosyası Windows loader tarafından belleğe yüklendikten sonra , bellekteki bu
yapı artık MODULE olarak adlandırılır.
DOS MZ Header & DOS
stub
~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~
PE dosyası DOS işletim
sistemi altında çalıştırılmak istenildiğinde DOS işletim sistemi tarafından PE
dosyasının tanınması için PE dosyalarında bu başlık bulunur. 64 bayt
uzunluğundaki bu başlığın hemen ardından linkerlar tarafından otomatik olarak
yerleştirilen DOS Stub mevcuttur. DOS Stub, PE dosyasının DOS işletim sistemi
tarafından çalıştırılamayacağını belirten bir mesajı ekrana basar.
PE dosyasını DOS
işletim sistemi altında çalıştırmak istediğinizde DOS Stub tarafından
yukaridaki mesajı alırsınız.
DOS başlığının ilk 2
baytı Magic number olarak bilinir ve
Magic number “MZ” string değerini içerir.
Bu başlığın son
elemanı e_lfanew ise PE Header
başlığının dosya üzerinde nerede olduğunu belirten offset bilgisini barındırır.
Yukarıdaki resimde
e_lfanew, 0x000000f8 (little endian
formatta saklandığından tersten okumanız gerekmektedir.) değerini göstermektedir. Bu offset değeri PE Headerın başlangıcını göstermektedir.
PE Header
~~~~~~~~~~~~~~
~~~~~~~~~~~~~~
Bu başlık winnt.h
başlığı içerisinde IMAGE_NT_HEADERS olarak tanımlıdır. Windows loader
tarafından kullanılacak gerekli bilgiler bu başlıkta bulunur.
Signature , 4 baytlık alan işgal eder ve “PE” stringini
içerir. PE başlığının
Signature değeri
bir önceki resimde altı kırmızı ile çizilmişti.
FileHeader ve OptionalHeader ayrı başlıklar altında
inceleyerek yazımıza devam edelim.
OptionalHeader, FileHeader içerisinde belirt baytlık bir alanı
işgal eder.
IMAGE FILE HEADER– [ FileHeader ]-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FileHeader, 20 baytlık bir alanı işgal eder.
FileHeader, IMAGE_FILE_HEADER structının kendisidir ve
yukarıdaki resim IMAGE_FILE_HEADER
yapısını açıklar. Bu structın bazı elemanları açıklayalım.
Machine , Mimari bilgisini içerir. örneğin x86 mimari için Machine elemanı 0x014c değerine ve x64 mimari için 0x8664 değerine sahiptir.
NumberOfSections , PE dosyasındaki section sayısını
verir. Sectionlar bir PE dosyasını
kod,data gibi bilgileri içeren bölümlere ayırır. Windows loader tarafından bu
sectionlar belleğe Page tabanlı / Page Hizalamalı bir şekilde yüklenir.
PointerToSymbolTable, PE(Portable Executable) dosyalar için
buranın bir anlamı yok bu yüzden burası 0 değerine sahiptir. Fakat Windows
tarafında COFF/Object dosyaları(linkerlar tarafından input olarak kullanılabilirker ve bir çok object dosyası bir araya getirilerek
istenilirse PE executable dosya oluşturulabilir ) için burası dosya
içerisindeki sembol tablosunun offsetini belirtir. Tekrardan belirtelim PointerToSymbolTable , COFF(Windows işletim sisteminin Object dosyaları için kullandığı format
) için anlamlıdır.
NumberOfSymbols, COFF formatı için sembol tablosundaki
sembollerin sayısını verir. Windows PE/Çalıştırılabilir dosyaları için anlamsız
ve bu yüzden 0 değerine sahiptir.
SizeOfOptionalHeader, OptionalHeaderın uzunluğunu bayt
cinsiden belirtir.
Characteristics, PE formatına ilişkin bazı önemli bilgiler bu
alanda bitsel olarak kodlanmıştır. Her bir bit belli bir özelliği
belirtmektedir. Örneğin ; dosyanın executable bir dosya olduğunu veya dll
dosyası olduğunu , dosyanın 32-bitlik bir işlemci üzerinde çalışacağı , PE
dosyasının 2 GB dan daha fazla bir alanı kullanacağı gibi buna benzer
bilgiler burada belirtilir.
IMAGE OPTIONAL HEADER -[ OptionalHeader ]-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PE dosya formatında kesinlikle bulunması gereken bir
başlıktır. Yukarıda bahsettiğimiz üzere
Windows işletim sisteminin kullandığı Object dosya formatı COFF bu başlığı içermez.
Bu başlık içerisindeki bazı elemanları açıklayalım.
SizeOfCode, PE dosyasında bulunan kod sectionın uzunluğunu
belirtir.
SizeOfInitializedData, PE dosyasında bulunan data
sectionın(ilk değer ataması yapılmış global ve static değişkenlerin
değerlerinin tutulduğu yer) uzunluğunu
belirtir.
SizeOfUninitializedData, PE dosyasında bulunan ilk değer
ataması gerçekleştirilmemiş global ve
statik değişkenlerin uzunluğunu
belirtir.
Bu sayede Windows loader
startup aşamasında bellekte bu elemanın belirttiği kadar bir yer ayırır ve bu
alana ilk değer olarak 0 ataması yapılır.
ImageBase, Windows PE loader tarafından PE dosyası ImageBase
tarafından belirtilen adresten itibaren yüklenmeye başlanır. Executable dosyaların ImageBase
elemanı 0x00400000 adresini, DLL
dosyaların ise 0x10000000 adresini
gösterir. Burada bu adreslerin öncelikli adresler olması DLL veya Executable
dosyanın ImageBase tarafından belirtilen adrese yükleneceğini garanti etmez.
ImageBase içerisinde belirtilen adresin dolu olması durumunda Windows Loader ,
PE dosyasını başka bir adresten itibaren yüklemeye başlayacaktır.
AddressOfEntryPoint, PE dosyası belleğe yüklendikten ve
çalıştırılmaya hazır hale getirildikten sonra AddressOfEntryPoint elemanının
belirttiği adrese sıçrama yapılacak ve buradan itibaren PE dosyası çalıştırılmaya
başlanacaktır. Buradaki adres göreceli sanal adres ( Relative Virtual Address)
olarak bilinir. ImageBase adresi ile RVA(Relative Virtual Address) toplanarak
Sanal Adres/Virtual Adress elde edilir.
(*) Bir X adresinin Göreceli
Sanal Adres olması demek , X
adresinin
ImageBase’den X kadar uzak
olması demektir.
BaseOfCode, kod bölümünün başlangıcını belirten göreceli
sanal adresi(RVA) içerir.
BaseOfData, data bölümünün başlangıcını belirten göreceli
sanal adresi(RVA) içerir.
SectionAligment,
Sectionlar/Bölümler belleğe yüklenirken burada belirtilen değerin
katları şeklinde yüklenir/hizalanır. Örneğin;ilk section Windows loader
tarafından ve sectionın uzunluğu 28 bayt olsun.
İkinci section yüklenirken Windows loader 28.bayttan hemen sonra ikinci
sectionı belleğe yüklemez.
SectionAligment’ın
belirttiği değere göre ikinci sectionı hizalar. Eğer
SectionAligment’ın
değeri 4096(4K,varsayılan olarak böyledir) ise bu durumda 2.section
4096.bayttan itibaren yüklenmeye başlanacaktır.
FileAligment, PE
dosyasındaki bölümlerin disk üzerindeki hizalanışını ise FileAligment belirler.
Varsayılan olarak bu değer 512((1 sektör) değerine sahiptir. Bu durumda
bölümler disk üzerinde 512 nin katı şeklinde hizalanır.
SizeOfImage, PE dosyasının bellekteki boyutunu belirtir.
SizeOfHeaders, Tüm headerların(IMAGE_DOS_HEADER,IMAGE_FILE_HEADER,IMAGE_OPTONAL_HEADER) ile section
headerlarının(kısacası section tablosunun) toplamı,
FileAligment elemanın belirttiği değerin katına yuvarlanarak bu eleman
içerisine yazılır.
Subsystem, PE dosyasının
Windows uygulamasımı, Konsol
uygulamasımı , Mobil uygulamamı veya bir Windows sürücüsü olup olmadığı gibi
alt sisteme ilişkin bilgiler buranın yorumlanması ile elde edilir. Subsytem
değerlerini yorumlamacak için internet üzerinden kısa bir araştırma
yapabilirsiniz.(MSDN bununla ilgili geniş dökümana sahip)
DllCharacteristics, DLL karakterestiğine ilişkin bilgilerin
bitsel olarak kodlandığı alandır. DllCharacteristics elemanının değerlerine
ilişkin tabloya MSDN üzerinden göz
atabilirsiniz.
SizeOfStackReserve, Prosesin kullanacağı stack alanın
miktarını belirtir. Bir process veya thread yaratılırken stack alanın
miktarı bu değere göre belirlenir. Default
olarak bu değer 1 MB değerine setlenmiştir.
SizeOfStackCommit , Yukarıda belirttiğimiz SizeOfStackReserve
aslında prosesin sanal adresinde alan
yaratmaktatır. Bu yüzden prosesin/threadin stack gereksinimi fiziksel olarak
ram’den karşılanması gerekir. Fakat bu
alan SizeOfStackReserve’ün belirttiği kadar değil SizeOfStackCommit’in
belirttiği kadar ile karşılanır ve prosesin adres alanına maplenir. İlerde
ihtiyaç olması halinde ve SizeOfReserve aşılmadığı müddetçe Fiziksel alandan bu gereksinim
SizeOfStackCommit’lik bir alan ile
karşılanmaya devam edilir.
SizeOfHeapReserve, prosesin heap alanın boyutunu/miktarını
belirtir.
SizeOfHeapCommit, proses ilk oluşturulduğunda commit edilecek
olan alanın miktarını belirtir. Yukarıda Commit ve Reserve arasındaki farktan
bahsettiğimizden tekrardan burada neden böyle bir ayrım yapıldığından
bahsetmeye gerek yok.
LoaderFlags, ilerde kullanmak için rezerve edilmiştir.
NumberOfRvaAndSizes, bir sonraki IMAGE_DATA_DIRECTORY türündeki
DataDirectory yapısının kaç elemanı olduğu bilgisi burada tutulur.
IMAGE DATA DIRECTORY – [ DataDirectory ] –
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IMAGE_OPTIONAL_HEADER yapısının son elemanı olan
IMAGE_DATA_DIRECTORY türündeki DataDirectory bir veri dizinidir.
DataDirectory
yapı dizisi içerisinde çeşitli tablo ve dizinler hakkında bilgi barındırır.
DataDirectory’e ait kayıtlar yukarıdaki resimde
görülmektedir. Biz bu yazıda Export Directory, Import Directory ve Import
Address Table hakkında konuşacağız.
DataDirectory dizisinin elemanı olan Directory kayıtlarına
ilişkin yapı aşağıdaki resimde görülmektedir.
VirtualAdress, ilgili tablonun veya dizinin kendisini işaret eden RVA adresidir.
Size, ilgili tablonun veya dizinin uzunluğunu bayt cinsinden belirtir.
Bu DataDirectory tablosuna lord-pe programı ile kolayca göz atabilirsiniz.
(*) Yazının ilerleyen kısımlarında IAT,Import Table ve Export
Table directorylerinden bahsedeceğiz.
Yazının buraya kadar ki kısmında DOS MZ Header, DOS Stub, PE
Header kısımlarını tamamladık. Section Tablosunu inceleyerek devam edelim.
[---- Section Table
----]
~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~
Önceki kısımlarda da belirttiğimiz gibi sectionlar
disk üzerinde dosyanın mantıksal olarak bölümlere ayrılmış halidir. Örneğin; Kod
sectionında/bölümünde makine diline ait kodlar bulunmaktadır. PE dosyası
belirli sectionlara ayrılmıştır.
PE dosyalarını incelerken .text, .data gibi sectionlara karşı
geliceksiniztir. Fakat biz sectionların detayına girmeyeceğiz dilerseniz onları
araştırabilirsiniz. Bizim burada section
ile ilgili bilmemiz gereken disk üzerindeki dosyamızı mantıksal olarak
bölümlediğimizdir.
PE dosyası içerisinde sectionlara ait bilgiler section
tablolarında kayıtlar halinde tutulur. Her bir kayıt ilgili sectionın kendisi
hakkında bilgiye sahiptir. Windows loader belleğe PE dosyasını maplerken bu
bilgilerden faydalanır.
Section Tablosundaki her bir kayıt yukarıdaki
IMAGE_SECTION_HEADER yapısı ile
tanımlanmıştır.
Name, 8 bayt uzunluğundadır ve sectionın ismini
belirtir.ASCII string olmadığından dolayı null(0) karakteri ile bitmek zorunda değildir.
VirtualSize, Sectionın bellekteki uzunluğunu bayt cinsinden belirtir.
VirtualAddress, Sectionın RVA değeri. Örneğin ; VirtualAddress 0x1000 ise ve ImageBase
0x400000 ise bu durumda Windows pe loaderı bu sectionı 0x401000 adresinden itibaren yüklemeye
başlayacaktır.
SizeOfRawData, Sectionın disk üzerindeki boyutunu ifade eder.
Bu elemanın değeri FileAligment elemanın
katına yuvarlanmıştır.
(*) Eğer SizeOfRawData elemanın değeri VirtualSize elemanın
değerinden daha küçükse bu durumda geriye kalan alan bellekte 0 değerine
setlenir.
PointerToRawData, Sectionın başlangıc offset değerini
belirtir. Windows loader sectionların dosya üzerinde nerede olduğunu buraya
bakarak bulmaktadır.
PointerToRelocations , PointerToLinenumbers,
NumberOfRelocations, NumberOfLinenumbers .
Bu elemanlardan PointerToRelocations ve NumberOfRelocations
elemanları relocation işlemi gerçekleştirileceği zaman kullanılmaktadır.
Windows PE Loader eğer IMAGEBASE adresine PE dosyasını yükleyemezse PE
dosyasını uygun bir adrese yükler ve ardından .reloc sectionındaki bilgileri
kullanarak PE dosyası üzerinde gerekli
düzeltmeleri gerçekleştirir.
Characteristics , Sectionın karakteristiğine ilişkin
bilgileri tutar.
Örneğin ; ilgili sectionın yazılabilir,okunabilir,
çalıştırılabilir olup olmadığı gibi bilgiler buradan öğrenilir.
~~~~~~~~~~~~~~~~~~~
DLL dosyaları Windows OS tarafından direk olarak
çalıştırılabilir olan dosyalar değildir. EXE dosyaları tarafından bu DLL
dosyaları ateşlenebilir veya daha düzgün
ifade ile DLL içerisinde barındırılan fonksiyonlar EXE dosyaları tarafından
çağrılabilir/icra edilebilir.
DLL dosyası da tıpkı EXE dosyaları gibi aynı PE formatına
sahiptirler. DLL dosyalarının fonksiyonlarına bir başka modül
tarafından(bellekteki executable/dll dosyalarına modül denilmektedir ) arka planda nasıl erişilebildiğini anlamaya
çalışalım.
DLL dosyalarındaki fonksiyonlara diğer modüller ya fonksiyon
ismi ile ya da ordinal numaraları ile 2
farklı şekilde erişebilirler. Ordinal numarası DLL içerisindeki
fonksiyonlara özel 16-bitlik bir numaradır. Bu numaralar sayesinde DLL
içerisindeki her bir fonksiyon birbirinden ayrılmış olur.
Yazının önceki kısımlarında PE dosya formatının
DataDirectory dizisinden bahsetmiştik.
Bu dizinin elemanlarından birisi ve ilk elemanı da Export Directory.
Export Directory yapısı aşağıdaki resimde görülmektedir.
Buradaki elemanlardan bizim için önemli olanlara göz atalım.
Base, Başlangıc ordinal numarasıdır.
NumberOfFunctions , export edilmiş(dışarıdan
erişilebilmesi için) fonksiyonların sayısını belirtir.
NumberOfNames, isim ile export edilmiş fonksiyonların
sayısını belirtir. Tüm export edilmiş fonksiyonların sayısını belirtmez.
(*) Yukarıda belirttiğimiz üzere bir DLL dosyası ismi ve
ordinal numarası ile fonksiyonlarını export edebilir.
AddressOfFunctions, Export Address Tablosunu ( EAT ) işaret eden RVA değerini içerir. Bu Tablo
export edilmiş fonksiyonların RVA değerlerini içerir. Kısacası export edilmiş
fonksiyonların adreslerinin bir tabloda tutulduğunu düşünelim , Bu durumda
AddressOfFunctions bu tablonun başını gösteren bir pointer olarak
düşünebiliriz.
AddressOfNameOrdinals, Export Ordinal Tablosunu(EOT) işaret eden RVA değerini içerir. Bu tablo isim ile export edilmiş fonksiyonların ordinal değerlerini içerir.
Tüm bunları bir resim ile özetlemek isteseydik , kaynak
olarak da belirteceğim makaledeki gibi çizimlerdik.
Resimde anlatılmak isteneni bir örnek ile açıklayalım.
Bir DLL dosyası 20 tane fonksiyonu export etmiş olsun. Bu durumda
AddressOfFunction elemanın işaret ettiği Export Address Tablosunda 20 adres
olacaktır aynı şekilde NumberOfFunctions
elemanın değeri de 20 olacaktır.
Bu durumda bir fonksiyonu ismi ile bulmaya çalışalım. Windows
işletim sistemi öncelikle Export Directory içerisindeki NumberofNames ve
NumberOfFunctions değerlerine elde edecektir.Ardından AddressofNames elemanın
işaret ettiği Export Name Tablosunda bu fonksiyonu arayacaktır. Bu aramayı gerçekleştirirken
aynı zamanda Export Ordinal Tablosunda da paralel olarak ilerleyecektir.
Fonksiyonu Export Name Tablosu içerisinde bulması durumunda bu isme karşı düşen Ordinal değerini Export Ordinal Tablosundan elde edecektir. Bu Ordinal
numarasını Export Addres Tablosuna index olarak kullanarak ilgili fonksiyonun
Adresini elde edecektir.
20-fonksiyonlu-DLL dosyamızda aradığımız fonksiyon eğer
Export Name Tablosunda 13.sırada bulunan
topla() isimli bir fonksiyon ise bu
durumda Export Ordinal Tablosunda 13.
sıradaki ordinal numarasını alırız ve
Export Address Tablosuna index olarak verip, topla() fonksiyonunun
adresi elde etmiş oluruz.
Eğer elinizde zaten topla() isimli fonksiyona ait ordinal
numarası varsa bu durumda direk Export Address Tablosundan topla()
fonksiyonunun adres değerini elde edebilirsiniz. Gerçekte Ordinal numarasını
kullanarak fonksiyona ulaşmak isim ile o fonksiyona ulaşmaktan daha hızlıdır.
Fakat bunun dezavantajı yazılımcının DLL dosyasına yeni fonksiyonlar eklemesi
durumunda ve DLL dosyasının
güncellenmesi durumunda, Ordinal
numaralarının değişmesi durumudur. Bu durumda topla() isimli fonksiyonuna ait X
ordinal numarası güncelleştirilmiş DLL de Y isimli bir fonksiyona ait olabilir.
~~~~~~~~~~~~~~~~~~~
Windows Executable dosyaları ihtiyaç duydukları DLL dosyalarını kullanırlar. Executable dosyaların kullandıkları her bir DLL ve onlara ait fonksiyonların bilgisini Windows PE Loader Import Directory aracılığı ile öğrenir. Bu başlık Executable dosyaların kullandığı bu yapıyı açıklamaya çalışacağız.
Executable dosyaları tarafından importlanan/ithal edilen DLL
dosyaların her biri IMAGE_IMPORT_DESCRIPTOR yapısı ile tanımlanmıştır.
Eğer Executable dosyamız 10 tane farklı DLL dosyasını import
edecekse bu durumda 10 tane IMAGE_IMPORT_DESCRIPTOR yapısı Import Directory
içerisinde bulunacaktır.
Bu yapıda önemli olan
elemanları açıklayalım.
OriginalFirstThunk, IMAGE_THUNK_DATA yapısını işaret
eden RVA değerini içerir.
Name, DLL dosyasının isminin belirten RVA değerini
içerir.
FirstThunk, IMAGE_THUNK_DATA yapısını işaret eden RVA
değerini içerir.
IMAGE_THUNK_DATA yapısı ;
Disk
üzerindeki dosyada IMAGE_THUNK_DATA yapısı importlanmış fonksiyonun ordinal
numarasını ya da IMAGE_IMPORT_BY_NAME yapısını işaret eden RVA değerini içerir.
Ordinal
Numarasını bu yapının Ordinal elemanı belirtiken, IMPORT_BY_NAME yapısını
AddressOfData elemanı işaret eder.
Bu yüzden importlanan fonksiyonlara 2 türlü erişim sağlanabilir tıpkı bir önceki export ile ilgili başlıkta bahsettiğimiz gibi. Öncelikle IMAGE_IMPORT_BY_NAME yapısını açıklayalım.
Bu yüzden importlanan fonksiyonlara 2 türlü erişim sağlanabilir tıpkı bir önceki export ile ilgili başlıkta bahsettiğimiz gibi. Öncelikle IMAGE_IMPORT_BY_NAME yapısını açıklayalım.
IMAGE_IMPORT_BY_NAME
yapısı ;
Hint, fonksiyonu içeren DLL dosyasının Export Tablosunda
index olarak kullanılır. Fakat kimi zaman bu değer 0 olabilir bu durumda PE
loader bu değer ile ilgilenmez.
Name, importlanmış fonksiyonun ismini belirtir.
Daha önce de belirttiğimiz gibi eğer bir DLL executable
dosyasına import edilmişse bu durumda executable dosyanın Import Directory
içerisinde bu DLL
IMAGE_IMPORT_DESCRIPTOR yapısı ile ifade edilir.
Bu yüzden PE Loader Executable dosyasının çalışma anında
ihtiyaç duyacağı tüm DLL dosyalarını Import Directory dizisine göz atarak
prosesin sanal adres alanına yükler.
Bu işlemin nasıl
gerçekleştiğini anlatalım.
IMAGE_IMPORT_DESCRIPTOR yapısının elemanı olan
OriginalFirstThunk, IMAGE_THUNK_DATA yapısından oluşmuş diziyi işaret eder. Bu dizinin boyu DLL dosyasından import edilen
fonksiyonların adeti kadardır. IMAGE_IMPORT_DESCRIPTOR yapısının FirstThunk
elemanı ise tıpkı OriginalFirstThunk elemanı gibi IMAGE_THUNK_DATA yapısına ait
bir diziyi işaret eder. Başlangıc aşamasında bu iki dizi birbirinin aynısıdır. Bu
dizilerdeki IMAGE_THUNK_DATA yapısının
her biri yine IMAGE_IMPORT_BY_NAME yapılarını işaret eder. Bu yapılar Import
edilmiş Fonksiyonların isimleri tutarlar. Burada bulunan fonksiyonların
isimleri belirtilen DLL dosyası içerisinde bulunur. Bulunan fonksiyonların
adresleri FirstThunk ile işaret edilmiş dizinin ilgili elemanına yerleştirilir.
Bu şekilde FirstThunk ile işaret edilen dizi artık executable tarafından import
edilmiş olan tüm fonksiyonların adreslerini içeren tablo haline gelmiş olur. Bu
tabloya IMPORT ADDRESS TABLE denilmektedir.
OriginalFirstThunk tarafından işaret edilen diziye ise IMPORT NAME TABLE
denimektedir. Bu arada belki dikkatinizi çekmiştir herhangi bir şekilde Import
Directory eleman sayısını belirten herhangi bir eleman PE dosyasında
belirtilmemiştir bu durumda Loader kaç tane Executable dosya tarafından DLL
importlandığını bilemez. Bu yüzden Import Directory dizisinin son elemanı 0
değeri ile setlenmiştir.
(*) Bu anlattıklarımızı bir resim ile özetleyecek olursak
işte şöyle olurdu. Bu resim kaynak olarak belirtilen yazı/yazılardan alınmıştır.
Burada bahsetmiş olduğumuz importlama aslında fonksiyonların
isimlerine göredir. Kimi durumlarda bu ordinal ile de gerçekleştirilebilir. Bu
durumda IMAGE_THUNK_DATA yapısına ait
dizinin her bir eleman IMAGE_IMPORT_BY_NAME yapılarını işaret etmez. Bunun
yerine Ordinal numaraları taşırlar.Yukarıda IMAGE_THUNK_DATA yapısına göz
atarsanız bu yapının Ordinal elemanını göreceksiniz. Bu sefer PE Loader Ordinal
numarasını baz alarak DLL içerisinde fonksiyonun adresine ilişkin arama gerçekleştirecektir(DLL
içerisinde fonksiyonları arama işlemi bir önceki yazıda belirtilmiştir).
Peki PE Loader IMAGE_THUNK_DATA dizisindeki her bir elemanın
bir IMAGE_IMPORT_BY_NAME yapısını mı işaret ettiğini yoksa Ordinal numarası mı
saklı tuttuğunu nasıl bilebiliyor. Bunun cevabı IMAGE_THUNK_DATA dizisindeki
her bir elemanın en anlamlı bitinde saklıdır.
IMAGE_THUNK_DATA dizisindeki her bir elemanın en anlamlı
bitine bakılır. Eğer en anlamlı bit 0 ise bu durumda importlamanın isim ile
olduğunu eğer 1 ise bu durumda importlamanın Ordinal ile yapılacağını anlar.
Kaynaklar :
Portable Executable File Format – A Reverse Engineer View , Goppit
January 2006
EOF
Thank you muadil tonerler
YanıtlaSil