Murat ERDEM - Personal Research Blog




PE Files - 0x06

August 05, 2020

Bu bölümde artık bahsettiğimiz sectionların görevleri ve önemini anlayacağız.

Öncelikle CFF’e bakacak olursak Section Header alanının Optional Header alanından hemen sonra geldiğini görebiliriz. PE dosyalarında da konumu Optional Headerin hemen sonrasıdır. Bu alana erişmek için basit bir matematik yapmamız gerekecek. Kodlama kısmından önce bu alanda ne bulunduğunda bakalım.

Bu alan aslında 40 byte uzunluğunda arka arkaya sıralanmış yapılardır. File Header kısmında kaç adet section olduğunu tespit etmiştik. Bu alan o sectionların genel bilgisini tutan kısımdır ve aşağıdaki gibi tanımlanırlar.

Bu verilerden VirtualAddress bizim için belki en önemlilerindendir. Bu adres bize bu sectionun bellek üzeriden nereye yükleneceğini belirtir ancak DLL’ler bu kurala uymayabilirler. Bir diğer önemli alan PointerToRawData adresidir bu belleğe yüklenecek verilerin tutulduğu adresi göstermektedir.

Section Headerin başlangıç noktasına ulaşamak için basit bir matematik işlemi yapacağımızı söylemiştik şimdi öncelikle bizim IMAGE_NT_HEADERS32 tipinde bir ntheader değişkenimiz vardı. Bu adresten sonra DWORD tipinde bir PE imzamız ve IMAGE_FILE_HEADER tipinde bir yapımız vardı bunlara ek olarak IMAGE_OPTIONAL_HEADER yapımız vardı ve bunun içerisindeki verilerin olup olmayacağı konusunda kesin bir şey söylemek zordu ancak bu yapının boyutu IMAGE_FILE_HEADER içerisinde tanımlı olarak bulunmaktaydı biz bu yapıların boyutu kadar IMAGE_NT_HEADERS32 adresinden ilerlersek Section headerin ilk tanımlı olduğu adrese ulaşabilriz. Bunu yapan kod aşağıdaki gibidir.

    
        DWORD sectionPoint = (DWORD)ntheader + sizeof(DWORD) + (DWORD)sizeof(IMAGE_FILE_HEADER) + (DWORD)imageFileHeader.SizeOfOptionalHeader;
    

Bu adres bizim ilk noktamızı temsil etmekte buradan sonra header bilgilerini ekrana yazdırabiliriz.

    
        cout << endl << "______PIMAGE_SECTION_HEADER______" << endl;
        for (int i = 0; i < imageFileHeader.NumberOfSections; i++) {
            PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)sectionPoint;
            sectionHeaderWriter(sectionHeader);
            sectionPoint += (DWORD)sizeof(IMAGE_SECTION_HEADER);
        }
    

Burada ekrana yazdırmak için sectionHeaderWriter fonksiyonunu kullandık daha sonra ise bir sonraki section değerine zıplamak için section header boyutu kadar adresimizde ileriye gittik. sectionHeaderWriter fonksiyonu aşağıdaki gibidir.

    
        void sectionHeaderWriter(PIMAGE_SECTION_HEADER sectionHeader) {
            cout << "Name : " << hex << sectionHeader->Name << endl;
            cout << "Misc PhysicalAddress : " << hex << sectionHeader->Misc.PhysicalAddress << endl;
            cout << "Misc VirtualSize : " << hex << sectionHeader->Misc.VirtualSize << endl;
            cout << "VirtualAddress : " << hex << sectionHeader->VirtualAddress << endl;
            cout << "SizeOfRawData : " << hex << sectionHeader->SizeOfRawData << endl;
            cout << "PointerToRawData : " << hex << sectionHeader->PointerToRawData << endl;
            cout << "PointerToRelocations : " << hex << sectionHeader->PointerToRelocations << endl;
            cout << "PointerToLinenumbers : " << hex << sectionHeader->PointerToLinenumbers << endl;
            cout << "NumberOfRelocations : " << hex << sectionHeader->NumberOfRelocations << endl;
            cout << "NumberOfLinenumbers : " << hex << sectionHeader->NumberOfLinenumbers << endl;
            cout << "Characteristics : " << hex << sectionHeader->Characteristics << endl;
            cout << endl << endl << endl;
        }
    

