Valhalla Legends Forums Archive | Assembly Language (any cpu) | Writing a Keygen

AuthorMessageTime
iago
I wrote this awhile back, so I'd might as well finally give it a good home :)

-----------------
VERY IMPORTANT DISCLAIMER:
Spiderweb Software and Jeff Vogel make Exceptional games that I've been playing for years, and I am totally in favour of supporting them! I don't recommend doing this for any reason than educational purposes!!
-----------------


This is going to be a tutorial which will take you, step by step, through how I made This.

Necessary Programs
First of all, get Avernum 2. This is the demo of Avernum 2, which is protected by something similar to the name/key type, but I won't get into that yet.
Note: you can get demos of Avernum 1 and 3 there, as well, and they are very similar.

Other tools you'll need are:
TSearch - This will let you search/edit the memory of a process
Softice - This is a very powerful debugger, I can't give you a link to it since it's different for every version of Windows. I'd suggest using Kazaa ["Softice 6 NEW" will run under Windows XP]

That being said, let's begin!

Getting started
First off, run the program. You'll be presented with a startup screen telling you what you're missing. Press ok.

Now, you have the menu. In the bottom right corner, it reminds you that it's unregistered and gives you a randomly generated code.
[quote]Unregistered Copy. Select How To
Order to find out how to register.
Registration code: 18677[/quote]

All right, let's try registering. Click on "Register Copy." You get a nifty sound effect, and it brings up a dialog box which displays the code (in my case, "18677"), and a textbox for the code supplied. Type in something like "123," and press ok. Now we have another messagebox with the title, "Oops!"

Finding the right spot
Now, what we're going to do is find where the program looks at the provided code because, logically, the program will take the code it provides, mangle it somehow, and end up with the code it expected you to type in.

Run TSearch. From the Process menu, select "Avernum 2." Click on "Hex Editor" at the top. Another window should appear. Also click Calculator.

Because the number is two bytes, we can assume it's always two bytes [short int]. If it was lower than 256, we would assume it's one byte [byte], and if it was greater than 65536 (or whatever 256*256 is :-/), we would assume four bytes [long int]. So, select "short int" from the combobox, and type in the decimal string. Make a note of the value in "Rev Hex," that's the important one.

