Keygenme by b1u3D4rK 02.10.2009



Öncelikle tüm okurlarımıza merhaba... Bugün sizlerle crackmes.de adresinde yayınlanmış olan bir çalışmaya keygen yazmaya çalışacağız. Bu program zorluk derecesi olarak 10 üzerinden 2 olarak gösterilmiş olsa da bu düzeyde yayınlanmış olan birçok çalışmadan biraz daha zor bir çalışma olduğunu birazdan hep beraber inceleyeceğiz. Fakat bundan önce bu keygenme hakkında biraz bilgi sahibi olalım.. Sitede keygenme yazarı tarafından açıklanan bilgilerden görebileceğimiz gibi programda herhangi bir koruma yok fakat RSA algoritması kullanıldığı açıklanmış. Peki RSA algoritması nedir?(wikipedia::RSA). RSA hakkında bilgi edinmek için verdiğim linkteki makaleyi okuyabilirsiniz. Eğer makaleyi inceleyecek olursanız bu algoritmanın modüler aritmetikten yararlandığını görebilirsiniz.. Keygen yazacağımız bir çalışmada modüler aritmetik gibi geri dönüşümsüz sistemlerin devreye girmesi bizim için hem avantaj hem de dezavantajlara ne olabilir. Avantajlarından birisi olarak sistemde birden fazla sonuç işlemi doğrulayabilir.(ÖRN: MOD10(5)==5 iken MOD10(15)==5 olabilir. Her iki değer de aynı sonucu verebilir.) Dezavantajı ise bize extradan işlemlere sebep olur.. Birazdan bunun ne anlama geldiğini anlayacaksınız... Dilerseniz şimdi programı incelemekle başlayalım..

Öncelikle programımızı ollydbg yüklemekle başlıyoruz. Ben kendime ait modifiyem olan gctolly i kullancam fakat siz herhangi birini kullanabilirsiniz.. Çalışan bir versiyonu işimizi görecektir.

Programı olly e yükledikten sonra yukarıdaki bir bir ekran ile karşılacaksınız. ilk satırımız bizim Oep(Orginal entry point) imizdir. F9 ile programımızı başlatacak olursak önümüze Name ve Serial girmemizi isteyen bir form geleceğinizi göreceksiniz.. Herhangi bir name ve serial girdikten sonra kontrol edilmesi için Check tuşuna basacak olursanız ve girdiğimiz bilgiler de doğru değilse programın kapanıp olly nin terminated olduğunu göreceksiniz. Yani program herhangi bir hata vermeden direkt olarak kapanıyor.. ctrl+f2 ye basarak programı tekrar olly e yükleyelim(terminated olmuş ve program kapanmış iken)..

Programın hata vermemesi bizim için bir sorun teşkil edebilir fakat bu sorun aşılamaz bir sorun değildir. Önceliklte programın hata vermeden direkt olarak kapandığını biliyoruz peki döngüyo nasıl bulacağız?

Ollye yüklemiş olduğumuz programın kodlarını incelersek yukarıdaki resimde gösterilmiş olan 1 nolu komutu göreceksiniz.. Bu komut programımızı sonlandıran komuttur. Yani kontrol algoritması ile işlem test edildikten sonra eğer girilen değerler yanlış olarak girilmiş ise programın kapandığını gözlemlemiştik. Programımızı sonlandıran komut satırı yukarıda ki resimde 1 ile gösterilmiştir.

Eğer keygen kodlama ile uğraşmış biri iseniz bu komutun programı sonlandıran komut olduğuna aşinasınızdır. Çünkü her programın sonlandırılması için bu rutine ihtiyacı vardır. Bu yazımı okuyan bazı arkadaşların aklına neden bu prosedürü nop lamıyoruz sorusu gelebilir. Bu prosedürü nop ile iptal edebilme gibi bir şansımız yok. Bunun sebebi de şu şekilde açıklanabilir; bir program çalışıp işlemini tamamladıktan sonra sonlandırlması gerekmektedir. Bunun içinde bir sonlandırma prosedürü kullanmanız gerek.. Burada da yapılan iş buna benzer şekilde gerçekleştirilmiş. Fakat gerçekleştirlen sistem sayesinde bu prosedür programı sonlandıran prosedür ile ilişkilendirilmiş. Sonuç olarak eğer bu komut iptal edilecek olursa program hiçbir şekilde düzgün olarak sonlandırılamaz... Sonuç olarak gerçersiz işlem yürütüp kapanır... Ve yine de amacımıza ulaşamamış oluruz.

