[CRACK] CEdit serial fishing, with an interesting discovery

The target is CEdit v1.12, from the UIC. I don’t really know what the program does.

Opening Nag, we are about to violate international copyright law.

After opening the program we are greeted with a nag that asks to register. We can press on and see that the program opens. By opening ? > Register CEdit we find the registration form.

img2 Classic

We write something random and it doesn’t register (sigh). Time to launch our debugger. Today we are on Windows 10 and we are feeling modern so we use x86dbg.

img3 Welcome home

Strings searching and an uncanny discovery

What can we do? There are several approaches. The simplest one is starting from the strings list and finding the one that writes the bad registration text for the nag screen, in this case, “Sorry, this registration code is invalid”. And there it is. If you are like me you have probably searched for the word “registration”, trying to find all the strings related to the registration process. This can help also to find the area of the code where you want to make the execution go, instead of just finding where you don’t want to go. With this simple search, We can already see something interesting…arguably more interesting than this whole register the program business.

PROTIP: If you don’t find the strings, you are probably searching in the wrong .dll, pause the program and F11 until you pop in the [cedit.exe] executable, then do the search.

img4 How does it know??

Instead of using a crack code, register your copy of CEdit!

That’s a direct call out to us. Does this program have a way to see if it is being cracked? How can it be? We will explore that later in the bonus part, don’t worry.

Let’s get back to cracking.

Serial Fishing & Keygen

PROTIP: A little bit of glossary. “Nags” are the pop-ups that inform you that a program is not registered, Bad/Good boys are the jumps that decide if the program considers you registered or not.

If we put the breakpoints in all three possible appearances of the string and try to register, we can see that only the one on 0047495F pops, we can remove the others. Now we want to know how we arrive there. When the nag message is printed is already too late, probably the choice has been made long before and is where we want to be.

So let’s go back with the breakpoints and insert the same random credentials. x86dbg is very helpful in this regard, as you can see the “zone” of the code in which we are is completely skipped by a jmp (at line 00475943) so for sure we have to jump in the zone between 00475945 and where we have the breakpoint. Try to think of ways to find where the jump lands (or at least to find a way to reduce the possibilities).

img5 The important choices are all made in this area, be sure to understand well what is happening

As you can see from the image, if you go up enough you find the jump that we want relatively close. Now, x86dbg is probably too helpful there. You can see in my case a really suspicious “47474117417” in the annotation/comments area. Casually, that is the key for my random user “aaaa111”. There is no fun in inserting it, so let’s keep the crack going and try to understand as much as possible about this program. Note: “111111” you can probably guess is my random key. Did you see anything else of interest in this section? “Thank you for registering!”. This means we could have found this zone by following this string too. Now, with a bit of naivete you could ask why we can just change the jump and enter the “good zone”, let’s try it!

PROTIP: Change the flag value on the right without having to touch the code!

img6 Yay?

Try to check under ? > About CEdit… what it says. Unregistered. We just made the program print the right message, but behind it, it knows that we are not registered! We need another way, so let’s keep going back.

We know something more than before this time. We know that this jmp that we are not taking by manually changing the flag, has to be skipped and so the previous comparison must be true (because the jump is a jne, jump if not equal). Strangely, there is not a comparison but a call to a function as the previous instruction. Remember that jne just checks the zero flag, and that can be set that whoever, even by us manually. What I’m trying to say is that even if there isn’t a cmp opcode before the jne as is customary, there will be probably an opcode that sets the zero flag in the function call. Let’s try to see what there is inside it.

img7 Lot’s of jumps…mmmm

Take a step back, thinking logically, what do we expect this function to do? It will probably do a check with the key/username and if it is deemed correct, will set the 0 flag. What we can see in this big function is a series of jumps more or less in the same zone, that in one way or another arrive at a “ret” opcode at line 00403CD9. There are no jumps out of this range and no function calls. We can guess that at least here is the core of the equality checking between our key and the correct key. Let’s analyze the first few opcodes:

