Murat ERDEM - Personal Research Blog




PE Files - 0x04

July 27, 2020

En son bir PE dosyasındaki MZ Header alanından bahsetmiş ve bu alandaki verilere nasıl ulaşabileceğimizden bahsetmiştik şimdi ise bir sonraki alanımız olan PE File Signature ve PE Header alanından bahsedelim ve buradaki verilere nasıl erişebileceğimizi tartışalım.

Öncelikle aşağıdaki resmi hatırlayalım burada e_lfanew verisindeki değeri PE header alanına erişim için kullandığımızı söylemiştik.

PE başlığına basit bir matematikle aşağıdaki şekilde erişebiliriz.

Burada e_megic adres değeri 0x00 dır bu adreslerin gerçekte sıfır olmadığını bu değerin Image Base adrese olan uzaklığı temsil ettiğini unutmayalım.

e_lfanew değeri ise 0xF8 dir bu iki adresi toplayarak PE signature adresine ulaşabileceğimizi söylemiştik.

Bu matematik işlemini kullanarak bizde aşağıdaki kodlar ile PE signature adresine ulaşabiliriz. Bu adresteki bilgileri elde etmenin kolay yolu ise PIMAGE_NT_HEADERS32 yapısını kullanmaktır.

    
        PIMAGE_NT_HEADERS32 ntheader = (PIMAGE_NT_HEADERS32)(PImageHeader->e_lfanew + (LONG)fileData);
        cout << endl << "______PE_SIGNATURE______" << endl;
        cout << "PE SIGNATURE : " << hex << ntheader->Signature;
        cout << endl << endl;
    

Burada sadece imza yazdırmak için ayrı bir fonksiyon yazdırmaya gerek duymadım.Kullandığımız PIMAGE_NT_HEADERS32 yapısının tanımlanması ise aşağıdaki gibidir.

Burada signature değişkeni bizim PE ifademizin hexedecimal karşılığı 0x00004550 değerini tutmaktadır. Diğer değişkenler ise kendi içinde farklı bilgiler tutmaktadır. FileHeader içerisindeki verileri elde etmek için IMAGE_FILE_HEADER yapısını kullanabiliriz. IMAGE_FILE_HEADER yapısı ise aşağıdaki gibi tanımlanmıştır.

Bu veriler bizim için ifade ettiği anlam için ise genel olarak yazılan programın karakteristik özelliklerini tutuyor diyebiliriz. Hangi işlemci için yazıldığı, nasıl işleneceği gibi veriler içerir.

Örnek olarak burada NumberOfSection PE dosyamızdaki section sayısını verir sectionlara daha sonra değineceğiz ancak bu değere göre sectionlar doğru şekilde ayrılarak programın hatasız çalışması sağlanır ve bu değer en fazla 96 olabilir.

Machine değeri ise programın çalışması için gerekli işlemci mimarisini göstermektedir.

Bir başka değerimiz olan Characteristics değeri ise aşağıdaki gibi özellikleri bize göstermektedir, Bu özelliklerden birden fazlasını barındırıyorsa mantıksal “veya” işlemi ile ilgili değerleri birleştirerek tek bir değer olarak içerisinde tutar. Daha fazlasını windowsun dokümanlarında bulabilirsiniz.

SizeOfOptionalHeader ise bir sonraki veri alanı olan Optional header kısmının boyutunu tutmaktadır.

Optional Header kısmına geçmeden önce Image File Header kısmındaki bilgilere nasıl ulaşacağımıza bakalım. aşağıdaki kodlar bunun için yeterli olacaktır.

    
        IMAGE_FILE_HEADER imageFileHeader = (IMAGE_FILE_HEADER )ntheader->FileHeader;
        ImageFileHeaderWriter(imageFileHeader);
    

Burada ilgili verileri yazdırmak için kullanacağımız ImageFileHeaderWriter fonksiyonunu oluşturduk onun kodları ise aşağıdaki gibidir.

    
        void ImageFileHeaderWriter(IMAGE_FILE_HEADER ImageFileHeader) {
            cout << endl << "______PIMAGE_FILE_HEADER______" << endl;
            cout << "Characteristics : " << hex << ImageFileHeader.Characteristics << endl;
            cout << "NumberOfSections : " << hex << ImageFileHeader.NumberOfSections << endl;
            cout << "TimeDateStamp : " << hex << ImageFileHeader.TimeDateStamp << endl;
            cout << "PointerToSymbolTable : " << hex << ImageFileHeader.PointerToSymbolTable << endl;
            cout << "NumberOfSymbols : " << hex << ImageFileHeader.NumberOfSymbols << endl;
            cout << "SizeOfOptionalHeader : " << hex << ImageFileHeader.SizeOfOptionalHeader << endl;
            cout << "Characteristics : " << hex << ImageFileHeader.Characteristics << endl;
            cout << endl << endl << endl;
        }
    

Buraya kadar yaptıklarımızı bir test edecek olursak çıktımız aşağıdaki gibi olacaktır.