Solving KeygenMe V7 by MaxX0r – part 1

This KeygenMe I’ve got personally from the author (MaxX0r). Thank you for such a nice gift and congrats for a good job!

The difficulity level is 3 (according to
It doesn’t contain any advanced crypto, but is nicely obfuscated, self-modifying and containing some defensive features. Due to this properties, it have pretty high detection ratio on VirusTotal. But I ensure you, it is not a malware (just a false positive)! If you still have doubts, you can read thought the code (I will try to describe it clearly in my tutorial).


The KeygenME is a 32-bit PE file. You can download it here, password: “crackme”. It is also available on (and some of it’s mirrors).

KMv7_trialAuthor sets several achievement levels:

GOLD: * create a working keygen
* remove the nag screen

SILVER: * get a working username/serial combination
* remove the nag screen

BRONZE: * make it accept every username/serial combination
* remove the nag screen

WOOD: * remove the nag screen || make it accept every username/serial combination


Here I would like to present my solution for GOLD, that is:

* create a working keygen
* remove the nag screen

KeyGen source:



Tutorial 1 – removing the nag message

Due to the fact, that I would like to make a detailed tutorial, that is easy to follow for beginners, I divided it in 2 parts. This one is dedicated to removing Nag screen. In the next part, I will describe keygenning. If you wish to see explanation of obfuscation techniques only, you can go directly to the end of the article.

First, I loaded the KeygenMe into ImmunityDebugger:


and searched for “All referenced text strings” (left mouse button click, then: Search for -> All referenced text strings)

It could possibly show as the place, where the “Success” message is displayed. But, no luck this time – no such thing is on the list. Taking a closer look, we notice, that neither the Nag message is present. For sure they are encrypted/obfuscated somehow!

By the way, I spotted some other interesting strings: ntdll.dll, wcsnmp, memset, wscsncpy, comctl32.dll, InitCommonControls, SeDebugPrivilege. It seems, the application loads some functions dynamically. We will not go in details right now, but lets keep it in mind.

Let’s first focus on finding the place, where the Nag and SuccessMsg are stored.

IDA graphs are going to be helpful, so I loaded the same file into IDA.

Graph of the start function has a weird shape, which suggest that some of the transitions between code chunks are obfuscated.

Also, we can see there some long block of code which seems to load some obfuscated string (WindowName). It moves a lot of bytes into consecutive variables (probably forming some buffer). Then, it calls a function and performs some decoding with its help – I given to the called function a name _decode


The body of _decode function is simple. However, it also contains some obfuscation – by breaking the standards of argument passing. Usually, the ECX register is used as a counter, the ESI – as a source and the EDI as a destination. But not here, as you can see…


Let’s see Xrefs to this function – maybe it will help in finding the Nag and the “Success” message:


As we see it is called from 3 functions:

  • start – as we see above, it decodes WindowName
  • sub_401856
  • sub_402551

Let’s take a closer look at the 2 remaining calls.

sub_401856: Function _decode is called in several places. However, it decodes not strings, but some small (max 5-bytes long) chunks…

Anyways this function looks interesting. It refers to function like GetDlgItemTextW. It seems to be fetching user input and then processing it. Accidentally, we found the key verification function! So, from now I will refer sub_401856 as KeyVerification.


We are searching for the strings, so let’s not go in details of this function for now and just move on…

sub_402551: looks like a much more promising candidate for the function responsible for displaying the Nag or the Success, because:

  • It has two paths of execution, one of them is decoding some lengthy buffer (probably a string)
  • it refers to MessageBoxW


It is referred directly from the start function – so, it seems to be the Nag rather than the Success.


As we can see, in this case a reference doesn’t mean a direct call. The address is pushed on the stack. After that, some other addresses are also pushed, and then there is RET instruction. It is some more complex version of calling a function via PUSH-to-RET. The function that is called from here is in reality sub_4013B0:


The function takes 2 parameters. It above case, one is the address 4029F6 and the other is the handle to CreateWindowExW.  The address of our interest: sub_402551 (the potential Nag) is not among them. To see how it is called, we must first understand how the sub_4013B0 works.

Inside we see the heap allocation and changing access rights to allocated space. Seems, some code is going to be written.

Also, two functions are called by the handlers, that are dynamically filled somewhere else:

  • dword_40300C
  • dword_403000

Some dynamic analysis will be required (and I prefer doing it in ImmunityDebugger). So I loaded it in Immunity, set breakpoint at 4013B0 and run. The mysterious handlers got filled, and now we see that

  • dword_40300C -> ntdll.memcpy
  • dword_403000 -> ntdll.memset

Let’s see what they are used for. Here is dump of parameters:

|dest = 001F7268
|src = USER32.CreateWindowExW
\n = 5

|s = USER32.CreateWindowExW
|c = E9 -> JMP
\n = 1

|dest = USER32.7685EC7D
|src = 0012FEB0
\n = 4

|s = 001F726D
|c = E9 -> JMP
\n = 1

|dest = 001F726E
|src = 0012FEB0
\n = 4

Do you see the “code stealing”? First, 5 bytes from the beginning of CreateWindowExW is copied into allocated space. It is overwritten by the jump (E9 = opcode of JMP) to the address, stored on the stack (at 12FEB0). Then, another JMP is written – this time into the allocated space. Let’s have a look how the CreateWindowExW looks after the modification:


Do you recognize the address of the jump? It’s sub_402551(the potential Nag)! So, finally we found how it is called – whenever CreateWindowExW is called, it jumps to our procedure first.

Let’s see what is written into the allocated space:

001F7268 | MOV EDI,EDI
001F726A | PUSH EBP
001F726B | MOV EBP,ESP
001F726D | JMP USER32.7685EC81

It is the stolen prolog and redirection to the rest of the code of CreateWindowExW. Ok, now we know, that original CreateWindowExW can be now called by referring to the address 0x1F7268.

Coming back to the 0x4013B0 (from now I will refer is as: StealCode), I also set the breakpoint on the return and follow where the navigation goes next:

004029F6  | MOV DWORD PTR SS:[EBP-34],EAX
004029F9  | MOV EAX,DWORD PTR SS:[EBP-34]
004029FC  | MOV DWORD PTR DS:[403018],EAX
00402A01  | PUSH KeygenMe.004017BE
00402A06  | PUSH DWORD PTR DS:[&CharUpperW]
00402A0C  | PUSH KeygenMe.00402A17
00402A11  | PUSH KeygenMe.004013B0; StealCode
00402A16  | RETN

Another “code stealing” is performed. Now the affected procedure is CharUpperW. I don’t think it is necessary to go again in details, just set a breakpoint at the end of StealCode and see what was overwritten:

At the beginning of CharUpperW the redirection has been placed:

7686E981 | JMP KeygenMe.004017BE

The procedure at 0x4017BE takes as a parameter a  buffer of characters. It contains a big switch, which is substituting some characters by the others, following a defined pattern. I named it MixCharacters. So now we know, that CharUpperW leads to calling MixCharacters.

After the second “code stealing”, navigation goes to:

00402A17 | MOV DWORD PTR SS:[EBP-38],EAX

– in the start function. It leads to the chunk that is decoding WindowName, which we already saw in the previous part of the research.


Then, the CreateWindowEx is called (as we found previously). Since it is hooked, the call leads to execution of sub_402551 (the potential Nag). Then, the message dispatching loop is executed.

Let’s  set a breakpoint at call CreateWindowExW. The redirection occurs and the parameters are passed to the function at 0x402551. Now we can notice, that the parameter deciding which path should be followed, is in reality the WindowWidth*!

* to be explicit: CreateWindow/CreateWindowEx creates not only windows, but a variety of controls, such as labels, buttons, etc.


To sum up:

  • if the control width is different than 0x78 = 120 -> Execute original CreateWindowEx.


  • if the window width == 120 -> Execute the Nag.

Knowing this, we can remove a nag window by simply patching this check (at 0x40255E), and changing the conditional jump into the unconditional.

Obfuscation and defensive techniques

The thing I enjoyed the most in this KeygenMe, was its obfuscation and set of defensive techniques (similar techniques are used by malware, that’s why AV systems picked on this).

I noted following:

  • dynamically loaded imports

(Handles to some functions were dynamically loaded and stored in variables. Then, calls were made via this variables  – to make static analysis more difficult)

Loading functions:


Using loaded function (example):


  • self-modifying code

Inside the key verification function we can find some points of code, that ends with Infinite loops (opcodes: EB FE)


It would not make any sense… However, during program execution, those instructions are overwritten, and they are replaced by the jumps to the continuation of this code!

  • calling functions in non-obvious way, i.e. complex “push-retn”KMv7_call_nag
  • “stolen code”, import hooking
    • beginning of the imported function is stolen (removed, and copied inside the KeygenMe space)
    • it is substituted by the call to “a proxy function” (belonging to a KeygenMe)
    • original code of imported function is called after the “proxy function” finished execution

Execution flow:

Let’s see all this elements on the example of the KeygemMe:
User32.dll -> CharUpperW (original):


User32.dll -> CharUpperW (modified):


stolen code + jump to rest of the function:


Function inside the KeygenMe, calling the stolen code via saved pointer (refered on the graph as: Application.myFunction ):


  • time-based anti-stepping protection:

RDTSC is called and it’s result is saved in a variable. In another place, RDTSC is called second time, and the result is compared with the saved one.

If the time difference is > 70000h, the process of generating key is distorted:

About hasherezade

Programmer and researcher, interested in InfoSec.
This entry was posted in CrackMe. Bookmark the permalink.

8 Responses to Solving KeygenMe V7 by MaxX0r – part 1

  1. Doni says:

    I would love to follow this guide; however, some images are not showing. Can you please revive them?

  2. Stingered says:

    And the ZIP file is not longer available, which is on this site???

  3. Stingered says:

    Brilliant, but annoying! There is no part two. I assume you get bored easily.

  4. Arlequim says:

    Congratz, you got great reversing skills 😉

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s