Bir üst prosedürde ise DialogBoxParamA gibi bir ifade görüyoruz.. Bu api sayesinde bir diyalog formunun çağrıldığını görebiliriz. Yani bu prosedür incelediğimiz programın ana penceresini oluşturmaktadır. 2 ile göstermiş olduğum kısımdaki adres form kontrollerini içermektedir(4010EE).

Adrese gittiğimiz de yukarıdaki gibi bir prosedür ile karşı karşıya kalacağız.. Bu prosedür programın ana penceresi üzerinde kontrolleri denetleyen prosedürdür. 3 ile göstermiş olduğum alt program Check butonuna basıldığında aktive olmaktadır. Dolayısıyla programın serial algoritması da 3 nolu alt programın içinde bulunmaktadır. Bu satıra (401106) F2 ile breakpoint koyduktan sonra programı F9 ile çalıştırırsanız ve açılan ana penceredeki name ve serial kısmına herhangi bir şeyler girdikten sonra Check butonuna basarsanız bu satırda programın duracağını göreceksiniz.. 3 nolu alt programda çağrılan adresin 4011dc olduğu görülmektedir. Bu adrese gidip proseduru incelemekle işe başlayalım...

Alt program kodları yukarıdaki gibidir. User32.GetDlgItemTextA apisi formdan veriyi almak amacıyla kullanılır. Bizim incelediğimiz programda alınacak veriler name ve serialdir. Bu yüzden de iki adet alt prosedürü ile veriler alınacaktır. Yukarıdaki resimde get Name ve get Serial yazdığım prosedürlerde değerler alınmaktadır. Ayrıca ilk prosedür olan Name in alınma prosedüründen sonra uzunluk kontrolü gerçekleştirilmektedir. İsim uzunluğunun 4 ten büyük 16 dan küçük olması istenmiş ve kontrolleri burada yapılmaktadır.

İsim uzunluğu eğer aralık içinde ise Serial ana pencereden alınmaktadır.

Alınan isim ve serial daha sonra yukardaki kod grubunda kodlanıyor. 0DD ve 0FD bizim sabit değerlerimiz(Constants). Sistem bu kısımda şu şekilde işliyor. Elimizde tek bir döngümüz var ve name ile serial bu döngüye girecek. Ve daha sonra bazı ek işlemler sonucunda çıkan iki sonuç karşılaştırılacak ve eğer elde edilen değerler aynı ise şifre ve ismin geçerli olduğunu belirten bir uyarı mesajı karşımıza çıkacak.. İlk kodlama işlemi burada gerçekleştirilmektedir. Ki birazdan içeriğine baktığımızda RSA algoritmasına benzer bi yapı ile de karşılaşacağız. Az önce bahsettiğimiz bu iki döngüyü birbirinden farklı kılmak için ise sabit değerleri değiştirmeyi uygun görmüş. 0DD ve 0FD bizim sabit değerlerimizi oluşurmaktalar.

Sabitten sonra gelen call ın içine bakacak olursak yukarıdaki döngü ile karşılaşacaksınız.. [EBP+C] adresimizde önceki kısımdan gelen sabit değer mevcut. EDI de ise ilk döngü de name olacak. Bu algoritmada name in karekterleri hex olarak alınacak ve ilk etapta 1 ile çarpıldıktan sonra sabite göre modu olacak. Mod sonucunda çıkan değer tekrar aynı karekterle çarpılır ve tekrar modu alınır. Bu döngü her bir karekter için 9D defa gerçekleştirilir. 9D defa yapılan her işlem sonucu belleğe yazdırılır. Serial içinde aynı algoritma kullanılmıştır. Bu döngü ne kadar RSA ya benzese de RSA da geri dönüşüm mümkün iken burada geri dönüşüm mümkün değildir.

