I add this tutorial in my gallery because It is very analitical and has great educational value
,5 diferent ways to reverse this proggy 5 different ways to think as a cracker.


Kwazy Webbit vs. MaD doG

AuthorKwazy Webbit
Subject (AKA 'victim') PaneKiller 1.24 by MaD doG (any 1.2x should work I think)
Tools required SoftIce 3.xx
A text-viewer (notepad will do)
Win32 API programming reference (win32.hlp)
Find it using any search engine.
You could also ask just about any programmer for it. (on IRC for example)
Your favorite compiler (only for chapter 3)
WDasm 8.9x (only for advanced chapters)
A hex-editor (only for advanced chapters)
Date of writingMay 31st, 1999

Hello folks! They call me Kwazy Webbit. I am a new member of the Dutch cracking group DREAD.
In this essay I hope to help you get started with Reverse Engineering, which basically means that I want to do more than just tell you how to crack this program, but I want to help you understand how you can crack any program out there. I'm gonna try to take you through the process of reversing this program one step at a time, unlike A LOT of other crackers who just write down what they do, and forget to explain WHY they are doing it.
I was a beginner not too long ago, so I've read about a zillion essays, which would be most of the supposedly good ones. Well, I can tell you, most of them were too much to understand for a newbie, and they taught the advanced crackers nothing new. Basically, they were useless to both sides.
In this essay, I'm assuming you have some minor experience with SoftIce (If you don't have any, I'll tell you what key to press for everything you should do).
I suggest you use an 800*600 resolution and a maximized browser to view this text, so everything will look ok. What would be even better is to print it out, because you won't be able to read it from the screen when you're using SoftIce.


Let's start up PaneKiller if it's not already running, and see how we register it. It has it's own popup menu, and we see an option called "Unlock with registration code". Sounds good to me. Let's check it out. It has text fields where we should enter our name, company name and registration code. Ok, time to get cracking :

First thing we noticed was that the program gets our info through regular textfields (like just about every Win95 program).
"How can we use that to our advantage?", you say? Well, a program has to call a function to get information out of these text boxes. And since it is a lot of work to program a procedure for that, Windows 95 has the needed functions ready to go already, saving the programmer a lot of trouble.
The fact that all programs can use the same functions is also the great weakness, when it comes to protection schemes. All programmers know what these functions are, and so do all crackers (that would be us :).
The most commonly used functions in Windows 95 are:


Definition in the API programmer's reference:
The GetWindowTextA function copies the text of the specified window's title bar (if it has one) into a buffer. If the specified window is a control, the text of the control is copied.


Definition in the API programmer's reference:
The GetDlgItemText function retrieves the title or text associated with a control in a dialog box.
If you're not a programmer, you might be wondering what a dialog box is. Well, it's basically the same as a normal window, but it works different internally, which means it requires a different function.


Definition in the API programmer's reference:
The GetDlgItemInt function translates the text of a specified control in a dialog box into an integer value. This one's basically the same as GetDlgItemTextA, but this function directly returns a number (a decimal number) without having to translate it from a string.

Let's try setting these breakpoints in SoftIce: 'bpx GetDlgItemTextA', 'bpx GetWindowTextA', 'bpx GetDlgItemInt' (lots of programs use this one for serial numbers) and add another if you feel there's an important one missing. In case you didn't know already: 'setting a breakpoint' means that from now on, every time the specified function is used (called) softice stops everything that's going on and pops up on your screen.

Let's enter our info (name and company), and a bogus serial number. We press the 'OK' button, and SoftIce pops up. (kewl, one of our breakpoints worked. That's a good start!) We look in the SoftIce windows and see that GetDlgItemTextA is the function that was called. We are now one instruction after when the function was called. That means, we're now INSIDE the function GetDlgItemTextA. We can press F11 to get back out of the function, and back into the code of our program.
There we see the call the program made to GetDlgItemTextA, on instruction before our current position. First, let's look up what parameters that function uses in our Win32 API reference.

The reference says GetDlgItemTextA uses four parameters:

hDlgThe handle to the dialog box. Basically it's the dialog box version of hWnd, which is the handle to a program window, used by many (if not most) windows functions.
(Not much use to us right now)
nIDDlgItemThe ID of the 'control' where it should get the text from. A control is just a part of the Dialog box, like a text field or a button, and they are referenced by their unique control ID.
(Not much use to us right now)
lpStringThe memory address where the function should put the string it gets. Now this is very important! This way we can see where the program puts our name, company name and serial.
nMaxCountThe maximum number of characters the function will get.
(Not much use to us right now)

Windows uses the method of PUSH-ing things onto the stack when it calls a function. Something very important to remember is that in assembly language, the last parameter gets PUSH-ed first. I'm not going to explain why, coz it would take too long. If you must know, I suggest you learn ASM.

So what you are really seeing in ASM is this:

PUSH nMaxCount
PUSH lpString

One of the signs of this method of using the stack is the constant tampering with the ESP and EBP register at the beginning and end of a function. Also, there will always be some more right after the function call. All this is totally unimportant to us reversers, and can be ignored.
We see two more calls to GetDlgItemTextA, and there should be coz there are three info fields. This means SoftIce will pop up two more times for the other calls. Just step over them, and F11 out of 'em like you did for the first one. Now you should be just after the calls, and (what will probably prove to be) the beginning of the protection. The program has just finished getting the info you entered, so let's check now where he put it.
let's see what address is PUSH-ed to the GetDlgItemTextA functions as lpString:

