Cracking Tutorial Nr. 2
Weiterfhrendes Assemblerwissen und wie man einen Keygen fr Winamp schreibt. (Demonstriert an der v2.23)
Hubertus Loobert
eMail: ger.tuts@redseven.de
Internet: http: //www.gaston.notrix.de

Voraussetzungen: Das Lesen meines ersten Tutorials. Weiterhin sind Programmierkenntnisse wnschenswert. 


1.			****************Weiterfhrendes Assemblerwissen*******************

Im letzten Teil wurden solche Begriffe wie Stack oder Register aufgegriffen. Ich werde heute versuchen diese Begriffe nher zu erklren.

1.2 Der Stack:
Der Stack ist der temporre Speicher des Prozessor, wo er wichtige Daten, wie z.B. Rcksprungadressen von einem Unterprogramm speichert. Der Befehl zum speichern lautet PUSH. Der entgegengesetzte Befehl ist POP. Dieser Befehl holt das Oberste vom Stack wieder herunter. Doch wo befindet sich dieser Speicher? Die Adresse vom Stack ist in SS:ESP gespeichert. In SoftIce kann man sich den Stack durch den Befehl "D SS:ESP" anzeigen lassen. Wenn man etwas auf den Stack kopiert mit dem Befehl PUSH, wird das Register ESP um 4 verringert. Anders ginge es ja gar nicht, denn sonst wrde jedesmal der zuletzt im Stack abgelegte Wert berschrieben werden. Wir werden uns den Stack noch in Aktion ansehen. 

1.3 Die Register
Es existieren folgende Register:

EAX	EBX	ECX	EDX	ESI	EDI	EBP	ESP	EIP	||	CS	DS	SS	ES	FS	GS

Die Register bis zur Abgrenzung haben eine Gre von 32 BIT. Die anderen sind jeweils nur halb so gro. 


Die Register EAX,EBX,ECX und EDX werden uns wahrscheinlich am meisten begegnen und dienen dem Prozessor einzig und allein zum Ablegen verschiedener Daten mit denen er rechnen will. Ich werde hier jeweils nur die Namen der Register angegeben. Die nhere Bedeutung eines Registers werde ich, falls es ntig wird, erlutern.

EAX	=	accu register
EBX	=	base register
ECX	=	counter register
EDX	=	data register

ESI	=	source index register
EDI	=	destination index register
EBP	=	base pointer register
ESP	=	stack pointer register
EIP	=	instruction pointer register

CS	=	code segment register
DS	=	data segemnt register
ES	=	extra segment register
SS	=	stack segment register

FS	=	help segment 1 register
GS	=	help segment 2 register


1.4 Flags
Folgende Flags gibt es:

O	=	overflow flag			
D	=	direction flag
I	=	interrupt enable flag
T	=	single step flag
S	=	sign flag
Z	=	Zero flag
A	= 	auxiliary flag
P	=	parity flag
C	=	carry flag

Wie auch schon bei den Registern gilt, ich erklre die Bedeutung nur, wenn es ntig ist.

1.4.1 Doch auf ein Flag mchte ich kurz eingehen: Das Z Flag.
Diese Flag wird gesetzt bei verschiedenen Operationen z.B. wenn der Vergleich zweier Operanden positiv war. Doch das Z flag wird genauso oft ausgelesen, wie es gesetzt oder nicht gesetzt wurde. Und zwar am meistem von bedingten Jumps, wie z.B. "JZ".
in SoftIce wird "JZ" oder "JNZ" angezeigt. Wenn aber mit W32dsm das gleiche Programm disassembelt, erscheint an der gleichen Stelle "JE" oder "JNE". Es hier gesagt, das diese beiden Befehle gleich sind, denn beide lesen das ZERO flag aus.





2.				*******************Auf zur Praxis*******************

Doch genug von der Theorie. Auf zum praktischen. Letztes Mal haben wir eine Serial in Winamp herausgefunden. Und wir bleiben bei Winamp. Heute gucken wir uns einmal die API Funktion EnableWindow genauer an. Dazu schlagen wir in der Windows API Referenz nach und finden folgendes:

Public Declare Function EnableWindow Lib "user32" Alias "EnableWindow" (ByVal hwnd As Long, ByVal fEnable As Long) As Long

Was sagt uns das? Die Funktion EnableWindow kommt aus der Programmbibliothek USER32.DLL und erwartet 2 Parameter
1. Was deaktiviert oder aktiviert werden soll
2. Ob es aktiviert oder deaktiviert werden soll

