Herkese selamlar,
Bu yazıda tersine mühendisliğe (Reverse Engineering) giriş seviyesinde, ancak pratiğe bağlanan kapsamlı bir bakış atacağız. Disassembler, Decompiler, Debugger araçlarının ayrımından başlayarak; PE/ELF ikili dosya yapılarını, Obfuscation (kod karartma) ve Packer/UPX mekanizmalarını, statik ve dinamik analizin kullanım alanlarını, bu ekosistemin açık kaynaklı gücü Ghidra'yı ve tersine mühendislikte sık karşılaşılan temel x86/x64 Assembly kavramlarını inceleyeceğiz.
Önemli bir not: Mataroa'nın sayfa düzen yapısından kaynaklı ekran görüntüleri ne yazık ki Medium veya blogspotta olduğu gibi net görünmeyebiliyor. Ekran görüntülerini tam net görmek isterseniz görsel üzerine sağ tıklayıp yeni sekmede aç biçiminde inceleyebilirsiniz.

1. Tersine Mühendislik (Reverse Engineering) Nedir?
Tersine mühendislik; üretilmiş herhangi bir sistemin çalışma mantığını dışarıdan gözlemleyerek veya bileşenlerine ayırarak adım adım anlama sürecidir. Elinizde kaynak kod, devre şeması veya tasarım dokümanı bulunmadığı durumlarda sistemin nasıl inşa edildiğini ortaya çıkarmayı hedefler.
Otomotiv, savunma sanayi, gömülü sistemler ve donanım geliştirme gibi pek çok alanda kullanılsa da bu yazıda kapsamımız zararlı yazılım analizi (malware analysis) odaklı olacaktır.
2. Temel Analiz Araçları: Disassembler, Decompiler ve Debugger
Derlenmiş bir ikili dosyanın (binary) davranışını çözmek için üç temel araç türü kullanılır:
Disassembler Nedir?
Derlenmiş makine kodunu (0 ve 1'lerden oluşan ikili baytları) doğrudan işlemcinin anlayacağı Assembly talimatlarına (MOV, PUSH, CALL, JMP vb.) çeviren araçtır. Yazılımın en yalın makine talimatı akışını gösterir.
Decompiler Nedir?
Derleyici (compiler) tarafından üretilen makine kodunu mantıksal olarak yorumlayarak C/C++ benzeri yüksek seviyeli sözde koda (pseudocode) dönüştüren araçtır. Decompiler çıktısı orijinal kaynak kodun birebir geri elde edilmiş hâli değildir; derleme sırasında kaybolan değişken adları, yorumlar, tip bilgileri ve bazı yüksek seviyeli yapılar ancak analiz ve yeniden tiplendirme ile kısmen geri kazanılabilir. Buna rağmen analistin kod akışını çok daha hızlı kavramasını sağlar.
Debugger Nedir?
Yazılımı işletim sistemi ve işlemci düzeyinde adım adım çalıştırmayı sağlayan dinamik hata ayıklama aracıdır. Decompiler veya disassembler çıktısından bağımsız olarak; CPU yazmaçlarını (registers), çağrı yığınını (stack) ve bellek durumunu anlık olarak izleyip kesme noktaları (breakpoint) koyarak yürütme akışını denetler.
3. Statik ve Dinamik Analiz Farkları
Zararlı yazılım incelemelerinde yaklaşım iki ana eksende ilerler:
- Statik Analiz: Kodu çalıştırmadan; dosya başlıkları (PE/ELF headers), string değerleri, import/export tabloları ve disassembler/decompiler çıktıları üzerinden yürütülen mimari yorumlama sürecidir.
- Dinamik Analiz: Kodu kontrollü bir ortamda çalıştırarak; işlem ve thread davranışlarını, bellek hareketlerini, ağ trafiğini, işlemci durumunu, dosya sistemi ve kayıt defteri değişikliklerini anlık inceleme sürecidir. İncelenen hedefe göre donanım veya endüstriyel kontrol arayüzleri de analize dahil edilebilir. Genellikle
x64dbg,WiresharkveProcess Hacker (System Informer)gibi araçlar kullanılır.
4. Dinamik Analizin Riskleri ve Donanım Düzeyi Kalıcılık (Persistence)
Önemli Analist Uyarısı: Statik analizle davranış kalıpları ve amacı tam olarak çözülmemiş bir zararlıyı doğrudan debugger içinde veya izole edilmemiş bir ortamda çalıştırmak ciddi operasyonel riskler barındırır.
Bilinçsiz yapılan bir çalıştırma işlemi:
- Analiz makinesini bir C2 (Command & Control) altyapısına bağlayabilir ve bulunduğu yerel ağdaki diğer sistemleri riske atabilir.
- İşletim sistemi, servisler veya boot zinciri üzerinde kalıcılık mekanizmalarını tetikleyebilir.
- Nadir fakat ileri seviye tehdit sınıflarında UEFI / BIOS, çevre birimi firmware'leri veya diğer düşük seviye kalıcılık mekanizmaları hedeflenebilir.
Firmware veya boot zinciri seviyesindeki implantlar sıradan kullanıcı-seviyesi zararlılarda yaygın değildir; bunlar özel yetenek ve erişim gerektiren ileri tehdit senaryolarıdır. Bununla birlikte bu tür tehditler güvenlik ürünlerinin gördüğü telemetriyi azaltabilir, yanıltabilir veya bazı denetim katmanlarını atlatabilir.
Bu nedenle şüpheli bir örnek mümkün olduğunca önce statik analizle karakterize edilmeli, risk profili ve beklenen davranışları çıkarılmalıdır. Dinamik analiz gerekiyorsa yalnızca ağ, depolama, kimlik bilgileri ve donanım erişimi açısından uygun şekilde izole edilmiş laboratuvar ortamlarında yürütülmelidir. Bazı packer, runtime şifre çözme ve ortama bağlı davranışların yalnızca çalışma anında ortaya çıkabileceği de unutulmamalıdır.
5. İkili Dosya Formatları: PE ve ELF
Tersine mühendislikte analiz edilen dosya doğrudan bir ham veri yığını değildir; işletim sisteminin yükleyicisine (loader) programın belleğe nasıl yerleşeceğini anlatan yapılandırılmış bir formatta sunulur.
Portable Executable (PE)
Windows işletim sisteminde çalıştırılabilir dosyaların (.exe), dinamik kütüphanelerin (.dll), sürücülerin (.sys) ve denetim masası eklentilerinin (.cpl) standart formatıdır.
+------------------------------------+
| DOS Header (MZ Signature: 0x5A4D) | -> Geriye dönük uyumluluk için DOS Stub barındırır.
+------------------------------------+
| PE Header / NT Headers (PE\0\0) | -> Dosya mimarisi (x86/x64), zaman damgası.
+------------------------------------+
| Optional Header | -> AddressOfEntryPoint, ImageBase, Subsystem.
+------------------------------------+
| Section Table (Bölüm Tablosu) | -> Bölüm tanımları ve bellek izinleri.
+------------------------------------+
| .text / .code | -> Yürütülebilir makine kodları (Read + Execute).
| .data | -> Başlatılmış global değişkenler (Read + Write).
| .rdata | -> Salt okunur veriler ve stringler.
| .idata | -> Import bilgileri ve çoğu PE'de IAT/ILT yapıları.
| .rsrc | -> İkonlar, menüler, gömülü dosyalar (Kaynaklar).
+------------------------------------+
- Import Address Table (IAT): Programın içe aktardığı API fonksiyonlarına çalışma anında erişebilmesi için kullanılan adres tablosudur.
kernel32.dll,ws2_32.dll,advapi32.dllgibi kütüphanelerden yapılan importlar; ağ erişimi, dosya işlemleri, süreç yönetimi veya kayıt defteri manipülasyonu gibi davranışlar hakkında hızlı ipuçları verebilir. AncakLoadLibrary/GetProcAddressbenzeri mekanizmalarla dinamik çözümlenen API'ler statik IAT içinde görünmeyebilir.
Executable and Linkable Format (ELF)
Linux, BSD, Android ve gömülü sistemlerin (IoT/Router yazılımları) standart ikili dosya formatıdır.
- ELF Header: Dosyanın mimarisini (ARM, MIPS, x86_64), endianness (Little/Big Endian) yapısını ve program giriş noktasını belirtir.
- Sections (
.text,.rodata,.bss): Derleme ve bağlama (linking) aşamasında kullanılan bölümlerdir. - Segments (Program Headers): İşletim sistemi yükleyicisinin dosyayı RAM'e haritalarken (memory mapping) kullandığı yürütme bloklarıdır.
6. Obfuscation (Kod Karartma) ve Packer Kavramı
Geliştiriciler fikri mülkiyeti korumak, saldırganlar ise tespit edilmemek (AV/EDR evasion) ve tersine mühendislik sürecini zorlaştırmak için analiz engelleme yöntemleri kullanır.
Obfuscation (Karartma) Nedir?
Programın işlevselliğini bozmadan kaynak veya makine kodunun insanlar ve analiz araçları tarafından anlaşılmasını zorlaştıracak biçimde dönüştürülmesidir.
- Dead Code / Junk Code Insertion: Anlamsız Assembly talimatları (
NOP,XOR EAX, EAX; ADD EAX, 0) ekleyerek akış şemasını (Control Flow Graph) bozma. - Instruction Substitution: Basit bir
ADD EAX, 5komutu yerine mantıksal olarak aynı işi yapan karmaşık bit kaydırma ve XOR dizileri yerleştirme. - Control Flow Flattening (Akış Düzleştirme):
if/elseve döngü yapılarını parçalayarak devasa birswitch-casedurum makinesi arkasına saklama. - String Encryption: URL, IP, C2 komutları veya API adlarını XOR, RC4 veya AES gibi algoritmalarla şifreleyip sadece çalışma anında (runtime) bellekte çözme.
7. Packer Nedir ve UPX Nasıl Çalışır?
Packer; bir çalıştırılabilir dosyanın kod ve veri bölümlerini sıkıştırabilen, şifreleyebilen veya farklı bir temsil içine dönüştürerek çalışma anında yeniden oluşturan araç sınıfıdır. Amaç dosya boyutunu küçültmek, dağıtımı kolaylaştırmak veya tersine mühendisliği zorlaştırmak olabilir.
Paketlenmiş Dosyanın Çalışma Mantığı:
- Dosya diskteyken orijinal kodun önemli bir bölümü doğrudan analiz edilebilir biçimde görünmeyebilir; bunun yerine bir Unpacking Stub (çözücü kod) bulunur.
- Dosya çalıştırıldığında işletim sistemi yükleyicisi önce mevcut PE/ELF giriş noktasındaki stub'ı başlatır.
- Stub, sıkıştırılmış veya dönüştürülmüş gövdeyi bellekte yeniden oluşturur.
- Packer'a bağlı olarak importlar yeniden kurulabilir veya gerekli API adresleri dinamik olarak çözümlenebilir (
LoadLibrary/GetProcAddress). - Son aşamada yürütme akışı orijinal program koduna, çoğu durumda Original Entry Point (OEP) olarak adlandırılan noktaya aktarılır.
UPX (Ultimate Packer for eXecutables)
Açık kaynaklı, yüksek performanslı ve taşınabilir bir çalıştırıcı paketleyicisidir.
- Klasik PE örneklerinde orijinal kod/veri yapısının önemli bölümünü sıkıştırır; sıkça
UPX0veUPX1adlı bölümler görülür.UPX0dosyada az veya hiç ham veri taşımayıp çalışma sırasında açılan kod/veri için sanal alan görevi görebilir;UPX1ise sıkıştırılmış içerik ve unpacking stub'ın bir bölümünü barındırabilir. - UPX ile paketlenmiş bir dosya Ghidra veya IDA'ya yüklendiğinde orijinal fonksiyonların, stringlerin ve importların önemli kısmı doğrudan görünmeyebilir. Görünen import sayısı ve bölüm düzeni UPX sürümüne, hedef mimariye ve dosyanın nasıl paketlendiğine göre değişir.
[Normal Dosya] --> [ .text ] [ .data ] [ .rdata (Tüm API'ler) ]
|
UPX ile Paketleme
v
[Paketli Dosya] --> [ UPX0 (Boş Bellek) ] [ UPX1 (Sıkıştırılmış Kod + Stub) ]
- Unpacking Yöntemi: Standart ve değiştirilmemiş UPX dosyaları çoğu durumda komut satırından
upx -d dosya.exeile açılabilir. Özel veya değiştirilmiş packer stub'larında ise yöntem örneğe göre değişir. Klasik bazı 32-bit UPX örneklerindePOPADsonrasındaki OEP transferi analiste ipucu verebilir; ancak bu kalıp evrensel değildir ve x64'tePOPADtalimatı bulunmaz. Bu nedenle gerçek analizde stub'ın kontrol akışı, belleğe yazdığı bölgeler ve orijinal koda yaptığı son transfer birlikte incelenmelidir.
8. Uçtan Uca Tersine Mühendislik Analiz Süreci
Zararlı bir binary analiz edilirken izlenen standart adımlar:
+-------------------------------------------------------------------+
| 1. Statik Triage & Format Tespiti (PE-bear, DIE, Detect It Easy) |
| - Dosya formatı nedir? (PE/ELF/Mach-O) |
| - Packer var mı? (UPX, Themida, VMProtect) |
| - Derleyici nedir? (MSVC, GCC, Golang, Rust, .NET) |
+-------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------+
| 2. Unpacking & Deobfuscation |
| - Packer varsa açılır (Statik veya Dinamik OEP Dump). |
| - Şifrelenmiş stringler statik scriptlerle çözülür. |
+-------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------+
| 3. İleri Statik Analiz (Ghidra / IDA Pro) |
| - Symbol Tree, Export/Import ve Defined Strings taraması. |
| - OEP / Main fonksiyonunun tespiti. |
| - Decompiler ve Disassembly üzerinden lojik akışın çözülmesi. |
+-------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------+
| 4. Dinamik Analiz & Hata Ayıklama (x64dbg / İzole Lab) |
| - Breakpoint'ler ile kritik API çağrılarının anlık takibi. |
| - Bellek enjeksiyonu, ağ trafiği ve kalıcılık takibi. |
+-------------------------------------------------------------------+
9. Statik Analizin Açık Kaynaklı Gücü: Ghidra Nedir?
Ghidra; NSA (Ulusal Güvenlik Ajansı) tarafından geliştirilen ve 2019 yılında açık kaynaklı olarak topluluğa sunulan profesyonel bir yazılım tersine mühendisliği çatısıdır (Software Reverse Engineering Framework). Java tabanlıdır; Windows, Linux ve macOS üzerinde çalışır.
Ghidra'nın Öne Çıkan Özellikleri:
- Dahili Decompiler: Makine kodu ve Ghidra'nın ara temsilini analiz ederek C benzeri pseudocode üretir.
- SLEIGH İşlemci Tanımlama Dili: Ghidra'nın farklı işlemci instruction setlerini tanımlamak ve bunları P-code ara temsiline çevirmek için kullandığı mimari tanımlama dilidir. SLEIGH, decompiler'ın adı değildir.
- Geniş Mimari Desteği: x86, x86-64, ARM/AArch64, MIPS, PowerPC, SPARC, AVR ve çok sayıda başka instruction-set mimarisini destekler.
- Scripting ve Otomasyon: Java tabanlı GhidraScript altyapısının yanında güncel sürümlerde PyGhidra üzerinden CPython 3 ile analiz ve otomasyon yapılabilir.
- Ghidra Server: Projelerin merkezi bir repository üzerinde paylaşılmasına, sürümlenmesine ve ekip üyeleri arasında kontrollü biçimde birlikte yürütülmesine imkân tanır.
- Headless Analysis: Grafik arayüz açmadan toplu analiz, script çalıştırma ve otomasyon süreçleri yürütülebilir.
Ghidra ile Temel Statik Analiz Akışı
Yeni bir binary Ghidra'ya alındığında pratikte sık kullanılan çalışma sırası şöyledir:
- Import ve Language Seçimi: Dosya formatı, işlemci mimarisi ve uygun compiler specification doğrulanır.
- Auto Analysis: Function discovery, reference analysis, stack analizi ve veri referansları gibi analizörler çalıştırılır.
- Program Trees / Symbol Tree: Importlar, exportlar, fonksiyonlar, namespace'ler ve tanımlı stringler incelenir.
- Listing ve Decompiler Birlikte Okunur: Assembly görünümü ile C benzeri pseudocode karşılaştırılır.
- Cross References (Xrefs): Bir fonksiyonun veya verinin nerelerden çağrıldığı/kullanıldığı takip edilir.
- Rename / Retype:
FUN_00401230,param_1veyaundefined4gibi otomatik isim ve tipler, elde edilen kanıta göre daha anlamlı hâle getirilir. - Function Signature ve Calling Convention: Parametreler, dönüş tipi ve çağırma düzeni düzeltilerek decompiler çıktısının kalitesi artırılır.
- Call Graph ve Data Flow: Kritik fonksiyon aileleri, veri akışı ve olası davranış zincirleri çıkarılır.
Basit bir Assembly örneği:
push ebp
mov ebp, esp
sub esp, 20h
call FUN_00401230
test eax, eax
jz LAB_00401300
Decompiler bunu kabaca aşağıdaki gibi daha yüksek seviyeli bir yapıya dönüştürebilir:
result = FUN_00401230();
if (result == 0) {
goto LAB_00401300;
}
Buradaki önemli nokta, decompiler çıktısını nihai gerçek olarak kabul etmemektir. Yanlış fonksiyon imzaları, eksik tip bilgileri, bozuk stack analizi, obfuscation veya packer kullanımı pseudocode'u yanıltabilir. Bu nedenle Listing, Xrefs, register kullanımı ve kontrol akışı birlikte doğrulanmalıdır.
10. Tersine Mühendislik Araç Karşılaştırması
| Araç | Türü | Öne Çıkan Yönü | Lisans |
|---|---|---|---|
| Ghidra | Disassembler / Decompiler | Geniş işlemci desteği, dahili güçlü C decompiler, script desteği | Açık Kaynak |
| IDA Pro + Hex-Rays | Disassembler / Decompiler | Olgun analiz ekosistemi, güçlü decompiler ve eklenti desteği | Ticari |
| Binary Ninja | Disassembler / Decompiler | Modern arayüz, güçlü BNIL ara temsil mimarisi | Ticari / Demo |
| x64dbg / x32dbg | Dinamik Debugger | Windows kullanıcı modu (user-mode) için hafif ve pratik hata ayıklama | Açık Kaynak |
| Radare2 / Cutter | CLI / GUI Framework | Komut satırında hafif ve otomasyona son derece uygun | Açık Kaynak |
11. Reverse Engineering İçin x86/x64 Assembly Temelleri
Ghidra'nın Listing ve Decompiler pencerelerini birlikte okuyabilmek için temel Assembly komutlarını, register hiyerarşisini ve calling convention mantığını bilmek gerekir. x86 ailesinde 16-bit, 32-bit ve 64-bit çalışma modları aynı tarihsel instruction set ailesini paylaşsa da register genişlikleri, adresleme biçimleri, instruction encoding'i ve ABI/calling convention kuralları arasında önemli farklar vardır.
Temel komutlar ve mimariler arası farklar aşağıda yapılandırılmıştır.
11.1. Yazmaç (Register) Hiyerarşisi
Her yeni mimari, önceki neslin yazmaçlarını kapsayacak şekilde genişletilmiştir:
| 64-bit (Quadword - 8 Byte) | 32-bit (Doubleword - 4 Byte) | 16-bit (Word - 2 Byte) | 8-bit Yüksek / Düşük (Byte) | Temel Kullanım Amacı |
|---|---|---|---|---|
| RAX | EAX | AX | AH / AL | Akümülatör / Fonksiyon dönüş değeri |
| RBX | EBX | BX | BH / BL | Base Register / Bellek işaretçisi |
| RCX | ECX | CX | CH / CL | Sayaç (Döngüler ve Shift işlemleri) |
| RDX | EDX | DX | DH / DL | Veri / G/Ç işlemleri / Aritmetik taşma |
| RSI | ESI | SI | SIL (yalnızca x64) | Kaynak İndeksi (String/Bellek kopyalama) |
| RDI | EDI | DI | DIL (yalnızca x64) | Hedef İndeksi (String/Bellek kopyalama) |
| RBP | EBP | BP | BPL (yalnızca x64) | Stack Base / Stack Frame Tabanı |
| RSP | ESP | SP | SPL (yalnızca x64) | Stack Pointer / Yığın Tepe Noktası |
| RIP | EIP | IP | — | Instruction Pointer / Yürütülen Komut |
| R8 - R15 | R8D - R15D | R8W - R15W | R8B - R15B | x64 ile eklenen 8 adet genel amaçlı yazmaç |
11.2. Veri Taşıma Komutları (Data Transfer)
MOV dest, src: Veriyi kaynaktan hedefe kopyalar.- 16-bit:
mov ax, 0x1234 - 32-bit:
mov eax, [ebp-4](Bellekten oku) -
64-bit:
mov rax, rbx(Not: x64'te 32-bit alt yazmaca yazıldığındamov eax, 1, RAX'in üst 32 biti otomatik sıfırlanır). -
LEA dest, src(Load Effective Address): Bir bellek adresinin kendisini veya basit aritmetik işlemleri hesaplayıp hedefe yazar (İşaret edilen veriyi değil, adresi alır). -
lea rax, [rbx + rcx*4 + 0x10](Diziler ve işaretçi aritmatiğinde sıkça kullanılır). -
PUSH src/POP dest: Yığına (stack) veri ekler ve yığından veri çeker. - 16-bit: SP'yi 2 azaltır/artırır (
push ax). - 32-bit: ESP'yi 4 azaltır/artırır (
push eax). -
64-bit: RSP'yi 8 azaltır/artırır (
push rax). -
XCHG dest, src: İki operandın değerini takas eder. MOVSX/MOVZX: İşaretli (sign-extend) veya sıfırla genişleterek (zero-extend) daha küçük bir veriyi büyük yazmaca aktarır (movzx eax, byte ptr [rsi]).
11.3. Aritmetik ve Mantıksal Komutlar
ADD/SUB dest, src: Toplama ve çıkarma yapar. Bayrakları (ZF,CF,OF,SF) etkiler.INC/DEC dest: Değeri 1 artırır/azaltır. Legacy 16/32-bit modlarda bazı tek baytlık encoding'leri bulunurken x86-64 modunda aynı opcode aralığı REX prefix'leri için kullanılır.IMUL/IDIV: İşaretli çarpma ve bölme.-
IDIVişleminde register çifti bölüneni (dividend) taşır: 16-bit içinDX:AX, 32-bit içinEDX:EAX, 64-bit içinRDX:RAX. Bölen iseIDIVkomutunda belirtilen operanddır. Örneğinidiv ebxişlemindeEDX:EAX,EBX'e bölünür; bölümEAX, kalanEDXiçine yazılır. -
AND/OR/XOR/NOT: Bitsel mantık işlemleri. -
Bir register'ı sıfırlamak için yaygın idiom
xor eax, eaxbiçimidir. x86-64'teEAX'e yazmakRAX'in üst 32 bitini de sıfırladığı için çoğu durumda ayrıcaxor rax, raxkullanmak gerekmez. -
SHL/SHR/SAR: Bitsel sola/sağa kaydırma yapar.SARaritmetik sağa kaydırmada işaret bitini korur. Değişken miktarlı kaydırmalarda sayaç olarakCLkullanılabilir.
11.4. Karşılaştırma ve Koşullu Dallanma (Control Flow)
CMP dest, src:SUBgibi çıkarma yapar ancak sonucu bir yere kaydetmez; sadeceEFLAGS / RFLAGSbayraklarını günceller.TEST dest, src:ANDişlemi yapar; sonucu kaydetmez, bayrakları günceller. Özellikle bir yazmacın0veyaNULLolup olmadığını denetlemek için kullanılır (test eax, eax).- Koşullu Sıçramalar (Jumps):
JZ / JE: Sıfır / Eşitse sıçra (ZF = 1).JNZ / JNE: Sıfır değil / Eşit değilse sıçra (ZF = 0).JG / JL: İşaretli büyüktür / küçüktür sıçraması.-
JA / JB: İşaretsiz büyüktür / küçüktür sıçraması. -
JMP target: Koşulsuz sıçrama.
11.5. Fonksiyon Çağrıları ve Yığın Yönetimi
CALL target: Bir sonraki komutun adresini (Return Address) stack'ePUSHeder ve hedefe sıçrar.RET: Stack'teki dönüş adresiniPOPederekIP / EIP / RIPyazmacına yükler ve çağrıldığı yere döner.NOP(0x90): Mimari durum üzerinde anlamlı bir işlem yapmayan tek baytlık NOP encoding'idir. Kod hizalama, hot-patching alanı, binary patching ve bazı obfuscation tekniklerinde kullanılabilir.
11.6. Mimari Düzeyinde Kritik Farklar (16 vs 32 vs 64)
[16-bit Real / Protected Mode]
- Segmentli adresleme yaygındır: Segment:Offset -> CS, DS, SS, ES.
- Real Mode klasik olarak 20-bit fiziksel adreslemeyle yaklaşık 1 MiB adres alanına erişir.
- 80286 Protected Mode 24-bit fiziksel adresleme ile 16 MiB'a kadar fiziksel belleği adresleyebilir.
- Calling convention derleyiciye ve ortama bağlıdır; parametrelerin stack üzerinden aktarılması yaygındır.
[32-bit Protected Mode]
- Modern işletim sistemlerinde çoğunlukla düz (flat) 32-bit sanal adres alanı modeli kullanılır.
- Teorik sanal adres alanı 4 GiB'tır; bunun kullanıcı/kernel paylaşımı işletim sistemine göre değişebilir.
- `cdecl` ve `stdcall` gibi convention'larda parametreler ağırlıklı olarak stack üzerinden aktarılır; `fastcall` türevlerinde bazı parametreler register'lardan geçebilir.
- `EBP` klasik stack frame tabanı olarak kullanılabilir ancak optimize edilmiş kodda frame pointer tamamen kaldırılabilir.
[64-bit Long Mode]
- RIP-relative adresleme yaygındır: `lea rax, [rip + 0x200]` veya `mov eax, [rip + 0x200]`.
- `R8` - `R15` arasında 8 yeni genel amaçlı register bulunur.
- Yaygın 64-bit ABI'lerde ilk parametrelerin register üzerinden aktarılması standarttır:
* Windows x64 ABI: `RCX`, `RDX`, `R8`, `R9`; çağıran taraf 32-byte shadow space ayırır.
* System V AMD64 ABI: `RDI`, `RSI`, `RDX`, `RCX`, `R8`, `R9`; Windows'taki shadow space kuralı bulunmaz.
12. Özümseme ve Genel Değerlendirme
Tersine mühendislik yalnızca bir disassembler veya decompiler açıp ekrandaki pseudocode'u okumaktan ibaret değildir. Dosya formatını, loader davranışını, calling convention'ları, register ve stack kullanımını, packer/obfuscation tekniklerini ve statik-dinamik analiz arasındaki farkları birlikte değerlendirmek gerekir.
Ghidra bu sürecin büyük bölümünü tek bir çalışma ortamında bir araya getirir; ancak otomatik analiz sonuçları her zaman analistin doğrulamasına ihtiyaç duyar. Özellikle zararlı yazılım analizinde hedef, mümkün olduğunca çok davranışı çalıştırmadan karakterize etmek ve dinamik analize ancak gerçekten gerektiğinde, uygun biçimde izole edilmiş bir laboratuvar ortamında geçmektir.
Her ne kadar VirusTotal, Hybrid Analysis ve Cuckoo Sandbox gibi servis ve araçlar savunma tarafının işini kolaylaştırsa da otomatik analiz sonuçlarının her zaman nihai gerçek olarak kabul edilmemesi gerekir. Bazı örnekler sandbox veya analiz ortamlarını tespit ederek davranışlarını değiştirebilir; bu nedenle otomatik çıktılar mümkün olduğunca manuel statik inceleme ve kontrollü laboratuvar doğrulamalarıyla desteklenmelidir.
13. Ghidra Kurulumu, İlk Analiz ve UPX Katmanının Çıkarılması
Bu bölümde teoriyi kendi çalışma akışımla birleştireceğim. Amacım yalnızca Ghidra'nın nereden açıldığını göstermek değil; bir örneği projeye ekleme, ilk statik analizi başlatma, paketlenmiş ve açılmış dosya arasındaki farkı görme, Script Manager'ı kullanma ve son olarak UPX katmanını doğrulayarak kaldırma sürecini uçtan uca anlatmak.
Burada kullandığım örnekler bir zararlı yazılım analizi laboratuvarından geliyor. Dosyaları çalıştırmadan, mümkün olduğunca statik yöntemlerle ilerliyorum.
13.1. Gerekli Paketlerin Kurulması
Önce Ghidra ve yardımcı analiz araçları için ihtiyaç duyduğum temel paketleri kuruyorum. Benim Debian tabanlı ortamımda kullandığım paket grubu şu şekildeydi:
sudo apt update sudo apt install -y \ openjdk-21-jdk \ python3 \ python3-pip \ python3-venv \ python3-dev \ unzip \ zip \ git \ build-essential \ curl