Kodlama işlemlerinden sonra

Name:

Serial:

yukarıdaki gibi olur. İşlemler name ve serial için sırayla yapıldıktan sonra kodun devamında aşağıdaki yere gelinir.

PART 1: Bu bölümde [EDI] adresi içindeki string kodlanmış name i içermektedir. Görülebildiği gibi mov al, byte ptr ds:[edi] komut satırı ile AL registerına byte byte kodlanmış name değeri alınır. Daha sonra da 7F değeri ile xor landıktan sonra 403244 adresindeki değere eklenir. Bu değer başlangıçta 0 olarak ayarlanmıştır. Yani buradan çıkacak olan sonuç bizim girmiş olduğumuz ismin kodlanmış halinin karekter karekter xorlanak toplanması ile oluşacaktır.

PART 2: Bu kısımda bir önceki bölüme benzer bir yapıya sahiptir. Bu kısımda da serial karekterlerinin kodlanmış hali karekter karekter toplanmıştır. Önceki bölümden farklı olarak bu bölümde bir xorlama işlemi mevcut değildir. Kodlanmış veri direkt olarak toplanmıştır.

Sonraki bölüm ise kontrol kısmını oluşturmaktadır. 403244 adresinde name den elde edilen sonucumuzu bulmuştuk. 403248 adresinde ise serialden gelen sonuç bulunmaktadır. Bu iki değer karşılaştırıldıktan sonra eğer aynı ise program bize girilen şifrenin doğru olduğuna dair bir mesaj verecektir. Aksi halde program kapanacaktır.

 

Keygeni Nasıl Kodlayabiliriz?

Sistemi genel olarak düşündüğümüzde keygen olayının zor olabileceği görülmektedir. Çünkü işin içinde en başta bir toplama algoritması mevcut.. Elimizdeki toplam sonuca dayanarak key için uygun karekterler seçmek ve bir serial üretmek çok stabil bir algoritma ile mümkün olabilir. Fakat yapacağımız sistem deneme yanılma sistemine dayanmamalıdır. Çünkü bu deneme yanılma sistemi keygenin donmasına sebep olabilmektedir. İkinci bir olay ise RSA döngüsü. Yukarıda gösterilmiş olan sistemin tersini gerçekleştirecek bir fonksiyon üretmek neredeyse mümkün değildir. Ben de fonksiyonun tersini bulmak yerine daha basit bir yöntem geliştirmeyi uygun gördüm. Geliştirdiğim yöntem şu şekilde çalışmaktadır.

Öncelikle klavyeden yazabildiğimiz tüm rakam ve harfleri içeren bir tablo oluşturalım ve daha sonra bunu serial algoritmasına sokarak karşılıklarını elde edelim. Ve bu sonuçları kullanarak nameden elde edeceğimiz toplam sonuca eşit bir değer elde etmeye çalışalım. Yani işlem şu şekilde olacak diyelim ki elimde a=10, b=20, c=30 gibi değerler mevcut ve name den gelen sonuçta 100 gibi bir değer olsun. Bu değerlerle 100 sayısına nasıl ulaşabilirim?

1. adımda c yi kullandığımızı varsayalım (100-30=70)
2. adımda c yi tekrar kullanalım (70-30=40)
3. adımda da c yi halen kullanabilmekteyiz (40-30=10)
4. adımda artık c kullanılamaz ama onun yerine a yı kullanabiliriz (10-10=0)

Sonuç olarak ccca değeri bizim serialimize eşdeğer olabilir. aynı şekilde 10 tane a dan oluşan yada 5 tane b den oluşan bir serial de elde etmek mümkün olacaktır. fakat minimum karekter ile seriali elde etmeye özen göstereceğiz.

Bu örnekten sonra işlememiz gereken metod aşağıdaki gibi olmalıdır.

1) Name in kodlanmış hali elde edilir ve kodlanan değerlerin xorlanmış hali toplanarak bir toplam sonuç elde edilir.

2) Sadece klavyeden yazılabilien karekterleri içeren bir tablo oluşturulur.

3) Oluşturulan bu tablo serial ın sabitini içeren algoritmaya sokulur ve karşılıkları elde edilir.

