Bu sitede bulunan bütün bilgi ve araçlar SADECE eğitim amaçlıdır, ne niyetle kullandığınız sizi bağlar ve sizin sorumluluğunuzdadır! [Demedi deme]

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

Tarih 11 Nisan 2013. KATEGORI Bilisim

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 ;)

Yorumlar (2)

  • Emin

    Emin

    14 Mart 2016 14:12 zamanında |
    Hocam merhaba,

    Öncelikle emeğinize sağlık.

    Küçük bir noktaya değinecektim.
    keygen için ufak bir bug fix gerekiyor.
    Bölum elde edilirken orjinal karakterin ascii değeri (ord(harf)) kullanılmamış.
    Farkı alınmış değer (char) kullanılıyor.
    Test case olarak iki karekter kullanınca gözden kaçmış olabilir.
    Örnek olarak emin için çalışmıyor (ve bundan başka birçok değer için muhtemelen çalışmayacak)
    v0.2 burada, pastebin.com/xn5L6jNQ

    Selamlar,

    yanıtla

    • K.A.

      K.A.

      15 Mart 2016 11:23 zamanında |
      Emin,
      Güzel yakalamışsın kardeşim, kod için de ayrıca teşekkür ederim.

      yanıtla

Bir yorum yapın

Misafir olarak yorum yapıyorsunuz.