10 Mayıs 2015

Virüs 101 – Nasıl Virüs Yazılır – Bölüm 1

Zararlı yazılımlar on yıllardır bilişim dünyasının gündemindedir. Etkileri iş dünyasında bilinir ve bu yüzden pek çok antivirüs üreticisi ticari olarak başarılı olabilmiştir. Hatta antivirüs üreticisi olarak başlamış ve satın alma yoluyla bilgi güvenliği teknolojilerinin diğer alanlarına el atmış firmalar da bulunmaktadır. Ancak zararlı yazılımların ne olduğu, tam olarak ne yaptıklarını ve nasıl yaptıklarını bilen sayısı çok fazla değildir. Zararlı yazılımlarla ilgili çıkan yazılar, haberler daha çok; aman dikkat, bakın çok zeki insanlar var sistemlerinizi ele geçiriyor tarzında hedefi olmayan korku yaratma türünde veya daha saygıdeğer bir kategori olarak değerlendirebileceğimiz zararlı yazılımların dinamik analizi ve çok az bir kısmı da statik analizi ile tersine mühendislik çalışmalarının yayınlanmasından oluşmaktadır.


Virüsler zararlı yazılımların ilk türü olarak bilinir, ancak virüs teknolojisinin halen zararlı yazılım türleri arasındaki en sofistike teknoloji olduğu gerçeği zararlı yazılımların nasıl geliştirildiği konusundaki bilgi ve yetkinlik eksikliği nedeniyle bilinmez. Virüs’lerin teknik uzmanlık seviyesi ile yarışabilecek tek zararlı yazılım teknolojisi Rootkit teknolojisidir. Ancak virüs’ler ve rootkit’lerin ayrıştığı çok önemli bir nokta vardır, virüsler çalıştırılabilir dosya formatı ağırlıklı bir bilgi gerektirirken rootkit’ler işletim sistemi mimarisi ağırlıklı bir bilgi gerektirir. Dolayısıyla çok farklı türler olduklarını söyleyebiliriz. Bunun yanında elbette işlemci mimarisi ve hafıza yönetimi gibi konularda ortak uzmanlıkları da içermektedirler.

Basit bir zararlı yazılım herhangi bir yazılımdan farklı değildir. Sosyal mühendislik yöntemleri ile kullanıcıların aldatılarak çalıştırmaya yönlendirildikleri çalıştırılabilir kodlarda güvenlik uzmanlığı olarak kayda değer çok az bir bilgi birikimi olduğunu söyleyebiliriz. Bu bilgilere örnek olarakta her zararlı yazılımın sahip olması gereken varlığını sürdürme yeteneği ve dolayısıyla işletim sistemi reboot işleminin özelliklerinden faydalanarak her sistem açılışında aktif hale gelebilme (bu aktifleşme ihtiyacı için farklı işletim sistemlerinin farklı özellikleri de kullanılabilir, örneğin Windows için belli bir türdeki dosyanın her açılışında veya çalıştırılışında çalıştırılacak kodlara referans veren register anahtarlarının kullanılması gibi) ve mümkün olduğunca yayılma (dosya paylaşımları aracılığı ile yayılma, kendini taşınabilir belleğe kendini kopyalama, kurban bilgisayar üzerindeki e-posta istemcisinde kayıtlı e-posta hesaplarına kendini postalama gibi) bilgileri sayılabilir.

Yukarıda belirtilen nedenlerle herhangi bir dilde yazılım geliştiren herhangi bir yazılım geliştirici yukarıdaki fonksiyonel ihtiyaçları yerine getiren bir zararlı yazılım geliştirebilir. Bu zararlı yazılım fonksiyonel gereksinimlerine ilişkin çok fazla kişinin bilgi ve deneyim sahibi olmaması sadece bu alanda yasal gelir imkanının sınırlı olmasından kaynaklanmaktadır. O yüzden bu tür yazılımları geliştiren kişiler alınmasın ama pek de özel değiller, milyonlarca yazılım geliştirme uzmanından hiçbir farkları yok. Sadece hayat onları bir şekilde karanlık tarafa yönlendirmiş.