4) Daha sonra bu karşılıkları kullanarak name ın toplam sonucuna eşit sonuç verecek bir serial oluşturulur.

 

Her şey çok güzel fakat ufak bir sorunumuz daha var! Tablomuzu oluşturup değerleri incelerseniz minimum sonucun 02 ve 03 olduğunu görebilirsiniz yani 01 yok! ki çıkarma işleminin sonucunda da asal sayılar karşımıza çıkabilir ve bu sayılar 2 ve 3 e tam bölünemeyebilirler..

Mesela 7 sayısını ele alalım! 7 den iki defa 3 sayısının çıkarılması sonucu kalan sonuç 1 olacak fakat tablomuzda 1 sayısı yok! Aynı şekilde 2 çıkarmaya kalkarsakta kalan sonuç yine 1 olmaktadır! Buna nasıl bir çözüm getirebiliriz??

Ben bu sorunu şu şekilde çözmeyi daha uygun gördüm! çıkarma işlemleri bir standarta bindirmemiz gerekiyor yani sonucun hiçbir şekilde 1 kalmaması gerekiyor. Bu şu şekilde mümkün olabilir. Eğer sayı tek ise ve siz ondan tek bir sayı çıkarırsanız yada sayı çift ise siz de ondan sürekli olarak çift çıkarırsanız sonuç hiçbir zaman 01 kalmaz!

Bir örnekle pekiştirelim; 7 sayısını tekrar ele alalım sayımız tek! o yüzden 3 çıkaralım, kalan sonuç 4. Şimdi de sayımız çift o yüzden çift sayılarımızı çıkaralım yani 2 yi sonuç 2! Sonucun 2 olması sebebiyle tekrar iki çıkaralım ve sonuç 0 kaldı!

Pratikte çok güzel sonuçlar veren bir yöntem, bir anda aklıma geldi :)

 

Generate PROTO :DWORD
GenProc1 PROTO :DWORD,:DWORD,:WORD
GenProc2 PROTO :DWORD

.const

edit_name_max = 16

.data?

szName CHAR 20h dup(?)
szSerial CHAR 60h dup(?)

tmp dd ?
tmp1 dd ?


.data

nameerr db "Minimum character 4!",0
nameerr1 db "Not Found! Change The Name",0

szBuffer1 dw 0000h,0 ;Name den elde edilen sonuç burada tutulacak

tek db "2349aeghijmprsuvyzCDEFJKLMNQTVWXY",0 ; algoritma sonucunda tek sayı elde edebileceğimiz tablo
tek1 db "2349aeghijmprsuvyzCDEFJKLMNQTVWXY",0
cift db "015678bcdfklnoqtxwABGHIOPRSUZ",0 ; algoritma sonucunda çift sayı elde edebileceğimiz tablo
cift1 db "015678bcdfklnoqtxwABGHIOPRSUZ",0

.code

;########################################################Generate PROC####################################################
Generate PROC USES eax ebx ecx edx esi edi , _hWin:DWORD

invoke RtlZeroMemory,addr szName,sizeof szName
invoke RtlZeroMemory,addr szSerial,sizeof szSerial
invoke GetDlgItemText, _hWin, EDIT_NAME, ADDR szName, edit_name_max

mov dword ptr ds:[szBuffer1],00h

cmp eax,04h
jb @@err ;küçükse

invoke GenProc1,ADDR szName,ADDR szName,0DDh ;name kodlansın
invoke GenProc2,ADDR szName ;szBuffer1 ; name den elde edilen sonuç szBuffer1 de

invoke GenProc1,ADDR tek,ADDR tek1,0FDh ; tek lere ilişkin tabloyu kodla
invoke GenProc1,ADDR cift,ADDR cift1,0FDh ; çiftlere ilişkin tabloyu kodla

push edi
push esi
push ecx

xor ecx,ecx
xor edx,edx
xor edi,edi

xor esi,esi


mov eax,dword ptr ss:[szBuffer1] ;name den elde edilen sonucu eax e yükle

@don:
cmp eax,0h ;sonuç sıfır mı kontrol et yoksa devam et
je @bitti