Buradaki JDK sürümü kullandığınız Ghidra sürümüne göre değişebilir. Ben sistemimde Java 21 kullanıyorum; ancak belirli bir Ghidra sürümünü kurmadan önce o sürümün kendi kurulum dokümanındaki minimum JDK gereksinimini kontrol etmek gerekir.
13.2. Java Ortamının Doğrulanması
Kurulumdan sonra hangi Java binary'sinin kullanıldığını ve JAVA_HOME değerini doğruluyorum:
readlink -f "$(which javac)" echo "$JAVA_HOME" java -version
Benim sistemimde Java yolu:
/usr/lib/jvm/java-21-openjdk-amd64
olduğu için ortam değişkenlerini şu şekilde kalıcı hâle getirdim:
cat >> ~/.bashrc <<'EOF' # Ghidra / Java 21 export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 export PATH="$JAVA_HOME/bin:$PATH" EOF source ~/.bashrc

Bu adımın amacı Ghidra'yı çalıştırmadan önce sistemin beklediğimiz JDK'yı kullandığından emin olmaktır.
13.3. Ghidra'yı Manuel İndirme ve Kurma
Bu yazıda otomatik kurulum scripti kullanmak yerine Ghidra'yı manuel olarak indirip kuracağız.
Ghidra 11.1.2 arşivini resmî GitHub release sayfasından indirebilirsiniz:
Ghidra 11.1.2 — Resmî GitHub Release Sayfası
Release sayfasında Ghidra 11.1.2 PUBLIC arşivini indiriyoruz. İndirdiğimiz dosya ZIP formatındadır ve klasik anlamda bir kurulum sihirbazı gerektirmez.
Örneğin dosyayı ~/Downloads dizinine indirdiyseniz masaüstüne şu şekilde çıkarabilirsiniz:
cd ~/Downloads unzip ghidra_11.1.2_PUBLIC_20240709.zip -d ~/Desktop/
Arşiv açıldığında aşağıdaki gibi bir klasör oluşur:
/home/cybermachine/Desktop/ghidra_11.1.2_PUBLIC
Linux ortamında ana başlatıcı dosya ghidraRun dosyasıdır. Çalıştırma izni yoksa:
chmod +x ~/Desktop/ghidra_11.1.2_PUBLIC/ghidraRun
komutuyla izin verebilirsiniz.
Ben bu çalışmada Ghidra 11.1.2 kullandım. Daha güncel bir sürüm de kullanılabilir; ancak seçtiğiniz sürümün JDK ve scripting gereksinimlerini kendi sürüm dokümanından kontrol etmeniz gerekir.
13.4. JDK ve Python Uyumluluğu
Not: Ghidra sürümleri arasında JDK ve scripting altyapısı değişebilir. Bu nedenle kullandığınız sürümün kendi kurulum dokümanını esas alın. Bu yazıda kullandığım Ghidra 11.1.2 kurulumu için minimum JDK 17 gereksinimini temel aldım; kendi sistemimde ise Java 21 ile çalışıyorum.
Python, Jython veya PyGhidra tabanlı otomasyon kullanmayı planlıyorsanız ilgili Ghidra sürümünün hangi Python altyapısını desteklediğini ayrıca kontrol etmeniz gerekir.
13.5. Ghidra'yı Çalıştırma
Kurulum tamamlandıktan sonra Ghidra'yı kurulum dizinindeki ghidraRun dosyası üzerinden başlatıyorum:
/home/cybermachine/Desktop/ghidra_11.1.2_PUBLIC/ghidraRun

