http://www.uhppp.com - Webpage ($3,995).
A brief thanks to the kind person in #ucf2000 who gave me this program for examination. SPD98 (hereafter abbr.) is an AutoCAD plug-in which uses a serial number as the basis for its protection. However there are evidently several options and interesting areas to study, first of all the product is installed as a demo version with a time-trial, however we are given an unlocking option. 2 numbers are generated by the unlocking mechanism, the first "Code Entry" is a random number, the computer # however appears to be constant (not surprising as I believe its generated from the AutoCAD serial number).
As ever we'll follow the standard practise using SoftICE, I leave this as a mindless exercise, you'll soon identify 2 dll's which you are interested in, keylib32.dll which performs the maths and uhpws100.dll which is responsible for checking the result. Here's the relevant code:
:100018A9 CALL 10002E5A <-- KEYLIB32.pp_tcode
:100018AE MOV EDI, EAX <-- Store EAX in EDI.
:100018B0 LEA EAX, DWORD PTR [EDI-01] <-- The pointer we just stored.
:100018B3 CMP EAX, 6 <-- Acceptable range now defined.
:100018B6 JA 10001A4A <-- Obviously > 6 is bad.
:100018BC JMP DWORD PTR [4*EAX+10001A64] <-- Use EAX's value to determine where we are going.
This is yet another rudimentary switching procedure, a bad code which is not acceptable sets EAX=-1, therefore the only other acceptable values must range between 0 & 6, its just a simple question of establishing where each EAX value jumps to and by examining the String References which would be desirable.
if eax = -1 we know its a bad code.
if eax = 0,1,2 then JMP 100018C3 (adds an amount of days to the trial period).
if eax = 3 then JMP 1000195F (application unlocked).
if eax = 4,5,6 then JMP 100019BF (slight variation on unlocking).
So we can see our objective, pp_tcode() must return EAX=3, lets have a little trace of it and see how it works. The function is boring and tedious but I worked through the maths as you should when you encounter schemes of this nature. If you can tolerate following this then I'll lead you through how to generate a valid code, both the Code Entry and Computer # are used as well as an interesting other value.
Start(): (Computer # stored in EDI, Code Entry in EAX,ECX,EDX).
:xxxx101D AND EDX, 0007E000 <-- AND Code Entry.
:xxxx1023 AND EAX, 00780000
:xxxx1028 SHR EDX, 0D
:xxxx102B SHR EAX, 13
:xxxx102E ADD EDX, 000007BC <-- 7BCh = 1980 dec.
:xxxx1034 IMUL EDX, 00000044 <-- In SoftICE this is IMUL EDX,44,44 (single multiplication).
:xxxx1037 IMUL EAX, 00000108
:xxxx103D ADD EDX, EAX
:xxxx103F MOV EAX, ECX <-- Restore Code Entry in EAX.
:xxxx1041 AND EAX, 00001F00
:xxxx1046 SHR EAX, 8
:xxxx1049 IMUL EAX, 000000F3
:xxxx104F LEA ESI, [EDX+EAX] <-- Add results.
:xxxx1052 MOV EDX, ECX <-- Restore Code Entry again.
:xxxx1054 AND EDX, 7F800000
:xxxx105A AND ECX, 000000FF
:xxxx1060 SHR EDX, 0F
:xxxx1063 MOV EAX, DWORD PTR [EBP+14] <-- Where does this constant come from.
:xxxx1066 ADD EDX, ECX
:xxxx1068 IMUL EDX, 00000003
:xxxx106B IMUL EAX, 00000007
:xxxx106E LEA EDI, DWORD PTR [EDX+2*EDI] <-- Use Computer #.
Rather than bore you with more code, a few more arithmetic operations are performed before the result will be compared with the one you entered, however the first result isn't the one which you want because we need EAX=3, the first failure increments EAX (from 1 to 2), thus on the 2nd pass which fortunately doesn't repeat all this maths, EAX=2 (again not desirable), it is the 3rd pass value when EAX=3 that we need to enter to unlock. The only other issue before we convert this into a key generator or patch it (lazy reversers only) is where does the mysterious [EBP+14] value come from. My guess is that it is a constant (650947h = 6621511 dec), however we had better verify this.
The early part of pp_tcode() adds 34h (52 dec) to [EBP+14], lets backtrace from there. [EBP+14] is merely copied via another pointer [EBX+30] at address 1000292C, the call immediately prior to this is another import from keylib32.dll (pp_cenum()), this looks initially promising but doesn't have any effect on our mysterious value, it must have been set before, more backtracing seems inevitable but lets think about this. We know already that the computer # is a constant value and that your supposed to call UHP to get an unlock code, therefore it is impossible for this constant to be system specific unless it is calculated from the 2 codes, but we know that the Code Entry is random so such a relationship cannot exist.
You can download my Java (yes Java!) key generator source code. If you are interested you might like to locate the secret registry key used by the program (HKEY_CLASSES_ROOT\vdsp) for example. If you are not a Java fan then there is also aVisual Basic key generator, the code behind the cmdGenerateCode procedure is simple to say the least.