CALL 1 : [EBP-50]
CALL 2 : [EBP-40]
CALL 3 : [EBP-D0]

Don't let them fool you, the order in which the code gets input doesn't have to be the same order as the textfields on the window. Let's check which address has which value.
To do that we type 'd ebp-50', 'd ebp-40' and 'd ebp-D0'.
You'll discover the following:

[EBP-50]=serial number (also referred to by me as s/n or reg code)
[EBP-D0]=company name

That's good to know!

Let's try and get a general idea about how the protection works before getting into the exact details. Lots of crackers don't do this, and will often get lost in a lot of code, that will afterwards prove to be totally irrelevant.
Now, we're just after the program got our info. Here we see a function being called. It's an internal function (that simply means that its not an API call), so we don't know (yet) exactly what it does. We can still check out the parameters its getting just by looking what was PUSH-ed just before the call.
There is only one variable being PUSH-ed, [EBP-50] which is our reg code. hmmm... well, it can't be the registration check, coz that would (probably) need our name / company name and neither is PUSH-ed here. Let's just skip this function for now.

Next is another function call. Its parameters are our s/n, our name, our company name, and one other variable, [EBP+08]
I'm naturally curious, and immediately want to know what that variable is. Curiosity is in my humble opinion the most important demand for becoming a good cracker. Because if you don't check out everything and want to rush things, you'll usually fail. It's important to try to understand what's going on. If you can do that, you can take on any protection scheme.
Well, that's all fine.. but how do we find out what [EBP+08] is? Ok, first we'll check what's at EBP-08 ('d ebp-8') hmmm... Well it obviously isn't a string. so it's probably a number. We can see that it is a DWORD value (which is 4 bytes = 32 bits), because [EBP+08] is being put into ECX before being PUSH-ed, and ECX is a 32-bit register.
You'll see a hexadecimal figure there. In my case it said 'FC070000' Variables are put in memory in reverse, one byte at a time. Confused? Well, let me help you. One byte is two nibbles. (nibbles are the ones that go from 0-F and are used for hexadecimal numbers). In other words, you have to take those numbers in the data window two nibbles (one byte) at a time. In my case : 'FC 07 00 00'. Is it becoming a bit clearer? good.
They are put into memory in reverse, so the real number would be '00 00 07 FC'.
Hmmm, now how much is that? Let's ask SoftIce : '? 7FC' (use your figure instead of mine) SoftIce will give you the number in hex, decimal and ASCII. the number still tells us nothing! Damnit!
Well, there is one more thing we can do: check if the variable has been used before. Maybe that can help us. YES! It's been used in every one of the GetDlgItemTextA calls.
It's always the last variable to get PUSH-ed, so that makes it ...(drum roll)... the hDlg!
In this case it proved to be unimportant for the cracking of this program, but it's always a good idea to check these 'mystery variables' out, even if it's just to help you to better understand the program.

Alright, now we understand ALL of the parameters given to the function: Our name, company, reg code and the window handle. Wow, this looks like an important function, requiring all that info! This will probably turn out to be the function where the core of the protection scheme is set up. However, first we'll look a bit more ahead. Remember, we're only trying to get a good general idea at this point. We'll look into the function(s) later. After the function call (and cleanup) there is a TEST being done, to check if EAX is zero or not. Then there is a JZ instruction, in other words : if EAX is zero, he jumps to the given address, otherwise he keeps on going where he is. Simple logic should make you realize that de value of EAX will probably be the result of the registration check. (GOOD/BAD)
Let's look at what the different bits of code actually do, so we can check whether that assumption was correct or not.

Let's assume for now EAX was 0 => TEST EAX,EAX => the zero flag is set => the JZ ('jump if zero'- instruction) decides to jump to 403102 => once there he immediately starts to prepare for a call of the function MessageBoxA. Let's look it up in our API reference. We find it has the following parameters:

hWnd=>The window handle (see earlier in this essay for more details)
lpText=> The address of the text to be displayed in the messagebox
lpCaption=> The address of the text to be displayed in the title bar of the messagebox
uType=> The style of the messagebox

(remember the parameters are being PUSH-ed in reverse order)

Let's take a look at the text being displayed in the messagebox. That should give us a good idea of why it's being displayed. Let's see, the third variable being PUSH-ed was 00422194 so that should be the address of the text. We do a 'd 00422194', and look at the memory window. We see: "The registration code you have entered is incorrect, Please try again."
Hmmm, so this is a messagebox for when you've entered the wrong code.

Now, for the other possibility:
EAX was not 0 => TEST EAX,EAX => the zero flag is cleared => the JZ decides not to jump => first he calls some function with as parameters our name, company and s/n. After that, he shows us a MessageBox.
Let's see what this one would say: 'd 00422188'. It says: "Thank You!".
That sounds like something it would say when you enter a correct s/n.

Apparently, EAX should not be zero when we reach the TEST EAX,EAX instruction.

Okay, that's all for the first global reconnaissance (I love army language :).
Let's take a look at what we know the progarm does so far:

Call GetDlgItemTextA (3x)To get our code, name and company. (In that order)
Call Function1Still unidentified, does something with our serial number, but is an unlikely candidate for being the protection scheme.
Call Function2This one uses our name, company, AND our serial. It seems to be the best guess to find the protection.
Test to see if Function2 returned zero.Yes =>Show MessageBox: "The registration code you have entered is incorrect, Please try again."
No =>Do something with our name/company/serial,
then show MessageBox: "Thank You!".