İlk açılışta proje alanı boş olabilir. Benim ekran görüntümde daha önce oluşturduğum çalışma alanı ve analiz için eklediğim dosyalar bulunduğu için Active Project altında birden fazla örnek görünüyor.
Bu yazıda debugger tarafına özellikle derin girmiyorum. Önceliğim statik analiz araçlarının mantığını göstermek; başka bir ifadeyle önce direksiyonun, gazın ve frenin nerede olduğunu öğrenmek.
13.6. Yeni Ghidra Projesi Oluşturma
Henüz bir çalışma alanınız yoksa:
File > New Project
yolunu izleyin.

Ghidra iki temel proje tipi sunar:
- Non-Shared Project: Tek kullanıcı tarafından yerel olarak yürütülen proje.
- Shared Project: Ghidra Server üzerinden ekip çalışmasına uygun proje.
Ben bu analizde paylaşımlı çalışma kullanmadığım için Non-Shared Project seçtim.
Ardından:
- Project Directory: Projenin fiziksel olarak tutulacağı dizin.
- Project Name: Ghidra projesinin adı.
alanlarını belirliyoruz.

Proje yolunu düzenli tutmak önemli. Analiz sırasında Ghidra yalnızca binary'yi değil; fonksiyon isimleri, yorumlar, veri tipleri, XREF'ler ve yaptığınız diğer analiz değişikliklerini de proje veritabanında saklar.
Dosya ve dizin adlarında mümkün olduğunca sade ASCII karakterleri kullanmak pratikte sorun yaşamayı azaltabilir.
13.7. Analiz Edilecek Dosyayı Projeye Eklemek
Yeni proje oluşturulduktan sonra:
File > Import File
üzerinden inceleyeceğimiz binary'yi projeye ekleyebiliriz.

