HOW TO REVERSE ENGINEER ZMUD 4.62
(Expiring Registration Codes - A New Breed)
by +Sync
(09 July 1997)
Courtesy of Fravia's page of reverse engineering
Well, this reverse enginnering essay is brilliant as it is but its sort of "unfinished".
+Sync (who is a VERY GOOD
reverse engineer and a classmate of mine in the +HCU) asks for help. You'll find at the bottom
of this essay a first answer by Epic Lord (25 July 1997).
		   
			HOW TO CRACK ZMUD 4.62
				By +Sync
		Expiring Registration Codes - A New Breed
Software: http://www.zuggsoft.com/zmud
This was requested by someone so I decided to take a look at it.  They had
said that the file could not be patched because it checked the file integrity
somehow, however I patched it with no problems.  I would like to say here 
that while patching this program was not very difficult, I tried to figure
out how the code is generated and it was extremely difficult.  if someone
could explain this one to me, I'd be thankful. Start out like usual, read
the documentation thouroughly and then run the program.  You immediately
are dumped into a dialog which tells you it is an evaluation version, and
gives you the opportunity to enter a registration number.  Enter a bogus
name and number, I used +Sync and 12121212 (thanks +Orc).  A small dialog
pops up telling you "Invalid registration code or name".  Well, we all know 
what to do now, right?  Stop and think about what your next move is before
reading on.  You guessed it open up ZMUD.EXE with W32DASM (thanks Frogprint,
however I must say that the protections for 8.5 and 8.7 have been pretty darn
good).  What do we do with this 20+ meg monstrosity?  We look for the text 
that was in the dialog.  You will find it at the code below. Notice the 
telltale signs of a registration flag right before the dialog is put up.
:00491F46 A17CE34F00              mov eax, [004FE37C]
:00491F4B E868A30300              call 004CC2B8			;Call to some protection scheme
:00491F50 803D8CE34F0000          cmp byte ptr [004FE38C], 00	;Is flag 0 ?
:00491F57 740F                    je 00491F68			;Yes, Beggar Off
:00491F59 8B45FC                  mov eax, dword ptr [ebp-04]
:00491F5C C7802801000001000000    mov dword ptr [ebx+00000128], 00000001
:00491F66 EB0A                    jmp 00491F72			;No, go on Good Guy
* Referenced by a Jump at Address:00491F57(C)
|
* Possible StringData Ref from Code Obj ->"Invalid registration code or name"
                                  |
:00491F68 B8AC1F4900              mov eax, 00491FAC		;Display "Bad" Dialog Box
:00491F6D E852B6FAFF              call 0043D5C4
Obviously the memory location at [4fe38c] is the registration flag.  When it
is 0 you are unregistered, 1 and you are registered.
	Next is the fun part.  Load up Soft-Ice and do a BPMB 4FE38C W to 