There are three ways to crack this program (and most other programs):

1- The BAD way:using SoftIce to force the program to accept our bad serial number as if it was correct, AKA 'patching'.
2- The GOOD way:determining/calculating the right s/n for your name.
3- The PROFESSIONAL way:making a 'key generator'. If you don't know how to code, you can skip this method.

I will also go over two imho awesome variations on this method, both for assembly programmers ONLY:
  • The DREAD way

  • Kwazy Webbit style

I'll go over all three methods here.

1- The BAD way
At the test EAX should not be zero, so some you would think some easy ways to patch this program would be:

-just before the TEST-instruction we could change the value of EAX to a non-zero value.
(Here, I found out something interesting: EAX can only be 00000001 (good s/n) or 00000000 (wrong s/n), otherwise Windows WILL crash!! There was no way to know this without trying (well, not without studying all the code first), so just consider me your guinea pig :)
-right after the TEST we could turn off the Zero flag, so we still won't jump even though EAX was zero at the TEST.
(In softice, use the mouse to click on the Z on the top-right of the window, it should now be highlighted. Press Insert to toggle it off or on.)
-we could change the JZ 00403102 into a JNZ 00403102 (jump if NOT zero) which would mean that EAX should now be zero at the TEST. Now that won't be too hard to do, will it? :)

I have tested all of these, and I can truly say none of them work, because this program checks if the s/n is correct every time it starts. We can of course try to find that part of the program and patch the program permanently to make sure we can still crack it, but why shouldn't we simply get the right serial, and enter that? It'll probably be just as much (or maybe even less) work, AND without the risk of having damaged a part of your program, that you'll need later on. So, instead let's try to do it:

2- The GOOD way
Now we're going to look at how the program checks whether you entered the right serial. (also known as the 'protection scheme'). To do that, we're going to look at the two functions.

The first function:

Let's single-step (F10) through the code until we reach the call to the first function. We'll take a look inside it (F8). We see that the first character (byte) of our s/n is put into ECX. Then, it checks whether it's a NULL sign (00 in nibbles). Null signs are used to indicate the end of a normal string (it's VERY important you know that!). That's why they call them zero-terminated strings. Then it checks if it's a space, a '-' or a '.' Let's assume for a moment that those characters are not required. This could save us a lot of trouble trying to figure out what it does exactly when each character is encountered.
(Let's not take that curiosity thing too far now, we don't want to be here for days doing something that will prove to be useless :) After all, if we are proven wrong, we can always go back and still find out what to do.
When a normal number was entered as s/n, the function does nothing. It just checks each character for the ' ', '-' and '.' and moves on to the next when done. Apparently, this function just checks for strange characters in our reg code. Lets step out of this function (F11) and move on towards the second function (using F10).

The second function:

This one seemed earlier like it would be the protection scheme, and it is now pretty much the only possibility left, so it appears we were right. Let's step inside.(F8)
First we see that a function is being called with three parameters. Let's see what they are. the first is an address put into EAX. When we dump at that address ('d eax') we see what looks like another address (in reverse order, explained earlier). when we see what's at that address, there's nothing we can recognize there. Hmm, strange. But let's move on for now. The next parameter being PUSH-ed is 0422078h. Now that looks like an address too. let's dump that one. Again, we are left with something unknown. I don't like this one bit, but let's keep moving for now, we might find out later on what it is. The third and last variable PUSH-ed is also an address. Now, let's see if this one can be identified. After the code puts the address in ECX, type 'd ecx' to see what it points to. AHA! Now that seems familiar. It's our reg code.
So, the only known (to us) parameter it gets is our serial number. As mentioned before, the correct reg code is probably dependent on the name and/or company name you enter, so this is not likely to be the protection scheme. So what is it? What does do to our s/n? Calm down, and let's try and figure that out. A quick look inside the function (F8) doesn't tell us much, because another function is also being called, making things difficult. Let's step out of the function, and see what would be next. Right after the function call, we see a call to MessageBoxA. Let's see what it would say: 'd 42207C'. The data window says: "The registration code is not in the correct format". OK! So, it is probably safe to assume now, that the function just checks whether the reg code is in the correct format or not, which probably means without and weird characters or something like that. Okay, our code is in the correct format, so we just jump past the messagebox, and move on, deeper into the protection..

Next, a function is called with our name as parameter. If we look ahead, we see that the exact same function is being called for our 'company name' as its parameter. After each call, EAX is stored into memory. But wait, what's EAX?! Well, after a call to a function, EAX always contains the return value. Programmers (should) know exactly what I mean by that. The rest of you can just take it from me that functions pretty much always return a value, and that that value is pretty much always put in EAX before returning. Knowing that, we can see that the return values of the two calls to that function are stored in [EBP-08] and [EBP-0C] respectively. Let's step over the functions, and see what's in those two addresses. Hmmm, two numbers that mean nothing to me. But they are probably important, because they are used again right after.

Now comes the most important part of the protection:

(1)MOV EAX, [EBP-04]
(2)XOR EAX, [EBP-08]
(3)CMP EAX, 047D6D93
(4)JNZ 00402E74
(5)MOV EAX, 00000001
(6)JMP 00402E74