00403C38 | 53                       | push ebx                                | ebx:"<VG"
00403C39 | 56                       | push esi                                | esi:"41747411741417"
00403C3A | 57                       | push edi                                | edi:"111111"
00403C3B | 89C6                     | mov esi,eax                             | esi:"41747411741417", eax:"41747411741417"
00403C3D | 89D7                     | mov edi,edx                             | edi:"111111", edx:"111111"
00403C3F | 39D0                     | cmp eax,edx                             | eax:"41747411741417", edx:"111111"
00403C41 | 0F84 8F000000            | je cdedit.403CD6                        | unico check importante per la crack, si no (e checka molto altro)

The function is saving the registers ebx, esi, and edi and loading esi and edi with a “strange” number (we don’t know the key yet) and the key we inserted. What happens then is grotesque. A comparison and a jump…to the end of the function. As you can see in the last instruction before the ret, the three registers are restored, and the function returns. What did it just happen? That compare will set the zero flag, which means that if that compare gives True, the zero flag is set to 1, and if the 1 flag is set to 1 and no other instruction changes it between the return and the check we care about, we are golden! And as we discussed before, after the function call there is the jne so we are sure that we go to the right place. This means that having the value loaded in esi equal to what we insert in the key form is the only thing we care about.

I don’t know what the function does, but if you want you are free to investigate. I have my theories about it, and they point to a waste of time, so I’ll skip it for now (and you should decide at the end of this tutorial if try your luck with it ;)).

Fishing the key

Let’s take a step back and exit the function. We know that the check is done by comparing eax and edx. edx is our password, so we need to find what is in eax and who puts things in it. Without going too much back, we can see how there are 2 function calls and a mov made on eax. Break on all of them one at a time and go back. Remember that we already know with what our inserted password is being compared, in other words, we know what is the key, so we just keep an eye out for where it appears.

img8 Area we want to break on

Breaking at 004758D4 we can see how stepping over the function changes ebp-4 from our username to our inserted password. Our eax is not touched, so we go back for another try. NOTE: eax is popped just before the check, so what we want to be in eax should be the last push before the check.

The mov instruction moves something else in our eax so we don’t care about it. You can see how the stack is already set up with our key on the top. Let’s go back to the other function call. Already breaking on it we can see that from x86dbg all references in the comment about the key disappeared, that’s probably because the key is created in this function, you can check by stepping over it and finding out. Also, the following few instructions set up the stack for the pop. Let’s investigate the function.

img9 Key generation, not from the first instruction

We see how first some data is loaded onto the stack and then our username is loaded onto eax. Now we can see some sort of a loop, with a simple check (cmp eax,9). This means that those instructions will be repeated until something exceeds 9. Stepping slowly with F8 we can see how our name is repeated until it has more than 9 char (if you put a user with more than 9 chars you won’t notice anything). Let’s go on.

img10 The meat of the key generation function

From 00473C04 the core part of the key generation starts. Let’s go step by step to understand what is happening. I’ll show what happens with what I usually put for username and password, meaning aaaa and 1111 respectively. The choice of them is not completely random. I use a repeated pattern of simple numbers because it can help me see things in the code at first glance. For example, I know “a” in hex is 61, so if I see somewhere 61616161 it might mean that my username was loaded there. It is repeated because it makes it easier to see if in a keygen there is a relation between each password digit (for example, the first digit of the username might be sum to the second one and make the first digit of the final key). But let’s see what happens in our program.

Trying some inputs.

