Pack’leme işlemi genel olarak statik analizlerde (dinamik analizler için de olabilir) program kodlarını karmaşıklaştırarak tersine mühendisliği zorlaştırmak ve av’ler tarafından farkedilmemek için yapılır.
Basit Statik Analizler, packlenmiş dosyalar üzerinde pek kullanışlı olmaz. Önce unpack edilmesi gerekir, daha sonra analizlere devam edilmelidir.
Packer’lar genel olarak şunları yapabilirler:
Compressing (Sıkıştırma)
Crypting (Şifreleme)
Anti Reverse Engineering
Anti-Disassembly
Anti-Debugging
Anti-VM
Packer Kategorileri
Packleme işlemini yapan programlara packer denir. Packer’ları kendi aralarında kategorize edecek olursak;
Compresser: Orijinal programın boyutunu küçültür.
Protector: Tersine mühendislik yapmayı zorlaştırır.
Crypter: Orijinal programı şifreler.
Encoder: Orijinal programı belirli algoritmalarla şifreler.
Bundler: Bir çok dosyalı programı tek dosya olarak çalıştırır.
Mutater: Program kodunu değiştirir (aynı komut seti ve mimari, ancak değiştirilmiş kod)
Virtualiser: Turns original code into virtual code with embedded virtual machine
Packer’lar, tüm dosyayı pack’leyebilir veya yalnızca text ve data bölümlerini de pack’leyebilir.
Pack ve Unpack Mantığı
Peki kod karmaşıklaştı tamam da, bilgisayar packli programı nasıl anlayacak? diyebilirsiniz. Packlenmiş programlar execute edildiğinde run-time sırasında memory’de açılır. Bunu sağlayan yapıya da Unpacking Stub deniyor.
Normal bir program pack’leme işleminden geçtikten sonra, oluşan pack’lenmiş programa Upacking Stub denen bir yapı eklenir (bu yapı pe dosyasının herhangi bir yerinde olabilir). Bu yapı tahmin edeceğiniz üzere pack’lenmiş programı eski haline (orijinal programa) çevirmeyi sağlıyor. Eğer packlenmiş programı debugger’a sokup analize kalkışırsanız, artık Entry Point Unacking Stub bölümünü işaret edecektir.
Entry Point’in ne olduğunu bilmiyorsanız Windows Portable Executable (PE) Dosyaları yazıma bakabilirsiniz.
Packlenmiş program execute edildiğinde Unpacking Stub, orijinal binaryi çözer (run-time sırasında) ve ardından kontrolü OEP’e (yani Original Entry Point) aktararak orijinal programın yürütülmesini sağlar. Packer’ların bunu yapmasına da “Tail Jump” deniyor.
Unpacking stub aşağıdaki üç adımı sırasıyla gerçekleştirir:
- Orijinal programı belleğe açar.
- Orijinal programın tüm import’larını çözer.
- Yürütme kontrolünü orijinal giriş noktasına (OEP) aktarır.
Packer’lar bu yöntemi çaktırmadan yapmak için de genelde NtContinue veya ZwContniue fonksiyonlarını ret veya call ederek sanki normal bir işlemmiş gibi göstermeye çalışırlar.
Unpacking stub genellikle küçüktür, çünkü programın ana işlevselliğine katkıda bulunmaz ve işlevi genellikle orijinal programı açmaktır.
Single, Multi ve Re-Pack Nedir?
Şu anda, tek katmanlı paketleme, yeniden paketleme veya çok katmanlı paketleme algoritmaları, kötü amaçlı yazılımın tespit edilmeden kalmasına yardımcı olmak için kötü amaçlı yazılım geliştirmede yardımcı olmak için kötü amaçlı yazılım geliştirmede yaygın olarak kullanılmaktadır.
Single Layer Packing
Tek katmanlı paketleme algoritması: Bu paketleyiciler en basit durumu temsil eder. Tek katmanlı paketleme, belirli bir ikili dosyayı paketlemek için yalnızca bir paketleyici kullanır. Bu paketleme tekniği program dosyanın boyutunu, bölüm sayısını ve adını değiştirir (Şekil 2).
Multi Layer Packing
Çok katmanlı paketleme algoritması: Bu paketleyiciler, her biri bir sonraki rutinin paketini açmak için sırayla çalıştırılan birden fazla paket açma katmanı içerir. Orijinal kod yeniden oluşturulduktan sonra, son geçiş kontrolü ona geri aktarır. Bu paketleme, belirli bir ikiliyi paketlemek için potansiyel olarak farklı paketleyicilerin bir kombinasyonunu kullanır ve aynı giriş ikilisinden çok sayıda paketlenmiş ikilinin oluşturulmasını kapsamlı bir şekilde kolaylaştırır. Çok katmanlı paketleme algoritmaları, tek katmanlı paketlenmiş bir program dosyanın boyutunu, bölüm sayısını ve adını değiştirir.
Re-Packing
Yeniden paketleme algoritması: Bu paketleyiciler, her biri bölümlerin her birini açmak için sırayla çalıştırılan yeniden paketlenmiş paket açma katmanları içerir. Yeniden paketleme algoritması, belirli bir ikili dosyayı paketlemek için aynı paketleyiciyi iki kez kullanır ve tek katmanlı paketleyiciye benzer sıkıştırma teknikleri kullanır, ancak boyutu değiştirir.
Bu konu hakkında daha detaylı bilgi almak isteyenler için baya emek verilmiş bir yazı paylaşıyorum: Packer Detection for Multi-Layer Executables Using Entropy Analysis
Memory’e Map Etme Süreci
Normal program dosyalar yüklendiğinde, bir yükleyici disk üzerindeki PE başlığını okur ve bu başlığa dayanarak yürütüprogramnın her bir bölümü için bellek ayırır. Yükleyici daha sonra bölümleri bellekte ayrılan alanlara kopyalar.
Paketlenmiş program dosyalar da PE başlığını biçimlendirir, böylece yükleyici bölümler için alan ayırır. Bu bölümler, orijinal programdan gelebilir veya açma öğesi bölümleri oluşturabilir. Açma öğesi, her bir bölüm için kodu açar ve ayrılan alana kopyalar. Kullanılan kesin açma yöntemi, paketleyicinin hedeflerine bağlı olarak değişir ve genellikle açma öğesi içerisinde bulunur.
Kısacası packlenmemiş programlar işletim sistemi tarafından direkt yüklenir. Fakat packlenmiş programlarda önce unpacking stub yüklenir daha sonra unpacking stub orijinal programı yükler.
IAT’nin Gizlenme ve Çözülme Süreci
Orijial programlar kullandığı api’leri import tablolarında gösterirken, packlenmiş programlar bu fonksiyonları gizlemek ister (genelde öyle yaparlar, yapmayanları da var). Bu yüzden de programın import tablosunda oynama yaparlar. Eğer packlenmiş bi programın iat’sine bakarsanız sayılı api’leri import ettiğini göreceksiniz (packer’lar genelde belirli bikaç fonksiyonu import eder, ona da geleceğiz).
Packlenmiş program memory’e girdiğinde IAT’sini tekrar ayarlamak zorundadır (bunu da unpacker stub ile yapması gerekiyor). Çünkü işletim sisteminin pe loader’ı packlenmiş bir programın import tablosunu o haliyle okuyamaz.
Programın IAT’si memory’de tekrar oluşurken bazen zorlu ve zaman alıcı olabilir, ancak programın çalışması için her türlü bu işlem uygulanmak zorunda.
Packer’ların en yaygın kullandığı yöntem; LoadLibrary
ve GetProcAddress
fonksiyonlarını kullanarak IAT’yi yeniden oluşturmaktır. LoadLibrary ile dll’i belleğe yükleme çağrısı yapılır, GetProcAddress ile de her import edilen fonksiyon için adres alır.
Packer’lar aşağıdaki yöntemlerle IAT’de oynama yapabilirler
Sadece LoadLibrary ve GetProcAddress fonksiyonlarını import ederek IAT’yi çözümleme (en yaygını bu).
Orijinal IAT’yi korur. PE Loader zahmetsizce import edilen dll’leri belleğe yükler (fakat sıfır gizlilik).
Orijinal IAT’de import edilen her dll için yalnızca bir fonksiyonda oynama yapılmaz (bir öncekine oranla daha gizlidir, fakat belleğe yüklenen modüllerin ismi yine de gözükecektir).
Tüm IAT’yi kaldırır (LoadLibrary ve GetProcAddress fonksiyonları da dahil). Bu yöntem en karmaşık ve performans açısından yavaş olan yöntemdir ve unpacker stub’ın küçük bir alan olması gerekirker, haddinden fazla bir alan kaplayabilme ihtimali yüksektir (Fakat gizlilik için en iyi yöntem budur). Daha sonrasında LoadLibrary ve GetProcAddress fonksiyonlarını bulmak zorundadır ve diğer gizlenen fonksiyonları/modülleri de bu fonksiyonlar aracılığıyla çözmelidir.
Örneğin calc.exe’nin hem UPX ile packlenmiş halinin hem de orijinal halinin iat’sine bakalım:
Şimdi de compress edilmiş calc.exe dosyasına bakalım
IAT’nin halini görüyorsunuz… Modül sayısı aynı, ve her birinde sadece bir fonksiyon korunmuş. Demek ki UPX aracının 3. packleme yöntemini kullandığını çıkarabiliriz.
Nedir Bu Entropy?
Entropy programdaki kod düzensizliğin bir grafiği/hesaplamasıdır. Packer’ların amacı da program kodlarını karmaşıklaştırmak olduğundan, programların packlenip packlenmediğini entropy değerinden anlayabiliriz.
Packlenmiş programda, anlamsız byte’lar vardır (unpacker stub için anlamlı tabii). Bu yüzden entropy değerleri yüksek olur. Normal programların kodları daha düzgün bir şekilde olduğu için daha düşük entropy değerlere sahiptirler.
Örneğin yine normal bir calc.exe’nin entropy değerine bakmak istersek:
Şimdi de packlenmiş calc.exe’nin entropy grafiğine bakalım:
Programın Packlendiğine İşaretler
Önce başlıkta da belirttiğim gibi program packlenince IAT’sinden belli oluyor. Yani ya çok az api import edilir ya da hiç (ya da istisna, iat’ye dokunulmaz). Özellikle LoadLibrary ve GetProcAddress fonksiyonlarına dikkat edilmesi gerekiyor.
Ayrıyeten OllyDbg’nin program açtığında otomatik analizicisi, programın packlendiğine yönelik bir uyarı çıkarır. IDA’dan açıldığında sadece küçük bir kod miktarı gösterilir.
Programın pe yapısındaki section’ların isimleri normalden farklı bir isime sahiptir veye anormal boyutlara sahip section’ları vardır.
Yine önceki başlıkta bahsettiğim gibi, Entropy grafiği uçmuştur.
PE dosyasının raw size’ı, virtual size’ından küçüktür.
Programın packlenip packlenmediğini analiz eden programlar var. Programlardan en çok bilinenlerin listesi:
Detect It Easy
ExeInfoPE
PEiD
RDG Packer Detector
TrIDNet
Unpack’leme Yöntemleri
Packlenmiş zararlıyı üç ayrı şekilde unpack edebiliriz:
Automated Static Unpacking
Automated Dynamic Unpacking
Manual Dynamic Unpacking
Automated unpacking, manual dinamik unpack’lemeye göre daha hızlı ve kolaydır ama otomatik teknikler her zaman işe yaramaz. Kullanılan packer tipini bulursak otomatik unpacker var mı yok mu bulmamız gerekiyor. Yoksa son çare packlenmiş dosyayı manuel olarak açmak zorundayız.
Packlenmiş zararlılarla uğraşırken, biz analizciler genelde zararlının işlevlerine odaklanırız. Fakat şöyle bişey var; genelde zararlı unpack edildiğinde orijinal halinin tamamı getirilmez (ama işlevleri orijinaliyle aynıdır).
Automated Static Unpacking
Packlenmiş bi dosyayı unpack etmenin en hızlı yöntemidir. Dosyayı açmadan tamamen packer’ın algoritmasının tersine giderek unpackleme işlemi uygulanır. Otomatik statik açma programları tek bir paketleyiciye özgüdür, örneğin ASPacker ile packlenmiş bi dosyayı UPX Unpacker ile unpack edemezsiniz.
Örnek unpacker programlarından TitanMist, popüler packer’ların unpacker’larını tek bi çatı altında toplamaya çalışmıştır.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
C:\TitanMist>TitanMist.exe -i packed.exe -o unpacked.exe -t python
Match found!
│ Name: UPX
│ Version: 0.8x - 3.x
│ Author: Markus and Laszlo
│ Wiki url: http://kbase.reversinglabs.com/index.php/UPX
│ Description:
Unpacker for UPX 1.x - 3.x packed files
ReversingLabs Corporation / www.reversinglabs.com
[x] Debugger initialized.
[x] Hardware breakpoint set.
[x] Import at 00407000.
[x] Import at[x] Import at 00407008.[Removed]
[x] Import at 00407118.
[x] OEP found: 0x0040259B.
[x] Process dumped.
[x] IAT begin at 0x00407000, size 00000118.
[X] Imports fixed.
[x] No overlay found.
[x] File has been realigned.
[x] File has been unpacked to unpacked.exe.
[x] Exit Code: 0.
Unpacking succeeded! 00407004.
Automated Dynamic Unpacking
Manuel Dynamic Unpacking
Yanlış Anlaşılan Pack’ler
Packed DLL’s
Kaynakça ve Referanslar
Pratical Malware Analysis, No Strach Press
Learn Malware Analysis, Packt
Mastering Malware Analysis, Packt
Antivirus Bypass Techniques, Packt
The Antivirus Hacker’s Handbook, Wiley
Entropy | Free Full-Text | Packer Detection for Multi-Layer Executables Using Entropy Analysis