Wenn wir uns nun noch einmal die Stelle vom letzten mal angucken:
		.
		.
		.
(*01*)	:00401EC7	LEA	EAX, [EBP-80]		//[EBP-80] = Der eingegebene Name
(*02*)	:00401ECA	PUSH	EAX			//Kopier die Adresse unseres Namens auf den Stack
(*03*)	:00401ECB	CALL 	004259B1		//Hier wird die Serial erzeugt
(*04*)	:00401ED0	CMP	EAX,ESI			//Vergleich von eingegebener Serial und richtiger Serial
(*05*)	:00401ED2	POP 	ECX
(*06*)	:00401ED3	JNZ	00401EDF		//Wenn sie nicht bereinstimmen gehe zu schlecht
(*07*)	:00401ED5	CMP 	BYTE PTR [EBP-80],00	//Wurde berhaupt ein Name eingegeben?
(*08*)	:00401ED9	JZ	00401EDF		//Wenn nicht gehe zu schlecht
(*09*)	:00401EDB	PUSH	01			//Kopiert 01 auf den Stack, wenn die Serial stimmt
(*10*)	:00401EDD	JMP	00401EE1
(*11*)	:00401EDF	PUSH	00			//Kopiert 00 auf den Stack, wenn etwas nicht stimmt
(*12*)	:00401EE1	PUSH	01
(*13*)	:00401EE3	PUSH	EDI
(*14*)	:00401EE4	CALL	[USER32!GetDlgItem]	//Diese Api Funktion bestimmt was aktiviert oder deaktiviert wird
(*15*)	:00401EEA	PUSH	EAX			//Der Rckgabewert der Funktion ist der OK Button
(*16*)	:00401EEB	CALL	[USER32!EnableWindow]   //Dies ist unsere API Funktion!
(*17*)	:00401EF1	XOR 	EAX,EAX
		.
		.	
		.

Das alles sieht mir sehr nach einem C++ Code aus, der so aussieht:

if (Serial == Good_Serial) {

GetDlgItem(ID_OK)->EnableWindow(TRUE);

} else {

GetDlgItem(ID_OK)->EnableWindow(FALSE);

)


3.			************************Der schwere Teil************************

Nachdem wir ungefhr geklrt haben, was da passiert, wollen wir uns wieder dem Cracken zuwenden: Jetzt werden wir einen Keymaker zu Winamp schreiben. Dies ist vielleicht etwas hart fr das zweite Tutorial, aber dennoch glaube ich, da man sehr viel dabei lernt.

3.1 Beim schreiben eines Keymaker ist aus meiner Sicht nicht das Verstehen des Algorithmus, sondern das Sondieren, von relevanten Informationen und nicht relevanten Informationen. Doch wir werden das ben. Womit wir anfangen, drfte wohl jedem klar sein: Wir setzten einen Breakpoint auf den Call wo die Serial kalkuliert wird. Wir gehen aus Softice raus, verndern wieder das Textfeld der Serial (es sollte ein gengend langer Namen eingegebenen sein; ab 7 Zeichen) und SoftICE erscheint. Wir befinden uns vor der Ausfhrung des Unterprogrammes. Der CALL ist hervorgehoben. Nun drcken wir F8 um in den CALL hineinzukommen. Dort erwarten uns dies Zeilen.




:004259B1 55                      push ebp	//Hier sind wir
:004259B2 8BEC                    mov ebp, esp
:004259B4 81EC00010000            sub esp, 00000100
:004259BA 53                      push ebx
:004259BB 56                      push esi
:004259BC 57                      push edi
:004259BD 6A3F                    push 0000003F
:004259BF 33DB                    xor ebx, ebx		//EBX wird gelscht
:004259C1 59                      pop ecx
:004259C2 209D00FFFFFF            and byte ptr [ebp-0100], bl
:004259C8 33C0                    xor eax, eax
:004259CA 8DBD01FFFFFF            lea edi, dword ptr [ebp-00FF]
:004259D0 6800010000              push 00000100
:004259D5 F3                      repz
:004259D6 AB                      stosd
:004259D7 FF7508                  push [ebp+08]
:004259DA 66AB                    stosw
:004259DC AA                      stosb
:004259DD 8D8500FFFFFF            lea eax, dword ptr [ebp-0100]
:004259E3 50                      push eax
:004259E4 E857000100              call 00435A40		
:004259E9 83C40C                  add esp, 0000000C
:004259EC 389D00FFFFFF            cmp byte ptr [ebp-0100], bl	// Unser Name wird mit 0 verglichen
:004259F2 7428                    je 00425A1C			// Wenn nichts eingegeben wurde jump
:004259F4 8DB500FFFFFF            lea esi, dword ptr [ebp-0100]	// Die Adresse unseres Namens wird nach esi kopiert
:004259FA 33FF                    xor edi, edi			// EDI wird gelscht

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425A1A(C)
|
		*************************Hier beginnt der Algorithmus****************************