- (1)Hey, what's in [EBP-04]? Let's step over the first line (F10) and look at what was put into EAX. Ok, that's our serial number. Now comes the test to see if you are truly paying attention, there's something strange going on with our serial number! 'no way, it looks ok over here..' I hear you saying. True, but that's the strange part! It shouldn't be looking normal. It's not in decimal or ASCII display. It's in hexadecimal notation. It shouldn't look like your serial at all. Apparently, PaneKiller assumes the serial code is a hexadecimal number, instead of a decimal. In other words, we can use a hexadecimal number as a reg code, so we can use 0-F instead of 'just' 0-9. Very useful info.. If you didn't notice this right away, don't worry. Neither did I. :) Anyway, EAX now contains our reg code.
- (2)In this line our reg code is XOR-ed with the number that was derived from our name (remember, from that function earlier?). In case you don't know what XOR is, I'll give you a very short explanation. XOR is short for 'eXclusive OR'.
What it does:
It takes one bit from each variable (that means it now has two bits)
It returns 1 if they are different (one of them is 0 and the other is 1)
It returns 0 if they are the same (both are 0, or both are 1)
It puts the result in the appropriate position in variable 1
It goes to the next bit and starts all over again

Maybe this 'truth table' will help clear it up a bit:

XOR Truth-table


Got it? I hope so. So, in this case the result is stored in EAX (which is variable 1)
- (3)It now checks if the result of the XOR is 047D6D93 h.
- (4)If it isn't, it jumps and we are now at another attempt (a second chance) to see if it will get the correct result by using our company name in line (2) and a different number in line (3). When you fail that one it jumps to a line where EAX is XOR-ed with itself. (all bits in EAX are of course the same as in EAX, so all bits will be set to zero. It is basically the same as MOV EAX, 0) Then the function returns with EAX being zero. As we saw earlier, that means: wrong serial. We don't want that. If it is, the jump is not made, and
- (5)MOV EAX, 00000001 is executed. In other words: EAX is now 1 (=NON-ZERO!)
- (6)Then it still jumps to the end of the function, this time skipping the XOR EAX, EAX instruction, and returning with EAX not zero. YES! Our registration is successful.

The beauty of the XOR instruction is that it is so easy to reverse.
What I'm saying is that after:

A=B XOR C (A,B and C being variables)

and you have two of the three variables, you can get the third by XOR-ing the two you have. So, after the previous XOR instruction,


REALLY?! sure, just check with the truth table above. Draw your own conclusions.

So, how do we use that here? Well, you know that after the XOR instruction, EAX should be 047D6D93 h (in the above example that would be A, because the result is stored in variable1, remember?) Also, we know the code that was derived from our name.('d ebp-8', and ALWAYS remember, that numbers are stored in reverse order in memory. In my case, in memory it was EB A6 D5 D2 (derived from the name 'Kwazy Webbit'), so my 'name-code' would be D2D5A6EB h) Let's call that the B in our example. now, we can use the C=A XOR B to get C. C would then be [EBP-04], our serial number! KEWL!
How do I do that? Well, there are several ways, I'm sure there are special calculators available on the web, but I don't have one personally. You could also make your own, if you are a programmer (remember that you'll need hexadecimal numbers for this one). I always do these operations on a simple piece of paper. First you write down the binary number for the two hexadecimal numbers. (not as hard as it seems, you can simply translate them one nibble at a time, using this table:


Then just use the XOR truth-table to do the rest.
to convert the result back to hex, just use the hex/binary table again.
I'm sure a calculator would be faster though, but I haven't really searched for one yet.


D2D5A6EB h was the name-code for Kwazy Webbit

My serial number should be: 047D6D93 XOR D2D5A6EB

________________________________________________ XOR
Serial Number=>110101101010 1000 1100 1011 0111 1000

Serial Number = D6A8CB78 h

Since we discovered the s/n is a hexadecimal number, we can simply leave it this way. We don't have to convert it to decimal like most other serials. Just try to enter 'Kwazy Webbit' as name and enter this s/n (or if you've already made one, by all means, use your own!) It should give you the messagebox we all knew was coming: "Thank You!"
The next time you start up PaneKiller, it will be the registered version. And we managed to do it the way it was meant to be done, without brutally forcing the program into accepting our bad serial, but by getting the right one instead.

As mentioned before we'll make a 'keygen' here. That's short for key generator, a program that will let you enter your name and/or company, and will give you the matching serial number. To make this, you obviously have to know how to program. I'm assuming you know how to do that, from this moment on. Now, I'll leave the coding of the rest of the progam over to you (The way the window looks, making a WndProc, etc.), just make sure you have a window with two text fields and a button.
The first textfield is for you to enter your name.
The second textfield is for the program to put the calculated reg code.
The button is to start the calculation.

