Hacker'ın Olmazsa Olmazı Assembly #4 Çilingir Cemal

Serinin bu yazısında seviyeyi bir basamak arttırıp,crack konusunu işlemeye devam ediyoruz.Teknik konuların sıkıcılığını olayı hikayeye dökme ve araya espriler katma yöntemiyle en aza indirmeye çalışıyoruz.Bu seferki hikayemiz Çilingir Cemal hakkında, gelsin hikayemizin başlıkları:

Nesiller boyu süren baba mesleğiydi çilingirlik.İşlerinin hep en iyisi olmuşlardı ama 21. yüzyılda az kazandıran mesleklerden biriydi artık.Cemal memnun değildi halinden,hayalleri vardı ve bu hayaller için ihtiyacı olan şey paraydı.Öğrenmişti, bu mesleğin de teknolojik hali vardı,çok daha iyi paralar kazandıran.Çok pahalı programları alıp kırıyorsun,ondabir fiyatına satıyorsun demişti bir cracker arkadaşı.Bilgisayarı hep oyun için kullanan Cemal, öğrenmeye başlamıştı bile teknolojik çilingirliği...

Keygen Nedir?

Bir önceki yazımızda incelediğimiz programda doğrulamayı sağlayan sadece bir pin numarası (6161) vardı. Değindiğimiz yöntemlerden biriyle bu sayıyı (anahtarı) bulduğunuz an işlem tamamlanıyordu.Gerçek hayatta bu kadar kolayını zor bulursunuz demiştik.Zor örneklere baktığımızda karşımıza bir anahtar değil,birçok matematik işlemi sonrası oluşturulan daha karmaşık anahtarlar çıkıyor.

Buna verebileceğimiz örneklerden biri Maykrosoft amcanın bütün ürünlerinde kullandığı Product Key olarak isimlendirdiği anahtarlar olabilir. Mesela kullandığım Windows 7 Pro'nun ürün anahtarı RBXXX-FFXXX-6DXXX-83XXX-YHXXX [müsadenizle birazını değiştirip yazayım :)].Akla gelen ilk şey, ben bu anahtarı girdiğimde program bunu doğrulayabiliyorsa demek ki bazı matematiksel hesapları yapıp kontrol ediyor,doğal olarak bu işlemleri bilen birisi ya da bu iş için yazılacak başka bir program bu kontrolu geçebilecek birçok anahtar oluşturabilir.

Bahsedilen anahtarları oluşturabilen programlara Anahtar Oluşturucu, Key Generator kısaca keygen deniyor.Teorik kısmını anladık şimdi işi pratiğe dökelim ve bunlardan bir tanesini hep beraber yapalım.

Kurbanımızı Tanıyalım

Kurban olarak kullanacağımız programımız crackmes.de adresine üye olan (üyelik ücretsiz) herkesin indirebileceği cm1 by lagalopex isminde bir program.Üye olarak giriş yaptıktan sonra cm1 olarak aratınca ilk sırada geliyor zaten. Programı indirdik,unzip edip çalıştırıyoruz:

ka@ka-vm ~/keygen $ ./cm1
Hello ka, lets see what you've done so far...
ka@ka-vm ~/keygen $ echo $?
1
ka@ka-vm ~/keygen $ file cm1
cm1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
ka@ka-vm ~/keygen $ ldd cm1
linux-gate.so.1 => (0xb77ad000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75eb000)
/lib/ld-linux.so.2 (0xb77ae000)
ka@ka-vm ~/keygen $ strings cm1
...
Hello %s, lets see what you've done so far...
%s/.key_%s
Seems you've got it ;)
ka@ka-vm ~/keygen $ readelf -s cm1
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
[!] 1: 00000000 301 FUNC GLOBAL DEFAULT UND getpwuid@GLIBC_2.0 (2)
2: 00000000 406 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.0 (2)
3: 00000000 414 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2)
4: 00000000 57 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.0 (2)
[!] 5: 00000000 17 FUNC GLOBAL DEFAULT UND getuid@GLIBC_2.0 (2)
6: 00000000 59 FUNC GLOBAL DEFAULT UND snprintf@GLIBC_2.0 (2)
[!] 7: 00000000 122 FUNC GLOBAL DEFAULT UND open@GLIBC_2.0 (2)
8: 0804874c 4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used
9: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
[!] 10: 00000000 122 FUNC GLOBAL DEFAULT UND read@GLIBC_2.0 (2)
11: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__

