|
|
Program : #Cracking4Newbies CrackMe #3
Protection : User name and valid serial code
Authors : Kwai_Lo & ytc_
Size : 12k
Homepage: http://c4nprojects.cjb.net/ - TORN@DO
Before we start I'd just like to point out that this is my first tutorial and it may be a bit dodgy and confusing in some places so you'll have to bear with me. I
would greatly appreciate any comments/suggestions/crtisisms/ etc. etc., my e-mail address is at the bottom of the page.
So lets begin. The actual object of this crackme was to "Explain how the serial number is generated ... and if possible, code a KeyGEN for it.", which we
shall do together. My approach to keygenning a name/serial protection is to obtain a valid serial with a valid user name and work backwards using the serial
for reference when needed in working out the algorithm(how the program makes the valid serial).
Lets begin by taking a look at our targets dead-listing in W32Dasm. Let us now see what string-references there are for some clues as to where the protection routine may lie. This is what we get:
" ((((( " " " "#Cracking4Newbies CrackMe #2" "]_^[" "About" "Bad!" "Congratulations!!" "Don't you have a name?!" "Good!" "No name!" "No serial number entered!" "No serial!" "Wrong number!!"
Wow! Look at 'em all! So many but which one to explore? Remember the message recieve when we entered in our fake name and serial code?
"Wrong number!!". Yes indeed, so lets check out what calls this message and why. After double clicking on the "Wrong number!!"
string-reference we land here:
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401117(C) ; Something has been compared at location 00401117 and CONDITIONALLY jumps from there to here
| * Possible StringData Ref from Data Obj ->"Bad!" | :00401134 6840504000 push 00405040 ; Pushes the "Bad!" title bar text onto the stack
* Possible StringData Ref from Data Obj ->"Wrong number!!" | ; *We land here*
:00401139 6830504000 push 00405030 ; Pushes the "Wrong number!!" string onto the stack |
:0040113E 53 push ebx * Reference To: USER32.MessageBoxA, Ord:01BEh
| :0040113F FF1598404000 Call dword ptr [00404098] ; Display the "Wrong number!!" message
:00401145 5E pop esi :00401146 33C0 xor eax, eax :00401148 5B pop ebx :00401149 8BE5 mov esp, ebp :0040114B 5D pop ebp :0040114C C21000 ret 0010 ; Return to main program after messagebox is closed
So lets see what is calling this message box. It is referenced
from a CONDITIONAL jump at location 00401117.
So lets GOTO CODE LOCATION 00401117, we see this:
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004010E4(C) | :00401102 8D55F4 lea edx, dword ptr [ebp-0C] :00401105 52 push edx :00401106 E840010000 call 0040124B :0040110B 8B4D08 mov ecx, dword ptr [ebp+08] :0040110E 83C404 add esp, 00000004 :00401111 03CE add ecx, esi
:00401113 3BC8 cmp ecx, eax ; Compare our serial with valid serial
:00401115 6A00 push 00000000 :00401117 751B jne 00401134 ; Jump if they are NOT equal | We land here
* Possible StringData Ref from Data Obj ->"Good!" | :00401119 685C504000 push 0040505C ; If they ARE equal save the "Good!" message box title onto the stack
* Possible StringData Ref from Data Obj ->"Congratulations!!"
| :0040111E 6848504000 push 00405048 ; Save the "Congratulations!!" text onto the stack
:00401123 53 push ebx * Reference To: USER32.MessageBoxA, Ord:01BEh | :00401124 FF1598404000 Call dword ptr [00404098] ; And display the GOOD GUY message box
:0040112A 5E pop esi :0040112B 33C0 xor eax, eax :0040112D 5B pop ebx :0040112E 8BE5 mov esp, ebp :00401130 5D pop ebp :00401131 C21000 ret 0010 ; After the message box has been closed return to main program
So there it is in all its glory, basically its a "compare entered serial with valid serial and jump to the "Bad!" message box if they are NOT EQUAL and
display the "Good!" message box if they ARE EQUAL. From what we can see from the code above at location :00401113, eax probably holds our
serial, and it is begin compared to the valid serial which should be in ecx. Lets test our hunch out. Lets enter some fake details, fire up SoftICE, find a
suitable break-point, and get our valid serial code. *easy huh?*. I entered:
Name: Boggy
Serial: 123123
GetDlgItemTextA does it for me, so we have entered the program. We must now step out of the function that we broke-in by pressing F11, then press
F12 or Ctrl-D once more to let the program read our serial.
*BEGGINERS NOTE* - If you use Ctrl-D to let the program run again you must press F11 to step out of the function and get back into the main code,
this is not the case when you press F12 because it takes you right to the ret instruction. *Confused? Sorry just disregard this if
you are : )
Okay so after all that we arive at this piece of code:
:004010DB FF15A0404000 Call [USER32.GetDlgItemTextA] :004010E1 85C0 test eax, eax ; Check if anything was entered in the serial box
:004010E3 5F pop edi :004010E4 751C jnz 00401102 ; If something WAS entered jump to location 00401102
:004010E6 50 push eax :004010E7 6880504000 push 00405080 ; If nothing was entered,
:004010EC 6864504000 push 00405064 ; save the "Nothing was entered" strings
:004010F1 53 push ebx ; onto the stack,
:004010F2 FF1598404000 Call [USER32.MessageBoxA] ; and display the "Nothing was entered" message
:004010F8 5E pop esi :004010F9 33C0 xor eax, eax :004010FB 5B pop ebx :004010FC 8BE5 mov esp, ebp :004010FE 5D pop ebp :004010FF C21000 ret 0010 ; Return to main program after message box closes
*BEGGINERS NOTE* - How did I know that it was checking to see if we had entered in anything into the serial box? Well without going into Win32 API,
basically in reference to the GetDlgItemText function, eax returns the length of the text you entered in the box, try it, enter
? eax in the command window and it should return the length of your serial. e.g. When I enter ? eax I get:
00000006 00000006 ""
Remember my serial was "123123" , thats six digits = )
Our serial has more than 0 digits therefore we jump to location 00401102 here is where we land: :00401102 8D55F4 lea edx, [ebp-0C] ; Loads our serial into edx
:00401105 52 push edx ; Saves our serial onto the stack
:00401106 E840010000 call 0040124B :0040110B 8B4D08 mov ecx, dword ptr [ebp+08] :0040110E 83C404 add esp, 00000004 :00401111 03CE add ecx, esi :00401113 3BC8 cmp ecx, eax ; Remember this little routine? = )
:00401115 6A00 push 00000000 :00401117 751B jne 00401134 :00401119 685C504000 push 0040505C ; Save the "Good!" message box title onto the stack
:0040111E 6848504000 push 00405048 ; Save "Congratulations!!" string onto the stack
:00401123 53 push ebx :00401124 FF1598404000 Call dword ptr [00404098] ; Display the GOOD GUY message
:0040112A 5E pop esi :0040112B 33C0 xor eax, eax :0040112D 5B pop ebx :0040112E 8BE5 mov esp, ebp :00401130 5D pop ebp :00401131 C21000 ret 0010 ; Return to main code after message box has been closed
Okay, lets test our hunch shall we? Trace down with F10 until the highlight bar is highlighting location :00401113. Lets check the value of eax:
? EAX
0001E0F3 0000123123 "¤" ; 123123 = My serial
? ECX
0000FDF8 0000065016 "ýø" ; 65016 = Valid serial maybe?
Lets test out our newly aquired serial shall we? Name: Boggy Serial : 65016 WOOP! We're regged! But wait, we are only half way there my dear reader. We must now work out the routine which this program uses to create the valid
serial for any name entered.
So we know that the serial is generated based on the username given (Well we don't actually know that for certain but we can safely assume this, as most
name/serial protections work in this manner). So lets enter our program just as we did before, set a breakpoint on GetDlgItemTexta, when we enter the
program we want to step out of the function by pressing F11 but don't let the program break again as we did before because we only want to investigate the
what is happening to the name we entered and see what it is doing to generate the valid serial. So after breaking into the program and stepping out of the
function we see this peice of code:
:00401084 FF15A0404000 Call [USER32.GetDlgItemTextA] :0040108A 85C0 test eax, eax ; Check if we entered anything
:0040108C 751C jnz 004010AA ; If we did then jump to serial generation routine
:0040108E 50 push eax ; If we didn't then...
:0040108F 68A4504000 push 004050A4 ; Save "No name!" title box text onto the stack :00401094 688C504000 push 0040508C ; Save "Don't you have a name?!" string onto the stack :00401099 53 push ebx :0040109A FF1598404000 Call [USER32.GetDlgItemTextA] ; Display "Don't you have a name?!" message
:004010A0 5E pop esi :004010A1 33C0 xor eax, eax :004010A3 5B pop ebx :004010A4 8BE5 mov esp, ebp :004010A6 5D pop ebp :004010A7 C21000 ret 0010 ; Return to main program after message box is closed
I won't explain this piece of code as it is identical to the "check serial length routine" mentioned before. The comments should be enough plus you can
check the explanation above if need be.
So because we have entered text into the "Name: " box we jump to location 004010AA:
:004010AA 33F6 xor esi, esi ; Initialize esi register for use (empty them out) :004010AC 57 push edi :004010AD 33D2 xor edx, edx ; Initialize esi register for use (empty them out) :004010AF 0FBE4415C0 movsx eax, byte ptr [ebp+edx-40] ; Take the next byte of our name and put it in eax :004010B4 03F0 add esi, eax ; Add its ascii value to esi :004010B6 8D7DC0 lea edi, [ebp-40] ; Load our name into edi register :004010B9 83C9FF or ecx, -01
:004010BC 33C0 xor eax, eax :004010BE 42 inc edx ; Add one to the counter :004010BF F2AE repnz scasb :004010C1 F7D1 not ecx :004010C3 49 dec ecx :004010C4 3BD1 cmp edx, ecx ; Check if we have done the routine enough times by comparing the edx counter to the use name length
:004010C6 76E7 jbe 004010AF ; If not go back and do it again :004010C8 897508 mov dword ptr [ebp+08], esi ; Moves the sum value of our name into memory location [ebp+08] :004010CB C1650807 shl dword ptr [ebp+08], 07 ; Multiply the value in mem. loc. [ebp+8] by 128 (Shit Logical Left 7 places) :004010CF 8D4DF4 lea ecx, dword ptr [ebp-0C] :004010D2 6A0A push 0000000A :004010D4 51 push ecx :004010D5 68E9030000 push 000003E9 :004010DA 53 push ebx
Now this is the most important part of this essay so I will take you through it line by line. The first three are irrelivent to us so will omit their explaination, so lets
start with the instruction:
movsx eax, byte ptr [ebp+edx-40]
What this is basically doing is taking the byte in memory location [ebp+edx-40] and putting it in the eax register. We can show this by entering this command: D ebp+edx-40
Now if we look in the ascii window of the memory we can see our name. So lets execute that line of code by tracing over it with F10. So it's taking a byte out
of our name in memory location ebp+edx-40 and MOVing that byte value into the eax register. Lets now check the value of the eax register: ? eax
00000042 00000066 "B"
From left to right it show us: The hexadecimal value 42 is equal the the decimal value 66 and both are the ascii equivalent of the character "B".
So the code is definitely manipulating our name in some way, lets examine the next instruction:
add esi, eax
Well it does what is says: adds the (hexadecimal)value of eax to the (hexadecimal)value value of esi. *Beginners Note* - All numbers and arithmetic you see in SoftICE are hexadecimal values.
So lets execute that line of code my tracing over it with F10 and then checking its outcome.
The value of eax for me was 42h and the value of esi was 0. After executing the line esi's value increased to 42h. So we can make the assumption that esi will keep the sum of all the values of the characters in our name. Next instruction:
lea edi, [ebp-40]
This instruction loads(Load Effective Address) the contents of memory location [ebp-40] into edi. Lets check whats in this memory location: D ebp-40
Its our name! Our name is being loaded into the edi register. Now to be honest with you, what is does with our name and the next couple of intructions have
baffled me but they turn our to be irrelevent anyways, so if you are wondering why have not commented on an instruction it's probably cause I don't know
what the hell it is doing with the data :p
Inc edx All this does is increment(add to) the register by 1 every time it is executed. It is being used as a counter to keep count of the amount of times the rountine
loops. Hence:
cmp edx, ecx
This is comparing the amount of times the routine has looped with the number of characters in the name entered. Lets check: ? ecx 00000005 00000005 "" ; Boggy = 5 Characters
? edx 00000001 00000001 ""; 1st time through
So ecx = name length, edx = number of times the rountine has looped. So:
jbe 004010AF
Is controling the flow of the routine and will keep looping the routine until all the characters have been added up. So basically all this routine is doing is suming
all the ascii values of the characters in our name together and storing them in the esi register. The sum of the values of the characters in my name is 1F8h
When we finally break out of the loop we are on:
:004010C8 897508 mov dword ptr [ebp+08], esi
*Begginers Note* -
'B' = 42h + 'o' = 6Fh +
'g' = 67h +
'g' = 67h +
'y' = 79h = 1F8h
So the value 1F8h gets stored into memory location [ebp+8]. Next instruction:
:004010CB C1650807 shl dword ptr [ebp+08], 07
This instruction baffled the most in the course of cracking this program. I fried my brain to trying to work out how to calulate it, but my trusty
"Art of Assembley" - by Randall Hyde book came to my rescue. Refer to Chapter 6 Part 3 for an explanation. Basically what I found out was that:
SHL register, 7 = register*128d
*Begginers Note* - Number followed by the letter 'd' denotes a DECIMAL number, on the same token Number followed by the letter h or the pre-fix 0x
denotes a hexadecimal number. - Number represents any integer value.
e.g. 16h, 0x16, 67h, 0x56 = Hexadecimal Numerals
e.g. 56d, 34d, 23d = Decimal Numerals
So all shl dword ptr [ebp+08], 07 is doing is multiplying the sum total all the characters in our name by 128d. So the value in memory location
[ebp+08] becomes FC00h for me.
:004010DA 53 push ebx
Well after stepping over this instruction we land here. Much of this is familiar code which has been commented on so I added on the important comments
to this peice of code. Your should be able to understand it yourself:
:004010DB FF15A0404000 Call [USER32.GetDlgItemTextA] :004010E1 85C0 test eax, eax :004010E3 5F pop edi :004010E4 751C jne 00401102 :004010E6 50 push eax :004010E7 6880504000 push 00405080 :004010EC 6864504000 push 00405064 :004010F1 53 push ebx :004010F2 FF1598404000 Call [USER32.MessageBoxA] :004010F8 5E pop esi :004010F9 33C0 xor eax, eax :004010FB 5B pop ebx :004010FC 8BE5 mov esp, ebp :004010FE 5D pop ebp :004010FF C21000 ret 0010 :00401102 8D55F4 lea edx, dword ptr [ebp-0C] :00401105 52 push edx :00401106 E840010000 call 0040124B :0040110B 8B4D08 mov ecx, [ebp+08] ; Move (28*sum of character values) into ecx :0040110E 83C404 add esp, 00000004 :00401111 03CE add ecx, esi ; Add the sum of character values to ecx | This is your serial :00401113 3BC8 cmp ecx, eax ; Compare the good serial with the serial entered :00401115 6A00 push 00000000 :00401117 751B jne 00401134 ; Jump to GOOD! Message if EQUAL else goto of to BAD! message
:00401119 685C504000 push 0040505C :0040111E 6848504000 push 00405048 :00401123 53 push ebx :00401124 FF1598404000 Call [USER32.MessageBoxA] :0040112A 5E pop esi :0040112B 33C0 xor eax, eax :0040112D 5B pop ebx :0040112E 8BE5 mov esp, ebp :00401130 5D pop ebp :00401131 C21000 ret 0010
So in concluding this section the algorithm is this:
1. Sum up the value of each character in your name;
2. Take the total of that and multiply it by 128d 3. Add the multiplied total to the original total
Viola! Keygen` a` la Boggy = )
Well for those who can't code in PASCAL or can but don't want to (I understand : p) the algo should be enough to get you going. Here is very empty keygen, I have eclosed a much more visually asthetic one which I coded earlier. All I am aiming to do is to show you a working piece of code that can generate a valide
registration code from any given name for the #Cracking4Newbies CrackMe #3 and here it is: Program C4NCrackme3; Uses WinCrt; {*NOTE* I am using the Windows version of Turbo Pascal, if you wish to compile this under the DOS Turbo Pascal version you should replace "WinCrt" with "Crt"} Var CharTot, Serial : LongInt; Loop : Integer; Name : String; Begin Write ('Enter your name: '); Readln (name); For Loop:= 1 to Length(name) Do {For every character in our name do this} CharTot:= Ord((Name[Loop])) + CharTot; {Convert the character of your name to its numerical equivalent and add it to the CHARTOT variable} Serial:= CharTot * 128; {Remember the "SHL 7" instruction and how it was equivalent to multiplying by 128d?} Serial:= Serial + CharTot; {Adds the "SHL 7" return value to the sum value of each character in our name} Writeln ('Valid Serial: ',serial); End.