:004259FC 56                      push esi			// Die Adresse wird auf den Stack kopiert
:004259FD E861000000              call 00425A63			// Ein Wichtiger CALL
:00425A02 59                      pop ecx			// Das oberste vom Stack wird in ECX geschrieben
:00425A03 8BCF                    mov ecx, edi			// EDI wird nach ECX kopiert
:00425A05 83E10F                  and ecx, 0000000F		// Falls ECX = F ist, wird ECX gelscht
:00425A08 D3E0                    shl eax, cl
:00425A0A 85C0                    test eax, eax			// berflssig
:00425A0C 7C04                    jl 00425A12			// berflssig
:00425A0E 03D8                    add ebx, eax			// berflssig
:00425A10 EB02                    jmp 00425A14			// berflssig

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425A0C(C)
|
:00425A12 2BD8                    sub ebx, eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425A10(U)
|
:00425A14 47                      inc edi			// INC erhht den angegebenen Operanden um 1
:00425A15 47                      inc edi			//	 " 			"
:00425A16 46                      inc esi			//	 "			"
:00425A17 803E00                  cmp byte ptr [esi], 00	// Wann kommt das ENDE des Namens?
:00425A1A 75E0                    jne 004259FC			// Falls noch nicht zu Ende: Springe hoch.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004259F2(C)
|
:00425A1C 8BC3                    mov eax, ebx
:00425A1E B940420F00              mov ecx, 000F4240
:00425A23 6BC025                  imul eax,eax,00000025
:00425A26 99                      cdq
:00425A27 F7F9                    idiv ecx
:00425A29 8BCA                    mov ecx, edx
:00425A2B 8BF2                    mov esi, edx
:00425A2D 6BC964                  imul ecx,ecx,00000064
:00425A30 33FF                    xor edi, edi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425A49(U)
|
:00425A32 85F6                    test esi, esi
:00425A34 7415                    je 00425A4B
:00425A36 8BC6                    mov eax, esi
:00425A38 6A64                    push 00000064		// 64 wird auf den Stack kopiert
:00425A3A 99                      cdq
:00425A3B 5B                      pop ebx		// 64 wird vom Stack heruntergeholt und in EBX geladen
:00425A3C F7FB                    idiv ebx		
:00425A3E 8BC6                    mov eax, esi
:00425A40 53                      push ebx
:00425A41 5E                      pop esi
:00425A42 03FA                    add edi, edx
:00425A44 99                      cdq
:00425A45 F7FE                    idiv esi
:00425A47 8BF0                    mov esi, eax
:00425A49 EBE7                    jmp 00425A32

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425A34(C)
|
:00425A4B 8D047F                  lea eax, dword ptr [edi+2*edi]
:00425A4E 6A04                    push 00000004		// 4 wird auf den Stack geholt
:00425A50 99                      cdq
:00425A51 5E                      pop esi		// 4 wird vom Stack geholt und in ESI geladen
:00425A52 F7FE                    idiv esi
:00425A54 6A64                    push 00000064		// 64 wird auf den Stack kopiert
:00425A56 5E                      pop esi		// 64 wird heruntergeholt und in ESI kopiert
:00425A57 5F                      pop edi	
:00425A58 99                      cdq
:00425A59 F7FE                    idiv esi
:00425A5B 5E                      pop esi
:00425A5C 5B                      pop ebx
:00425A5D 8BC2                    mov eax, edx	
:00425A5F 03C1                    add eax, ecx		// In EAX befindet sich nach dieser Operation unsere richtige Serial
:00425A61 C9                      leave
:00425A62 C3                      ret


		******************Hier folgt der CALL 00425A63 von der Adresse 004259FD**********************

:00425A63 55                      push ebp
:00425A64 8BEC                    mov ebp, esp
:00425A66 51                      push ecx
:00425A67 8065FC00                and byte ptr [ebp-04], 00
:00425A6B 57                      push edi
:00425A6C 33C0                    xor eax, eax
:00425A6E 8D7DFD                  lea edi, dword ptr [ebp-03]
:00425A71 66AB                    stosw
:00425A73 6A04                    push 00000004
:00425A75 FF7508                  push [ebp+08]
:00425A78 AA                      stosb
:00425A79 8D45FC                  lea eax, dword ptr [ebp-04]
:00425A7C 50                      push eax
:00425A7D E8BEFF0000              call 00435A40
			---------------Hier geht's wieder los---------------				
