Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


KeygenMe ?

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming -> Crackmes
View previous topic :: View next topic  
Author Message
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Sun Oct 21, 2007 4:41 pm    Post subject: KeygenMe ? Reply with quote

I found this on another forum. Is it even a keygen me ?



There seems to be a conditional jump (JNZ) to say whether the serial is correct or not but whatever you input seems to have no bearing since just before that there is a TEST AL,AL. Doesn't that mean the Z-flag is set no matter what and so the jump will never be taken ?

Been bothering me all day now Sad

I only started reversing a week or two ago so it's quite possible there's something that I don't know about which explains this but I can't see any jumps straight to the good message either so I don't see how code execution could possibly get there.
Back to top
View user's profile Send private message
opcode0x90
Cheater
Reputation: 0

Joined: 05 Aug 2006
Posts: 27

PostPosted: Sun Oct 21, 2007 9:45 pm    Post subject: Reply with quote

Code:

004010BA   |.  A2 8E324000       MOV BYTE PTR DS:[40328E],AL
004010BF   |.  6A 5A             PUSH 5A                                    ; /Count = 5A (90.)
004010C1   |.  68 DA314000       PUSH cim_crac.004031DA                     ; |Buffer = cim_crac.004031DA
004010C6   |.  6A 65             PUSH 65                                    ; |ControlID = 65 (101.)
004010C8   |.  FF75 08           PUSH [ARG.1]                               ; |hWnd = 7FFDF000
004010CB   |.  E8 34010000       CALL <JMP.&user32.GetDlgItemTextA>         ; \GetDlgItemTextA
[b]004010D0   |.  E8 7A000000       CALL cim_crac.0040114F[/b]
004010D5   |.  84C0              TEST AL,AL
004010D7   |.  75 16             JNZ SHORT cim_crac.004010EF
004010D9   |.  6A 40             PUSH 40                                    ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL


Did you notice there's a call right after GetDlgItemTextA? Thats the verify_serial function.
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Mon Oct 22, 2007 12:08 am    Post subject: Reply with quote

Just fiddled with this for a few minutes, working key:

Name: test
Pass: Mx-3917C312

How to make a keygen? Not the slightest clue, I'm new to cracking and shit as well so yea >.> I'm surprised I got this far Very Happy

EDIT::

Just to toss this up there too, this is the algo where the key is checked:

Code:
0040114F  /$  A0 8E324000   MOV AL,BYTE PTR DS:[40328E]
00401154  |.  8BC8          MOV ECX,EAX
00401156  |.  33DB          XOR EBX,EBX
00401158  |.  33C0          XOR EAX,EAX
0040115A  |.  BA 5F000000   MOV EDX,5F
0040115F  |>  8A81 7F314000 /MOV AL,BYTE PTR DS:[ECX+40317F]
00401165  |.  03D8          |ADD EBX,EAX
00401167  |.  0FAFDA        |IMUL EBX,EDX
0040116A  |.  49            |DEC ECX
0040116B  |.^ 75 F2         \JNZ SHORT cim_crac.0040115F
0040116D  |.  33D2          XOR EDX,EDX
0040116F  |.  B9 33000000   MOV ECX,33
00401174  |.  8BC3          MOV EAX,EBX
00401176  |.  F7F9          IDIV ECX
00401178  |.  8A8A 46304000 MOV CL,BYTE PTR DS:[EDX+403046]
0040117E  |.  3A0D DA314000 CMP CL,BYTE PTR DS:[4031DA]
00401184  |.  74 06         JE SHORT cim_crac.0040118C
00401186  |.  33C0          XOR EAX,EAX
00401188  |.  F7D0          NOT EAX
0040118A  |.  EB 65         JMP SHORT cim_crac.004011F1
0040118C  |>  33D2          XOR EDX,EDX
0040118E  |.  B9 33000000   MOV ECX,33
00401193  |.  8BC3          MOV EAX,EBX
00401195  |.  35 4D694300   XOR EAX,43694D
0040119A  |.  F7F9          IDIV ECX
0040119C  |.  8A8A 46304000 MOV CL,BYTE PTR DS:[EDX+403046]
004011A2  |.  8035 DB314000>XOR BYTE PTR DS:[4031DB],20
004011A9  |.  380D DB314000 CMP BYTE PTR DS:[4031DB],CL
004011AF  |.  74 06         JE SHORT cim_crac.004011B7
004011B1  |.  33C0          XOR EAX,EAX
004011B3  |.  F7D0          NOT EAX
004011B5  |.  EB 3A         JMP SHORT cim_crac.004011F1
004011B7  |>  8035 DC314000>XOR BYTE PTR DS:[4031DC],2D
004011BE  |.  74 06         JE SHORT cim_crac.004011C6
004011C0  |.  33C0          XOR EAX,EAX
004011C2  |.  F7D0          NOT EAX
004011C4  |.  EB 2B         JMP SHORT cim_crac.004011F1
004011C6  |>  8BC3          MOV EAX,EBX
004011C8  |.  50            PUSH EAX                                 ; /<%0.8X>
004011C9  |.  68 14304000   PUSH cim_crac.00403014                   ; |Format = "%0.8X"
004011CE  |.  68 34324000   PUSH cim_crac.00403234                   ; |s = cim_crac.00403234
004011D3  |.  E8 1A000000   CALL <JMP.&user32.wsprintfA>             ; \wsprintfA
004011D8  |.  83C4 0C       ADD ESP,0C
004011DB  |.  8D35 34324000 LEA ESI,DWORD PTR DS:[403234]
004011E1  |.  8D3D DA314000 LEA EDI,DWORD PTR DS:[4031DA]
004011E7  |.  83C7 03       ADD EDI,3
004011EA  |.  56            PUSH ESI                                 ; /String2 => "3917C312"
004011EB  |.  57            PUSH EDI                                 ; |String1 => "3917C312"
004011EC  |.  E8 3D000000   CALL <JMP.&kernel32.lstrcmp>             ; \lstrcmpA
004011F1  \>  C3            RETN