Dosya projeye eklendikten sonra sağ tıklayıp:
Open With > CodeBrowser
seçeneğiyle açıyorum.
Ghidra dosyanın daha önce analiz edilmediğini görürse Auto Analysis başlatmak isteyip istemediğimizi sorar.

Burada Yes diyerek devam ediyorum.
Auto Analysis; fonksiyon başlangıçlarının bulunması, referansların oluşturulması, stack analizi, importların işlenmesi, veri referanslarının çıkarılması ve işlemci mimarisine özgü çeşitli analizörlerin çalıştırılması gibi birçok işlemi otomatikleştirir.
13.8. Analysis Options Ekranı
Auto Analysis başlamadan önce analizörlerin listelendiği Analysis Options ekranı gelir.

Ben bu özel çalışmada Shared Return Calls analizörünü devre dışı bırakarak ilerledim. Bu benim bu örnek için tercih ettiğim bir analiz ayarıdır; her binary için genel bir kural değildir. Farklı compiler kalıpları veya kontrol akışları için bu analizör yararlı olabilir.
Ardından Analyze düğmesiyle işlemi başlatıyorum.
Analiz tamamlandığında Ghidra bazı sembolleri ve giriş noktalarını tanıyabilir. Örneğin benim örneğimde Ghidra:
Analysis found the symbol "entry". Would you like to go to that symbol?
mesajını gösterdi.
Bu, Ghidra'nın program giriş noktalarından birini anlamlandırabildiğini gösterir; ancak bulunan entry fonksiyonunun decompiler çıktısının doğru olduğu anlamına tek başına gelmez.
13.9. Paketlenmiş Conficker ile UPX Açılmış Stage2 Arasındaki Fark
Şimdi iki görüntüyü karşılaştıralım.
İlk görüntü, henüz UPX katmanı kaldırılmamış örneğin Ghidra görünümüdür:

Program Tree tarafında:
Headers UPX0 UPX1 UPX2 tdb
gibi UPX'e özgü section isimleri görüyoruz.
Decompiler tarafında ise:
WARNING: Control flow encountered bad instruction data
ve benzeri uyarılar bulunuyor. Listing ekranında da çok sayıda:
??
tanımsız byte göze çarpıyor.
Bu görüntüyü doğrudan "bütün kod anlamsız" şeklinde yorumlamak doğru olmaz. Burada gördüğümüz şey, Ghidra'nın programın gerçek gövdesine ulaşmadan önce hâlâ packer stub'ı, sıkıştırılmış veri veya code/data sınırları belirsiz bölgelerle karşılaşmasıdır.
İkinci görüntü ise UPX katmanı kaldırılmış Stage2_UPX_UNPACKED.exe dosyasına aittir:

Bu kez Program Tree daha klasik bir PE görünümüne yaklaşır:
Headers .text .data .reloc tdb
Ayrıca entry fonksiyonunda düzenli bir x86 fonksiyon prologu görülebilir:
PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI
Decompiler da artık fonksiyon sınırlarını daha sağlıklı takip ederek C benzeri pseudocode üretebilmektedir.
Bu karşılaştırma bize önemli bir şey gösteriyor:
Unpacking, tersine mühendisliği bitirmez; yalnızca asıl analizi mümkün hâle getiren ilk büyük engellerden birini kaldırır.
13.10. Listing, Decompiler ve Adrese Gitme
CodeBrowser'da ana çalışma alanlarından biri Listing ekranıdır. Burada Assembly talimatlarını, adresleri, tanımlanmış verileri ve XREF ilişkilerini görürüz.
Decompiler penceresi ise Listing'de gördüğümüz makine kodunu ve Ghidra'nın P-code ara temsilini kullanarak C benzeri pseudocode üretmeye çalışır.
Bu iki ekranı birlikte okumak önemlidir. Decompiler kodun yüksek seviyeli mantığını daha hızlı kavramamızı sağlar; Listing ise bu yorumun dayandığı gerçek makine talimatlarını görmemizi sağlar.
Belirli bir adrese gitmek için Listing'e odaklanıp:
G
tuşunu kullanabilirsiniz. Bu kısayol Ghidra'nın Go To işlevini açar.
Buradaki G, C dilindeki goto ifadesiyle tarihsel veya teknik olarak aynı şey değildir; yalnızca arayüzdeki navigasyon kısayoludur.
13.11. Script Manager ile Analizi Otomatikleştirmek
Tek tek yüzlerce fonksiyonu elle incelemek her zaman verimli değildir. Ghidra'nın güçlü taraflarından biri de scripting altyapısıdır.
Script Manager'a:
Window > Script Manager
yoluyla ulaşabiliriz.

