Hata ayıklayıcı ile adım adım yürütme
- Hata ayıklayıcı programı istediğin satırda durdurur, kayıtçıları ve belleği gösterir, çözülmüş veriyi doğrudan okumana yol açar.
- Windows tarafında x64dbg, Linux tarafında pwndbg eklentili gdb standarttır.
- Bu derste kesme noktasını doğru yere koymayı öğrenirsin.
Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +55 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.
Statik analiz bir noktada tıkanır. Altay Kargo'nun örneğinde yapılandırma bloğu dosyanın içinde şifreli duruyor ve dizelerde yalnız anlamsız baytlar görünüyor. Anahtarı bulup algoritmayı yeniden yazmak saatler sürebilir. Daha kısa bir yol var: programın kendi kendini açmasını beklemek.
Program çalışırken yapılandırmayı belleğe çözer, çünkü kullanmak için ona düz haliyle ihtiyaç duyar. Hata ayıklayıcı bu anı yakalar. Akışı istediğin satırda durdurur, kayıtçıları ve belleği okur, gerekirse değerleri değiştirirsin. Bu derste kesme noktasını doğru yere koymayı, adım komutlarını seçmeyi ve örneğin hata ayıklayıcıyı fark etme çabasını boşa çıkarmayı öğrenirsin.
Hata ayıklayıcı ne verir
Hata ayıklayıcı üç şey verir. Akışı istediğin yerde durdurursun. Kayıtçı ve bellek içeriğini okursun. Değerleri değiştirip farklı yolu denersin.
Üçüncüsü en güçlüsüdür, çünkü koşulları sen kurarsın: bir kontrol örneği erken kapatıyorsa dönüş değerini değiştirip yolu açarsın. Windows tarafında x64dbg, Linux tarafında gdb standart araçlardır. gdb'ye eklenen pwndbg eklentisi her durakta kayıtçıları, yığını ve sıradaki komutları tek ekranda basar. Aşağıdaki örnekler gdb ile ilerler ama kavramlar x64dbg'de birebir aynıdır, yalnız menü ve kısayol adları değişir.
(gdb) break *0x401156 Breakpoint 1 at 0x401156 (gdb) run Breakpoint 1, 0x0000000000401156 in main () (gdb) info registers rax rdi rax 0x0 0 rdi 0x4040a0 4210848 (gdb) x/s $rdi 0x4040a0: "c2.ornek.example"
Bu oturumu satır satır oku. break komutu 0x401156 adresine bir kesme noktası koyar; program o adrese gelince durur ve kontrolü sana bırakır. run örneği baştan başlatır ve konan işarete kadar akışı sürdürür. Durduğu anda kayıtçılar donmuş haldedir.
info registers rax ile rdi değerlerini basar ve rdi bir bellek adresini, 0x4040a0'ı tutar. x/s komutu bu adresteki baytları dize olarak okur ve c2.ornek.example adını gösterir. Linux'ta bir yordamın ilk argümanı rdi kayıtçısıyla gider, yani program bu adı bir yordama vermek üzeredir. Yanlış adrese konan kesme noktası hiç tetiklenmez, bu yüzden adresi statik analizden doğrularsın.
Kayıtçıları okurken çağrı kuralını bilmen gerekir. Çağrı kuralı, argümanların yordama hangi kayıtçılarla gittiğini belirler. Linux ve macOS'taki x86-64 kuralında ilk altı tamsayı argümanı sırasıyla rdi, rsi, rdx, rcx, r8 ve r9 ile gider.
Windows x64'te ilk dört argüman rcx, rdx, r8 ve r9 ile taşınır, kalanlar yığına yazılır. Dönüş değeri iki kuralda da rax'ta durur. x64dbg'de bir CreateFileW çağrısında durduğunda dosya adını rcx'in gösterdiği adreste ararsın, gdb'de bir Linux yordamında ise rdi'de.
Etkinlik 1 / 7
gdb içinde bu iki komut ne yapar?
Önce kodu oku, sonra çıktıyı seç.
(gdb) break *0x401156 (gdb) run
Akışı yönetmek: adım komutları ve kesme noktası türleri
Durduktan sonra akışı adım adım ilerletirsin. stepi tek bir makine komutunu yürütür ve bir call satırında çağrılan yordamın içine girer. nexti çağrıyı tek adım sayar ve üstünden geçer; yordam arka planda tamamen koşar.
finish geçerli yordamın sonuna kadar koşar ve çağıranın içinde durur. continue ise sonraki kesme noktasına kadar durmadan ilerler.
Doğru seçim zaman kazandırır. printf ya da memcpy gibi tanıdık bir kütüphane çağrısının içine girmek boşunadır, nexti ile geçersin. Bilinmeyen bir yordamı ise stepi ile içeriden izlersin. Bu küçük karar, saatlerce sürecek bir analizi yarım saate indirebilir.
Etkinlik 2 / 7
Hata ayıklayıcı komutunu işiyle eşleştir.
Kesme noktası üç türlüdür. Yazılım kesme noktası, durulacak komutun ilk baytını geçici olarak 0xCC ile değiştirir. Bu bayt x86'da int3 komutudur ve işlemciyi hata ayıklayıcıya döndürür. Kod değiştiği için kendi baytlarını denetleyen bir örnek bunu fark edebilir.
Donanım kesme noktası ise adresi işlemcinin DR0 ile DR3 arasındaki hata ayıklama kayıtçılarına yazar ve koda dokunmaz. Sayısı dörtle sınırlıdır ama daha sessizdir. Bellek kesme noktası, gdb'deki adıyla watchpoint, belirli bir adrese okuma ya da yazma olduğunda durur. Bir yapılandırma bloğuna kimin dokunduğunu bulmanın en kısa yolu budur.
Etkinlik 3 / 7
Örnek kendi kodunda 0xCC baytı arıyor. Hangi kesme noktası türü bu taramaya yakalanmaz?
Oturumu başlat ve ilk durağı seç
Oturum dosyayı yüklemekle başlar. gdb ./ornek komutu programı belleğe alır ve sembolleri okur ama henüz çalıştırmaz. x64dbg ise varsayılan ayarda önce sistemin yükleyicisinde, sonra programın giriş noktasında durur.
İlk çalıştırmada nereye duracağını bilmek zordur. İyi bir başlangıç giriş noktasıdır. Daha iyisi ilgilendiğin kütüphane çağrısıdır.
Dosya yazımını merak ediyorsan Windows'ta CreateFileW çağrısına, ağ bağlantısını merak ediyorsan connect ya da InternetOpenUrlW çağrısına kesme noktası koyarsın. Program o çağrıya geldiğinde durur. Yığındaki dönüş adresi de seni çağrıyı yapan koda götürür.
Koşullu kesme noktası gürültüyü kırpar. Dize çözen bir yordam binlerce kez çağrılabilir. Her durakta devam komutuna basmak yerine yalnız belirli bir argümanda durmasını istersin. gdb'de break komutunun sonuna if ve bir koşul eklersin, x64dbg'de kesme noktası penceresine bir ifade yazarsın.
Betik desteği bir adım daha ileri gider. Kesme noktasına ulaşıldığında bir komut dizisi koşar: dönüş değerini okur, dizeyi basar ve akışı sürdürür. Bu kısa betik yüzlerce çözülmüş dizeyi tek koşuda toplar ve elle saatler sürecek işi dakikaya indirir.
pwndbg> break decrypt_string if $rdi == 0x404120 Breakpoint 2 at 0x4011c0 pwndbg> break *0x4011f8 Breakpoint 3 at 0x4011f8 pwndbg> commands 3 >silent >x/s $rax >continue >end
Etkinlik 4 / 7
x64dbg yerine komut satırında çalışıyorsun. ornek dosyasını gdb'ye yükle.
Çözümlü örnek: yapılandırmayı bellekten okumak
Şimdi Altay Kargo örneğindeki şifreli yapılandırmayı açarsın. Ekip aynı ailenin Linux sunucularını hedefleyen bir sürümünü de buldu ve iki sürüm aynı yapılandırma yordamını taşıyor. Burada Linux sürümünü gdb ile açarsın, Windows sürümünde aynı adımları x64dbg ile atarsın.
Statik analizde decrypt_config adlı bir yordam ve onu çağıran main yordamını gördün. Yordamın içindeki algoritmayı çözmeye çalışmazsın. Onun yerine yordamın dönüşünde durursun, çünkü çıkışta veri düz haliyle bellekte durur.
x86-64'te bir yordam dönüş değerini rax kayıtçısında bırakır. decrypt_config de çözdüğü bloğun adresini rax ile döndürür. Plan basittir: çağrıyı bul, hemen ardındaki komuta kesme noktası koy, programı çalıştır ve rax'ın gösterdiği belleği oku.
$ gdb -q ./ornek pwndbg> info functions config All functions matching regular expression "config": Non-debugging symbols: 0x0000000000401196 decrypt_config pwndbg> disassemble main 0x0000000000401134 <+30>: mov rdi,rax 0x0000000000401137 <+33>: call 0x401196 <decrypt_config> 0x000000000040113c <+38>: mov QWORD PTR [rbp-0x10],rax pwndbg> break *0x40113c Breakpoint 1 at 0x40113c pwndbg> run Breakpoint 1, 0x000000000040113c in main () pwndbg> x/s $rax 0x4052a0: "host=cdn-guncelleme.example;port=443;sleep=60"
Oturumu satır satır oku. info functions config, adında config geçen yordamları listeler ve decrypt_config'in adresini verir. disassemble main ana yordamın komutlarını basar; çıktının ilgili üç satırı yukarıda duruyor.
0x401137'deki call çözme yordamını çağırır. Hemen ardındaki 0x40113c satırı dönüş değerini yerel bir değişkene kaydeder. Kesme noktası tam bu satıra konur, çünkü burada çağrı bitmiş ve rax dolmuştur.
run programı başlatır ve akış bu adreste durur. x/s $rax, rax'ın gösterdiği adresteki baytları dize olarak okur. Çıktı yapılandırmanın tamamıdır: komuta sunucusunun adı, bağlantı noktası ve altmış saniyelik bekleme süresi.
Etkinlik 5 / 7
Kesme noktasını koymak için en yararlı satırı seç.
Aynı sonuca ikinci bir yoldan da varırsın. decrypt_config'in girişine kesme noktası koyar, program orada durunca finish yazarsın.
gdb yordamın sonuna kadar koşar ve çağıranın içinde, tam 0x40113c'de durur. Bu yol çağrı yerini bilmediğinde işe yarar, yordamın adı ya da adresi yeter. Girişte durmanın da bir değeri vardır.
Girişte rdi ve rsi kayıtçıları şifreli verinin adresini ve uzunluğunu taşır. Giriş ve çıkıştaki belleği yan yana koyarsan hem şifreli hem çözülmüş hal elinde olur. Bu çift, sonradan algoritmayı tanımlamanı kolaylaştırır.
Etkinlik 6 / 7
Dinamik analizde kesme noktası koymanın en verimli yeri neresidir?
Analiz karşıtı kontroller ve müdahale kaydı
Bazı örnekler hata ayıklayıcı altında çalıştığını anlamaya çalışır. MITRE ATT&CK bu davranışı T1622 koduyla izler. Windows'ta en basit kontrol IsDebuggerPresent çağrısıdır ve süreç ortam bloğundaki bir bayrağı okur. CheckRemoteDebuggerPresent ve NtQueryInformationProcess aynı soruyu başka yoldan sorar.
Linux'ta örnek kendine ptrace uygular. Bir süreci aynı anda yalnız bir izleyici izleyebildiği için çağrı gdb altında başarısız olur. /proc/self/status dosyasındaki TracerPid satırı da izleyiciyi ele verir. Zamanlama da bir yöntemdir: iki nokta arasında çok uzun süre geçmişse biri adım adım ilerliyordur.
pwndbg> catch syscall ptrace Catchpoint 4 (syscall 'ptrace' [101]) pwndbg> run Catchpoint 4 (call to syscall ptrace), 0x00007ffff7e1a9cd in ptrace () from /lib/x86_64-linux-gnu/libc.so.6 pwndbg> continue Catchpoint 4 (returned from syscall ptrace), 0x00007ffff7e1a9cd in ptrace () from /lib/x86_64-linux-gnu/libc.so.6 pwndbg> print $rax $1 = -1 pwndbg> set $rax = 0
Oturumu oku. catch syscall ptrace, ptrace sistem çağrısının girişinde ve dönüşünde durur; 101 bu çağrının x86-64'teki numarasıdır. İlk durak girişi, continue sonrasındaki ikinci durak dönüşü gösterir. print $rax -1 basar.
Çağrı başarısız oldu, çünkü süreci zaten gdb izliyor. set $rax = 0 dönüş değerini başarıya çevirir ve örnek izlenmediğini sanıp yoluna devam eder. Windows tarafında x64dbg için ScyllaHide eklentisi bu kontrollerin çoğunu tek ayarla gizler. Hangi kontrolü nasıl aştığını hemen not edersin.
Bu kontroller savunma için de ipucudur. Kum havuzu raporlarında hata ayıklayıcı ve sanal makine sorguları ayrı bir davranış olarak işaretlenir. Yine de tek başına karar vermez, çünkü kopya koruması kullanan meşru yazılımlar da aynı çağrıları yapar.
Kontrolü bir tespit kuralına çevireceksen onu kalıcılık ya da şüpheli ağ davranışıyla birlikte ararsın. Akışı zorladığın her noktayı raporda belirtirsin. Hangi adreste hangi değeri değiştirdiğini yazmazsan, gözlediğin davranışın doğal mı yoksa senin ürettiğin mi olduğu belirsiz kalır.
Hata ayıklayıcı örneği gerçekten koşturur. Yalıtım kuralları burada da aynen işler: ağ taklit servise bağlı, paylaşılan klasör kapalı, anlık görüntü hazır olmalıdır. Bir kesme noktasını kaçırırsan örnek kaldığı yerden tam hızla çalışmaya devam eder ve kalıcılık kurabilir. Bu yüzden oturum bitince, örnek tam çalışmamış görünse bile makineyi temiz görüntüye döndürürsün.
Analiz karşıtı kod bilerek geçersiz bir komut çalıştırıp çökebilir de. Hata ayıklayıcı durunca telaşlanmazsın, o nokta çoğu zaman asıl ilginç yerdir.
Derinleş:Yazılım kesme noktasının perde arkası
Yazılım kesme noktası koyduğunda hata ayıklayıcı, hedef adresteki ilk baytı bir kenara kaydeder ve yerine 0xCC yazar. Linux'ta bunu ptrace ile sürecin belleğine yazarak, Windows'ta WriteProcessMemory ile yapar. İşlemci 0xCC'ye gelince bir kesme üretir, işletim sistemi süreci durdurur ve hata ayıklayıcıya haber verir. Bu anda komut göstergesi kesme noktasının bir bayt ötesini gösterir.
Hata ayıklayıcı onu bir geri alır ve özgün baytı yerine koyar. Devam ettiğinde önce tek bir komut yürütür, sonra 0xCC'yi yeniden yazar ve akışı serbest bırakır. Bu döngü her durakta tekrarlanır.
Kendi kodunun özetini hesaplayan bir örnek, 0xCC yazıldığı anda farklı bir değer bulur ve davranışını değiştirir. Böyle örneklerde donanım kesme noktası ya da bellek erişim kesme noktası seçersin. Kod okuma sırasında çalışan bir özet kontrolü görürsen, kesme noktalarını o kontrolün kapsadığı aralığın dışına koymak da bir çözümdür.
Etkinlik 7 / 7
Doğru mu, yanlış mı?
Hata ayıklayıcı ile adım adım yürütme örneği laboratuvar dışına çıkarmaz.
Okumayı istediğin sırayla sürdürebilirsin. Ders, bütün etkinlikleri cevapladığında tamamlanır.
- Etkinlik 1· Hata ayıklayıcı ne verir
- Etkinlik 2· Akışı yönetmek: adım komutları ve kesme noktası türleri
- Etkinlik 3· Akışı yönetmek: adım komutları ve kesme noktası türleri
- Etkinlik 4· Oturumu başlat ve ilk durağı seç
- Etkinlik 5· Çözümlü örnek: yapılandırmayı bellekten okumak
- Etkinlik 6· Çözümlü örnek: yapılandırmayı bellekten okumak
- Etkinlik 7· Analiz karşıtı kontroller ve müdahale kaydı