Back to top
View user's profile Send private message Visit poster's website
killersamurai
Expert Cheater
Reputation: 0

Joined: 10 Sep 2007
Posts: 197
Location: Colorado

PostPosted: Mon Oct 22, 2007 2:26 am    Post subject: Reply with quote

This is how the serial is calculated
Code:

total = 0
last char in user name add to total
multiply total by 5f
repeat till end of user name

divide by 33
use remainder to get the char value
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

compare with first value of serial

xor total with 43694d
divide by 33
use remainder to get the char value
xor 20 (change to upper case or lower case)
compare with second value of serial

append -
append total X8



To code a keygen, it will look something like this. (Done in c#)
Code:

if (txtUser.Text.Length < 3)
            {
                MessageBox.Show("User name needs to be greater than 2");
            }
            else
            {
                uint total = 0;
                uint remainder = 0;
                string cKey = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
                char[] cTemp = txtUser.Text.ToCharArray();
                string sSerial = "";

                for (int i = cTemp.Length - 1; i >= 0; i--)
                {
                    total += cTemp[i];
                    total *= 0x5f;
                }

                remainder = total % 0x33;
                sSerial += cKey[(int)remainder];
                remainder = (total ^ 0x43694d) % 0x33;
                sSerial += (char)(cKey[(int)remainder] ^ 0x20);
                sSerial += '-';
                sSerial += total.ToString("X8");

               
               
                txtSerial.Text = sSerial;
            }


Keygen attached
Back to top
View user's profile Send private message
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Mon Oct 22, 2007 2:39 am    Post subject: Reply with quote

Thank guys.

I actually saw that algorithm before but I still don't understand why it works when you put in the correct key. Doesn't "TEST AL,AL" set the Z-flag unconditionally ? So all previous settings of the Z-flag are ignored ?

So now I look a little deeper, I see the two calls to the GetDlgItemTextA (at 004010AB and 004010D0) which read the contents of the textboxes the username and password was entered into. My question is how do you know where those contents are then stored ? I tried stepping into the calls but didn't get too much out of that.

Then the algorithm includes a load of mathematical juggling which is just up my street. It then seems clear the username and password change into two strings, "String1" and "String2" where "String2" is the username and "String1" the password.

So I started trying to interpret the algorithm bit by bit. And so:
MOV AL,BYTE PTR DS:[40328E]

So that means moving the byte held with the value within 40328E. Looking at where that is, it immediately says:
ADD AL,0

So that makes me assume that MOV can be ignored. Then that instruction changes when I scroll a little.. To:
ADD BYTE PTR DS:[EAX+EAX],AL

Imma just like wtf. Well if I input the Name and Pass Wiccaan posted, EAX happens to be 0000000B. So 40328E should hold the value 1A.

I'm way confused now. Is there some unwritten rule which allows instructions to randomly change ?

A little later, I notice xor is used to make make EBX and EAX 0 after moving EAX into ECX. Then 5F is moved into EDX.

Then we get the little loop. Each letter of the username is moved into AL (from the last letter to the first, it moved T then S then E then T). EAX is then added to EBX. EAX will hold the value of AL since it was xor'red earlier. Therefore EAX holds each letter. EBX holds zero since it was xor'red earlier too. Then EDX is multiplied by EBX and the result stored in EBX. Then ECX is decremented. ECX is 4 because from earlier EAX was moved into it. But because I don't understand how the instruction at 0040114F works, I couldn't get any further.

I'd really like some help with this. Although I only started reversing very, very recently I've started this one out of frustration since someone on GzN patched the JNZ to a JZ or JMP and claims that this meant he had solved the serial-me.

Actually I'd be very happy to just understand the algorithm and possibly manually find a solution to a given username.

So any help on this at all would be very much appreciated, not necessarily the answer but hints would be awesome.

Btw Wiccaan, how did you get that working combination ?

//edit : just read killer's post. I should be able to manually get a password for whatever username I want. Only problem is, I only know how to use xor as a bitwise operator not with a word. How's that done ? Just convert to ASCII then xor ? Btw if you could answer the other questions that I got stuck at that would be great too.

Also let's say I try to put Slugsnack as the username. So total starts as 0. Then changes to k. how do I multiply that by 5f ?
Back to top
View user's profile Send private message
killersamurai
Expert Cheater
Reputation: 0

Joined: 10 Sep 2007
Posts: 197
Location: Colorado

PostPosted: Mon Oct 22, 2007 12:08 pm    Post subject: Reply with quote

Slugsnack wrote:

Also let's say I try to put Slugsnack as the username. So total starts as 0. Then changes to k. how do I multiply that by 5f ?


Each character has a number to it. The character k is 107 in decimal and 6b in hex.
Back to top
View user's profile Send private message
Slugsnack
Grandmaster Cheater Supreme
Reputation: 71

Joined: 24 Jan 2007
Posts: 1857

PostPosted: Mon Oct 22, 2007 12:30 pm    Post subject: Reply with quote

Ahh yes, the ASCII table. x0r explained to me earlier along with the other bits that I wasn't getting :p Well thanks for all your help everyone.

I don't know enough programming to make a keygen yet so I'm going to manually make a password for my name just to see if I understand it fully yet.

And of course I was making some stupid mistakes earlier, thinking TEST was the same as CMP although strictly that wasn't my fault (you know who you are), looking in the disassembly instead of the dump window (my bad Very Happy).

Well I'll keep you posted, I should have a solution pretty soon if I get the time.

Thanks again !

//edit : Finished looking at it last night and made a "guide" on how to solve it for some people back at GzN. Probably will help less people here (the people that read it will most likely already know this :p). Anyway here it is:

Okay had a look at this last night and with some help from x0r, I can manually make a serial number from any valid username. If you look at the good message that we want to jump to, you should see that there is a JNZ just before it. Now we don't want to take this jump because it leads to the bad message. Therefore the line before this jump must result in a zero.



How can the result of "TEST AL,AL" be zero ? Well the TEST opcode basically performs the AND operation on both operands. So you should be able to see the result will only be 0 when AL is 0. So that is our aim, that by the end of the call before this instruction, AL is set to 0.

Let's first run the program through once and see what it does:



We get this screen where we have to enter a name and a serial. So I entered the following information the first time round:

Code:
Name : Slugsnack
Serial : Password




[color=blue]Oops. So we get that message. Then I looked for that text string and double-click the relevant line to go to the push in the disassembly:








In case you didn't know, what that API was for, right-click it the call and click "Help on symbolic name" (requires API help files to be installed):[/color]





Pretty self-explanatory. This is where the bad message box is created and displayed. If we scroll up a little bit, Olly tells us why this call is triggered:



Back to that JNZ we were talking about before. In fact if you scroll up a little bit more, we can start from somewhere we are interested in:



There are two calls to a certain API called GetDlgItemTextA. So we right-click again to find out more about it:



So the API fetches the string from a field in a form (retrieves text associated with a control in a dialog box). Let's look at the first call to this API first.

Code:
Count = 5A (90.)


And the API help file tells us if we exceed that character count (90 in decimal), the string is truncated (extra characters are chopped off).

Code:
Buffer = cim_crac.00403180


This parameter is a little more interesting. It tells us the buffer address that the string is moved into. Let's just follow that in the dump window at the bottom to check:





So the first call to GetDlgItemTextA fetches the contents of the first (username) box.

The rest isn't too relevant at the moment.

Looking at the next few lines:




EAX holds the number of characters copied to the buffer:



Code:
CMP EAX,3


This checks that EAX (number of characters you entered into username) is 3 or more.

Code:
JL SHORT cim_crac.00401105


If EAX is less than 3 then the jump is taken to:



where there is a call to SetDlgItemTextA to set "Put more than 2 chars ...." into the second field (password).

Since Slugsnack is 9 characters long, we don't have to worry about this jump. And we continue to the next line:


Code:
CMP EAX,1E


EAX (number of characters moved to buffer) is again compared but this time with 1E which in decimal is 30). So now we are checking whether EAX is larger/smaller/equal to 30.