Schematically the program would do this:
1-Button pressed
2-Get entered name from textfield1 and put it in memory (a variable)
3-Use the name to calculate the matching name-code (just like Painkiller does) and store that into a variable
4-Name-code XOR 047D6D93 (remember?), store the result (that's the serial number)
5-Translate the serial number to the matching string
6-Put that string in textfield2

The most important part is obviously the translation from the name to the name-code.
Something like this might be difficult, but when we think about it for a second, it's easy: We should just do the same thing as PaneKiller uses for name to name-string translation. Let's look up where that was again. The function we want to mimic is located at 04031AAh. Now take a look inside (F8). Alright, there are no other functions being called and there are no jumps being made to a place outside this function. (04013AA to the RET-instruction) That will keeps things nice and simple. We can just copy the code inside the function, and put it into our own (sorry, no copy&paste in SoftIce ;). 'But wait! That's not in my programming language', you say? Alright, I program in assembly myself, but I've been told it can also be done very easily in C/C++ by putting the code in an 'asm{ ... }'-block. For other languages, I suggest you check your help file for info.
Now there's one more problem with that code: the [EBP-08], [EBP-04], etc. Those are just local variables. But what kind? Well, you can see that [EBP-04] and [EBP-08] are DWORD variables, because of the instructions at the beginning of the function:

MOV DWORD PTR[EBP-04], 00000000
MOV DWORD PTR[EBP-08], 00000000

The uncompiled version would look something like this:

MOV DwordVar1,0
MOV DwordVar2,0

Got it?

Then there's another 'variable' being used: [EBP+08]
(remember, this is a new function so this is NOT the same variable as hDlg, even though they look the same). When we dump this variable ('d ebp+8'), we see what looks like an address. (Stored in reversed bytes, remember that.) In my case, and if I'm not mistaken, it should be the same for you, it said 'D0 F6 67 00'. When we dump that address ('d 67F6D0'), we see it is our name. So, that variable holds the address to the start of our name. In C/C++ this would be a pointer to the beginning of the array of char's if I'm not mistaken. Of course! It's the parameter that was PUSH-ed before calling this function! I suggest you start writing down all the code in this function, except of course for al the 'cleanup work' with EBP and ESP at the start and the end of the function. Just use any names you like for the local variables. (I know, I know... There are a lot of crackers wondering why the HELL I'm telling you to write down the code instead of decompiling the program, and copy&pasting it. Well, the reason is that I wanted to keep the 'Required tools' section down to an absolute minimum, also i'm hoping you'll start to think a bit about what the code is doing.)
For getting the name parameter to the function, you can (I think) just use the syntax that matches your programming language.(Again, you might wanna check your reference on that) If you did everyhting right, you should now have a function you could call NameToNameCode which uses one parameter(the address to our name), and returns a DWORD value: our namecode. Then you XOR that value with 047D6D93h, giving you the serial number!
Now there's just one final problem: how do you show it to the user? You'll need to convert it to a string first. Of course we could write our own function (I did), but there is an easier way to do this. There is an API function specially designed for this kind of thing: wsprintf. If you check you API reference, you'll see that it can convert numbers to strings, etc. The explanation you'll find there is not very good(I didn't get it at first), so I'll give a (hopefully) easier explanation here. When you want to print a variable into a string, It will need to be converted to characters. There are several ways for that, depending on what the variable is: a single character, another string, a decimal number, and a hexadecimal number. To show the function where inside the string you want it, you'll need to put a symbol there. wsprintf uses the '%' token, plus a character indicating what the variable is.
example (in C/C++):
int		Age		=	50;
char		Question	=	'?';
char[50]	buffer;

wsprintf(&buffer,"Wow, you are %d already%c",Age,char);

After this call, buffer would be "Wow, you are 50 already?"

In assembly (masm) this would be:
FormatString   db   "Wow, you are %d already%c",0   ;Remember, strings are zero-terminated..
buffer         db   50 dup(0)   ; This creates an array of 50 bytes all initialized at 0
Age            dd    50
Question       db   "?"

push char ;Remember, pushing in reversed order push Age lea edx, FormatString push edx lea edx, buffer push edx call wsprintf

(note that you can't use the MASM 'invoke' syntax here, because wsprintf has an unknown number of parameters.)

Now we just need to convert a hexadecimal number to a string, we dont need anything else there. Looking in the reference, we see that for an uppercase hexadecimal number you need to use "%X". So, that's going to be our entire FormatString. Also, the number we want to use, is returned in EAX, so we can just use EAX as our parameter when using assembly:
FormatString	db	"%X"
buffer		db	50 dup(0)

...		;The call to NameToNameCode
push eax
lea edx, FormatString
push edx
lea edx, buffer
push edx
call wsprintf

Not too bad, eh?
In C/C++ the same can be achieved by doing:

char[50] buffer;

All we have to do now, is put the buffer into textfield2, so the user can see it too.

In masm:
invoke SetWindowTextA, hWnd, ADDR buffer

In C/C++:

Et voila! We have just made a key generator. Just compile it, and test it.

The DREAD way
This is a variation on 'The PROFESSIONAL way'. It is hard but also very cool. I call it the DREAD way, because it is the preferred method of several DREAD members.We are going to take PaneKiller, and build the keygenerator into the program itself. The source code can only be decompiled as assembly, and that is why I said earlier that if you don't know assembly, you can forget about cracking the DREAD way.

We are going to add code to the program, so we will need some space to put it in. There is never any room left in the program (why would there be?), so with most programs you will have to append your own code to the end and then fiddle around with internals of the parent program (see Razzia's essay on making his razziapad for more info). In our case we do not have to go through this (difficult) process, because we can 'make' some space inside PaneKiller.
You may be wondering where, beacuse there doesn't seem to be an obvious space avaiable. However, after thinking about how to do this, I remembered something. We've always assumed it uses our name to calculate our serial number. But as we saw earlier, if we fail that test it checks to see if the code is correct for our company name. Aha! Now, if we will just assume the serial number 'matches' our name (just like we've done so far), we do not need this code. We can just take it out, and put our keygenerator in it's place. A problem arises: that is only a very tiny bit of code, how are we supposed to fit a whole keygenerator in there? Well, the answer is simple, just use the equipment available. Use what the coders have given you. As we saw, the program will do the following when you enter a wrong serial:

(1)- It calculates our 'name-code'
(2)- It calculates our 'company-code'
(3)- It checks whether the code is right for the entered name and fails.
(4)- It checks whether the code is right for the entered company name and fails.
(5)- It returns with EAX=0
(6)- It displays a messagebox saying we have the wrong serial number

As you may already have noticed, the code in (2) is no longer necessary, because we're not going to perform (4) anymore. This will give us some more space to work with. Let's take a look at the code:

00402E42movecx, dword ptr [ebp+0C];Put the address of our name in ecx
00402E45pushecx;Push ecx for use in function
00402E46call004031AA;Call function to get name-code
00402E4Baddesp,00000004;Restore the stack after the function call
00402E4Emovdword ptr [ebp-08],eax;Store name-code in memory
00402E51movedx, dword ptr [ebp+10];Put the address of our company in edx
00402E54pushedx;Push edx for use in function
00402E55call004031AA;Call function to get company-code
00402E5Aadd esp,00000004;Restore the stack after the function call
00402E5Dmovdword ptr [ebp-0C],eax;Store company-code in memory

See? We can take out that entire second half! That is 00402E60-00402E51 = 0Fh or 15 bytes.
The next code consists of the two checks to see if the serial number was correct:

00402E60moveax, dword ptr [ebp-04];Put serial number in eax
00402E63xoreax, dword ptr [ebp-08];XOR s/n with name-code
00402E66cmpeax, 047D6D93;Compare result with 047D6D93h
00402E6Bjne00402E74;If it is not the same, check if it 'matches' the company-code
00402E6Dmoveax, 00000001;Otherwise, put 1 into eax, causing the program to be registered
00402E72jmp00402E8B;Jump to the end of the function, return with eax=1
00402E74movecx, dword ptr [ebp-04];Put serial number in eax
00402E77xorecx, dword ptr [ebp-0C];XOR s/n with company-code
00402E7Acmpecx, 0177F29F;Compare result with 0177F29F
00402E80jne00402E89;If it is not the same jump to 00402E89
00402E82moveax, 00000001;Otherwise, put 1 into eax, causing the program to be registered
00402E87jmp00402E8B;Jump to the end of the function, return with eax=1
00402E89xoreax, eax;Zero eax, causing the program to remain unregistered
00402E8B - 004202E8ECleanup stuff and return from function

In this code, we can skip the bit from 00402E74 up to -but not including- 00402E89 . We cannot skip the instruction at 00402E89 because the program needs either 0 or 1 in eax to keep from crashing.
Anyway, this will give us another:
00402E89-00402E74 = 15h or 21 bytes of space.

This brings the total to 15 + 21 = 36 bytes of space for us to use. Any highlevel language programmer would probably see this amount as something not even worth mentioning, but for any decent assembly-coder, this is quite enough. (You'll see later on, we'll have room to spare :)

Let's start coding...

Well, I assume you've read the part about how to make a keygen, because I'm not going to explain the entire procedure again. I mentioned earlier that the trick to making this kind of built-in keygen in a minimal amount of space, is to use what the program gives to you to the max. Let's make that a little more concrete: In our keygen, the most difficult part of the 'real' code was where we reproduced the code the program used to calculate the name-code from the name. Well, we don't have to do that AT ALL here. It's already been calculated by the program, and stored in memory at [ebp-08]. Well, that saves us a lot of coding. Another part of our keygen, was the use of wsprintf to convert the hexadecimal number to a string, so we could see it as a user. If you use WDasm to decompile PaneKiller and look in the imported API functions you will see it already imported the function wsprintfA (it's the last one in the list). All we have to do is use it... KEWL! They've made it too easy! :]
By the way, in case you were wondering: wsprintf is the same as wsprintfA. So, that should be easy enough to use in our code. What else do we need? Oh, of course.. We need some way to show (the string version of) our serial number to the user. Oi, sounds like a problem. Well, it isn't! We already know the user is going to see a messagebox saying he had the wrong serial. Why shouldn't we just overwrite that text with our calculated serial number? Then the messagebox would show the REAL serial number if you enter a wrong one..

Now, let's get coding already...

Before we start coding, there's one more problem we need to address: there's a part of the original program (00402E60-00402E72) between our two code snippets. This means that we cannot store things in the registers used in that part. In this case, it's not a big problem. The code only uses eax. Well, I think we can avoid using eax for storage between the two sections, don't you? :)
By this I also mean the code snippet doesn't use the stack at all (no PUSH or POP or any other messing around with ebp or esp), so we can go ahead and use the stack too.

How much more am I going to have to read through to get to the coding part?!

Well, here we start coding.


We know the name-code is stored in [ebp-08], so let's put that in eax:
mov eax, dword ptr [ebp-08]

Now we do the same thing we did in our keygen, we XOR it with 047D6D93h :
xor eax, 047D6D93h

We have the valid serial number in eax now, we just have to convert it to a string:
push eax
lea  eax, [ebp-0C]
push eax
mov  [ebp-0C], 00005825
push 00402194
call wsprintfA

NOTE: you CAN stilluse eax for anything but storage, because the original program doesnt
store anything in it either (You can see that because of the instruction at 00402E60).

The second line might call for some further explanation: the DWORD variable at [ebp-0C] used to be the 'company-code', so it is no longer used. We can used it for our own purposes, by storing a DWORD there. But what is 00005825? Well, first of all, it's going to be put in memory backwards: 25 58 00 00
this is a zero-terminated string: '%X',0,0
You may recognize this from the Keygen tutorial, as the format string to convert hexadecimal numbers to strings. We cannot PUSH it directly (ie PUSH 00005825), because the wsprintf function requires the address to the format string, not the string itself. So, we do
lea eax, [ebp-0C]
to get the address of the string into eax, and then we
push eax
to give it to the function.
The last variable to be PUSH-ed (the first in the API description) is the destination for the string. As we said before, we'll print it over the 'invalid code' message. To get the address to that variable, we look (in WDasm) at the "Data Hex". We search for the message:
"The registration code you have entered is incorrect. Please try again."
You'll find it at 00402194, so we push that to wsprintfA.
Then, we call the function wsprintfA (after all its parameters are PUSH-ed) and just let the original code go on. We should see the messagebox that previously said our code was invalid, and now it shows us the real code. But we're not that far yet. We have to change the code first. We don't have the source code, so we can't just enter our code and compile it. We're going to to use a hex editor instead. To do that, we'll have to replace the hexadecimal representation of the old code with the hexadecimal representation of the new (wow, tough words).
We can find the old one pretty easily, it's next to the disassembled instruction. just write down the code for the parts we're gonna replace.
For the first part you should get:
8B 55 10 52 E8 50 03 00 00 83 C4 04 89 45 F4
(this is the code representing the instructions)

The second part should be:
8B 4D FC 33 4D F4 81 F9 9F F2 77 01 75 07 B8 01 00 00 00 EB 02

But how do we translate our assembly code to something like that?

With normal programs, it's simple. You just use a compiler for that. But we don't have a full program. We just want to replace a small part of it. The way I figured out what the appropriate codes were, was by looking at the codes for similar instructions in the code of PaneKiller (decompiled by WDasm), and looked at what the code was that they used. Allow me to demonstrate:
I have
mov eax, dword ptr[ebp-08]
When i start looking at the source of PaneKiller (I start at a random place), I quickly found these two instructions:
:0040E6EC 8B45A0       mov eax, dword ptr [ebp-60]
:0040E6FA 8B4590       mov eax, dword ptr [ebp-70]
Now, start thinking about how that code might work. I'll tell ya: '8B45xx' is used for every 'mov eax, dword ptr [ebp+xx]' instruction. In our case, xx is negative (-60 and -70). When the counter goes below zero, it starts over at FF. It's easier to explain by illustrating:
8B45FF       mov eax, dword ptr [ebp-1]
8B4500       mov eax, dword ptr [ebp]
8B4501       mov eax, dword ptr [ebp+1]
Knowing that, we can figure out that the code for:
mov eax, dword ptr [ebp-08]
should be:
Just do something similar with the other commands, and you'll have the whole list of codes:
8B45F8           (  =   mov  eax, dword ptr [ebp-08]   )
35936D7D04       (  =   xor  eax, 047D6D93             )
50               (  =   push eax                       )
8D45F4           (  =   lea  eax, [ebp-0c]             )
50               (  =   push eax                       )
C745F425580000   (  =   mov  [ebp-0c], 00005825        )
6894214200       (  =   push 00422194                  )
FF153CE24100     (  =   call dword ptr [0041e23c]      )
Make sure you still remember which code represents which instruction! The reason for that is the block of original code in the middle of our own. We can't cut a single instruction in half, so the cut will have to be made between two instructions. Remember, we have 15 bytes in the first part, and 21 in the second.
I made the cut between the second
push eax
mov  [ebp-0c], 00005825
Which gives me:
8B45F835936D7D04508D45F450 (13 bytes)
for the first part, and
C745F4255800006894214200FF153CE24100 (18 bytes)
for the second part.
You may notice you will have some space left over now (15-13=2 in the first part, and 21-18=3 in the second), this is not a big problem.. You can just NOP them out. NOP instructions do absolutely nothing, except take up 1 byte of space. The code for NOP is 90. This makes:
8B45F835936D7D04508D45F4509090 (15 bytes)
for the first part, and
C745F4255800006894214200FF153CE24100909090 (21 bytes)
for the second part.
That should do it. We now know what code we want to replace, and we know what to replace it with. Let's make it happen! Open Panekill.exe in your favorite hex editor, and find the code we want to replace. There are two ways to do this:

Replace both parts, and save the file. MAKE SURE YOU HAVE A BACKUP!!
Now start up the program, and try to register it. Enter any information you want, and enter a wrong serial number. Press 'OK', and you should see a messagebox with the correct registration code! Pretty kewl, eh?

Everything works now (if it all went ok), however there are still a few improvements that can be made. For instance, when we look at the code at 00402E51:
mov dword ptr [ebp-08], eax
mov eax, dword ptr [ebp-08]
you will (hopefully) realise the second command is a total waste of time and space. (note that the first one is not, because it stores the value to memory for use later on)
so, we can just NOP out the command at 00402E54 by replacing
(This change is really pretty much useless, but i cannot help but obsess about these things :) Also, the MessageBox we get, looks pretty lame. It just has the right registration number in it. Nothing more, nothing less. In our hexeditor, we could replace
The registration code you have entered is incorrect.  Please try again.
with something like
[Kwazy Webbit]
Your correct code is:
(To go to the next line, use a CR/LF pair. The code is 0A0D)
If we change nothing else, the registration code will now simply written over our text, and it will still be the only thing in the messagebox. To change that, we should make the wsprintfA write the code to a different place: the end of our text.
To do that, all we have to do is change the last parameter that gets PUSH-ed to wsprintfA (lpOut) by adding our textsize to it. So in my case, I'd change:
push 00402194
push 0040211B

That's all there is to it for reversing the DREAD way.

Kwazy Webbit style
This is more like a variation on 'The DREAD way'. While I was fiddling around with PaneKiller, writing the previous chapter, I noticed the 'The DREAD way', although coming close, still wasn't perfect. When you entered a wrong s/n the messagebox popped up, and gave you your correct serial. You might think that's as good as a keygen can get, right? Wrong. There's one more improvement that can be made to make it perfect. See, now you still have to look in the messagebox, and remember your code (or write it down). What if we could make the program enter the correct serial itself? It CAN be done.

I'm assuming you have made all the changes mentioned in the previous chapter (WITHOUT the extra improvements!). Now, the program shows us a messagebox with the serial number ONLY.
We've already seen that the program uses GetDlgItemTextA to get our info out of the textboxes. There is another API function to put info into those boxes: SetDlgItemTextA.
Let's compare GetDlgItemTextA with SetDlgItemTextA now:


(For a detailed explanation of all of these items, look in you programmer's reference, or further up in this essay)
Knowing that we want to put the valid serial number into the same textfield, we notice two things:
To find the needed values, we look at the call of the program to get our reg code. We'll find (in WDasm) it uses
:00403063   8B4D08          mov ecx, dword ptr [ebp+08]
:00403066   51              push ecx
to PUSH the hDlg, and
:0040305E   68FE030000      push 000003FE
to PUSH the nIDDlgItem, so we will use the same in our call to SetDlgItemTextA.
We just need to know the lpString now. That's easy, it's the address wsprintfA uses to print our registration code to:
push 00402194
(If you have made the 'improvements' to the messagebox, you may need to adjust this value)

So, the complete code for putting the serial in the appropriate textfield should be:
push  00402194
push  000003FE
mov   ecx, dword ptr [ebp+08]
push  ecx
call  SetDlgItemTextA
We're going to be facing the same problem as we did in the previous chapter: we don't have any space to put it in. The way the program is now, we see the messagebox with our code, which we won't be needing anymore after we change it. We can just rip that part of the program out, and replace it with our own code. the total call (PUSH-ing parameters and the CALL itself)to the messagebox is:
:00403102   6A40            push 00000040
:00403104   8B0D1C2B4200    mov  ecx, dword ptr [00422B1C]
:0040310A   51              push ecx
:0040310B   6894214200      push 00422194
:00403110   8B5508          mov  edx, dword ptr [ebp+08]
:00403113   52              push edx
:00403114   FF158CE24100    call MessageBoxA
:0040311A   ....
(That's a total of 0040311A-00403102=18h or 24 bytes)

To find out the hexadecimal representation for our code, use the same process as described in the previous chapter. You should end up with something like:
6894214200       ( = push 00422194                                             )
68FE030000       ( = push 000003FE                                             )
8B4D08           ( = mov  ecx, dword ptr [ebp+08]                              )
51               ( = push ecx                                                  )
FF152CE34100     ( = call dword ptr [0041E32C]     (call SetDlgItemTextA)      )
which is a total of 20 bytes. We have to add 4 NOP commands then:
6894214200 68FE030000 8B4D08 51 FF152CE34100 90 90 90 90 90
Just use your favorite hexeditor (like previous chapter) to change the original 'MessageBoxA-code' into our own 'SetDlgItemTextA-code'. Start up PaneKiller, and try to register with your own name and a fake reg number. When you press the 'OK' button, it should now automatically replace your fake s/n with the real one!! Just press 'OK' again, and it is should be registered to you.
You have just learned to 'crack like Kwazy'. Try something similar with other programs, experiment as much as possible. (remember: always have backups!)
Have fun!

-NOTE- If your copy of PaneKiller is already registered:
start up 'c:\windows\regedit.exe' (just 'regedit' will do), and go to HKEY_LOCAL_MACHINE/SOFTWARE/MaDdoG/PaneKiller. Delete 'Registered User', 'Registered User Company' and 'Registration code'. You now have the shareware version again.

I'd rate this protection at just below average, because I doubt a total newbie would crack this one on his own, but just about every average or advanced cracker will not have too much difficulty. The reason I picked this protection, is that it's relatively straightforward, and it shows several interesting aspects of cracking, like reversing the XOR instruction.
Also, I later found out this target had real potential for cracking it 'The DREAD way', as well as 'Kwazy Webbit style' (for more info, read the appropriate chapters).

Well, this was my first essay, hope you learned something. There will probably be more coming, but not right now. This one took me quite some time, and summer's coming!

Greetz to: all members of DREAD, koolzio (where the hell did you go?), all the other people I know on IRC. Also greetings to: ppl like Sandhopes, Hyrax, Dosy, Jo, Gelf and all others I see IRL.

Special thanx: Iczelion and Hutch, for teaching me win32 assembly

Wishing all of you a DREADful day ;-),

Kwazy Webbit of DREAD

"The only thing that keeps me sane, is my collection of singing potatoes..."

Look for me on IRC. I'm usually on EFnet in one or more of the channels:
#win32asm, #DREAD, #cracking4newbies and #cracking
CU there!

©1999 Kwazy Webbit productions