break every time the program tries to alter this mem location. Ctrl-D back
to the program.  Soft-Ice will return on the line of code below.
:004CD599 C6058CE34F0000          mov byte ptr [004FE38C], 00	;Move 'evil' into flag
Let the program go on and Soft-Ice will next land you here
:004CC2EE C6058CE34F0001          mov byte ptr [004FE38C], 01	;Move 'good' into flag
Now that's kind of odd, it put a zero in, then immediately put a one in. Hmm, 
well let's move on.  Ctrl-D again and you will land here.
:004CC458 8A45C7                  mov al, byte ptr [ebp-39]	;Load al
:004CC45B A28CE34F00              mov [004FE38C], al		;Put it in Flag
Now, we notice that in this case (because we are not regged) al is loaded with
a zero.  Why?  We don't even care for right now.  All we are doing is patching
for the moment, so lets change
8a45c7		mov al, byte ptr [ebp-39]
to
B001		mov al,01		;Load with 'good'
90		nop			;Pad it to 3 bytes
So now everytime this piece of code is executed, it will put a 1 back in our
flag, which is what we want.  But what if the flag is altered at other locations?
lets go on, Ctrl-D again and look at you next target.
:004CC4A3 8A45E7                  mov al, byte ptr [ebp-19]
:004CC4A6 A28CE34F00              mov [004FE38C], al
Look familiar?  It's just like the piece we just cracked, so patch it in a 
similar manner
8a45e7		mov al, byte ptr [ebp-19]
to
B001		mov al, 01
90		nop
There are several more places you need to patch.  A complete list (including)
the two we just did is below.
LOCATION			PATCH TO MAKE
----------------------------------------------
CS:4CD599			JUST CHANGE 00 TO 01
CS:4CC2EE			NONE REQUIRED
CS:4CC45B			MOV AL,01 - NOP
CS:4CC4A6			MOV AL,01 - NOP
CS:4CC5A5			MOV AL,01 - NOP
Now your program will run, registered.  You can even type in whatever name/#
combination you want over and over.  As nice as this seems, let's remember
what +Orc has taught us.  Never trust the programmer.  It sure seems like
ZMUD is running fine, and it may well be.  But set your clock ahead 30 days
and see what happens then.  Aha, it doesn't work like we thought after all.
Let's remember that patching a program, much less a patching it several
different places, is not the ZEN way.  The best way to crack this, or any
other program, is not to patch it at all.  Let's get a valid Name/Number pair.
I must admit that I have not been able to complete this crack.  If anyone can
show me what I have missed please let me know.
Let's look back at the original piece of code we ripped from WDASM.
:00491F46 A17CE34F00              mov eax, [004FE37C]
:00491F4B E868A30300              call 004CC2B8			;Call to some protection scheme
:00491F50 803D8CE34F0000          cmp byte ptr [004FE38C], 00	;Is flag 0 ?
:00491F57 740F                    je 00491F68			;Yes, Beggar Off
:00491F59 8B45FC                  mov eax, dword ptr [ebp-04]
:00491F5C C7802801000001000000    mov dword ptr [ebx+00000128], 00000001
:00491F66 EB0A                    jmp 00491F72			;No, go on Good Guy
So, let's look at the call right before the check.  Load up your 20 meg WDASM
listing and look for the subroutine starting at 4CC2B8.  I'll show the lines I
have tagged as important, with comments below.
:004CC310 E81F0D0000              call 004CD034		;Load name and # from LICENSE.INI
			;Puts Code at DS:4FE390 and Name at DS:4FE490