:00425A82 0FB645FE                movzx eax, byte ptr [ebp-02]	// Hier wird der dritte Buchstabe kopiert bzw. der 4.,5. usw
:00425A86 0FB64DFC                movzx ecx, byte ptr [ebp-04]	// Hier der erste, zweite, dritte usw.
:00425A8A 0FB655FD                movzx edx, byte ptr [ebp-03]	// Hier der zweite, dritte, vierte usw.
:00425A8E 33C1                    xor eax, ecx			// XOR 3. (4. usw.) mit dem ersten (zweiten usw.)
:00425A90 83C40C                  add esp, 0000000C		
:00425A93 0FB64DFF                movzx ecx, byte ptr [ebp-01]	// Hier wird der 4. (5. usw.) Buchstabe kopiert
:00425A97 F7D1                    not ecx
:00425A99 F7D0                    not eax
:00425A9B 33CA                    xor ecx, edx
:00425A9D 5F                      pop edi
:00425A9E 03C1                    add eax, ecx
:00425AA0 C9                      leave
:00425AA1 C3                      ret		// Hier jumpt er wieder zurck und beendet damit das Unterprogramm


		********************************Ende des Codes**********************************





Die Stelle mit der Adresse 004259FC ist der Punkt wo der fr uns wichtige Algorithmus beginnt. Alles andere oberhalb davon ist uninteressant! Doch wie kommt man auf diesen Punkt? Als erstes ist dies die erste Schleife in diesem Unterprogramm. Weiterhin ist es ganz einfach ein ausprobieren. Wenn man sich die Operationen oberhalb mit SoftIce anguckt, erkennt man, da dort nichts mit unserem Namen gemacht wird; ganz einfach.



3.2 Nachdem wir jetzt wissen wo wir ansetzten mssen, beginnt die eigentliche Arbeit: Wir versuchen diese Anweisungen zu verstehen und bersetzten sie in eine Hochsprache wie z.B. C++.
Als erstes gucken wir uns die erste Zeile an (an der Adresse 004259FC)

Hier wird die Adresse unseres Namens aus ESI auf den Stack kopiert. Danach folgt ein wichtiger CALL.

Der CALL:
Er fhrt uns an die Adresse 00425A63. Hier beginnt wieder die Sondierung. Und wieder gilt: Am schnellsten kommt man mit Ausprobieren dahinter, indem man sich anguckt mit was der Prozessor arbeitet. In diesem Fall beginnt der wichtige Teil unseres Algorithmus an der Adresse 00425A82. Der Befehl MOVZX bedeutet, da nur ein Byte kopiert werden soll. In diesem Fall kopiert er den dritten Buchstaben des eingegebenen Namens nach EAX. Danach den ersten Buchstaben nach ECX und dann den zweiten nach EDX. Es wirkt vielleicht etwas verwirren, weil es in falscher Reihenfolge passiert. Hier nochmals in richtiger Reihenfolge:
	
	movzx ecx, byte ptr [ebp-04]	// Hier der erste
	movzx edx, byte ptr [ebp-03]	// Hier der zweite
	movzx eax, byte ptr [ebp-02]	// Hier der dritte

Nun gut, aber was passiert dann? Dann kommt der Befehl XOR mit dem dritten und dem ersten Buchstaben. Der Befehl bedeutet, da der Prozessor eine logische ODER- Verknpfung ausfhrt; mit dem ASCII CODE des ersten und des dritten Buchstaben. Eine XOR Verknpfung findet auf Bit Ebene statt. Das erste Bit der ersten Zahl wird mit dem ersten Bit der zweiten Zahl verglichen. Daraufhin wird das zweite Bit der ersten Zahl mit dem zweiten Bit der zweiten Zahl verglichen... usw.

Wenn kein Bit gesetzt ist, ist das Ergebnis: 						Ein ungesetztes Bit. (0)
Wenn beide Bits gesetzt sind, ist das Ergebnis:						Ein ungesetztes Bit. (0)
Wenn ein Bit von beiden gesetzt ist, aber das andere nicht, ist das Ergebnis:		Ein gesetztes Bit.   (1)

Das Ergebnis wird in den ersten Operanden geschrieben. Damit drfte diese Operation erklrt sein.