Virüs yazılımlarının sıradan yazılım geliştiriciler tarafından geliştirilmesi ise mümkün değildir. Yazılımcı arkadaşlar kızmadan önce açıklamama izin verin lütfen; Assembly dili de dahil olmak üzere uygulama geliştirme dillerinin sağladığı değişken ve fonksiyon adres referans imkanları ve derleyiciler ve linkleyiciler tarafından bu adreslerin çözümlenmesi sayesinde adresleme konusu yazılımcılar için düşünülmesi gereken bir konu olmaktan çıkar. Ayrıca import edilen fonksiyonların adresleri hakkında düşünmeye de ihtiyaçları yoktur. Çünkü import edilen kütüphaneler çalışma anında OS loader tarafından hafızaya yüklenir (daha doğrusu uygulamanın sanal hafıza alanına haritalanır) ve kütüphane içindeki fonksiyon adresleri çözümlenir. Yazılımcının istese de bu adresleri önceden bilme şansı yoktur.

Virüs yazarının koşullarını değerlendirirsek; virüs kodu farklı bir çalıştırılabilir kodun disk üzerindeki imajının içine (veya sonuna) eklenmelidir. Bu ise hedeflenen program derlendikten sonra ve derleyici ve linkleyicinin sağladığı adres çözümleme imkanlarından mahrum olunan bir durumda gerçekleştirilmek zorundadır. Windows platformunda derlenmiş olan kodların çalıştırılabilmesi için PE (Portable Executable) formatında olması gerekmektedir. Virüs kodu kendini bir PE dosyasına eklediğinde ise bu dosya formatının kurallarına uygun biçimde bunu gerçekleştirmelidir. Aksi takdirde OS loader dosyayı hafızaya yükleme öncesinde yapacağı PE dosya formatı kontrolleri sırasında dosyanın bozuk olduğunu düşünerek uygulama çalıştırılamayacaktır. Bu durumda virüs yazarı PE dosya formatını çok iyi tanımalı, üstüne üstlük OS loader’ı tersine mühendislik yöntemi ile inceleyerek yaptığı format ve bütünlük kontrollerini anlamalıdır. PE dosya formatı tam olarak dokümante edilmemiş bir formattır, ancak bu konuda yapılmış çok sayıda tersine mühendislik çalışmaları olduğundan PE dosya formatını büyük ölçüde bu çalışmalara dayanarak tanıyabilirsiniz. Ne var ki OS loader için aynı şeyi söyleyemeyiz. OS loader’ın tersine mühendislik çalışması da yapılmıştır, ancak PE formatı kadar geniş ve net bir bilgiye ulaşmak mümkün olmamaktadır. Bu konuda en doğrusu virüs yazarının kendisinin OS loader’ı statik analiz ile tersine mühendisliğe tabi tutmasıdır. Açıkça paylaşmak isterim ki, ben bu adımı gerçekleştirmedim ve yazdığım virüs kodu kendini bazı PE dosyalarına eklediğinde sorunsuz çalışmaktadır. Ancak her kopyalama sonucu çalıştırılabilir bir kod ile sonuçlanmamaktadır. Virüs kodumun daha etkili ve stabil olabilmesi için bu çalışmayı yapmam ve PE dosya başlık alanlarında gerekli bütünlük ayarlarını yapacak kodları da virüs koduma dahil etmem gerekirdir. Amacım gerçekten etkili bir virüs kodu geliştirmek olmadığı ve virüs kodumu eğitim amaçlı olarak geliştirdiğim için bu aşamayı gerçekleştirmedim. Ancak daha ileriye gitmek isteyenler için kernel32.dll kütüphanesi içindeki CreateProcess fonksiyonunu başlangıç noktası olarak verebilirim.

Virüs kodumuzu anlamak için PE dosya formatı ile ilgili gerekli bilgileri “Stack Tabanlı Hafıza Açıklıkları” video serimizden edinebilirsiniz.