:004CC315 33C0                    xor eax, eax
:004CC317 A090E44F00              mov al, [004FE490]	;Size of Name in al		
:004CC31C 85C0                    test eax, eax		;Is it 0 length?
:004CC31E 7E6E                    jle 004CC38E		;Yes, beggar off
:004CC320 8945BC                  mov dword ptr [ebp-44], eax	;No, continue
:004CC323 C745DC01000000          mov [ebp-24], 00000001
Then lots of crap, that I'll skip for now and you'll find:
:004CC3ED 803D90E34F000C          cmp byte ptr [004FE390], 0C	;Is size of Code 12?
:004CC3F4 0F85E8000000            jne 004CC4E2			;No, beggar off
So this tells us that our code must be 12 digits.  Now we are getting somewhere
Skip down a bit and we get to this code, I'll skip how I figured out the 
comments I've added, it's just simple tracing.
:004CC41D B902000000              mov ecx, 00000002	;Counter (# of bytes)
:004CC422 B890E34F00              mov eax, 004FE390	;Point to Code
:004CC427 E85C63F3FF              call 00402788		;Copy 2 bytes of Code to DS:7CF4D0
:004CC42C 8D85B0FDFFFF            lea eax, dword ptr [ebp+FDB0] ;Point to DS:7CF4D0
:004CC432 E8F538F8FF              call 0044FD2C
At this point I printed out a copy of the subroutine at 44fD2C and anyalized
it's behavior.  All it did was take the two bytes copied from the code and
performed the following operation.
result = 1st*A +2nd
I also noticed that this code was executed several times.  What happens is this
, the code is analyzed in pairs, with each pair having a "result".  Farther down
in our code we come accross this:
:004CC49A E85566F3FF              call 00402AF4		;????
:004CC49F 85C0                    test eax, eax		;Test ax
:004CC4A1 7508                    jne 004CC4AB		
:004CC4A3 8A45E7                  mov al, byte ptr [ebp-19]	
:004CC4A6 A28CE34F00              mov [004FE38C], al		;Put 0 into our main flag
So obviously we want the call 00402af4 to return a 1, not a 0 so that we will
skip putting 0 into our REGFLAG, [4FE38C].  Well, we trace into the call and 
we find that what it does is compare the result for the current Pair to the
first character of the Name.  For example, I typed in Sync as my Name.  The
first character is "S" or 53h.  so I need "pairs" that give "results" of 53.
I used "83" as my pairs, the calculation is shown below.
8 * A + 3 =53h
So I used '838383838383' as my regcode, and it fails again.  So keep going.
:004CC4C8 8B45C8                  mov eax, dword ptr [ebp-38]	;Sum of first 5 "results"
:004CC4CB B964000000              mov ecx, 00000064
:004CC4D0 99                      cdq
:004CC4D1 F7F9                    idiv ecx		;Divide Sum by 64
:004CC4D3 3B55FC                  cmp edx, dword ptr [ebp-04]  ;Remainder = 6th "result"
:004CC4D6 742B                    je 004CC503		       ;Yes, jump ahead	
:004CC4D8 8A45C7                  mov al, byte ptr [ebp-39]	;No, 
:004CC4DB A28CE34F00              mov [004FE38C], al		;Put 0 in REGFLAG
:004CC4E0 EB21                    jmp 004CC503
So I need my 6th result, from the 11th and 12th digits of my Code to be the 
remainder of the the sum of the first five results divided by 64.  Easy enough.
results each = 53h so 53h + 53h + 53h + 53h +53h = 19F
19f/64 =4 with remainder of F
so I'll pick 1 as my first digit (arbitrarily)
1*A+_ =F    The _ must be 5 since A+5=F.   Easy, my 11th and 12th digits are
(for now) "15", to give a total code of '838383838315'.
If you trace through now, with an eye on our REGFLAG at [4FE38C], You'll notice
that we now skip over all the code which changes it from 1 to 0, until the
code below.
:004CC58A E88DADF3FF              call 0040731C		;THIS CALL
:004CC58F DD5DD0                  fstp qword ptr [ebp-30]
:004CC592 9B                      wait
:004CC593 33C0                    xor eax, eax
:004CC595 5A                      pop edx
:004CC596 59                      pop ecx
:004CC597 59                      pop ecx
:004CC598 648910                  mov dword ptr fs:[eax], edx
:004CC59B EB27                    jmp 004CC5C4
:004CC59D E9E66CF3FF              jmp 00403288
:004CC5A2 8A45C7                  mov al, byte ptr [ebp-39]	
:004CC5A5 A28CE34F00              mov [004FE38C], al		;Put 0 in REGFLAG
I have not been able to skip the code at 4cc5a2 yet, but here's what I have.
If you follow the call 0040731C, you enter a subroutine which in turn calls another 
at CS:40732f .  Here is the code out of that routine, as I understand it. DI
register is loaded with what is, I believe, the hex value of the first
character of the name, again 53h for "S".
:0040728F 6683FF01                cmp di, 0001		;Is it less than 1?
:00407293 7278                    jb 0040730D		;Yes, beggar off
:00407295 6683FF0C                cmp di, 000C		;Greater than C ?
:00407299 7772                    ja 0040730D		;Beggar off
This second check is crucial.  If you reverse this jump it runs fine and is
registered.  However, I cannot see how the first character of a Name could
possibly be between 1 and C hexadecimal.  They are unprintable chars.  If 
anyone can figure this out, please share.  Anyway below is my working crack, 
as I said earlier not quite complete.
-------------------------------------------------------------------------
Change the code above to:
:00407295 6683FFFE                cmp di, 00FE       ;All chars are 
   Here is the first answer I got, from Epic Lord (25 July 1997).
  
  Dear Fravia,
Please find below an analysis for zMud. Maybe it can help +Sync :-) 
The problem is converting dates to Reals (8 bytes) and making 
operations on them.
BTW, expiry of the registration code is NOT related to the reg code itself.
It is directly related to the current date :-)
Date coding is standard date to 8 byte real. As: 35643 = 1-Aug-97,
35632 = 21-Jul-97. I used M$-excel to translate the dates :-)
The document is not written for direct publishing purposes, but informative
instead. You may or may not use my handle :-)
Best wishes to you and +Sync,
Epic Lord
***********************************
Here is the first patch for Registration control
* Referenced by a  Jump at Address:004CC51F(C)
|
:004CC524 C1F804                  sar eax, 00000004
:004CC527 8945D8                  mov dword ptr [ebp-28], eax
:004CC52A 66B90100                mov cx, 0001
:004CC52E 668B55D8                mov dx, word ptr [ebp-28]
:004CC532 668B45C8                mov ax, word ptr [ebp-38]
:004CC536 E8E1ADF3FF              call 0040731C
:004CC53B 8B45E0                  mov eax, dword ptr [ebp-20]
:004CC53E DD9850030000            fstp qword ptr [eax+00000350]
:004CC544 9B                      wait
:004CC545 803D8CE34F0000          cmp byte ptr [004FE38C], 00 -> HERE *****
                                  cmp byte ptr [004FE38C], FF -> :-)