Dikkatimizi çeken noktalara bir bakalım.Mesela program kullanıcı adımı biliyor,sadece Hello demek için mi acaba?Ayrıca kullanılan fonksiyonlara bakınca anlıyoruz ki dosya açıp,içeriğini okuyor, kullanıcı hakkında bilgiler alıyor.Başına ünlem işareti koyduğum fonksiyonları bilmemiz lazım.Bilmediğimiz fonksiyonlar hakkında bilgiyi man komutuyla veya Google amca sayesinde öğrenip incelemeye devam ediyoruz.

ka@ka-vm ~/keygen $ strace ./cm1
execve("./cm1", ["./cm1"], [/* 32 vars */]) = 0
...
open("/home/ka/.key_ka", O_RDONLY) = -1 ENOENT (No such file or directory)
exit_group(1)

Programı debuggerda incelemeye başlamadan anlıyoruz ki kullanıcının home dizininde .key_kullanıcı_adı isminde bir dosya olması gerekiyormuş.Dosyayı oluşturup sonucu görelim.

ka@ka-vm ~/keygen $ echo ka >~/.key_ka
ka@ka-vm ~/keygen $ ./cm1
Hello ka, lets see what you've done so far...
ka@ka-vm ~/keygen $ echo $?
2
ka@ka-vm ~/keygen $ strace ./cm1
execve("./cm1", ["./cm1"], [/* 32 vars */]) = 0
...
open("/home/ka/.key_ka", O_RDONLY) = 3
read(3, "k", 1) = 1
exit_group(2) = ?

İlk kontrolu geçmeyi başardık ama dosyamızı açıp ilk karakteri okur okumaz 2 koduyla çıktığına göre şimdi de anahtar dosyamızda neler yazması gerektiğini belirlememiz gerekiyor.Bunun için emektar ayıklayıcımız olan gdb'ye başvuruyoruz.

ka@ka-vm ~/keygen $ gdb -q cm1
Reading symbols from /home/ka/keygen/cm1...(no debugging symbols found)...done.
(gdb) disas main
=> 0x080484eb <+23>: call 0x80483e0 [user id değerimiz?]
(gdb) p/s $eax
$1 = 1000
=> 0x080484f4 <+32>: call 0x80483a0 [id 1000 bilgileri /etc/passwd dosyasından gelsin]
(gdb) x/xw $eax
0xb7fc3d0c: 0x0804a008
(gdb) x/s 0x0804a008
0x804a008: "ka"
=> 0x0804850a <+54>: call 0x80483d0 [Hello mesajımız geliyor]
=> 0x08048537 <+99>: call 0x8048400 [Key dosyasını aç]
(gdb) x/s $ebx
0xbfffe9b6: "/home/ka/.key_ka"
=> 0x0804855a <+134>: jne 0x804863c [Açabildiysen ne ala 360dan devam ederiz]
0x08048560 <+140>: jmp 0x8048682 [Dosya yoksa 430dan güle güle kısmına]
=> 0x08048644 <+368>: call 0x8048410 [1 karakter okumayı dene,sonuç eax registerına]
0x08048649 <+373>: add esp,0x10
0x0804864c <+376>: dec eax [Okuma başarılıysa eax 1 olmalı,değeri düşür]
0x0804864d <+377>: je 0x8048565 [0 olmalı lazım, o zaman 145den devam]
=> 0x08048565 <+145>: mov dl,BYTE PTR [ebp-0x11] [Okuduğun karakteri dl (edx 8 bit) taşı]
0x08048568 <+148>: mov eax,edx
0x0804856a <+150>: mov BYTE PTR [ebp-0x1021],dl [Okuduğun karakter sonra lazım olucak,sakla]
0x08048570 <+156>: sub eax,0x2a [Karakterin hex değerinden 0x2a çıkart]
0x08048573 <+159>: cmp al,0x5 [5 ile karşılaştır (yine çıkartma işlemi)]
0x08048575 <+161>: ja 0x804867d [Eğer büyükse 425den güle güle,değilse ?]