Danach folgt der Befehl ADD. Hier erhht er das stack point register um 11. Diese Befehl hat etwas mit einem vorangegangenen CALL zu tun, und ist hier vollkommen bedeutungslos.

Danach kommt wieder der Befehl MOVZX. Diesmal kopiert er den vierten Buchstaben des Namens in das Register ECX.

Nun kommt 2 Mal der Befehl NOT. NOT ist die logische Verneinung. Er bewirkt das die Zahl Bit fr Bit umgedreht wird. Ein gesetztes Bit wird gelscht und umgekehrt. 
Hier wird der Befehl mit dem Register ECX, in dem der vierte Buchstabe ist; sowie mit dem Register EAX, in dem sich das Ergebnis der vorangegangenen XOR Operation befindet, ausgefhrt.

Was folgt ist ja nun bekannt: Eine logische ODER- Verknpfung mit den Registern ECX und EDX. 

Dann wird der oberste Wert vom Stack in EDI geladen.

Am Ende kommt noch der Befehl ADD. Es drfte wohl jedem klar sein, was dieser Befehl tut. Das Ergebnis wird in den ersten Operanden geschrieben, in diesem Fall EAX.

Danach ist das Unterprogramm beendet und das Programm luft an der Adresse 00425A03 weiter.


Dies klinkt vielleicht alles sehr kompliziert. Deswegen ist es immer gut, wenn man sich alles noch einmal durchliest.

Hier folgt ein kleines Programmbeispiel in C zur Verdeutlichung der bisherigen Kenntnisse ber den Algorithmus.

		****************************Winamp Keymaker 0.1******************************
#include<stdio.h>
#include<string.h>

main()
{

/* Deklaration der Variablen */

long eax,ecx,edx;		/* Original Registernamen */
char name[40];			/* Unser Name */

/* Ein und Ausgabe */

printf("Winamp Keymaker by Hubertus!\n");
printf("Bitte geben sie den Namen ein: ");
gets(name);

/* Beginn des Algorithmus */

/* Beginn des Unterprogrammes */

ecx = name[0];		/* Der erste Buchstabe nach ecx */
edx = name[1];		/* Der zweite nach edx */
eax = name[2];		/* Der dritte nach eax */

eax = eax ^ ecx; 	/* Exklusive ODER Verknpfung */	

ecx = name[3]; 		/* Der vierte Buchstabe nach ecx */

ecx = ~ecx; 		/* Bitweises Nicht */
eax = ~eax;		/* Das gleiche mit eax */

ecx = ecx ^ edx;	/* Wiederum die exklusive ODER Verknpfung */
eax = eax + ecx;	/* eax wird mit ecx addiert */

/* Ende des Unterprogrammes. Das Ergebnis steht in eax */

/* Ende des Algorithmus nach unserem bisherigen Wissen */ 


}


		*******************************Ende des ersten Programmes******************************

So weit so gut. Nachdem wir aus dem Unterprogramm heraus sind, wird etwas vom Stack geholt und gleich mit dem Wert aus EDI berschrieben. Dieser Wert hat eine bestimmte Bedeutung. Dazu gleich mehr.


	call 00425A63	// Unser Unterprogramm
	pop ecx		// Das oberste vom Stack wird in ECX geladen
	mov ecx, edi	// Ecx wird mit dem Wert aus edi berschrieben
	and ecx, F	// Diesen Befehl hatten wir noch nicht. Jetzt mehr dazu.

Der Befehl AND ist ein wunderbarer Befehl. Es ist die UND- Verknpfung. Dies bedeutet das wiederum (wie bei XOR) die Bits einzeln verknpft werden. Das bedeutet, da ein gesetztes Bit nur dann resultiert, wenn beide zu verknpfenden Bits gesetzt sind. Fr unseren Algorithmus heit das, das Folgende: Wenn das Register ecx den Wert F (= 15 in dezimaler Schreibweise) bersteigt, wird ecx gelscht. 

Weiter in unserem Programm stoen wir schon wieder auf den neuen Befehl SHL. Dieser Befehl hat die Aufgabe die im ersten Operanden angegebene Zahl, um die Anzahl der im zweiten Operanden angegebenen Zahl nach links zu verschieben.
Diese Operation ist recht ungewhnlich. Um sie einfacher zu rechnen z.B. auf ganz normalen Taschenrechnern, nimmt man die erste Zahl hoch der zweiten Zahl. 