Starting from 00473C04 let’s go step by step with F8. x86dbg helpfully tells us that “145” has been pushed into the stack after the function call. After that it jumps and loads 145 to eax, then it calls another function and checks if its return is equal to 9. It’s not so we go back up top where we left off before the first jump (at 00473C0B). Here we have a series of instructions 00473C60 in which something strange (don’t worry, we will see it later) happens. In particular, in the loop, you can see that the number 145 is consumed. Each number is read and removed from where it was loaded (the ebx register). Then after the cycle is reloaded in edx (with the lea instruction) and after to eax. Then there is a bit of magic with the stack, but what we can see is that “17” is loaded at ebp-10 by one of the function calls, and then reloaded. Shortly after we have a conditional jump and we restart the cycle. One thing is important here, we can see that there were a lot of increments in this sequence of opcodes. It is a loop so it’s not so strange but let’s get backs and think about what the key generator wants to do: reading a username and creating a key in some way. So, it will have to cycle on the username characters! And if we scroll around in the stack we can see that our username and its version lengthen by previous instruction. You should try to insert usernames with all different characters to understand what is happening (everything has its use!).

Example of abcd used as input.

While we are here we can see how the numbers loaded change. 145 is the same and we can infer that this number corresponds to “a”. But in the stack is saved as 10 instead of 17. We should do some more tests we different usernames. For now, we know that a is closely related to 145, but that “abcd” gives 10 while “aaaa” gives 17. I urge you to do your tests and come up with an explanation. Tip: try shorter usernames like aa, bb, ab, or different usernames with some pattern that can make you discover patterns in the key generation, like abbbbbb, aabb, ca, ba, and so on.

I’ll give you some answers and then I’ll explain I personally got them. Not that especially if you are a beginner, at this point you should experiment with different algorithms to understand what happens. You have everything you need and it is just a question of being exposed to the procedure with various usernames plus a bit of intuition.

What happens is that while stepping through the username characters, this is what the program is doing:

length_remaining*6 + ASCII_char => gives a hex that has to be converted to decimal. Let’s see how we could discover that:

  1. We can see how changing the username by one letter to the number that appears at 00473C09 changes by one number. I can infer that probably something is added to the letter (let’s suppose in ascii format). So something like X+ASCII_value.
  2. The final number that appears at ebp-14 changes by one and after some tests we can see that is a lower number and with some tests (or by understanding the loop at 00473C36 and 00473C5B) we can understand that the final number is the sum of the digits of the first. NOTE: with some more tests you can see that the final number is always a single digit, so we have to sum the digits of the obtained number until it’s just one.

So now we just have to understand how to get the initial number. First, we have to understand if the number changes based on the position or based on what is before or after. Few tests show us that it changes based on position and not based on what is around it. With these tests, we can also find out that the sequence of numbers summed to the ASCII values (multiples of 3, reversed). Putting everything together, we can easily note that the value obtained is length_remaining*3.

img13 Simple keygen in python written in simple terms to be easily understandable

Cracker Message

We cracked the program, that’s good, but what was about that message at the beginning? Let’s go back to string searching and go where the “Instead of using…” message to crackers is loaded (00473E49). frenzy[C4A]? Spider]PC98? the doctor? Who are they?

img14 1337 namez

So it seems that when a name is read, the message is loaded and printed. The people that wrote this implemented a rudimental check to stop crackers. The check is done based on the name inserted (as far as we know). We can infer that someone posted their login somewhere and the programmers of CEdit caught them and hardcoded their names to block the license even if the key is correct. Interestingly, these procedures made sure that their cracker name would be remembered forever in the program code and we, 20 years later, can still know of their feat. Also, we now are crackers too! 20 years later, we can consider them, with a little bit of arrogance ;), as our peers.

We should try to log in with their name to see if for real it works like we said: First, we try with one of the cracker names plus a random password. The answer is the wrong password. This means that the check is done at least after the correct password for that name is identified

We can use our keygen now. Computer the correct key for the name we have chosen and see the answer…and it works. What? If you have put the breakpoints in the right positions and carefully stepped through the program, you would have seen that the check is done just at the beginning (when the program is started) and not when the key is checked. So you think you have registered the software, but you didn’t. Restart CDEdit and see if that’s true.

Indeed.

Thanks for reading! If you want more check out the rest of the blog and keep exploring the software you crack (try to check register keys;)).

Greets

Quequero :: Stefano Falda :: the doctor :: Spider]PC98 :: frenzy [C4A]