← Back to list

crackmes.one — CrackNotMe’s Willy Wonka’s Chocolate Factory Writeup (Turkish)

Bu dosyada Ghidra ile statik analiz yapılmış olup dosya hakkında özet bilgiler şöyledir:

Muhammed Emin Ünal · 2026-05-06 06:23 · 0 claps · 6.0 min read
#reverse-engineering #crackme #cracking #ctf #ghidra
Open on Medium ↗

crackmes.one — CrackNotMe’s Willy Wonka’s Chocolate Factory Writeup (Turkish)

Bu dosyada Ghidra ile statik analiz yapılmış olup dosya hakkında özet bilgiler şöyledir:

ChocolateFactory.exe, kullanıcıdan XXXX-XXXX-XXXX-XXXX biçiminde bir Golden Ticket isteyen 64-bit bir Windows konsol crackme’i. Detect It Easy çıktısına göre dosya bir PE64 console binary ve Windows AMD64 hedefli. Program açıldığında kullanıcıya golden ticket soruyor; yanlış örnek girişlerde “The Oompa Loompas laugh at your laziness!” gibi tematik hata mesajları veriyor. Başarılı ve başarısız çalışma ekranları da bunu doğruluyor.

Bu crackme’in ayırt edici tarafı, tek bir doğru anahtara sahip olmaması. İlk üç kontrol bloğu ticket’ın ilk 12 karakterini sabitlerken, son blok aynı prefix ile birden fazla 4 karakterli suffix kabul ediyor. Bu yüzden çözüm tek bir golden ticket değil, bir geçerli ticket ailesi üretmek oluyor. Başarı ekranı paylaşılmış ekran görüntülerinde açıkça görülüyor.

1. Ana giriş noktası ve doğrulama akışının merkezi

String listesinde giriş prompt’unun XREF’i doğrudan ana doğrulama fonksiyonunu işaret ediyor:

s_Enter_your_Golden_Ticket_(16_cha_14001c070
XREF[1]: FUN_140002870:140002a6b(*)

Bu tek başına bile FUN_140002870’in giriş/validasyon merkezi olduğunu gösteriyor. Aynı fonksiyon içinde workshop isimleri, input işleme, süre kontrolü, anti-debug etkisi ve başarı/fail ayrımı bir arada bulunuyor.

Decompile tarafında da fonksiyonun kapsamı bunu doğruluyor: konsol hazırlanıyor, prompt basılıyor, input okunuyor, normalize ediliyor ve ardından dört ayrı workshop testi çalıştırılıyor. En sonda ya başarısızlık ekranına ya da başarı rutini olan FUN_140001c10’a gidiliyor.

2. Girdi biçimi görsel; gerçek kontrol normalize edilmiş 16 karakter üzerinde

Programın kullanıcıya gösterdiği format XXXX-XXXX-XXXX-XXXX, fakat gerçek doğrulama ham string üzerinde yapılmıyor. Decompile’da input alındıktan sonra satır sonları temizleniyor ve şu kritik koşul uygulanıyor:

if ((bVar1 != 0x2d) && (bVar1 != 0x20))

Yani - ve boşluk karakterleri atlanıyor; geri kalan karakterler local_188 tamponuna taşınıyor. Ardından gerçek uzunluk kontrolü bunun üzerinde yapılıyor. Bu nedenle crackme aslında görsel formatı değil, normalize edilmiş 16 karakterlik değeri doğruluyor.

Bu yüzden şu üç yazım mantıksal olarak aynı ticket’a karşılık gelir:

Ch0c-M1lk-CrMe-JAfa
Ch0c M1lk CrMe JAfa
Ch0cM1lkCrMeJAfa

Bu ayrıntı önemli, çünkü ilk bakışta format zorunluymuş gibi görünse de doğrulama mantığı biçimden bağımsız çalışıyor.

3. Özel durumlar: WONK, HELP ve 0000…

Ana çözüm yoluna geçmeden önce program bazı özel-case string’leri ayıklıyor. Assembly/data tarafında bunlar açıkça görülüyor:

DAT_14001c03c = 57 4F 4E 4B   ; "WONK"
s_HELPHELPHELPHELP_14001c050  ; "HELPHELPHELPHELP"
DAT_14001c064 = 48 45 4C 50   ; "HELP"

Bunların XREF’lerinin tamamı yine FUN_140002870 içine gidiyor. Bu da bunların normal workshop zincirinden önce test edildiğini gösteriyor.

Decompile’da gerçekten önce "WONK" paterni, sonra sıfırlardan oluşan giriş, sonra da "HELPHELPHELPHELP" ve "HELP" kıyaslamaları geliyor. Yani bunlar asıl çözüm değil; program içindeki özel mesaj/easter egg yolları.

4. Süre kontrolü ve anti-debug etkisi

Asıl doğrulama zinciri yalnızca normalize input 16 karakterse başlıyor. Bundan sonra GetTickCount() farkı sınanıyor; eşik aşılırsa doğrulama normal şekilde devam etmiyor. Bu, binary içinde hafif bir anti-analysis sürtünmesi oluşturuyor.

Buna ek olarak decompile’da açık bir anti-debug etkisi var:

BVar8 = IsDebuggerPresent();
uVar15 = (ulonglong)(-(uint)(BVar8 != 0) & 0x37);
...
uVar15 = 0x42;

Yani debugger algılanırsa veya PEB üzerindeki belirli bayraklar set ise ilk workshop’ta kullanılan tablo ofseti değişiyor. Bu, özellikle ilk 4 karakter çözümünü debugger altında kasıtlı olarak saptırabiliyor.

5. Workshop 1 — Cocoa Plantation

İlk blok, raw byte tablosu DAT_14001b560 üzerinden doğrulanıyor. Assembly/data kesitinde bu tablo doğrudan görülüyor ve XREF’leri de FUN_140002870 içindeki ilk workshop koduna bağlanıyor.

Decompile’daki kritik satırlar şunlar:

local_1b0 = CONCAT31(... (&DAT_14001b560)[uVar18 ^ uVar15] ...);
local_1b4[0] = local_1b0 == 0xc3811deb;

Yani ilk dört karakter, tablo üzerinden dönüştürülerek tek bir 32-bit sabite karşı kontrol ediliyor. Anti-debug ofseti yok sayıldığında bu terslenebiliyor ve ilk blok şu çıkıyor:

Ch0c

Başka bir deyişle Cocoa Plantation tekil bir çözüm üretiyor; burada bir çözüm kümesi yok.

6. Workshop 2 — Milk River

İkinci blok lineer bir dönüşüm. Decompile’da 4 karakterlik blok için katsayılarla çarpım ve toplam yapılıyor; sonra sonuçların düşük byte’ları hedef vektöre karşı sınanıyor:

uVar19 = uVar19 | (uint)*pbVar25 ^ uVar14 & 0xff;
local_1b4[1] = uVar19 == 0;

Bu, her satırın hedef byte ile eşleşmesini zorunlu kılıyor.

Assembly/data tarafında hedef byte’lar doğrudan görülüyor:

DAT_14001b6a0: 2d df 6b 9c

Aynı bölgede katsayı dizisinin başlangıcı da görülüyor:

14001b660 03 00 00 00
14001b664 07 00 00 00
14001b668 02 00 00 00
14001b66c 05 00 00 00
...

Bu veriler çözüldüğünde ikinci blok şu oluyor:

M1lk

Yani Milk River da tekil çözüm veriyor.

7. Workshop 3 — Caramel Oven

Üçüncü blokta program iki adet 16-bit ara değer oluşturuyor ve bunlara çıkarma, rotate, çarpma ve XOR işlemleri uyguluyor. Decompile’dan kritik bölüm:

uVar14 = CONCAT11(local_1b8,local_1b7) - 0x3502;
uVar20 = ((uVar14 & 0xffff) >> 0xb | (uVar14 & 0xffff) << 5) * 0x7a69 & 0xffff;
...
local_1b4[2] = uVar14 == 0x16cb7cb;

Bu, üçüncü blok için tek bir hedef 32-bit sonuç üretildiğini gösteriyor.

Bu aşamanın çözümü:

CrMe

oluyor. Böylece ilk üç workshop sonrası sabit prefix netleşiyor:

Ch0c-M1lk-CrMe-

Bu noktadan sonra geriye yalnızca Packaging Line kalıyor.

8. Workshop 4 — Packaging Line

Crackme’nin asıl ilginç kısmı burası. İlk üç aşamanın aksine dördüncü blok sabit bir string ile karşılaştırılmıyor. Decompile’daki belirleyici satırlar şöyle:

uVar5 = FUN_1400025f0(&local_19c,(char *)local_1a8,pcVar22,pcVar12);
iVar13 = (int)CONCAT62(extraout_var,uVar5);
local_1b4[3] = iVar13 == 0;

Yani Packaging Line başarısı, yalnızca FUN_1400025f0(...) dönüş değerinin sıfır olmasına bağlı. Bu çok önemli, çünkü burada "şu dört karaktere eşit ol" tipi bir kontrol yok.

FUN_1400025f0 decompile’ı da bunu destekliyor. Fonksiyon başlangıcında ilk üç bloğun toplamlarından ve son bloğun ilk baytından türetilen bir 16-bit başlangıç değeri kuruluyor; ardından tekrar tekrar şu yapı uygulanıyor:

uVar1 = uVar1 * 2 ^ 0x1021;

Bu, polinomu 0x1021 olan bit-serial CRC-CCITT benzeri bir mekanizma. Fonksiyonun amacı sabit bir string eşitliği değil, kalanın sıfır olması.

Tam da bu yüzden Packaging Line için birden fazla doğru suffix var. Aynı prefix ile şu farklı son blokların hepsi başarı verdi:

JAfa
iUga
xWwa
hEFa

Başarısız örnekte de ilk üç workshop PASS, yalnızca Packaging Line FAIL görünüyor; bu da son bloğun bağımsız bir koşul olduğuna pratik kanıt sağlıyor.

9. Neden birden fazla geçerli golden ticket var?

Teknik sebep doğrudan Packaging Line’ın tasarımında yatıyor.

İlk üç workshop şu yapıda çalışıyor:

  • ilk 4 karakteri tekil sabitliyor,
  • ikinci 4 karakteri tekil sabitliyor,
  • üçüncü 4 karakteri tekil sabitliyor.

Ama dördüncü workshop, tek bir sabit string istemiyor; yalnızca bir fonksiyon sonucunun sıfır olmasını istiyor. Decompile’daki local_1b4[3] = iVar13 == 0; satırı bunun özeti. Bu nedenle son 4 karakter için bir çözüm kümesi var.

Yani crackme’nin gerçek çözümü tek bir string değil, şu ailedir:

Ch0c-M1lk-CrMe-???? 

Buradaki ????, FUN_1400025f0 koşulunu sağlayan herhangi bir 4 karakterli sonek olabilir. Bu yüzden crackme aynı prefix ile birden fazla golden ticket kabul ediyor.

10. Başarı ve Başarısızlık Yolları

Ana fonksiyonun son kısmı başarı/fail ayrımını çok net gösteriyor. Eğer workshop’lardan biri başarısızsa program sonuç tablosunu basıyor; başarısız branch içinde "FAIL"/"PASS" üretimi de açıkça görünüyor. Bu yüzden ekranda tek tek workshop sonuçları görülüyor.

Tüm aşamalar geçildiğinde ise şu çağrı yapılıyor:

FUN_140001c10(local_1b0 ^ 0xc3811deb,uVar16,uVar23,pcVar12);

Bu önemli, çünkü Cocoa Plantation zaten local_1b0 == 0xc3811deb koşulunu zorladığı için başarı yolunda bu XOR fiilen sıfıra düşüyor. Yani ilk workshop sonucu final başarı ekranı yoluna da taşınmış oluyor.

FUN_140001c10 içinde de gömülü veri blokları bir bayt anahtarıyla XOR’lanarak açılıyor. Decompile’da bu net biçimde görülüyor:

*(byte *)((longlong)&local_98 + lVar2) =
    *(byte *)((longlong)&local_98 + lVar2) ^ bVar5;

Ardından bu açılmış metinler ekrana basılıyor. Paylaştığın başarı ekranları da “Congratulations! You found the Golden Ticket!” ve “Chocolate Champion Trophy!” mesajlarını doğruluyor.

11. Keygen

Dosya için doğru golden ticket üreten python keygen kodu şöyledir:

import string

# İstersen alfabeyi değiştir
ALPHABET = string.ascii_letters + string.digits

def step8(x):
    for _ in range(8):
        if x & 0x8000:
            x = ((x << 1) ^ 0x1021) & 0xFFFF
        else:
            x = (x << 1) & 0xFFFF
    return x

# Ch0c / M1lk / CrMe sabitlerinden gelen değer
C = 0xFFF3

valid_suffixes = []

for a in ALPHABET:
    for b in ALPHABET:
        s = step8(step8(C ^ (ord(a) << 8)) ^ (ord(b) << 8))
        c = (s >> 8) & 0xFF
        d = s & 0xFF

        if chr(c) in ALPHABET and chr(d) in ALPHABET:
            suffix = a + b + chr(c) + chr(d)
            valid_suffixes.append(suffix)

print("Toplam:", len(valid_suffixes))
for s in valid_suffixes:
    print(s)

print("\nTam ticketlar:")
for s in valid_suffixes:
    print(f"Ch0c-M1lk-CrMe-{s}")

메타데이터
post_id
d510b669828b
slug
crackmes-one-cracknotmes-willy-wonka-s-chocolate-factory-writeup-turkish-d510b669828b
url
https://medium.com/@eminxs/crackmes-one-cracknotmes-willy-wonka-s-chocolate-factory-writeup-turkish-d510b669828b
canonical_url
https://medium.com/@eminxs/crackmes-one-cracknotmes-willy-wonka-s-chocolate-factory-writeup-turkish-d510b669828b
author_url
https://medium.com/@eminxs
status
ok
fetched_at
2026-06-20 20:29:01