http://www.sky.net/~floersch/ - Webpage (fdrepl95.zip - (362k)).
Well I hope you enjoy this next tutorial as much as I enjoyed writing it, in the case of this program there are many things on which to commend the author, so lets start, note the time delay and prominent unregistered user screen at start-up. You should easily locate the Register option from the Help menu, note that the program gets your Name and Company from the same registry key which is used for the System control panel.
So insert a registration number and use >bpx Hmemcpy or GetWindowTextA, however your in for a surprise, the program doesn't check your code here, you can enter anything even the correct code and it will not be checked. Take note also that the author does not issue any message boxes either. What actually happens is that the program uses the API call WritePrivateProfileStringA (address 0040EA30) to place the code you entered inside the file wrepl.ini.
So you should now realise that the program will actually check the contents of the initialisation file at start-up, if you open the file in Notepad the first 2 lines are shown below.
RegNum=0123456789 <-- Code entered.
So lets take a look at the disassembly string references, once again the author has done well, usually in these scenarios you'll locate the unregistered user string reference and then just work your way back up the code tree. After some fishing I noted the following code (string reference RegNum).
:004103C5 PUSH 0046CFB0 <-- "RegNum".
:004103CA PUSH 0046CFA0 <-- "Registration".
:004103CF CALL [KERNEL32!GetPrivateProfileStringA] <-- API call.
:004103D5 MOV AL, BYTE PTR [ESP+000000E4] <-- First character of RegNum.
:004103DC LEA ESI, [EBP+00000170]
:004103E2 CMP AL,BL <-- Does RegNum actually have any value.
:004103E4 JZ 004103F0 <-- Jump_Unregistered.
:004103E6 LEA ECX, [ESP+000000E4] <-- Value of RegNum.
:004103ED PUSH ECX <-- Stack.
:004103EE JMP 004103F5
:004103F5 MOV ECX,ESI
:004103F7 CALL 004393A4 <-- Get length.
Well I'll just stop here, you should obviously use >bpx GetPrivateProfileStringA to gain an entry point at address 004103D5, (you may get other breaks before reaching here). The program will then just check that there is actually a code after the RegNum=, you'll note that I've omitted some code here because it is not relevant, functions are used to get the Windows directory and return your name and organisation names (obtained via registry calls earlier). Note the following addresses, 00410419 - (ECX loaded with name), 00410455 - (EAX loaded with organisation name).
Below you'll find the next section of code, I've omitted all of the irrelevant snippets, I would hope you can recognise a string length routine when you see one, REPNZ SCASB, NOT ECX etc.
:00410483 CALL 004393A4 <-- Get length of RegNum.
:0041048A CMP DWORD PTR [EDI-08], EBX <-- Check RegNum for 0.
:0041048D JZ 0041051F <-- Jump_bad_RegNum.
:004104BE MOV EDI, [EBP+00000168] <-- User Name.
:004104C4 CMP DWORD PTR [EDI-08], EBX <-- Check Name for 0.
:004104C7 JNZ 004104CF <-- Jump_good.
:004104F3 LEA ECX, [ESP+000000E4] <-- User Name.
:004104FA PUSH ECX <-- Stack it.
:004104FB PUSH EDX <-- Push RegNum also.
:004104FC MOV ECX,EBP
:004104FE CALL 00410550 <-- You_must_trace_this_function.
:00410503 TEST EAX,EAX <-- Check EAX return for 0.
:00410505 JZ 00410519 <-- Jump_bad.
So what we have here are some elementary length checks for 0 and this suspicious CALL 00410550 which has the RegNum and User Name stacked as parameters. You can actually permanently reverse this scheme by just NOP-ing away the bad JZ 00410519, however thats not the 'zen' way, trace 00410550 and take a look at a good calculation routine using tables, although the String Reference "W3RF95" is weak.
The calculation routine will perform manipulations upon only the registered user name (checking firstly that the name has length 3 or more), a loop will then strip away any spaces in the name before CALL 00423780 uppercases it. The first 9 characters of the code are generated from the first 3 uppercased values of the name, in the case of the name CrackZ each uppercase letter acts as a pointer to a ready made table of 3 characters.
:00410688 LEA EDI,[EAX*4+ECX-30] <-- Pointer into
table using name value.
C = PV1
R = KK0
A = 859
The next 6 characters of the RegNum are always the string W3RF95 which will be added to the end of the first 9 characters. The last 9 characters are generated in a similar fashion to the first 9 using the same ready made table, though this time the last 3 characters of the name will be used.
C = PV1
K = OP4
Z = JV7
So you can see how the RegNum for CrackZ will look (see below). I will just make a small correction here as well, earlier I stated that the program gets the user name directly from a registry key, this seemed to be the case when I ran Registry Monitor and these values were read by the program, however I wasn't able to fool the program by changing the registry name with Regedit although this seems the only logical place it could be getting the name from. Actually the registration number you need to insert is based on system specific details (so the registration number below won't help you so much :-) ). I will say that I think this scheme is actually reasonably good although I suspect after the critical function call a search in memory for 'W3RF95' might well find the good RegNum lying lazily in memory.
Registration Number: PV1KK0859W3RF95PV1OP4JV7
As a matter of interest I received feedback from the author of this protection, you can read what he had to say here.