Görüldüğü üzere satır satır çözüm yapılınca karmaşık kodlar,sanki şiir okurmuşcasına zevkli hale geliyor [Devamı çok fena,azıcık gaz lazım :)]. Şimdi kullanabileceğimiz karakterleri belirleyip,her karakterin kontrol mekanizmasına olan etkisini de çözünce keygen yazmaya başlayabiliriz. Anlayacağınız inceleme bitmedi,daha yeni başlıyor.

Doyamadım Kara Gözlüm

cmp al,0x5 öğrendiğimiz üzere karşılaştırma işlemi, işlemcinin al registerındaki değerden 5 çıkartıp sonucu tekrar al registerına yazmasıyla oluyor.ja yani Jump If Above, eğer yüksekse atla şartına takılmamak için kullanabileceğimiz karakterleri belirleyelim:

ka@ka-vm ~ $ python -c 'print "\x2a\x2b\x2c\x2d\x2e\x2f"' [Pythondan yardım alıyoruz]
*+,-./ [0, 1, 2, 3, 4, 5 <-çıkartma işlemi sonrası kalan sonuçlar]

Python yardımıyla karakterleri öğrendik,şimdi de karakterlerin başımıza ne işler açtığını kontrol ediyoruz:

=> 0x804857b : movzx eax,al
0x804857e : jmp DWORD PTR [eax*4+0x80487a4] [Çıkartma işleminden kalan sonuca göre atla]
(gdb) x/6xw 0x080487a4 [Atlanacak adresleri öğrenelim]
0x80487a4: 0x0804858f (*) 0x08048585 (+) 0x080485bc (,) 0x0804859b (-)
0x80487b4: 0x0804867d (.) 0x080485a5 (/)
=> 0x0804858f <+187>: add esi,0x5 [esiye 5 ekle,karşılaştır,219a atla]
0x08048592 <+190>: cmp BYTE PTR [ebp-0x102d],0x2a
0x08048599 <+197>: jmp 0x80485af
=> 0x08048585 <+177>: inc esi [esiyi 1 arttır,karşılaştır,219a atla]
0x08048586 <+178>: cmp BYTE PTR [ebp-0x102d],0x2b
0x0804858d <+185>: jmp 0x80485af
=> 0x080485bc <+232>: push ecx [Abovvvvv,detaylı açıklama aşağıda]
0x080485bd <+233>: lea ecx,[ebp-0x11]
0x080485c0 <+236>: push 0x1
0x080485c2 <+238>: push ecx
0x080485c3 <+239>: push edi
0x080485c4 <+240>: call 0x8048410
0x080485c9 <+245>: add esp,0x10
0x080485cc <+248>: dec eax
0x080485cd <+249>: jne 0x804867d
0x080485d3 <+255>: movzx eax,BYTE PTR [ebp-0x11]
0x080485d7 <+259>: cmp eax,esi
0x080485d9 <+261>: jne 0x804867d
0x080485df <+267>: mov eax,DWORD PTR [ebp-0x1028]
0x080485e5 <+273>: mov ecx,DWORD PTR [ebp-0x102c]
0x080485eb <+279>: mov edx,DWORD PTR [eax]
0x080485ed <+281>: movsx eax,BYTE PTR [edx+ecx*1]
0x080485f1 <+285>: cmp esi,eax
0x080485f3 <+287>: jne 0x804867d
...
=> 0x0804867d <+425>: mov edx,0x2 [Bizi direk çıkışa gönderdiği için . karakterini kullanmıyoruz]
=> 0x0804859b <+199>: dec esi [Esiyi 1 azalt,karşılaştır,219a atla]
0x0804859c <+200>: cmp BYTE PTR [ebp-0x102d],0x2d
0x080485a3 <+207>: jmp 0x80485af
=> 0x080485a5 <+209>: sub esi,0x5 [Esiden 5 azalt,karşılaştır,eşit değilse 343den]
0x080485a8 <+212>: cmp BYTE PTR [ebp-0x102d],0x2f
0x080485af <+219>: jne 0x804862b [Eşitse buradan devam et]

Karakterlerin neler yaptığına bakalım.Nokta karakteri direk şutlama operasyonu yaptığı için onu kara listeye aldık.Yıldız karakteri esi registerının mevcut değerini 5 arttıyormuş,artı karakteri 1 arttırıyormuş,eksi karakteri 1 azaltıyormuş,slash karakteri de 5 azaltıyormuş. Peki ama nedir bu esinin günahı?Bu sorunun cevabını virgül karakterinin işlemlerinde görücez ama çoğunda 219a şartsız atlama kısmını da halletmemiz lazım.Ne var bu 219ta?

=> 0x080485af <+219>: jne 0x804862b [Atlamadan önceki karşılaştırma eşit değilse 343e atla]
0x080485b1 <+221>: inc ebx [Ebx arttır]
0x080485b2 <+222>: cmp ebx,0x5 [5 ile karşılaştır]
0x080485b5 <+225>: jbe 0x8048630 [Eğer daha az veya eşitse 348e,]
0x080485b7 <+227>: jmp 0x804867d [Yok değile şartsız güle güle kısmına]

Programı detaylı incelemeden anlaşılması zor,noluyoo abi yaa dedirten ama aslında gayet basit bir durum var.Anahtar dosyamızda bir karakteri 5 defadan fazla kullanırsak booommmmm.Tamamdır bu detayı da kenara yazıp,meşhur virgül karakterimiz ne işler çeviriyor onu çözüyoruz.

=> 0x080485bc <+232>: push ecx
0x080485bd <+233>: lea ecx,[ebp-0x11]
0x080485c0 <+236>: push 0x1
0x080485c2 <+238>: push ecx
0x080485c3 <+239>: push edi
0x080485c4 <+240>: call 0x8048410
0x080485c9 <+245>: add esp,0x10
0x080485cc <+248>: dec eax
0x080485cd <+249>: jne 0x804867d

Bu kısmı incelediğimizde görüyoruz ki virgül karakteri kullandığımız zaman,esiye herhangi bir müdahele yok.Fakat virgül karakterinden sonra kesinlikle bir karakter daha olmalı,yoksa kapıya gidiyoruz.

0x080485d3 <+255>: movzx eax,BYTE PTR [ebp-0x11]
0x080485d7 <+259>: cmp eax,esi
0x080485d9 <+261>: jne 0x804867d

Zurnanın zırt dediği noktalardan birisi 255. satırda gerçekleşiyor.Virgülden sonra okunan karakterinin değeri,esi değeriyle aynı olmak zorunda. Buradan anlıyoruz ki önce esiyi uygun değere getirmemiz gerekiyor.Tabii izin verilen karakterleri 5 defadan fazla kullanamıyoruzu unutmadan.Diğer kontrollerle devam edelim.

0x080485df <+267>: mov eax,DWORD PTR [ebp-0x1028]
0x080485e5 <+273>: mov ecx,DWORD PTR [ebp-0x102c]
0x080485eb <+279>: mov edx,DWORD PTR [eax]
0x080485ed <+281>: movsx eax,BYTE PTR [edx+ecx*1]
0x080485f1 <+285>: cmp esi,eax
0x080485f3 <+287>: jne 0x804867d
0x080485f9 <+293>: inc ecx
0x080485fa <+294>: mov DWORD PTR [ebp-0x102c],ecx
0x08048600 <+300>: cmp BYTE PTR [edx+ecx*1],0x0
0x08048604 <+304>: jne 0x804861a

Kontrol üstüne kontrol yapan programımız bu seferde özene bezene hazırladığımız esi değerini başka bir eax değeriyle karşılaştırıyor,yine eşit değillerse meşhur kapıyı gösteriyor.Bu değeri incelediğimizde karşımıza Hello dediği kullanıcı adımız çıkıyor.Anlaşılan anahtar dosyasında kullanıcı adımız olmaz zorunda.300. satırda yapılan karşılaştırmada kullanıcı adımızın bir sonraki karakteri kontrol ediliyor.Eğer kullanıcı adımızın sonuna geldiysek kontrol devam ediyor,yok sonuna gelinmediyse işlem 326. satırdan devam ediyor.

0x08048606 <+306>: push edx
0x08048607 <+307>: lea eax,[ebp-0x11]
0x0804860a <+310>: push 0x1
0x0804860c <+312>: push eax
0x0804860d <+313>: push edi
0x0804860e <+314>: call 0x8048410
0x08048613 <+319>: add esp,0x10
0x08048616 <+322>: test eax,eax
0x08048618 <+324>: jne 0x804867d