mov edx,eax

;Sonuç tek mi çift mi?

and edx,0fh
@test:
cmp edx,01h ; son hanesine bak 1 se tek
je @tek
cmp edx,00h ; son hanesine bak 0 ise çift
je @cift
sub edx,02h
jmp @test

@cift: ;çiftlerin tablosu

mov dword ptr ss:[tmp],offset cift ; tablo adresini tmp yükle
mov dword ptr ss:[tmp1],offset cift1
jmp @devam

@tek: ;teklerin tablosu
mov dword ptr ss:[tmp],offset tek
mov dword ptr ss:[tmp1],offset tek1
jmp @devam

@devam:

xor edx,edx

mov ecx,dword ptr ds:[tmp1]
mov dl,byte ptr ss:[ecx+edi]; kodlanmış tablodaki ilk değeri al

cmp edx,0h ; eğer tablodan alınan değer 0 ise tablodaki tüm değerler kontrol edilmiş
je @@err1 ;hata var uyarıya git

cmp eax,edx ; eğer tablodaki değer nameden elde edilen sonuçtan büyükse başka bir değer almak için dallan
jl @degistir

sub eax,edx ; tablodaki sonuç küçük olduğu için sonuçtan çıkar

mov ecx,dword ptr ds:[tmp]
mov dl,byte ptr ss:[ecx+edi] ;tabloda kullanılan değerin orjinalini ekrana yazdırmak için için al

mov byte ptr ds:[szSerial+esi],dl ;kullanılan değerin orjinal(kodlanmamış halini) ekrana yazdır
inc esi ; diğer karektere geçmek için arttır
xor edi,edi


jmp @don

@degistir:
inc edi ;tablodan başka bir değer almak için değeri arttır

jmp @don


@bitti: ;end

pop ecx
pop esi
pop edi

invoke SetDlgItemTextA, _hWin, EDIT_KEY, offset szSerial ;seriali ekrana yazdır

@ended:
xor eax,eax
ret

@@err: invoke SetDlgItemTextA, _hWin, EDIT_KEY, offset nameerr
jmp @ended
@@err1: invoke SetDlgItemTextA, _hWin, EDIT_KEY, offset nameerr1
jmp @ended

Generate ENDP

;#######################################################################################################################

;Kodlama algoritması

;########################################################Generate1 PROC#####################################################
GenProc1 PROC USES eax ebx ecx edx esi edi , uName:DWORD,uResult:DWORD,uStatic:WORD


mov edi,uName
mov esi,uResult

xor ebx,ebx
jmp @5
@1:
mov bl,byte ptr ds:[edi]
mov eax,01h
mov ecx,09dh
@2:
mul bl
jmp @4
@3:
sub ax,uStatic
@4:
cmp ax,uStatic
ja @3
loopd @2
mov byte ptr ds:[esi],al
inc edi
inc esi
@5:
cmp byte ptr ds:[edi],0
jnz @1

xor eax,eax
ret
GenProc1 ENDP

;#######################################################################################################################

;Name in toplam sonucunu elde eden algoritma

;########################################################Generate2 PROC#####################################################
GenProc2 PROC USES eax edi, uName:DWORD

mov edi,uName

@@1:
xor eax,eax
mov al,byte ptr ds:[edi]
xor eax,07fh
add dword ptr ds:[szBuffer1],eax
inc edi
cmp byte ptr ds:[edi],0
jnz @@1

xor eax,eax
ret
GenProc2 ENDP

;#######################################################################################################################

darkshade,zugo,Gırgır,Caliber,_CC_

hiaxi,MoNZa,mendenn

blue_devil, rvaZero

and All GencliQ CT members...

[GencliQ CT Home Page]

Bu programı kullanarak para kazanıyorsanız lütfen satın alın. Bu yazının yazılma amacı program yazanlara programlarını daha iyi korumaları konusunda yol göstermektir. Lisanssız kullanımda Dokümanı hazırlayan sorumlu değildir.
Diyeceğim şudurki: Emek verilipte yapılmış bir şeyi çalma, onu satın al.


b1u3D4rK
E-Mail: [email protected]