• Welcome to Valhalla Legends Archive.
 

Writing a Keygen

Started by iago, October 19, 2003, 11:27 PM

Previous topic - Next topic

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.  
QuoteUnregistered Copy.  Select How To
Order to find out how to register.
Registration code: 18677

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:
bpm 0x501440 r

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:
:0041C07A 51                      push ecx
If you want to find out what's going on, you can use the "?" command.
? eax
0000007B   0000000123   '}'
? ecx
000048f5   0000018677


As you can see, ecx is the random code, and eax is the code you typed.  Now look at these lines:
: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

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!

: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


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:
:004255C0 8B542404                mov edx, dword ptr [esp+04] ; The random key was moved into edx

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?

#include <stdio.h>
#include <stdlib.h>


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

     long GenKey;
     short RegCode;
     printf("Keygen for Avernum 2\nCreated by iago <[email protected]>\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;
}


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
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


RyanIdium

Blades of exile three had me adicted for a long time.  but i forgot who made it until i found this :)

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.

iago

Quote from: vile on December 28, 2003, 03:12 AM
You don't need to put 0x in front of all hex values...you can just add an h to the end of it.

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 :)
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


vile

heh...putting the h at the end is just shorter.

iago

By one character.  I'll survive.
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


UserLoser.

the more he types, the better his typing skills get!

dxoigmn

Quote from: iago on December 28, 2003, 05:54 AM
Quote from: vile on December 28, 2003, 03:12 AM
You don't need to put 0x in front of all hex values...you can just add an h to the end of it.

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

It's also nice to know the number you'll be reading soon is in hexidecimal notation.

Grok

Quote from: iago on December 29, 2003, 03:10 PM
By one character.  I'll survive.

Is there an 'h'?  I'd like to try to solve it, Bob.  Is it 'hexadecimal'?

thetempest


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.