:004CC54C 0F8493000000            je 004CC5E5
:004CC552 33C0                    xor eax, eax
:004CC554 55                      push ebp
:004CC555 689DC54C00              push 004CC59D
:004CC55A 64FF30                  push dword ptr fs:[eax]
:004CC55D 648920                  mov dword ptr fs:[eax], esp
:004CC560 837DF060                cmp dword ptr [ebp-10], 00000060
:004CC564 7C0D                    jl 004CC573
:004CC566 8B45F0                  mov eax, dword ptr [ebp-10]
:004CC569 056C070000              add eax, 0000076C
:004CC56E 8945DC                  mov dword ptr [ebp-24], eax
:004CC571 EB0B                    jmp 004CC57E
***********************************
Here is the second patch for Registration control, however, 
reg expiry goes on
* Referenced by a  Jump at Address:004CC59B(U)
|
:004CC5C4 66B90100                mov cx, 0001
:004CC5C8 66BA0100                mov dx, 0001
:004CC5CC 66B8CC07                mov ax, 07CC
:004CC5D0 E847ADF3FF              call 0040731C
:004CC5D5 DC5DD0                  fcomp qword ptr [ebp-30]
:004CC5D8 DFE0                    fstsw ax
:004CC5DA 9E                      sahf
:004CC5DB 7608                    jbe 004CC5E5
:004CC5DD 8A45E7                  mov al, byte ptr [ebp-19] --> HERE *****
                                  mov al,01
                                  nop (thanks +Sync)
:004CC5E0 A28CE34F00              mov [004FE38C], al
After the two patches above, register as anybody wit any reg. id. to
eliminate the "unregistered" name :-)
***********************************
Here is the triggering part for the Date to Real Conversion routine.
* Referenced by a CALL at Addresses:004074A8, :0045E5CB, :004CC536, 
                                   :004CC58A, :004CC5B6, :004CC5D0  
|
:0040731C 53                      push ebx
:0040731D 56                      push esi
:0040731E 57                      push edi
:0040731F 83C4F8                  add esp, FFFFFFF8
:00407322 8BF9                    mov edi, ecx
:00407324 8BF2                    mov esi, edx
:00407326 8BD8                    mov ebx, eax
:00407328 54                      push esp
:00407329 8BCF                    mov ecx, edi
:0040732B 8BD6                    mov edx, esi
:0040732D 8BC3                    mov eax, ebx
:0040732F E828FFFFFF              call 0040725C  --> HERE *****
:00407334 84C0                    test al, al
:00407336 750A                    jne 00407342
* Reference to String Resource ID=65414: "Invalid argument to date encode"
                                  |
:00407338 B886FF0000              mov eax, 0000FF86
:0040733D E876EDFFFF              call 004060B8
***********************************
You'll find a date conversion routine in address 0040725C. 
If everything is OK, AL returns non zero, as follows.
- ------ cut
|:004072DA(C)
|
:004072DF C1FA02                  sar edx, 00000002
:004072E2 03F2                    add esi, edx
:004072E4 2BF0                    sub esi, eax
- ----- cut
:00407303 8B4508                  mov eax, dword ptr [ebp+08]
:00407306 DD18                    fstp qword ptr [eax]
:00407308 9B                      wait
:00407309 C645FD01                mov [ebp-03], 01
***********************************
And here is the init part of first dates
:004CC514 83C002                  add eax, 00000002
:004CC517 8945C8                  mov dword ptr [ebp-38], eax
:004CC51A 8B45DC                  mov eax, dword ptr [ebp-24]
:004CC51D 85C0                    test eax, eax
:004CC51F 7903                    jns 004CC524
:004CC521 83C00F                  add eax, 0000000F
* Referenced by a  Jump at Address:004CC51F(C)
|
:004CC524 C1F804                  sar eax, 00000004
:004CC527 8945D8                  mov dword ptr [ebp-28], eax
:004CC52A 66B90100                mov cx, 0001 ---------------> DAY *******
:004CC52E 668B55D8                mov dx, word ptr [ebp-28] --> MONTH *****
:004CC532 668B45C8                mov ax, word ptr [ebp-38] --> YEAR ******
:004CC536 E8E1ADF3FF              call 0040731C
:004CC53B 8B45E0                  mov eax, dword ptr [ebp-20]
:004CC53E DD9850030000            fstp qword ptr [eax+00000350]
************************************
(c) Epic Lord, 25 July 1997
You are deep inside Fravia's page of reverse engineering,  
choose your way out:
 homepage
 
 links 
 anonymity 
 +ORC
 students' essays
 tools
 cocktails
 Antismut CGI-scripts
 search_forms
 mailFravia
 Is reverse engineering legal?