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 (also available on

KMv7_trialAuthor sets several achievement levels (see full list).


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

* create a working keygen
* remove the nag screen

KeyGen source:

+ the version with Nag removed:



Tutorial 1 – removing nag screen

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 now to IDA.

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

Also, there is some long piece of code on the stage, which seems to load some obfuscated string (WindowName). It moves a lot of bytes into consecutive variables (probably forming some buffer) . Then calls a function and performs some decoding with it’s help- I given to the called function a name _decode


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


Let’s see Xrefs to this function – maybe it will help in finding Nag and SuccessMessage:


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 some small (max 5-bytes long) chunks… But we are searching for the strings, so let’s skip it for now… (anyways this function looks interesting. It refers to function like GetDlgItemTextW – 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)


Let’s move on:

sub_402551: looks much more promising. It have two paths of execution, one of them is decoding some lengthy buffer (probably a string) + refer to MessageBoxW.


It is referred from the start  – so, seems to be the Nag rather than the SuccessMsg.


As we see, in this case the 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. It is some complex version of calling 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 dynamicaly 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 overwriten by the jump (E9 = opcode of JMP) to the address, stored on the stack (at 12FEB0). Then, another JMP is writen – 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   8BFF             MOV EDI,EDI
001F726A   55               PUSH EBP
001F726B   8BEC             MOV EBP,ESP
001F726D   E9 0F7A6676      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 refering to the address 1F7268.

Coming back to the 4013B0 (from now I will refer is as: StealCode)

I also set the breakpoint on the return, and follow where the navigation goes next:

004029FC  MOV DWORD PTR DS:[403018],EAX
00402A01  PUSH KeygenMe.004017BE
00402A06  PUSH DWORD PTR DS:[<&USER32.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 4017BE takes as a parameter a  buffer of characters. It contains a big switch, which is substituting some characters by the others, by the defined pattern. I named it MixCharacters. So now we know, that CharUpperW leads to calling MixCharacters.

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


(in the procedure named start by IDA). 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 – it is hooked, and leads to execution of sub_402551(the potential Nag). And then the message dispatching loop is executed.

Let’s  set a breakpoint at call CreateWindowExW. It’s parameters are redirected to 402551. Now we can notice, that the parameter, deciding which path should be followed, is in reality the WindowWidth*!

* to be explicit: CreateWindow creates not Windows only, but a variety of controls, like 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 40255E), and changing conditional jump to unconditional.

Obfuscation and defensive technics

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, this places are modified, and they are converted to jumps to the continuation of the 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.

3 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?

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 )

Google+ photo

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

Connecting to %s