Es folgen weitere Befehle die ich nur noch kurz zu erklren brauche.

	shl eax,cl	// Cl ist der untere Teil aus dem Register ECX
	test eax, eax	// TEST fhrt eine logische UND Verknpfung durch, mit dem Unterschied, da die Operanden unverndert 			// bleiben und die Flags das Ergebnis dieser Operation anzeigen.
	jl 00425A12	// jump wenn das overflow flag und das sign flag verschieden sind. Oder jumpt immer wenn eax <> 0 ist

Dazu mchte ich noch etwas sagen: der Befehl jl 00425A12 wird immer ausgefhrt, denn eax ist niemals 0. Also knnen wir uns folgenden Teil schenken:

	jl 00425A12	// jumpt immer
	
	add ebx, eax	// vollkommen egal
	jmp 00425A14	// auch egal
	
	sub ebx, eax	// hier fhrt der Jump drei Zeilen drber hin. Ab hier wieder interessant.
			// Der Befehl dient zu Subtraktion. Er zieht den zweiten Operanden vom ersten ab und legt das 				// Ergebnis in den ersten Operanden 

Es folgt dreimal der Befehl INC. Dieser Befehl ist schnell erklrt. Er erhht den angegebenen Operanden um 1.
Also zusammengefasst erhhen die Befehle das Register EDI um 2 und ESI um 1. 

Dann kommen diese beiden Zeilen:

	cmp byte ptr[esi], 00	// Die eckigen Klammern bedeuten immer, die Zahl soll as Adresse benutzt werden
	jne 004259FC

Wer sich erinnert: Im Register ESI ist die Adresse unseres Namens gespeichert. Diese Adresse wurde eine Zeile hher um 1 erhht. Dann wird der zweite Buchstabe unseres Namens mit 00 verglichen. Dies Funktion hat die Aufgabe herauszufinden, wann unser Namen zu Ende ist. Wenn er noch nicht zu Ende ist springe hoch. Aber wohin? Er springt wieder nach oben; an den Anfang unseres Algorithmus.

Manche haben es vielleicht befrchtet: Dies ist eine Schleife!!! Aber keine Angst. Ich werde nicht alles noch einmal erklren. Aber was verndert sich nun? Dies ist relativ einfach: Das was im Unterprogramm passiert (CALL 00425A63) bleibt erhalten, nur das beim zweiten Durchlauf der 2. und der 3. und der 4. und der 5. Buchstabe verwendet wird. Um das alles zu verdeutlichen ist hier ein weiters Programm:



			*************************Winamp Keymaker 0.5*****************************
#include<stdio.h>
#include<string.h>