Click the "Search" icon on the hex editor (it's the one with the magnifying glass on it), make sure "Hex" is selected, and type in the "Rev Hex" number. Hopefully, it'll only find a single result (if not, you'll have to do the next part for each one).

The value you find SHOULD be at 0x501440 [in version 1.00, at least].

Finding where the code is processed
Once you make a note of the address you found, you can close TSearch (by the way, you can also change the number from here).

Click "Register Now" on Avernum 2's main screen. While it asks for a code, break into Softice (ctrl-d). Make sure in the bottom-right corner it says "Avernum" (if not, type "addr Avernum 2"). I would also type "set font 2".

We're going to set a breakpoint on the memory address of the code it gives you. At the softice prompt, type:
[code]bpm 0x501440 r[/code]

This will break when that memory address is read by Avernum 2.

Type in whatever you want, and hit enter. Hopefully, Softice will pop back up at this instruction:
[code]:0041C07A 51 push ecx[/code]
If you want to find out what's going on, you can use the "?" command.
[code]? eax
0000007B 0000000123 '}'
? ecx
000048f5 0000018677[/code]

As you can see, ecx is the random code, and eax is the code you typed. Now look at these lines:
[code]:0041C073 0FBF0D40145000 movsx ecx, word ptr [00501440]
:0041C07A 51 push ecx
:0041C07B E840950000 call 004255C0
:0041C080 59 pop ecx
:0041C081 66390542145000 cmp word ptr [00501442], ax
:0041C088 7509 jne 0041C093
:0041C08A C605BDBF4F0001 mov byte ptr [004FBFBD], 01
:0041C091 EB07 jmp 0041C09A[/code]
Remember 0x501440? Well, that's where it's read.
It's pushed and 0x004255C0 is called, it's popped, and 501442 (if you type ? 501442, you'll discover it's the code you typed) is compared to ax (the right half of eax, which is the function's return value). If you type ? ax, it will give you the decimal equivolant of ax, which happens to be the right code. That's great, but how's it generated?

How the code is generated
Well, clearly it's generated in the function 0x004255C0, so let's take a look at that!

[code]:004255C0 8B542404 mov edx, dword ptr [esp+04]
:004255C4 31C0 xor eax, eax
:004255C6 89D0 mov eax, edx
:004255C8 8D4213 lea eax, dword ptr [edx+13]
:004255CB 0FAFC2 imul eax, edx
:004255CE 8D0440 lea eax, dword ptr [eax+2*eax]
:004255D1 0565000000 add eax, 00000065
:004255D6 B9CF800000 mov ecx, 000080CF
:004255DB 99 cdq
:004255DC F7F9 idiv ecx
:004255DE 89D0 mov eax, edx
:004255E0 69C0FF070000 imul eax, 000007FF
:004255E6 B983740000 mov ecx, 00007483
:004255EB 99 cdq
:004255EC F7F9 idiv ecx
:004255EE 89D0 mov eax, edx
:004255F0 6BC025 imul eax, 00000025
:004255F3 B9FF7F0000 mov ecx, 00007FFF
:004255F8 99 cdq
:004255F9 F7F9 idiv ecx
:004255FB 89D0 mov eax, edx
:004255FD 69C0FF010000 imul eax, 000001FF
:00425603 B910270000 mov ecx, 00002710
:00425608 99 cdq
:00425609 F7F9 idiv ecx
:0042560B 89D0 mov eax, edx
:0042560D 0510270000 add eax, 00002710
:00425612 C3 ret[/code]

Wow, look at all that!

But if you look carefully, you'll see there are NO CALLS, and NO STACK MANIPULATION after the first line! Sweet!

Let's look at the first couple lines:
[code]:004255C0 8B542404 mov edx, dword ptr [esp+04] ; The random key was moved into edx[/code]

Well, that's all you actually need to know! A bunch've assembly stuff is done to it, and the function returns! Piece of cake!

Now, you might call me a cheater for this, and I am, but in a program this simple all we have to do is copy/paste the assembly into a c++ program as inline assembly, making sure we preserve the registers for c++, and we're done!

The Program
Well, all that aside, why don't we look at the program?

[code]#include <stdio.h>
#include <stdlib.h>


int main(int argc, char* argv[])
{

long GenKey;
short RegCode;
printf("Keygen for Avernum 2\nCreated by iago <iago@backstab.ca>\n\n");
printf("What's the Registration Code? --> ");
scanf("%d", &GenKey);

__asm
{
; Preserve our registers
push eax
push ecx
push edx

; Put the random key into edx, just like the real program does first
mov edx, GenKey

; The rest of this is just copied/pasted from W32Dasm
; if you do that, then Don't forget to put 0x in front of all hex values!!
xor eax, eax
mov eax, edx
lea eax, dword ptr [edx+0x13]
imul eax, edx
lea eax, dword ptr [eax+2*eax]
add eax, 0x00000065
mov ecx, 0x000080CF
cdq
idiv ecx
mov eax, edx
imul eax, 0x000007FF
mov ecx, 0x00007483
cdq
idiv ecx
mov eax, edx
imul eax, 0x00000025
mov ecx, 0x00007FFF
cdq
idiv ecx
mov eax, edx
imul eax, 0x000001FF
mov ecx, 0x00002710
cdq
idiv ecx
mov eax, edx
add eax, 0x00002710
mov RegCode, ax

; Get our registers back
pop edx
pop ecx
pop eax
}

printf("\n\nYour regcode is %d\n\n", RegCode);
system("pause");
return 0;
}[/code]

As you can see, the vast majority of it is just copied! That's how easy it is!!

Well, that's my 2 cents. If anybody else has any questions, or anything to add, you're more than welcome. I can also go through the assembly that I copy/pasted step-by-step, but This is much easier :-D.

Happy cracking!
-iago
October 20, 2003, 4:27 AM
RyanIdium
Blades of exile three had me adicted for a long time. but i forgot who made it until i found this :)
December 9, 2003, 9:48 PM
vile
You don't need to put 0x in front of all hex values...you can just add an h to the end of it.
December 28, 2003, 9:12 AM
iago
[quote author=vile link=board=7;threadid=3157;start=0#msg37315 date=1072602749]
You don't need to put 0x in front of all hex values...you can just add an h to the end of it.
[/quote]

In my opinion, that's ugly. I like 0x better, even when I'm talking to somebody, I say "oh ex 123" or something like that. It's more of a preference than anything, I suppose :)
December 28, 2003, 11:54 AM
vile
heh...putting the h at the end is just shorter.
December 29, 2003, 8:27 PM
iago
By one character. I'll survive.
December 29, 2003, 9:10 PM
UserLoser.
the more he types, the better his typing skills get!
December 29, 2003, 10:12 PM
kamakazie
[quote author=iago link=board=7;threadid=3157;start=0#msg37321 date=1072612475]
[quote author=vile link=board=7;threadid=3157;start=0#msg37315 date=1072602749]
You don't need to put 0x in front of all hex values...you can just add an h to the end of it.
[/quote]

In my opinion, that's ugly. I like 0x better, even when I'm talking to somebody, I say "oh ex 123" or something like that. It's more of a preference than anything, I suppose :)
[/quote]

It's also nice to know the number you'll be reading soon is in hexidecimal notation.
December 30, 2003, 3:06 AM
Grok
[quote author=iago link=board=7;threadid=3157;start=0#msg37516 date=1072732209]
By one character. I'll survive.
[/quote]

Is there an 'h'? I'd like to try to solve it, Bob. Is it 'hexadecimal'?
December 30, 2003, 6:16 PM
thetempest
lol ;D
December 31, 2003, 5:30 PM
vile
I guess you're right about having things in hexidecimal notation...but still, if you want to, you can add an h at the end of a number in Assembly, and it'll use it as a hexidecimal value. Just felt like I'd say that.
January 2, 2004, 11:39 AM

Search