Bir **cybersecurity şirketinde mi çalışıyorsunuz? Şirketinizi HackTricks'te reklamını görmek ister misiniz? ya da PEASS'ın en son sürümüne erişmek veya HackTricks'i PDF olarak indirmek ister misiniz? ABONELİK PLANLARI'na göz atın!
Önceki programda 9 program başlığı bulunmaktadır, ardından segment eşlemesi her bölümün hangi program başlığında (00 ile 08 arasında) bulunduğunu gösterir.
PHDR - Program Başlık
Program başlık tablolarını ve meta verileri içerir.
INTERP
Binary'nin belleğe yüklenmesi için kullanılacak yükleyicinin yolunu gösterir.
LOAD
Bu başlıklar, bir binary'nin belleğe nasıl yükleneceğini göstermek için kullanılır.
Her LOAD başlığı, bir bellek bölgesini (boyut, izinler ve hizalama) ve ELF binary'sinin oraya kopyalanacak baytlarını belirtir.
Örneğin, ikincisi 0x1190 boyutunda olup, 0x1fc48'de yer almalıdır, okuma ve yazma izinleriyle ve 0xfc48 ofsetinden 0x528 ile doldurulacaktır (tüm ayrılmış alanı doldurmaz). Bu bellek, .init_array .fini_array .dynamic .got .data .bss bölümlerini içerecektir.
DYNAMIC
Bu başlık, programları kütüphane bağımlılıklarına bağlamaya ve yer değiştirmeleri uygulamaya yardımcı olur. .dynamic bölümünü kontrol edin.
NOTE
Binary hakkında satıcı meta veri bilgilerini depolar.
GNU_EH_FRAME
Hata ayıklama araçları ve C++ istisna işleme çalışma zamanı işlevleri tarafından kullanılan yığın açma tablolarının konumunu tanımlar.
GNU_STACK
Yığın yürütme önleme savunmasının yapılandırmasını içerir. Etkinleştirilirse, binary yığından kod çalıştıramaz.
GNU_RELRO
Binary'nin RELRO (Relocation Read-Only) yapılandırmasını belirtir. Bu koruma, program yüklendikten sonra ve çalışmaya başlamadan önce belleğin belirli bölümlerini (örneğin GOT veya init ve fini tabloları) salt okunur olarak işaretler.
Önceki örnekte, 0x3b8 baytın 0x1fc48'e salt okunur olarak kopyalandığı ve .init_array .fini_array .dynamic .got .data .bss bölümlerini etkilediği belirtilmiştir.
RELRO'nun kısmi veya tam olabileceğini unutmayın, kısmi sürüm .plt.got bölümünü korumaz, bu bölüm tembel bağlama için kullanılır ve konumları arandığında kütüphanelerin adresini yazmak için bu bellek alanının yazma izinlerine ihtiyaç duyar.
TLS
TLS girişlerinin bir tablosunu tanımlar, bu tablo konu bazlı değişkenler hakkında bilgi depolar.
Bölüm Başlıkları
Bölüm başlıkları ELF binary'sine daha detaylı bir görünüm sağlar.
String tablosu: ELF dosyası tarafından gereken tüm dizeleri içerir (ancak program tarafından gerçekten kullanılanlar değil). Örneğin, .text veya .data gibi bölüm adlarını içerir. Ve eğer .text dizinindeki 45. sırada ise, name alanında 45 numarasını kullanacaktır.
Dize tablosunun nerede olduğunu bulmak için ELF, dize tablosuna bir işaretçi içerir.
Sembol tablosu: İsim (dize tablosundaki ofset), adres, boyut ve sembol hakkında daha fazla meta veri gibi semboller hakkında bilgi içerir.
Ana Bölümler
.text: Programın çalıştırılacak talimatları.
.data: Programda tanımlı bir değere sahip global değişkenler.
.bss: Başlatılmamış global değişkenler (veya sıfıra başlatılmış). Buradaki değişkenler otomatik olarak sıfıra başlatıldığından, gereksiz sıfırların ikili dosyaya eklenmesini engeller.
.rodata: Sabit global değişkenler (salt okunur bölüm).
.tdata ve .tbss: .data ve .bss gibi, thread-local değişkenler kullanıldığında (__thread_local C++ veya __thread içinde).
.dynamic: Aşağıya bakınız.
Semboller
Semboller, programdaki bir fonksiyon, global veri nesnesi, thread-local değişkenler gibi adlandırılmış bir konumdur.
readelf -s lnstat
Symbol table '.dynsym' contains 49 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001088 0 SECTION LOCAL DEFAULT 12 .init
2: 0000000000020000 0 SECTION LOCAL DEFAULT 23 .data
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtok@GLIBC_2.17 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND s[...]@GLIBC_2.17 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.17 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs@GLIBC_2.17 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.17 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (3)
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND perror@GLIBC_2.17 (2)
10: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
11: 0000000000000000 0 FUNC WEAK DEFAULT UND _[...]@GLIBC_2.17 (2)
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND putc@GLIBC_2.17 (2)
[...]
Her sembol girişi şunları içerir:
Ad
Bağlama özellikleri (zayıf, yerel veya global): Yerel sembol yalnızca program tarafından erişilebilirken, global semboller program dışında paylaşılır. Zayıf bir nesne örneğin farklı bir nesne tarafından geçersiz kılınabilir.
Tür: NOTYPE (belirtilmemiş tür), OBJECT (global veri değişkeni), FUNC (fonksiyon), SECTION (bölüm), FILE (hata ayıklayıcılar için kaynak kod dosyası), TLS (iş parçacığı yerel değişkeni), GNU_IFUNC (yeniden konumlandırma için dolaylı fonksiyon)
Yükleyici, bağımlılıkları yükledikten sonra yeniden konumlandırmak zorundadır. Bu relokasyonlar, REL veya RELA formatındaki relokasyon tablosunda belirtilir ve relokasyon sayısı dinamik bölümlerde RELSZ veya RELASZ olarak verilir.
Eğer program tercih edilen adresten farklı bir yere yüklendiyse (genellikle 0x400000) çünkü adres zaten kullanılıyor veya ASLR veya başka bir nedenle, statik bir yer değiştirme, binary'nin tercih edilen adreste yüklendiğini bekleyen değerlere sahip olan işaretçileri düzeltir.
Örneğin, R_AARCH64_RELATIV türünde herhangi bir bölüm, yer değiştirme sapması artı eklenen değerindeki adresi değiştirmiş olmalıdır.
Dinamik Yer Değiştirmeler ve GOT
Yer değiştirme aynı zamanda harici bir sembolü de referans alabilir (örneğin, bir bağımlılıktan bir işlev). Örneğin, libC'den malloc işlevi. Ardından, libC'yi yüklerken malloc işlevinin yüklendiği yeri kontrol eden yükleyici, bu adresi malloc'un adresinin belirtilmesi gereken GOT (Global Offset Table) tablosuna (yer değiştirme tablosunda belirtilen) yazacaktır.
Prosedür Bağlantı Tablosu
PLT bölümü tembel bağlama gerçekleştirmeyi sağlar, yani bir işlevin konumunun çözülmesi, erişildiğinde ilk kez gerçekleştirilecektir.
Bu nedenle bir program malloc'a çağrı yaptığında, aslında malloc'un PLT'deki karşılık gelen konumunu (malloc@plt) çağırır. İlk kez çağrıldığında malloc'un adresini çözer ve saklar, böylece bir sonraki malloc çağrıldığında, bu adres PLT kodu yerine kullanılır.
Program Başlatma
Program yüklendikten sonra çalışma zamanı gelir. Ancak, çalıştırılan ilk kod her zaman main fonksiyonu değildir. Bu, örneğin C++'da bir global değişkenin bir sınıfın bir nesnesi olduğunda, bu nesnenin main çalışmadan önce başlatılması gerektiği için olabilir, örneğin:
Bu global değişkenler .data veya .bss içinde bulunur ancak __CTOR_LIST__ ve __DTOR_LIST__ listelerinde bunları takip etmek için sırayla başlatılacak ve yok edilecek nesneler saklanır.
C kodundan aynı sonucu elde etmek GNU uzantıları kullanılarak mümkündür:
__attributte__((constructor)) //Add a constructor to execute before__attributte__((destructor)) //Add to the destructor list
Derleyici perspektifinden, main fonksiyonundan önce ve sonra bu işlemleri gerçekleştirmek için, INIT ve FIN olarak dinamik bölümde referans alınacak init ve fini fonksiyonları oluşturmak mümkündür ve bunlar ELF'in init ve fini bölümlerine yerleştirilir.
Bahsedildiği gibi diğer seçenek, dinamik bölümde INIT_ARRAY ve FINI_ARRAY girişlerinde __CTOR_LIST__ ve **__DTOR_LIST__ listelerine referans vermek ve bunların uzunluğu INIT_ARRAYSZ ve FINI_ARRAYSZ ile belirtilir. Her giriş, argümansız çağrılacak bir fonksiyon işaretçisidir.
Ayrıca, INIT_ARRAY işaretçilerinden önce çalıştırılacak işaretçiler içeren bir PREINIT_ARRAY oluşturmak da mümkündür.
Başlatma Sırası
Program belleğe yüklenir, statik global değişkenler .data içinde başlatılır ve başlatılmamış olanlar .bss içinde sıfırlanır.
Program veya kütüphaneler için bağımlılıklar başlatılır ve dinamik bağlantı gerçekleştirilir.
PREINIT_ARRAY fonksiyonları çalıştırılır.
INIT_ARRAY fonksiyonları çalıştırılır.
INIT girişi varsa çağrılır.
Bir kütüphane ise, dlopen burada biter, bir program ise, gerçek giriş noktası (main fonksiyonu) çağrılma zamanıdır.
İplik Yerel Depolama (TLS)
Bu değişkenler C++'da __thread_local anahtar kelimesi veya GNU uzantısı __thread kullanılarak tanımlanır.
Her iplik bu değişken için benzersiz bir konum tutar, bu nedenle yalnızca iplik değişkenine erişebilir.
Bu kullanıldığında, ELF'de .tdata ve .tbss bölümleri kullanılır. Bunlar, TLS için .data (başlatılmış) ve .bss (başlatılmamış) gibi.
Her değişken, boyutu ve TLS ofsetini belirten bir girişe sahip olacaktır, bu da ipliğin yerel veri alanında kullanacağı ofseti belirtir.
__TLS_MODULE_BASE, bir modülün tüm iplik yerel verilerini içeren bellekteki alanı işaret etmek için kullanılan bir semboldür.