main()
{
/* Deklaration der Variablen */

int cl,i;
long eax,ecx,edx,ebx;
char name[40];

/* Ein- und Ausgabe */

printf("Winamp Keymaker by Hubertus\n");
printf("Bitte geben sie den Namen ein: ");
gets(name);

/* Beginn des Algorithmus */

for(i=0;i <= (strlen(name)-1);i++){	/* Hier beginnt die Schleife */

	/* Beginn des Unterprogrammes */
	ecx = name[i];			/* i gibt jeweils an, welcher Buchstabe genommen werden soll. i wird nach jedem 
	edx = name[i+1];		/* Durchlauf um 1 erhht */
	eax = name[i+2];
	eax = eax ^ ecx;
	ecx = name[i+3];
	ecx = ~ecx;
	eax = ~eax;
	ecx = ecx ^ edx;
	eax = eax + ecx;
	/* Ende des Unterprogrammes */
					/* In EAX ist das Ergebnis des Unterprogrammes */
	cl = cl & 15;			/* cl and 15 */
	eax = eax << cl;		/* << bedeutet SHL */
	ebx = ebx - eax;		/* EAX ist der endgltige Wert(eine negative Zahl). Er wird zu der bisherigen Summe  
	cl = cl + 2;			/* addiert.( - und - ergeben +)

} /* Ende der Schleife und damit auch das vorlufige Ende des Algorithmus, nachdem die Schleife beendet ist. */

}

			***************************ENDE des Programmes***************************

Ich hoffe der bisherige Teil ist verstanden worden. Denn jetzt kommen wir zum zweiten Teil des Algorithmus (, welcher einfacher ist).

Nachdem die Schleife beendet ist, sind wir an folgender Stelle:

:00425A1C      mov eax, ebx		// In EBX ist das bisherige Ergebnis
:00425A1E      mov ecx, 000F4240	// F4240 = 1.000.000 in dezimal	
:00425A23      imul eax, eax, 00000025	// das bisherige Ergebnis * 25h
:00425A26      cdq			// Erweiterung eines DWORD in ein QWORD
:00425A27      idiv ecx
:00425A29      mov ecx, edx		// Restwert kommt nach ECX
:00425A2B      mov esi, edx		// Sowie nach ESI
:00425A2D      imul ecx,ecx, 00000064	// Ergebnis liegt in ECX
:00425A30      xor edi, edi		// EDI wird gelscht


Das bisherige Ergebnis wird nach eax kopiert. Die Zahl F4240 wird in das Register ecx kopiert. Der Befehl IMUL fhrt eine Multiplikation durch. In diesem Fall der Wert in EAX * 25h(= 37d). Wobei der erste Operand das Ziel der Operation
angibt. Der Befehl CDQ dient zur Erweiterung eines Registers. Er braucht uns nicht weiter zu interessieren. 

IDIV fhrt eine Division durch, wobei nur ein Operand bentigt wird, denn es wird EAX mit einbezogen. In diesem Fall bedeutet es, da das bisherige Ergebnis in EAX durch ECX(=1000000d) geteilt wird und in EAX das Ergebnis der Division abgelegt wird.  
Der Rest einer Division wird in EDX gespeichert. Da es im HEX Bereich keine Komma Zahlen gibt, mu man den Rest einer Division extra speichern.
Nach dieser Division wird der Restwert aus dem Register EDX nach ECX und ESI kopiert. Dann wird dieser Restwert mit in ECX mit 64h(= 100d) multipliziert und in ECX abgelegt.

Danach kommen folgende Befehle:

:00425A32   test esi, esi	// Ist ESI = 0?
:00425A34   je 00425A4B		// Wenn ja jumpe 00425A4B
:00425A36   mov eax, esi	// Restwer nach EAX
:00425A38   push 00000064	// 64 wird auf den Stack kopiert
:00425A3A   cdq
:00425A3B   pop ebx		// 64h wird vom Stack heruntergeholt und in EBX geladen
:00425A3C   idiv ebx		// Der Restwert der letzten Division durch EBX
:00425A3E   mov eax, esi	
:00425A40   push ebx		// EBX = 100d wird auf den Stack kopiert
:00425A41   pop esi		// Und wieder in ESI kopiert
:00425A42   add edi, edx
:00425A44   cdq
:00425A45   idiv esi		// Die gleiche Operation wie an 00425A3C
:00425A47   mov esi, eax	// EAX nach ESI
:00425A49   jmp 00425A32	// Unbedingter JUMP nach 0042A32.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00425A34(C)
|
:00425A4B   lea eax, dword ptr [edi+2*edi]	// Das Ergebnis der letzten Schleife * 3 und nach eax kopiert
:00425A4E   push 00000004			// 4 wird auf den Stack geholt
:00425A50   cdq
:00425A51   pop esi				// 4 wird vom Stack geholt und in ESI geladen
:00425A52   idiv esi				// EAX dividiert durch 4 
:00425A54   push 00000064			// 64h wird auf den Stack kopiert
:00425A56   pop esi				// 64h wird heruntergeholt und in ESI kopiert
:00425A57   pop edi	
:00425A58   cdq
:00425A59   idiv esi				// EAX durch 64h
:00425A5B   pop esi
:00425A5C   pop ebx
:00425A5D   mov eax, edx	
:00425A5F   add eax, ecx			// ECX noch von der Adresse 00425A2D
:00425A61   leave				// In EAX befindet sich unsere richtige Serial
:00425A62   ret

Der erste Befehl dient dazu zu berprfen, ob ESI = 0 ist oder nicht. Wenn ja geht es nach 00425A4E. Dieser Befehl ist der Beginn einer weiteren Schleife. Die Aufgabe dieser Schleife ist folgende: Sie bildet eine ART von Quersumme. Die Quersumme einer Zahl ist die Addition seiner einzelnen Ziffern. Doch diese Schleife interpretiert nicht jede einzelne Ziffer, sondern jeweils 2 Ziffern als ganze Zahl. 
Dies geschieht wie folgt: Der dritte Befehl der Schleife kopiert den zu zerlegenden Wert nach eax. Dann wird 64h auf den Stack kopiert. Dann kommt der Befehl CDQ der dazu dient ein DWord in ein QWord umzuwandeln.
Dann wird 64h wieder vom Stack herunter geholt und in ebx geladen. Jetzt wird der in eax gespeicherte Wert durch 100d geteilt, was dazu fhrt, da die letzten beiden Stellen 'abgeschnitten' werden. Danach wird wieder die Ursprngliche Zahl aus ESI nach EAX kopiert. Danach wird 100d auf den Stack kopiert und gleich danach wieder heruntergeholt und nach ESI geladen. Diese beiden Befehle sind somit gleich mit mov esi,ebx. Nun wird edx (der Rest der letzten Divison bzw. die letzten beiden Stellen, der Zahl die geteilt wurde) zu edi addiert. In EDI befindet im zweiten Durchgang die Zahl vom letzten Durchgang, bzw. die Zahl vom vorletzten Durchgang + die Zahl vom letzten Durchgang, usw.. Es folgt der wiederholte Befehl CDQ und darauffolgend wird die gleiche Division durchgefhrt wie 6 Befehle zuvor und danach eax (das Ergebnis der Division) nach esi kopiert. Das Programm jumpt nun wieder an die Adresse, wo die Schleife anfing. Jetzt gibt die erste Operation auch einen Sinn: Wenn ESI leer ist, bedeutet das, da die Zahl schon aufgeteilt ist; in diesem Fall geht er zu 00425A4E. Wenn ESI aber nicht 0 ist wird die Schleife ein weiteres Mal durchgefhrt. Nur dieses Mal mit der Zahl, die nach dem letzten Durchgang brig geblieben ist. 
Dies klingt vielleicht sehr verwirren, doch es wird klarer, wenn man sich die Schleife in Aktion whrend der Laufzeit von Winamp anguckt.

Wer diese Schleife auch noch kapiert hat, ist fast am Ziel. An der Adresse 00425A4B angelangt, wird das Ergebnis der vorangegangenen Schleife * 3 genommen und nach eax geladen. EAX wird dann durch 4 dividiert. In EAX liegt das Ergebnis dieser  Operation. Danach wird 64h auf den Stack kopiert und auf dann in ESI geladen. 3 Zeilen spter wird EAX durch 64h geteilt. Der Rest dieser Division wird nach eax geladen und zu der Zahl von der Adresse 00425A2D in EDX addiert. 

Das war's!!

In C sieht der fertige Keymaker wie folgt aus:

			***********************Winamp Keymaker 1.0****************************

#include<stdio.h>
#include<string.h>
#include<math.h>

main()
{
/* Deklaration der Variablen */

int cl,i;
long eax,ecx,edx,ebx;
char name[40];

/* Ein- und Ausgabe */

printf("Winamp Keymaker by Hubertus\n");
printf("Bitte geben sie den Namen ein: ");
gets(name);

/* Beginn des Algorithmus */

for(i=0;i <= (strlen(name)-1);i++){	/* Hier beginnnt die Schleife */

/* Beginn des Unterprogrammes */
ecx = name[i];				/* i gibt jeweils an, welcher Buchstabe genommen werden soll. i wird nach jedem 
edx = name[i+1];			/* Durchlauf um 1 erhht */
eax = name[i+2];
eax = eax ^ ecx;
ecx = name[i+3];
ecx = ~ecx;
eax = ~eax;
ecx = ecx ^ edx;
eax = eax + ecx;
/* Ende des Unterprogrammes */
					/* In EAX ist das Ergebnis des Unterprogrammes */
cl = cl & 15;				/* cl and 15 */
eax = eax << cl;			/* << bedeutet SHL */
ebx = ebx - eax;			/* EAX ist der entgltige Wert(eine negative Zahl). Er wird zu der bisherigen Summe  
cl = cl + 2;				/* addiert.( - und - ergeben +)

} /* Ende der Schleife */

eax = ebx * 37;
ecx = fmod(eax,1000000);		/* fmod(a,b) berechnet den Restwert einer Division */
eax = ecx;
ecx = ecx * 100;

while(eax > 0) {			/+ Beginn der Schleife */
ebx = fmod(eax,100);
edx = ebx + edx;
eax = eax / 100;
}					/* Ende der Schleife. Ergebnis in edx */

edx = edx * 3;
edx = edx / 4;
edx = fmod(edx,100);
printf("Reg#: %ld", edx+ecx);		/* Ausgabe des Keys */

}
		*********************************Ende des Codes*******************************

Ende meines 2. Tutorials!

P.S.: Der C Beispielcode ist niemals optimal. Er ist extra so gehalten, damit es leichter wird, den bergang von Assembler zu C zu verstehen. Anbei ist ein C Quellcode, der den Konventionen eher entspricht.

Ich bin fr Fragen, Anregungen, Kritiken oder sonstiges zu diesem Tutorial gerne offen

ger.tuts@redseven.de