Bir sectionun yapısı genel olarak bu şekilde ifade edilebilir. Birde bu section türlerinin ne işe yaradığına bakalım.

Windows sistemlerde genel olarak dokuz adet ön tanımlı sectionu vardır. Ancak bunların hepsi bir uygulamada olmak zorunda değildir uygulama ihtiyaç duyduğu kadar section oluşturur. gereksinim duyarsa dokuzdan daha fazlasını da kullanabilir.

.text sectionu

Bu section “executable code section” olarak bilinir ve uygulamanın yürütülebilir kodları bu alanda bulunur.

bss, .data, .rdata (Data Sections)

.bss sectionunda programın statik olarak tanımlı verileri tutulur.

.data sectionunda program tarafından erişilebilen global değişkenler tutulur.

.rdata sectionunda sadece okuma yetkisi olan veriler tutulur.

.rsrc sectionu (Resource)

Bu section alanında programın kullanacağı dosyalar bulunur. Bu bölüm ağaç yapısında tanımlanmıştır.

Bu yapının ilk katmınında ihtiyac duyulan dosyanın tipine göre ayrım yapılır bu kök alan IMAGE_RESOURCE_DIRECTORY olarak tanımlanmıştır. Burada tipten kasıt menu, icon, string gibi erişilmek istenen dosyanın tipidir. Bu alanlar da arka arkaya sıralanmış haldedir.

Burada hangi tip dosyaya erişilmek istendiği kararlaştırıldıktan sonra bir alt ağaçta hangi dosyanın istendiğini bulmak için isim karşılaştırması yapılır bu alanın tanımlanması aşağıdaki gibir.

Burada dosya ismi benzersizdir hedef dosyayı doğru bir şekilde bulmak için kullanılır. Dosya doğru bir şekilde tespit edildikten sonra konumunu bulmak için IMAGE_RESOURCE_DATA_ENTRY yapısı kullanılmaktadır bu yapı aşağıdaki gibi tanımlanmıştır.

Bu yapıda OffsetToData resource’un adresini, Size de boyutunu tutar. Burada dikkat etmemiz gereken nokta OffsetToData adresi Image Base adresine göre değil resource’un kök dizininin ilk pointine olan uzaklığını temsil etmektedir.

.edata section

Bu bölüm export edilecek olan veri dizinlerini içerir. yapısı IMAGE_EXPORT_DIRECTORY olarak aşağıdaki şekilde tanımlanmıştır.

Bu yapıda;

Name: yürütülebilir dosya adını,

NumberOfNames: modülde bulunan export edilecek olan fonksiyon isimlerini,

NumberOfFunctions: export edilecek fonksiyon sayısını,

AddressOfFunctions: export edilecek fonksiyon adreslerinin tutulduğu bir listeyi,

AddressOfNames: dışa aktarılan fonksiyonların isimlerinin tutuluğu listenin offset adresini tutar.

AddressOfNameOrdinals: AddresOfNames deki isimlerin sıra değerini tutan bir listenini offsetidir. Listenin her bir elemanı 2 byte boyutundadır.

.idata section (Import Data Section)

Bu bölümde import edilen veriler import addres name table ve diğer dâhil edilen veriler bulunur.

debug section

Normalde hata ayıklama bilgileri rdata sectionunda bulunur ama program çalışmaya başladığında bu bölüme yüklenirler. Bu bölümün tanımı aşağıdaki gibidir.

Bu yapıda AddressOfRawData bellek üzerindeki adresi PointerToRawData PE dosyası üzerindeki adresi göstermektedir. Buradaki hata ayıklama dosyaları .DBG uzantılı dosyalar olarak dışarıya çıkartılabilir. Ayrıca hata ayıklama tipleri ise aşağıdaki gibi tanımlanmaktadır.

Bir PE dosyasının genel yapısını bu şekilde açıklayabiliriz buraya kadar gördüklerimizi tekrar gözden geçirecek olursak detaylı yapısı aşağıdaki gibi ifade edilebilir.