http://www.winimage.com - Webpage.
winima40.zip - (279k).
Welcome to this updated tutorial. Using WinImage as my target I'm going to take you through all of the steps necessary to produce a key generator. The generation routine used here is fairly typical in complexity to that which you'll encounter a lot in reasonable shareware software. The actual comparison itself is not very well concealed and the good code is echoed in EAX but lets not dwell on that, instead we'll start the program and locate *Registering* from the Options menu.
Lets immediately start our intrusions with SoftICE, you'll find that >bpx GetDlgItemTextA works well. It might also be advantageous to also have a dissassembly listing of winimage.exe. Allowing the required 2 breaks should gain you a convenient entry point at address 004290A7 and a few F10 steps away lies our protection.
:004290AE CALL 0042CD83 <-- Below this function lies the protection scheme.
:004290B3 MOV ECX,[0043C800] <-- Set ECX=0.
:004290B9 XOR EDX,EDX <-- Clear EDX.
:004290BB ADD ESP,0C <-- Correct stack.
:004290BE CMP EAX,EDX <-- EAX's value is important here.
:004290C0 MOV [0043C5E8],EAX <-- Flag [0043C5E8] with value of EAX.
:004290C5 JZ 004290CD <-- Jump_bad_code, skipping the flag.
:004290C7 MOV [0043C7C4],ECX <-- Flag [0043C7C4] good but is there any need.
:004290CD CMP [0043C7C4],EDX <-- Pointless check or so it seems.
:004290D3 MOV DWORD PTR [0043CA54], ECX <-- Set [0043CA54] = 0.
:004290D9 JNZ 004290E0 <-- This looks like bad implementation.
:004290DB MOV [0043CA54],EAX <-- Flag [0043CA54] good using value of EAX.
:004290E0 PUSH 01
:004290E2 CMP EAX,EDX <-- The real check.
:004290E4 POP ESI
:004290E5 JNZ 00429113 <-- Jump_good_buyer.
Lets take a good look at this code snippet. After our protection scheme call
the value of EAX is very important, a good code sets EAX=1, else it will be
0. Address 004290B3 looks like a compiler fixup (using a flag to zero a register seems
inefficient but maybe as I suggest later its not). EDX is then zeroed by the
XOR at 004290B9, before being compared with the value of EAX at 004290BE.
The JZ 004290C0 acts as the first check, if your code was good then the program
flags [0043C7C4] to the value of ECX (which we know at this stage is 0), if
not we skip.
Continuing onwards, the JNZ check at 004290E0 looks like somethings gone awry with the protection scheme, why?, well after our protection scheme is called both EDX and our flag [0043C7C4] are always set to 0, so this JNZ can't possibly filter out any bad codes because its always comparing 0 with 0, therefore the code will never jump over 004290DB. In fact there is nothing wrong with this theory, but at this code we appear to have 3 flags (addresses [0043C5E8], [0043C7C4] & [0043C800]) which are either always 0 or when they are set it doesn't seem to matter.
In fact those of you with reversing minds will realise that the most likely culprit is not an inefficient compiler but another location which checks these flags. You could sift the disassembly blindly searching for them, that would work, but investigating the callers of 0042CD83 is the wiser move.
Lets return our mind to the real goal, ascertaining how WinImage transforms our User Name into a Registration Code. Tracing below 0042CD83 is definately the way to go as its the only call that could possibly hide the calculation routines. 1 level down call 0042CC87 gets the length of the user name and uses the API call CharUpperA to uppercase it, after returning we have an elementary length check for 0. Tracing onwards you must also step into call 0042CCB3, below you'll find the main calculation routine.
:0042CD05 PUSH 27 <-- Interesting pushed value.
:0042CD07 POP EDI <-- EDI=27.
:0042CD08 MOVZX EDX, BYTE PTR [ECX+ESI+03] <-- EDX = value of names first letter (upper case).
:0042CD0D LEA EAX,[ECX+03]
:0042CD10 IMUL EDX,EDI <-- EDX = EDX*EDI.
:0042CD13 ADD [EBP-04],EDX <-- Store the result, eventually the good_code_location.
:0042CD16 PUSH 0E <-- And another interesting push.
:0042CD18 CDQ <-- Clear EDX.
:0042CD19 POP EBX <-- EBX=0E (and pop).
:0042CD1A IDIV EBX
:0042CD1C TEST EDX,EDX <-- Test EDX=0.
:0042CD1E JZ 0042CD25 <-- No_jump_on_most_occasions (IMUL EDI,7 if it does).
:0042CD20 LEA EDI,[EDI*2+EDI] <-- EDI = EDI*3.
:0042CD23 JMP 0042CD28 <-- Unconditional.
:0042CD28 INC ECX <-- Loop counter.
:0042CD29 CMP ECX,[EBP+08] <-- Compare_for_end_of_name.
:0042CD2C JL 0042CCF9 <-- Jump_until_name_end.
Although this routine is fairly long, there is nothing particularly
complex about its mathematics and we can now set about ripping the
required code for our key generator. When the loop completes the good
code can be found at 0042CD2E in EAX (note the format), but continue onwards
and you'll notice several other interesting things. Before doing the actual
compare our good result will first be tested for B8DCDD26, I just wonder what
name actually produces that result, its not unheard of for authors to code in
names which they don't ever want to register, I tried a few well known ones
such as *Phrozen Crew, UCF* but evidently they are acceptable.
A little further you'll see that the 2 strings are actually compared using the function strcmp (exported from crtdll.dll), so this scheme could have been reversed by just loading crtdll exports into SoftICE and >bpx strcmp. There's also some more surprises, after this first compare the program will go on and generate a further 5 codes, of which only some will be valid. In my key generator I only output the first 2, although adding the remaining 3 shouldn't prove any great trouble.
WinImage v4.00.4000
Name: CrackZ
Registration Codes: 59A6F0, 175FC044, 105EC071.