Code:
JG SHORT cim_crac.00401105


If EAX is greater than 1Eh then we jump to 00401116. Following that in the disassembly:



where there is a call to SetDlgItemTextA again to set "Too long Name..." into the second field (password) if our username exceeds 30 characters.

Again we don't have to worry about this.


Code:
MOV BYTE PTR DS:[40328E],AL


If we follow 40328E in the dump window we can see that AL (number of characters moved to buffer) has been copied to 40328E:



And now we get onto the second GetDlgItemTextA call:



Same parameters as before except this time there is a different ControlID (because it's a different field) and there is also a different buffer (place where string is moved). So if we follow 004031DA in the dump window we should see the serial we inputted:



Unlike with the username, there is no check on the serial length except that it must not exceed 90 characters. Password is only 8 characters so that's fine.



Now we come onto the call just before the "TEST AL,AL". We want to step into this call because we know that somewhere in there, AL is set to 0 or not 0. So we set a breakpoint there with F2 or double-clicking the line in the hex dump.

We now restart the application and enter the same information as before. This time, Olly should break at the breakpoint:




We then step into the call with F7 and this is the entire algorithm for the serial check:



It looks quite daunting at the start but actually when you break it all down, it's just simple maths. Let's look at these 5 lines first:



Code:
MOV AL,BYTE PTR DS:[40328E]


If you still remember from before, AL was previously copied to 40328E. So 40328E stores how many characters were copied to the buffer from the username field (9).

Code:
MOV ECX,EAX


So now that value is moved back into ECX so ECX will hold the number of characters copied to the buffer.

Code:
XOR EBX,EBX
XOR EAX,EAX


When you use the eXclusive OR operator on two operands that are the same, the result will be zero since every corresponding bit will be the same so no pair will have just one set bit. So EAX and EBX are now set to 0.

Code:
MOV EDX,5F


5F is moved into EDX (seems like it's being set up for some sort of algorithm later).

Now check out the next four lines:




Code:
MOV AL,BYTE PTR DS:[ECX+40317F]


If you still remember, ECX currently holds the number of characters of the username. And if you remember from before, 403180 was where the string we entered into the username field was stored. So what we adding the number of characters of the username to the where the string is minus one character, we get the last letter of the username. Last character of slugsnack is "k" which in hex (reading off an ASCII table) is 6B.

Code:
ADD EBX,EAX


EBX was xor'red earlier so it is 0. EAX holds "k" (6Bh) after the last instruction. So adding the values of the two registers results in 6B which is moved to the destination operand (EBX).



Code:
IMUL EBX,EDX


This is a signed multiplication of the values held at EBX and EDX. The result is then stored in EBX. So EBX is 6B and EDX is 5F (set earlier).

Code:
6B*5F = 27B5


So now EBX holds the value of the hex value of the last letter of the username multiplied by the constant 5F:



Code:
DEC ECX
JNZ SHORT cim_crack.0040115F


ECX is decremented then if it is not zero, the four instructions we just went through are looped. I hope you can see that ECX is acting as a counter for when the loop finishes.

Now that ECX has decreased by one, the initial instruction in this loop moves the second from last letter of the username into AL to be used in calculations instead (c) so we can see each character of the username is moved from back to front into this algorithm. I won't go through each character in detail but these are all the register values after the final S has been moved in:




EAX = 53 (ASCII hex for capital S of Slugsnack)
ECX = 0 (Decremented until zero to set Z-flag to not take the jump to loop again)
EDX = 5F (has stayed constant)
EBX = BB5E8DBD (result of a lot of adding and multiplying)

Looking at the next chunk of instructions:




Code:
XOR EDX,EDX


Sets EDX back to 0.

Code:
MOV ECX,33


ECX is set to 33, probably as preparation for something else later.

Code:
MOV EAX,EBX


EBX is moved into EAX (that big result of the calculations before) so EAX will now hold BB6E8DBD.

Code:
IDIV ECX


This divides EAX by ECX and stores the quotient in EAX and the remainder in EDX:

Code:
BB6E8DBD/33 = 3ACD59A


The remainder is calculated by:

Code:
BB6E8DBD-(3ACD59A*33)=F


Now the next instruction:

Code:
MOV CL,BYTE PTR DS:[EDX+403046]


Following 403046 in the dump window:



There is an alphabet in lowercase then in capitals. This is used for generating a serial. So:

Code:
[EDX+403046] = [403055] = p = 70h




So 70h (ASCII "p") is moved into CL. We now have a compare:

Code:
CMP CL,BYTE PTR DS:[4031DA]
JE SHORT cim_crac.0040118C


If you remember from before, the string in the password field was moved to 4031DA so that means CL (p) is compared to it. So I'm guessing this application wants the first character of the serial to be a lowercase p. Unfortunately we had a uppercase p but I want to take the jump anyway to see what comes later so I step over to the jump and toggle the Z-flag (doubleclick the 0) to temporarily jump:





And look, now Olly tells us the jump will be taken:



So we take the jump and the next section to look at is:



These instructions look pretty familiar don't they ?

Code:
XOR EDX,EDX
MOV ECX,33
MOV EAX,EBX


We've already covered what those 3 instructions do so I won't go over them again.

Code:
XOR EAX,43694D


So EAX (result of the big calculation) is xor'red with 43694D to result in BB2DE4F0 which is then stored in EAX.

Code:
IDIV ECX


Just like before we have a IDIV. Quotient of EAX/ECX is moved into EAX and the remainder moved to EDX resulting in:

Code:
EAX = 3AB9109
EDX = 25


Code:
MOV CL,BYTE PTR DS:[EDX+403046]


This time a capital L is moved into CL:



Code:
XOR BYTE PTR DS:[4031DB],20


The second character of the serial is being xor'red with 20 so:

Code:
a (61h) xor 20h = 41


Then the result is compared with CL. CL is "L". Then if they are equal we jump (we want that). But at the moment they're not equal so let's find out what the second letter of the serial should be. If we just go backwards.. We want the result of the XOR at 004011A2 to be "L" (4Ch) so that would be the same as:

Code:
4C xor 20 = 6C


So the second letter of the serial must be 6C which in ASCII is lowercase "l".

So now we know the first two letters of the serial must be "pl". Again I toggled the Z-flag to temporarily jump so we can continue as if we had a correct serial to start.



Again a XOR then a check whether the result is equal to 2D. 2D in ASCII is "-". [4031DC] = 3rd character of serial. So that means the 3rd character is always 2D (-).

So now we know the first 3 characters of a valid serial for Slugsnack; "pl-".

Again I toggle the Z-flag so the jump is temporarily taken:




EBX is moved into EAX. The registers window becomes very useful since it's hard to keep track of what each register contains. After this instruction this is the status of the registers:



EAX is then pushed onto the stack ot prepare for the approaching call. Now we have a call to wsprintfA, go to the API help file for that call:



So the EAX is basically stored in a buffer at 00403234.

The next few lines are then used to set up the compare a bit further down:




The string stored in the buffer from wsprintfA is now stored in ESI and EDI stores the string that we inputted initially as the serial.

lstrcmpA is now called and the two strings compared. If there is no difference Z-flag is set and if there is a difference Z-flag is clear. Also if they are the same, EAX will hold 0. That was our aim from the beginning so we want String1 and String2 to be the same. That means the last characters of the serial (from the fourth character since we know the first three) must be BB6E8DBD. So putting together everything we know, a working serial for Slugsnack should be:


Code:
pl-BB6E8DBD


That will then result in EAX being 0 and therefore AL will be 0 so "TEST AL,AL" will be 0 and the code will jump to the good message.

Time to test:






It would've been a lot quicker if I knew enough programming to actually make the keygen than going through it manually but either way it only took a few minutes finding the serial. Hopefully some people will learn something from this. As always, making the "guide" has helped clarify and solidify knowledge for me too. Doubt many people will make it to the end of this one but thanks for reading if you did.

PS. Some of this might be wrong since I only started reversing like last week.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming -> Crackmes All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites