http://www.actify.com - Webpage, 3DViewInst.exe - (3.98Mb).
Well, let us commence analysis of this next program. Start the application and you are given a program serial #, (mine is AFY-502-535) from which you must input a KeyCode. Note that despite multiple re-installs I've never been able to get my serial # to change, we believe its a possibility that our good KeyCode is generated from this serial #. So lets enter some bogus KeyCode, (I've deliberately used 01234567890A).
Note here that you have a choice, you could either have traced with >bpx GetWindowTextA in SoftICE to the code I am going to discuss (doing some serious F12 & F10 work in the process, as you'll step back into user and mfc42.dll before reaching it) or you it might be easier to bpx directly.
:00427185 MOV EDI,[ESP+000000A0] <-- EDI holds code
:0042718C OR ECX,-1 <-- ECX=-1.
:0042718F XOR EAX,EAX <-- EAX=0 (maybe put your int 3 patch here).
:00427191 LEA EDX,[ESP+10] <-- EDX holds code entered.
:004271B1 MOV ESI,005316E4 <-- ESI moved to string '123443219876'.
:004271B6 MOV DL,[EAX] <-- DL holds first # of KeyCode entered.
:004271B8 MOV CL,DL <-- CL holds first # also.
:004271BA CMP DL,[ESI] <-- Compare it with ESI.
:004271BC JNZ 004271DA <-- Jump_good_code.
I'll just stop here, 123443219876 is not actually a valid code for the program and providing your code differs in some way from that you'll eventually jump out of the recursive loop compare routine resuming at 004271DA.
:004271DA SBB EAX,EAX <-- EAX=-1.
:004271DC SBB EAX,FFFFFFFF
:004271DF TEST EAX,EAX <-- Test EAX=0.
:004271E1 JNZ 00427228 <-- Jump_EAX_=-1.
:00427228 LEA EAX,[ESP+10] <-- EAX holds KeyCode entered.
:0042722C PUSH EAX <-- Stack it.
:0042722D PUSH 00530454 <-- StandardKeyCode string reference.
:00427232 CALL 00427420 <-- Creates Registry Key.
:00427237 ADD ESP,08 <-- Tidy stack.
:0042723A LEA ESI,[ESP+10] <-- Entered code.
:0042723E MOV EAX,EBP <-- EAX holds good StandardKeyCode.
Well, I'm going to stop here because the next few lines just perform the compare between the 2 codes. Its easy to pick out the good code from EAX, but the question for us reversers is how was it created, the answer, well here's one possibility. We know that any code entered will be written to the registry key HKEY_CLASSES_ROOT/3DView25.KeyCode (regardless of whether it is correct) so its probable the program does the key verification at run-time.
A little detective work inside a disassembled listing of 3dview.exe is now a good idea, take note of the string reference AFY, this is actually a hard-coded in default and is not used by the program, the serial number calculation routine works like this. Firstly we take away the AFY default, then strip away the hyphens. So for my code AFY-502-535 I am left with 502535, the program then makes a copy of this same number and prefixes it to the existing number, so I get 502535502535. In fact you may notice an additional digit which is generated earlier, although it is not used in the calculation routine.
Assuming the string 502535502535, a final subtle change is made at this code.
:00426F5A MOV AL, BYTE PTR [EAX-03] <-- Pointer into
:00426F5D MOV BYTE PTR [EBP+02], AL <-- Now move it to 505535502535.
:00426F72 MOVSX EDX, BYTE PTR [ECX] <-- Pointer to generated string.
:00426F75 LEA EAX, DWORD PTR [ESI+ECX]
:00426F78 MOV EBX, 00000009
:00426F7D IMUL EAX, EDX
:00426F80 LEA EAX, [EAX+4*EAX]
:00426F84 IDIV EBX
:00426F86 ADD DL, 30 <-- Maths required for a key generator.
:00426F89 MOV BYTE PTR [ECX], DL <-- ECX overwritten with character of good code.
:00426F8B INC ECX <-- Next_number.
:00426F8C DEC EDI <-- String_length.
:00426F8D JNZ 00426F72 <-- Loop_and_generate_good_key.
So you can see here all of the necessary code to produce a key generator, of course when you make the subtle change it probably isn't necessary to have the unused 13th digit, so amend your pointer to reflect that if you choose to use assembly language, else use a high level language.