Script Manager içinde yeni bir script oluşturma düğmesine bastığımızda Ghidra bize desteklenen script tiplerini sorar.

Ben burada Python seçiyorum.

Ardından script dosyasına bir isim veriyoruz:

Script oluşturulduktan sonra editör alanında Ghidra'nın başlangıç şablonu açılır. Burada yazacağımız scriptlerle örneğin:
- tanımsız byte bölgelerini raporlayabilir,
- belirli instruction kalıplarını arayabilir,
- fonksiyonları ve XREF'leri sınıflandırabilir,
- import kullanımını tarayabilir,
- belirli adres aralıklarında code/data ayrımını inceleyebilir,
- analiz sonuçlarını dosyaya raporlayabiliriz.
Bu noktada önemli bir prensip var: otomasyon scripti, analistin yerine karar vermemeli; mümkün olduğunca kanıt toplamalı ve raporlamalıdır. Özellikle zararlı yazılım analizinde otomatik yeniden adlandırma veya agresif program veritabanı mutasyonları yanlış hipotezleri kalıcı hâle getirebilir.
13.12. UPX Katmanını Nasıl Açtım?
Peki paketlenmiş Stage2 örneğinin UPX katmanını nasıl kaldırdım?
Bu aşamada debugger kullanmadım ve örneği çalıştırmadım. Statik analiz çizgisinde kalmak istediğim için UPX'in kendi komut satırı aracından yararlandım.
Kullandığım sürüm:
UPX 4.2.4 Ultimate Packer for eXecutables
Çalışma zincirimde üç ayrı artefakt bulunuyordu:
Stage2_candidate_RAW.bin Stage2_candidate_SIGNATURE_NORMALIZED.exe Stage2_UPX_UNPACKED.exe
Buradaki isimlendirmeyi özellikle koruyorum. Çünkü RAW, SIGNATURE_NORMALIZED ve UPX_UNPACKED dosyalarının her biri analiz zincirinin farklı bir aşamasını temsil ediyor.
13.12.1. İlk Görsel: UPX Tarafından Tanınan Aday Dosyayı Doğrulamak
İlk ekran görüntüsünde tek bir komuta bakmıyoruz. Aynı terminal oturumunda birbirini tamamlayan üç kontrol yapıyorum:
upx -tile dosyanın UPX tarafından geçerli bir paketlenmiş örnek olarak kabul edilip edilmediğini test ediyorum.upx -lile paketleme bilgilerini ve oranını listeliyorum.- Linux
filearacıyla dosyanın PE türünü ve UPX işaretini bağımsız olarak kontrol ediyorum.
İlk komut:
upx -t "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_candidate_SIGNATURE_NORMALIZED.exe"
-t parametresi dosyayı açmadan UPX açısından test eder.
Aldığım sonuç:
testing /media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_candidate_SIGNATURE_NORMALIZED.exe [OK] Tested 1 file.
şeklindeydi.
Buradaki:
[OK]
ifadesi kritik. Çünkü bu aşamada UPX 4.2.4, Stage2_candidate_SIGNATURE_NORMALIZED.exe dosyasını kendi paket formatı açısından geçerli bir örnek olarak kabul ediyor.
Fakat tek bir çıktıyla yetinmiyorum. Aynı dosyanın paket bilgilerini de listeliyorum:
upx -l "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_candidate_SIGNATURE_NORMALIZED.exe"
Çıktıda:
File size Ratio Format 84992 -> 42496 50.00% win32/pe
bilgisini görüyorum.
Buradan dosyanın UPX tarafından win32/pe olarak tanındığını ve paketlenmiş boyutun yaklaşık olarak orijinal boyutun yarısına indiğini anlayabiliyoruz.
Son olarak aynı dosyayı Linux'un file aracıyla kontrol ediyorum:
file "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_candidate_SIGNATURE_NORMALIZED.exe"
Çıktı:
PE32 executable for MS Windows 4.00 (DLL), Intel i386, UPX compressed, 3 sections
şeklindeydi.
Burada özellikle şu üç ifade önemli:
PE32 Intel i386 UPX compressed
Yani ilk ekran görüntüsünün tamamını birlikte yorumladığımda üç ayrı gösterge aynı hipotezi destekliyor:
upx -t -> [OK] upx -l -> win32/pe, 84992 -> 42496, 50.00% file -> PE32, Intel i386, UPX compressed, 3 sections
Bu nedenle bu noktada elimizdeki SIGNATURE_NORMALIZED dosyasının stock UPX 4.2.4 tarafından tanınabilen bir UPX-packed PE olduğunu söyleyebiliyoruz.

Buradaki önemli nokta şudur: ekran görüntüsünde [OK] sonucundan sonra görülen boyut/oran tablosu ve file çıktısı ayrı birer kanıttır. Yani yalnızca [OK] satırına bakarak karar vermiyorum; dosya türünü ve paketleme bilgisini birden fazla araçla çapraz kontrol ediyorum.
13.12.2. UPX Katmanını Kaldırmak
Aday dosya artık UPX tarafından düzgün biçimde tanındığı için asıl unpack işlemine geçebiliriz.
UPX'in bunun için kullandığı temel parametre:
-d
yani decompress seçeneğidir.
En temel kullanım:
upx -d "dosya.exe"
şeklindedir.
Ben analiz zincirini korumak ve ara artefaktların üzerine yazmamak için çıktıyı ayrı bir dosyaya yönlendirdim:
upx -d \ -o "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_UPX_UNPACKED.exe" \ "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_candidate_SIGNATURE_NORMALIZED.exe"
Böylece çalışma zinciri şu hâle geliyor:
Stage2_candidate_RAW.bin
|
v
Signature / yapı normalizasyonu
|
v
Stage2_candidate_SIGNATURE_NORMALIZED.exe
|
| upx -t -> [OK]
| upx -l -> win32/pe
| file -> UPX compressed
|
v
upx -d
|
v
Stage2_UPX_UNPACKED.exe
Bu isimlendirme yalnızca düzen için değil, analizin tekrar üretilebilirliği için de önemlidir. Sonradan hangi dosyanın orijinal veri, hangisinin normalize edilmiş ara örnek ve hangisinin unpack edilmiş çıktı olduğunu karıştırmıyoruz.
13.13. İkinci Görsel: Unpack İşlemini Nasıl Doğruladım?
İkinci ekran görüntüsünde artık Stage2_UPX_UNPACKED.exe dosyasını inceliyorum.
Burada da tek bir sonuca güvenmek yerine üç ayrı doğrulama yapıyorum:
fileile unpack edilmiş dosyanın türünü kontrol ediyorum.sha256sumile RAW, NORMALIZED ve UNPACKED artefaktlarını birbirinden ayırıyorum.upx -tile açılmış dosyanın artık UPX tarafından paketli olarak tanınıp tanınmadığını tekrar test ediyorum.
İlk kontrol:
file "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_UPX_UNPACKED.exe"
Çıktı:
PE32 executable for MS Windows 4.00 (DLL), Intel i386, 3 sections
şeklindeydi.
İlk görselde aynı aracın verdiği çıktı şuydu:
PE32 executable for MS Windows 4.00 (DLL), Intel i386, UPX compressed, 3 sections
Şimdi ise:
UPX compressed
ifadesi yok.
Bu, unpack sonrasında gözlediğimiz ilk önemli farktır.
Ardından üç dosyanın SHA-256 değerlerini alıyorum:
sha256sum \ "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_candidate_RAW.bin" \ "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_candidate_SIGNATURE_NORMALIZED.exe" \ "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_UPX_UNPACKED.exe"
Benim çalışma zincirimde değerler:
Stage2_candidate_RAW.bin d23f781ed211c5c69ecbfa7b4c630f8ce81b7d093f812f16d7812b1ccc700d48 Stage2_candidate_SIGNATURE_NORMALIZED.exe 568b7b512cb0b2f920e89140c56ef643a39643db2cfe7a92e8d15049938e0a6e Stage2_UPX_UNPACKED.exe 9025d031983030c79989abe2616334a90df95fd03f1a5d666cc5480ef44072da
şeklindeydi.
Bu hash'lerin birbirinden farklı olması beklenen bir durumdur. Çünkü üçü de farklı aşamalardaki farklı byte dizilerini temsil ediyor.
Son kontrol olarak unpack edilmiş dosyayı tekrar UPX'e veriyorum:
upx -t "/media/cybermachine/NEW VOLUME/DarkLab/conficker-work/05_stage2/Stage2_UPX_UNPACKED.exe"
Bu kez aldığım cevap:
NotPackedException: not packed by UPX Tested 0 files.
şeklindeydi.
İlk bakışta Exception kelimesi bir hata izlenimi verebilir. Fakat bu aşamada bizim aradığımız sonuç tam olarak budur.
UPX burada esasen şunu söylüyor:
"Bu dosyada artık benim tanıdığım bir UPX paketleme katmanı yok."
Dolayısıyla ikinci ekran görüntüsünü bütünüyle yorumladığımızda tablo şöyledir:
file -> PE32, Intel i386, 3 sections -> "UPX compressed" ibaresi artık yok sha256sum -> RAW, NORMALIZED ve UNPACKED için üç farklı hash upx -t -> NotPackedException: not packed by UPX

