Selamlar, bu yazımda ConfuserEx adlı “obfuscate” yazılımının içerisindeki AntiDump korumasının derin analizini yapacağım.

Bu Yazı Neler İçeriyor?

Bu yazı ve “ConfuserEx Deep Analysis” başlığı altında açılan yazılar genel olarak şu başlıklardan oluşacak :

  • ConfuserEx Nedir? (*)
  • AntiDump Nedir?
  • ConfuserEx AntiDump Çalışma Prensibi
  • Basit Bir AntiDump Yazalım
  • ConfuserEx Anti-AntiDump

ConfuserEx Nedir?

ConfuserEx is an free, open-source protector for .NET applications. It is the successor of Confuser project.

.NET Uygulamaları için bedava, açık kaynaklı bir koruma. “Confuser” projesinin varisidir.

AntiDump Nedir?

AntiDump, bellekten alınmak istenen dökümü bozan, engelleyen bir yapı. (?)

ConfuserEx AntiDump Çalışma Prensibi

ConfuserEx korumasının AntiDump’ının çalışma prensibini inceleyebilmek için kodlarını Github sayfasından alalım.

https://github.com/yck1509/ConfuserEx/blob/master/Confuser.Runtime/AntiDump.cs

Şundan da bahsetmeliyim ki ConfuserEx, Runtime çalışabilen korumalardan oluşuyor. Yani AntiTamper, AntiDebugger, AntiDump gibi korumalar için Runtime şekilde çalışabilecek korumalar üretiyor ve daha sonrasında seçilen .NET dosyanın içine bunu enjekte edip Rename işlemine sokuyor. Örneğin linkini yukarda verdiğim AntiDump.cs dosyası Runtime çalışan (.NET Kütüphaneleri dışında farklı bir kütüphaneye ihtiyaç duymuyor(DLL) ) bir kod parçası.

https://github.com/yck1509/ConfuserEx/blob/master/Confuser.Protections/AntiDumpProtection.cs

Bu kısımda gördüğümüz kısım, Runtime çalışan class’ı içerisine enjekte eden kısım :

 protected override void Execute(ConfuserContext context, ProtectionParameters parameters) {
			TypeDef rtType = context.Registry.GetService<IRuntimeService>().GetRuntimeType("Confuser.Runtime.AntiDump");

			var marker = context.Registry.GetService<IMarkerService>();
			var name = context.Registry.GetService<INameService>();

			foreach (ModuleDef module in parameters.Targets.OfType<ModuleDef>()) {
				IEnumerable<IDnlibDef> members = InjectHelper.Inject(rtType, module.GlobalType, module);

			MethodDef cctor = module.GlobalType.FindStaticConstructor();
			var init = (MethodDef)members.Single(method => method.Name == "Initialize");
			cctor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, init));

			foreach (IDnlibDef member in members)
				name.MarkHelper(member, marker, (Protection)Parent);
}

Runtime çalışan class’ın DLL Import kısmına bakalım.

[DllImport("kernel32.dll")]
static extern unsafe bool VirtualProtect(byte* lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);

Bu kısımda kernel32.dll ‘i içerisinde bulunan VirtualProtect apisi “VirtualProtect” ismi ile class’a dahil ediliyor.

VirtualProtect ?
BOOL VirtualProtect(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flNewProtect,
  PDWORD lpflOldProtect
);

Changes the protection on a region of committed pages in the virtual address space of the calling process.

Çağrılan işlemin sanal adres alanındaki korumayı değiştirir.

  • “lpAddress” : İşaretçi, koruması değiştirilecek alanın başlangıç adresi.
  • “dwSize” : Erişim koruma özniteliklerinin değiştirileceği alanın bayt cinsinden boyutu. lpAddress parametresi ve (lpAddress + dwSize) aralığında
  • “flNewProtect” : Koruma seçeneği. Daha detaylı bilgi için https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants sayfası ziyaret edilebilir.
  • “lpflOldProtect” : Belirtilen adres bölgesindeki ilk adresin önceki erişim koruma değeri.

https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect

VirtualProtect üstünden de basitçe geçtiğimize göre şimdi kodları inceleyelim.

Initalize method’u içerisindeki kısım yani en başından başlıyoruz.

uint old; //1

Module module = typeof(AntiDump).Module; //2

var bas = (byte*)Marshal.GetHINSTANCE(module); //3

//Bunun gerisi memory üzerinde istenilen yerin hesaplanması.

byte* ptr = bas + 0x3c; // 3c = 60

byte* ptr2;
ptr = ptr2 = bas + *(uint*)ptr;
ptr += 0x6;
ushort sectNum = *(ushort*)ptr;
ptr += 14;
ushort optSize = *(ushort*)ptr;
ptr = ptr2 = ptr + 0x4 + optSize;
  • uint türünde “old” adlı değişken tanımlanmış. Bunun kullanım amacı VirtualProtect apisinde bulunan “lpflOldProtect” parametresi olacak. out anahtar kelimesi ile parametre gönderilecek.
  • “module” değişkenine yüklenen modül aslında programın kendisi. Program runtime şekilde çalıştığından bu şekilde yazılmış
  • “bas” adlı değişken, programın memory’deki başlangıç yerini yani MZ Header başlangıç adresini tutuyor. Bunu göstermek istiyorum.

    Örnek bir proje oluşturuyorum Visual Studio’dan ve ConfuserEx’den sadece AntiDump korumasını içerisine enjekte ediyorum. Output olarak “Confused” klasörünün içerisindeki dosyayı dnSpy üzerinde açıp .cctor kısmına gidiyoruz. (Neden buraya gidiyoruz diyecek olursanız Inject Phase kısmında bunu .cctor’a enjekte ettiğini göreceksiniz.) Screenshot_1 dnSpy üzerinde açtıktan sonra GetHISTANCE fonksiyonunun çalışmasından hemen sonra olan satıra breakpoint koyuyoruz ve programı yine dnSpy üzerinde debugluyoruz. Screenshot_2 breakpoint koyduğumuz yere gelince program duruyor ve dnSpy’ın “locals” penceresinden bas adlı değişkenimizin değerine bakınca “0x006A0000” adresini görüyoruz(sizde farklı olacaktır). HxD yazılımını başlatıp “Open Main Memory…” özelliğini kullanarak açılan sekmeden programımızı seçiyoruz. Screenshot_3 CTRL + G kombinasyonu sonucunda açılan sekmeye “bas” değişkeninden aldığımız değeri yazıyoruz (0x silmeniz gerekiyor) daha sonra OK tuşuna bastığınızda sizi direkt MZ ascii karakterlerinin olduğu yere götürecek yani en başa. Screenshot_4

  • Geldik hesaplama kısmına. Aslında bu kısmı daha iyi görebilmek için son hesaplama satırının hemen altında bulunan if yapısına breakpoint atıp continue diyoruz ve Locals kısmında. istediğimiz değişkenin değerini HxD üzerinden inceleyelim.

    Screenshot_5 “ptr2” değişkeni aslında kod kısmında “sectNum” olarak geçiyor. İlgili adres ile birlikte HxD üzerinde offset araması yaptığımız zaman direkt bizi .text section’unun başına götürüyor. Screenshot_6

  • Bu kısımlarda boğulmayıp aslında direkt işlem yaptığı kısımlara gitsek daha mantıklı olur diyerek, debug ekranında daha rahat görebilmeniz açısından rename işlemine soktuğu VirtualProtect apisini temsil eden fonksiyonu tekrar ismini değiştiriyorum. İlk yaptığı VirtualProtect işlemine breakpoint atıp continue diyelim. Screenshot_7 “ptr7” değişkeninin tuttuğu alanı 11 bytelık yer şeklinde 64U flagi ile iznini değiştiriyor. VirtualProtect’i anlatırken verdiğim “memory protection constants” sayfasına giderseniz 0x40 constant’ının “PAGE_EXECUTE_READWRITE” iznini sağladığını göreceksiniz (64U onluk tabanda yazılmış hali onaltılık tabanda yazımını görmek için imleci üstüne getirirseniz dnSpy gösterecek). Locals kısmından ptr7’nin tuttuğu adrese gidiyoruz. Screenshot_8 ptr8 alanı da hemen öncesi CorExeMain .NET Flaglari. Silince dnSpy okuyamıyor programı…

Screenshot_9 Bu kısım… ptr2 adresinin tuttuğu alana gittiğimizde Section Headers’ları göreceğiz.

for (int k = 0; k < (int)num; k++)
{
	<Module>.VirtualProtect(ptr2, 8, 64U, ref num3); //8 baytlık alan ayırmasının sebebi section isimleri 8 bayttan oluşuyor.

	Marshal.Copy(new byte[8], 0, (IntPtr)((void*)ptr2), 8); //Silme işlemini tam olarak bu kısımda yapıyor.

	ptr2 += 40; //Bu kısımda birinde işini bitirdikten sonra 40 bayt ekleyerek diğer bayt'a geçiyor hepsi arasında 40 bayt var.

}

Bu kod parçası sectionlar içerisinde dolaşarak hepsinin ismini siliyor.

Screenshot_10 Bu kısımda BSJB flag’inin hesaplanmasını yapıp 4 byte’lık alan ayırıp onu siliyor. BSJB, meta veri tablosundaki ilk girdidir (Meta veri motorunda çalışan 4 adamın adlarının ilk 4 harfinin birleşimidir) evet, biraz saçma bir detay oldu :D.

AntiDump korumamız bu kısımları sildikten sonra dnSpy içerisindeki modules sekmesi ile dump alalım. aaa Gördüğünüz gibi bütün flagler silindiği için ve section isimleri silindiği için okunamıyor.

Basit Bir AntiDump Geliştirelim.

Başında uyarayım bu sadece bir sürü yöntemi olduğunu gösterme amaçlı bir antidump olacak yani o kadar stabil çalışmıyor.

internal unsafe static void AntiDumpFunction()
{
	uint num3 = 0U;
        Module module = typeof(Program).Module;
        byte* ptr = (byte*)((void*)Marshal.GetHINSTANCE(module));
        VirtualProtectFunc(ptr, 128, 64U, ref num3);
        Marshal.Copy(new byte[128], 0, (IntPtr)((void*)ptr), 128);
}

Runtime MZ Header Erase işlemi yapan bir kod parçacığı. Bu programı dump aldığımız zaman şöyle bir görüntü ile karşılaşıyoruz : bbbb

Anti-AntiDump

AntiDump olan bir .NET uygulamasından nasıl dump alırız? Bunun iki yöntemi veya birçok yöntemi olabilir.

1) JITFreezer (Bunun kodlarını inceleyelim) 2) x0rz’s antidump fixer tool ( BackBox üstadıma selamlar o7 ) x0rz bunun kodlarını obfuscate etmiş yani incelenmesini istemediği için mi o kısmı tam olarak bilmediğim için istemiyordur diye kodlarını deobfuscate etmeye gerek yoktur diye düşünüyorum.

JITFreezer

Çalışma prensibi çook basit.

try
{
	string fileName = args[0];
	Process process = new Process();
	process.StartInfo.FileName = fileName;
	process.StartInfo.CreateNoWindow = true;
	process.StartInfo.UseShellExecute = false;
	process.Start();
	int id = process.Id;
	for (;;)
	{
		if (Utils.GetCLRModule(id))
		{
			Utils.SuspendT(id);
			Utils.PrintLogo();
			Console.WriteLine("[!] .NET has been loaded.\n");
			Console.WriteLine("[»] Manually dump it (Ex: MegaDumper / Scylla / ExtremeDumper).");
			Console.WriteLine("\n[!] Press Enter to kill the process.");
			Console.ReadLine();
			process.Kill();
		}
	}
}
catch (Exception ex)
{
	Console.Clear();
	string str = "[x] Error: \n";
	Exception ex2 = ex;
	Console.WriteLine(str + ((ex2 != null) ? ex2.ToString() : null));
}

//(...)


public static bool GetCLRModule(int pID)
{
	ProcessModuleCollection modules = Process.GetProcessById(pID).Modules;
	for (int i = 0; i < modules.Count; i++)
	{
		if (modules[i].ModuleName.ToLower() == "clr.dll")
		{
			return true;
		}
	}
	return false;
}

clr.dll yüklendiği sırada uygulamaya suspend atıyor yani duraklatıyor tek işlevi bu. Suspend attığı sırada daha uygulama tam olarak yüklenmemiş olacağı için hiçbir fonksiyon çalışmamış olacak. Bu sırada dump alırsak AntiDump hiç devreye girmemiş olacak.

KULLANILABİLECEK MUHTEŞEM ÖTESİ KAYNAKLAR

https://www.codeproject.com/Articles/5841/Inside-the-NET-Application https://ring0.info/posts/pe-dosya-formatina-dalis