Bu masum satırlardan anladığımız üzere, anlıyoruz değil mi, kullanıcı adı bitmeden anahtar dosyasında okunacak başka karakter kalmadıysa yine acı sonla karşı karşıyayız.Peki karakter varsa ne oluyor, 326. satırda onu inceleyelim.

0x0804861a <+326>: mov edx,0xa [Hex 0xa,yani 10 ne iş?!]
0x0804861f <+331>: mov eax,esi
0x08048621 <+333>: mov ecx,edx
0x08048623 <+335>: xor edx,edx
0x08048625 <+337>: div ecx [Bölme işlemi var!]
0x08048627 <+339>: mov esi,eax [Esiye müdahele var,dağılın!]
0x08048629 <+341>: jmp 0x8048630

Assemblyde bölme işlemi biraz kafa kurcalayıcıdır,google amcadan aratıp öğrenebilirsiniz.Yukarıdaki işlemde esi değeri eax registerına taşınıp 10 ile bölünüyor,bölüm değeri eax registerından esiye taşınıyor.Yaniii ilk karakterden sonra esi değerine sıfırdan başlamıyoruz.10a bölme işleminden sonraki bölümü ikinci karakter değerimizden düşürüp ona göre esiyi ayarlıyoruz.

Bitti arkadaşlar,bilmem kaç kontrolden sonra herşeyi doğru yaptıysanız,aşağıdaki kontrolle "Kaptın sen bu işi" mesajını alıyor ve bir sonraki başlıkta keygenimizi yazıyoruz.

0x08048653 <+383>: mov edx,DWORD PTR [ebp-0x1028]
0x08048659 <+389>: mov ecx,DWORD PTR [ebp-0x102c]
0x0804865f <+395>: mov eax,DWORD PTR [edx]
0x08048661 <+397>: xor edx,edx
0x08048663 <+399>: cmp BYTE PTR [eax+ecx*1],0x0
0x08048667 <+403>: jne 0x8048682

Böööö geldi artık diyor,özetleyip geçiyoruz.Bak bakalım arkadaş, kullanıcı adının bütün karakterlerini işleme sokup,bütün kontrollerden adam akıllı geçebilmiş mi?Geçmiş ise ver mesajı:

0x0804866c <+408>: push 0x804878a
0x08048671 <+413>: call 0x80483b0
(gdb) x/s 0x0804878a
0x804878a: "Seems you've got it ;)"

Çilingir Cemal Keygen v0.1

Cemal işi gücü bıraktı teknolojik çilingirliğe kafayı taktı ama keygen yapabilmesi için bir de programlama dili bilmesi lazım.Neyse ki bu azimle Pythonu da öğrendi ve keygeni yazmaya başladı.Önce kuralları bir tekrarlayalım:

  • Kullanıcı ismini öğren,dosya oluştur
  • Kullanıcı isminin her harfinin ascii değerini bul
  • İzin verilen karakterlerle (max 5 defa) rakama ulaş,virgül koy,harfi yaz
  • Bulduğun değeri 10a böl,böleni bir sonraki harfin değerinden düşür
  • Kullanıcı ismini bitirince dosyayı kaydet

Önce keygenimizle anahtar dosyamızı oluşturuyor,sonra da programımız tekrar deniyoruz.Görüldüğü üzere sonuç, MUTLU SON..

ka@ka-vm ~/keygen $ ./cckeygen.py
Çilingir Cemal Keygen v0.1
[!] ka [!] kullanıcısı için işlemler başlatılıyor...
Dosya açıldı,karakterler hazırlanıyor...
İşlemler tamamlandı,dosya hazır.
ka@ka-vm ~/keygen $ ./cm1
Hello ka, lets see what you've done so far...
Seems you've got it ;)
ka@ka-vm ~/keygen $

Oynatalım Uğurcum

Yazı için hazırlanan videoyu YouTube'dan izleyebilirsiniz.Kurban programımızı indirmek isterseniz, yazıda bahsettiğimiz gibi crackmes.de adresine üye olup,indirebilirsiniz.Çilingir Cemal'in keygen programını da bu adresten inceleyip, farklı kaydet ile bilgisayarınıza kopyalayabilirsiniz.

Son olarak lapin kardeşlerimiz için olta görevi görecek tagleri de ekledik mi,tamamdır ;)