Ekran görüntüsünde aynı komut grubunun terminalde tekrarlandığını da görebilirsiniz. Bu ikinci kez farklı bir sonuç üretildiği anlamına gelmiyor; aynı doğrulamanın tekrar çalıştırılmış hâlidir. Her iki çalıştırmada da Stage2_UPX_UNPACKED.exe için UPX'in verdiği sonuç değişmiyor:
NotPackedException: not packed by UPX
13.14. İki Görseli Yan Yana Okuduğumuzda Ne Görüyoruz?
İki terminal görüntüsünü yan yana düşündüğümüzde paketleme katmanının durumu çok daha açık hâle geliyor.
İlk dosya:
Stage2_candidate_SIGNATURE_NORMALIZED.exe
için:
upx -t -> [OK] upx -l -> win32/pe, 50.00% file -> UPX compressed
sonuçlarını alıyoruz.
Unpack sonrasında oluşan:
Stage2_UPX_UNPACKED.exe
için ise:
file -> UPX compressed ibaresi yok upx -t -> NotPackedException: not packed by UPX
sonuçlarını alıyoruz.
Bunu basit bir akışla gösterirsek:
SIGNATURE_NORMALIZED
|
| UPX tanıyor
| upx -t -> [OK]
| file -> UPX compressed
v
upx -d
|
v
UPX_UNPACKED
|
| file -> UPX compressed yok
| upx -t -> NotPackedException
v
Statik analiz için daha uygun PE
Burada NotPackedException, önceki [OK] sonucuyla çelişmiyor. Çünkü iki komut aynı dosyaya uygulanmıyor.
İlk [OK]:
Stage2_candidate_SIGNATURE_NORMALIZED.exe
dosyasına aittir.
NotPackedException ise:
Stage2_UPX_UNPACKED.exe
dosyasına aittir.
Yani aslında bu iki sonuç birbirini tamamlıyor:
Önce: UPX katmanı mevcut ve tanınıyor. Sonra: UPX katmanı kaldırılmış ve artık tanınmıyor.
13.15. Terminal Sonucunu Ghidra ile Çapraz Kontrol Etmek
Terminal tarafındaki doğrulama tek başına güçlüdür; fakat Ghidra görünümü de aynı değişimi destekliyor.
Paketlenmiş örnekte Program Tree tarafında:
Headers UPX0 UPX1 UPX2 tdb
gibi UPX'e özgü section isimleri görüyorduk.
Decompiler tarafında:
WARNING: Control flow encountered bad instruction data
uyarısı bulunuyordu ve Listing ekranında çok sayıda:
??
tanımsız byte vardı.
UPX katmanı kaldırılmış Stage2_UPX_UNPACKED.exe dosyasını Ghidra'ya aldığımızda ise Program Tree:
Headers .text .data .reloc tdb
şeklinde daha klasik bir PE görünümüne yaklaşıyor.
Ayrıca entry fonksiyonunda:
PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI
gibi düzenli x86 fonksiyon prologları görülebiliyor ve Decompiler daha anlamlı C benzeri pseudocode üretebiliyor.
Böylece unpack işlemini dört ayrı düzeyde çapraz kontrol etmiş oluyoruz:
1. UPX doğrulaması - Packed aday: [OK] - Unpacked çıktı: NotPackedException 2. Linux file çıktısı - Packed aday: UPX compressed - Unpacked çıktı: UPX compressed ibaresi yok 3. SHA-256 artefakt takibi - RAW, NORMALIZED ve UNPACKED birbirinden ayrılıyor 4. Ghidra görünümü - UPX0/UPX1/UPX2 -> .text/.data/.reloc - bozuk/eksik CFG -> daha okunabilir Listing ve Decompiler
Bu yaklaşım tersine mühendislikte benim özellikle önem verdiğim bir prensibi gösteriyor:
Tek bir aracın tek bir satırına güvenmek yerine, aynı hipotezi mümkün olduğunca bağımsız göstergelerle doğrulamak.
UPX katmanının kaldırıldığını yalnızca upx -d komutunun başarılı çalışmasına bakarak kabul etmedim. Terminal çıktıları, hash zinciri ve Ghidra'daki yapısal değişim aynı sonuca işaret ettiği için bu aşamayı doğrulanmış kabul ettim.
13.16. Buradan Sonra Ne Yapacağız?
UPX katmanının kaldırılmasıyla işimiz bitmedi; asıl statik analiz bundan sonra başlıyor.
Stage2_UPX_UNPACKED.exe üzerinde artık:
entryfonksiyonundan başlayarak call graph çıkarmak,FUN_...isimli fonksiyonları davranışlarına göre sınıflandırmak,- import ve API çağrılarını incelemek,
- Defined Strings ve XREF ilişkilerini takip etmek,
- global
DAT_...alanlarını anlamlandırmak, - code/data sınırlarında hâlâ kalan belirsiz bölgeleri incelemek,
- gerektiğinde Ghidra Python scriptleriyle rapor üretmek
mümkün hâle geliyor.
Buradaki temel hedefim binary'yi çalıştırmadan mümkün olduğunca fazla anlam çıkarmak.
UPX bize yalnızca ilk kapıyı açtı; asıl tersine mühendislik bundan sonra başlıyor.
Peki neden bu işlemi yapmak zorundaydım? Conficker bir Windows worm'u ancak yukarıda belirttiğim üzere bazı zararlıların donanıma müdahile etme durumu olabiliyor. Conficker.B varyantına ait olduğuna dair bulgular buldum ancak bilinen çoğu conficker varyantında herhangi bir donanım müdahilesi yoktur, modifiye edilip edilmediğini görmek için şu an halen obfuscation kısımlarını kaldırmaya çalışıyorum.
Kod tamamen okunabilecek hale gelirse bu durumda dinamik analiz veya debugger kısımlarına geçilebilir. Ancak debugger kısımlarına bilerek şu ana kadar girmedim. Sebebi ise siz debugger çalıştırdığınızda bazı zararlılar VM içinde veya any.run gibi bir yerde bile kendini silebiliyor, makinenize donanımsal zarar verebiliyor veya VM atlatarak host makinenize sızabiliyor. Bu yüzden statik önceliktir.
Şu an çalışmalar devam ediyor, yazımın ikinci hali olacak devam serisinde yaptığım adımları biraz daha detaylı açıklamaya ve çıkarılan kod bloklarını, shellcode, string, DGA ve benzer yapıları incelemeye çalışacağız.
Python scriptlerini Github veya benzer bir platforma yüklemeyi hedefliyorum ancak zararlının kaynak kodunun reverse edilmiş örneği veya kendisini paylaşmayacağım yasalar gereği. Ekran görüntülerinden kısmi olarak mantığı anlatmayı hedefliyorum.
Yazımı okuduğunuz için teşekkür ederim
Telif ve Kullanım Koşulları
© 2026 Hakan ÇEVİK'e (Cybermachine) aittir. Tüm hakları saklıdır.