Virüs yazabilmek için gerekli ikinci önemli yetkinlik shellcode geliştirme yetkinliğidir. Shellcode’u tanımlamaya çalışırsak kabaca bir proses’in çalışma anında hafıza alanına yüklenen, fonksiyon import etme ve değişken adresi referanslarından mahrum olmasına rağmen başarı ile çalışabilen bir makine kodu olarak ifade edebiliriz. Shellcode yazabilmek veya okuyabilmek için birazdan okuyacaklarınız teknik olarak doğru olmakla birlikte maalesef bu konuya yeni olanlar için yeterli olmayacaktır. Shellcode geliştirme ile ilgili yeterli bilgiyi “Exploit Shellcode Geliştirme” video serimizden edinebilirsiniz. Shellcode geliştiricisi ihtiyacı olan işletim sistemi API’lerini çalıştırabilmek için şu yolları izlemek zorundadır:
  • Öncelikle hafıza alanında çalıştığı proses’in henüz yüklemediği bir kütüphaneyi (dll’i) hafıza alanına yüklemek için kernel32.dll’in taban adresini bulmalıdır. Kernel32.dll her proses için hafıza alanına yüklenen bir kütüphanedir ve Loadlibrary adlı fonksiyonu barındırır.
  • Kernel32.dll’in taban adresi bulunduktan sonra PE dosya formatının özelliklerini kullanarak kütüphanenin export ettiği fonksiyonların adlarını tek tek inceleyerek Loadlibrary fonksiyonunun sırasını bulmalıdır. Bu adın bulunduğu sırayı tespit ettikten sonra sırasıyla Ordinal tablosunu ve Adres tablosunu kullanarak Loadlibrary fonksiyonunun Kernel32.dll kütüphanesi içindeki adresini bulmalıdır. Daha sonra Kernel32.dll adresinin taban adresini bu değere ekleyerek Loadlibrary fonksiyonunun tam adresini hesaplamalıdır.
  • İhtiyaç duyulan kütüphanenin yüklenebilmesi için son olarak yüklenmek istenen kütüphanenin adını stack’e yerleştirmeli, yani parametre olarak bu bilgiyi stack’e yazmalı ve daha sonra da Loadlibrary fonksiyonunu çağırmalıdır. Loadlibrary fonksiyonu yeni yüklenen kütüphanenin taban adresini EAX register’ı içinde döndürecektir.
  • İhtiyaç duyulan kütüphane hafızaya yüklendikten sonra gerekli olan fonksiyonun adresi tıpkı yukarıda olduğu gibi yeni kütüphanenin export tablolarından faydalanılarak tespit edilmelidir. Bu noktadan sonra stack’e parametre olarak yüklenmesi gereken değerler ve sıraları tamamen çağrılacak fonksiyona bağlıdır. Bu değerleri ve sıralarını belirlemek için en önemli yardımcımız MSDN olacaktır.
Yukarıda kısaca anlattığım süreci “Exploit Shellcode Geliştirme” video serimizde son derece detaylı olarak bulabilirsiniz. Shellcode geliştirmek için aşılması gereken zorluklar ve aşım yöntemleri de bu video serimizin konularını oluşturmaktadır. Stack’in çalışma mantığı için ise öncelikle “Stack Tabanlı Hafıza Açıklıkları” video serimizi izlemenizi öneririm.

Yukarıda belirtilen işlemleri kodlayabileceğiniz tek uygulama geliştirme dili Assembly’dir. Yani virüs yazarı olmak istiyorsanız Assembly bilmeniz gerekmektedir. Çünkü hafızaya yüklenmiş bir proses’in yukarıda belirtilen veri yapılarına erişebilmek için Assembly dışında bir alternatifimiz yoktur. Buna ek olarak shellcode geliştirme sırasında fonksiyonlara parametre aktarma işlemi ve string parametrelerin kullanımı için derleme ve linkleme imkanlarımız olmadığından Assembly dilinin özelliklerinden faydalanan özel tekniklere ihtiyacımız olacaktır.

Virüs kodlama stratejimize ve virüs kodumuza geçmeden önce gerekli ön bilgileri tekrar sıralayalım:
  • PE dosya formatı
  • Assembly dili
  • Proses hafıza organizasyonu ve Stack yapısının çalışma yöntemi
  • Shellcode geliştirme teknikleri
Bu konuyla ilgili bir sonraki yazımızda virüs geliştirme stratejimizi ve geliştirdiğimiz örnek kodu açıklayacağım. Bu süreç daha önceden geliştirdiğiniz örnek kodlar olmadan oldukça emek yoğun bir süreç. Benzer şekilde öğrenme amaçlı olarak kodları incelediğinizde hızlı bir yol mümkün olmayabilir. Bu nedenle sabırlı olmanızı ve gerekli sayıda makale ve kodu tekrar okumanızı, ihtiyaç duyduğunuz temel bilgileri video serilerimizden tamamlayarak tekrar çalışmanızı tavsiye ederim. Bu yol zor ama tamamladığınızda güvenlikle ilgili çok temel konuları gayet net anlamanıza imkan sağlayacak bir yol olacaktır.

Gelecek makalede görüşmek üzere.


                                                                                                                                    Sonraki Bölüm>>