Windows Commander 3.02
An "antivirale" protection scheme defeated

by iNCuBuS++

(06 August 1997, slightly edited by Fravia)


Courtesy of Fravia's page of reverse engineering

Well, we did not have much at the +HCU about virale checksum protection scheme, a new welcome addition by a new welcome +cracker!

Cracking Windows Commander 3.02 (32 bit) by iNCuBuS++

An "antivirale" protection scheme defeated


This program uses key-file protection - that means that, when you register it, you receive a 1024 bytes long key file called "wincmd.key" from the author which contains info about registered user, eg. you. The file is encrypted and it took me weeks to trace through the decryption routines in order to break the code, but it got me bored to death. Ok, so it was the wrong approach! What happens if the key file isn't there? Well, prg opens the nagscreen saying that it is unregistered copy and that you need to press one of the 3 buttons (always different, of course) on the bottom of the window to proceed. So, we will have to eliminate the nagscreen in the usual way.

Now, the protection scheme itself isn't particularly interesting (though it will be explained) , but the fact that this target has a virus-protection mechanism that reacts in the case EXE file gets changed is indeed interesting for us. It prevents us from disabling the nagscreen because it will issue a virus warning and terminate the program if it detects any modification to WinCmd32.EXE.
Since there are no suspicious files in the progdir that could possibly carry some form of signature for EXE file and there are no entries in the system registry relative to WinCmd, the logical assumption is that some form of checksum (CRC) is hidden somewhere in the EXE file which is later compared with CRC value calculated by the program. If they do not match... well, you get the idea...
But, let's stay on the protection scheme for now.
We do a
bpx KERNEL32!CreateFileA
and start WindowsCommander.It will break into the ICE several times before we get to the right call. We'll check which file is being opened by doing
dd ss:esp+4
and looking at the parameters on stack. The first DWORD is a ptr to the filename string, so we can easily find out which call opens our key file.We are looking for something like
C:\UTIL\WINTOOLS\WINCMD\wincmd.key
We could just look at EAX register to find out the string's address (as it is loaded in EAX and then pushed onto stack), but it might depend on the compiler and so it is not reliable in all cases.

OK, so we have found the right call. We exit the function with F11 and start tracing through the code.Immediately after the function call we find this:
                14F:0043993A   MOV    [EBP-1C],EAX            ;EAX holds the error number
                14F:0043993D   CMP    DWORD PTR [EBP-1C],-01  ;Key file opened ?
                14F:00439941   JNZ    00439959                ;Yes, jump now!
                14F:00439943   CALL   004034FC                ;..and we proceed here
 		
After a while we'll get to the following piece of code:
                14F:489B08    MOV   WORD PTR [EBP-1E],00      ;There's no key file
                14F:489B0E    CMP   WORD PTR [EBP-1E],0080    ;IS there a key file?
                14F:489B14    SETZ  BYTE PTR [004B47F1]       ;Set the flag if there IS!
 				   
So the flag remains unset and we know it's address, so we do a
bpmb 4B47F1 rw ,
disable breakpoint on CreateFileA and continue execution of the program. SoftICE will pop up at
 
                14F:0048A1BC        MOV        AL,[004B47F1]
                14F:0048A1C1        RET
 			   
and it will continue at
                14F:004896D9        TEST   AL,AL       ;Does the key file exist ?
                14F:004896DB        JZ     0048971D    ;NO, we will jump now!
 						  
We trace just a few instructions more and we find
                14F:0048A1B4        MOV        AL,[004B47F0]
                14F:0048A1B9        RET
 					   
What could that be? Well, your instinct should say: "Hey! It's a flag that tells whether the nagscreen should be opened!" We exit the subroutine and get to the following:
                14F:00489722        TEST  AL,AL      ;SHOULD the nagscreen be opened?
                14F:00489724        JZ    0048974C   ;YES! So, jump!
 								  
And here we are! We just have to prevent program from jumping at 48974C ! We will do it by changing JZ into some "neutral" instruction like
OR EAX,EAX
and we're done. We could've used NOPs as well, but we'll stick to +ORC's teachings.

Now the nagscreen is removed, but there still remains the annoying message while the program is loading saying
This copy is registered to: NOT REGISTERED
The same thing appears in the window caption. This is ideal opportunity to sign our work. We will replace the string "NOT REGISTERED" with our handle. OK, we take some hex editor and start searching for the string. We try every letter combination possible, but find nothing. Well, that means the string is encrypted. So, we have to pinpoint the decryption code in order to break the encryption method and to find the string in its encrypted form.

At the point where we eliminated the protection, we can find several instances of string "NOT REGISTERED". We do a BPR on each instance, disable breakpoints in order to allow the program to continue executing and then, when it is up and running, quit it.Now we re-enable BPRs and start WinCmd again. It will break into the ICE at 14F:0040650D (your addresses may differ) where something is being copied into the first of BPRed locations (at 157:00504907). The source is at 14F:0043D522. We continue executing the program and the next time it will break at 14F:00426A97, in the middle of the decryption routine which looks like this:
 
                14F:00426A84     CMP     CL,0A       ;Skip 
                14F:00426A87     JZ      00426A96    ;characters !
                14F:00426A89     MOV     CL,[EDX]    ;Get char.
                14F:00426A8B     SUB     CL,20       ;THIS IS 
                14F:00426A8E     XOR     CL,77       ;THE DECRYPTION
                14F:00426A91     ADD     CL,20       ;CODE !!!
                14F:00426A94     MOV     [EDX],CL    ;Replace the decrypted char
                14F:00426A96     INC     EDX         ;Next char
                14F:00426A97     MOV     CL,[EDX]    ;Get char
                14F:00426A99     TEST    CL,CL       ;Is it the end of the string?
                14F:00426A9B     JNZ     00426A84    ;If NOT, return to the
                14F:00426A9D     RET                 ;the top of the loop !
 			  
Now we know the en/decryption method, original string's location and it's encrypted form (which we'll later use to find it when we edit the EXE...).

Notice the symmetrical construction of the decryption routine. It SUBs 20h, XORs 77h and ADDs 20h. If we wanted to ENCRYPT the string we would have to use the SAME sequence of ops. That means that the same routine can be used to encrypt the string as well as to decrypt it. How you're going to crypt your string is up to you, but the simplest method is to replace the original encrypted string with your unencrypted string and then let the program encrypt it using the above en/decryption routine.

There are few other annoying strings, but they reside in the resource section of the EXE file and they are not encrypted, so modifying them shouldn't present a problem.

Now is the time to crack the app. We get some hex editor (HEdit is my favourite), open the wincmd32.exe file and
SEARCH FOR:        74 26 A1 74 C5 4A 00 (it's at offset 88B24)
REPLACE WITH:      0B C0 A1 74 C5 ...
and the nag screen is eliminated! Now we are going to change our handle. In this example we will use mine (" iNCuBuS++ "):
SEARCH FOR:        79 78 63 97 65 72 70 7E 64 63 72 65 72 73 (the original string)
REPLACE WITH:      97 97 97 5E 79 74 42 75 42 64 9C 9C 97 97 (my handle) 
We find this sequence at file offset 3C914.

That should do it. But, now we come to the virus protection mechanism. We start cracked
WinCmd... it says "This copy is registered to: ", there is no nagscreen, but before you could do anything, the virus warning appears saying that the EXE file has been modified and the program terminates!

Now, we could disable the entire virus protection (I did it with WinCmd 3.01), but that just wouldn't be too good 'cause it would be unable to react in case of a real virus. It's better to "persuade" the program that our new CRC is the right one. That way, the virus protection will still be able to react in case some virus attaches itself to the EXE file.

Again, we do a
bpx CreateFileA
and start WinCmd. After a few breaks we will get to the point where WinCmd32.exe file is being opened. We will start tracing from here. If the file was successfully opened, program will enter the loop in which it reads the file to the buffer in parts 8000h bytes long and calculates the checksum splitting up each part in 5 chunks 15B0h bytes long. File buffer begins at 157:00520434 (your addresses may differ !!!). The checksum calculation routine looks like this:
 
                ; ESI = 000015B0 - chunk length
                ; EDI = 00520434 - ptr. to file buffer
 
                14F:004446F0        MOV    CL,[EDI]      ;Get a byte from buffer.
                14F:004446F2        ADD    EAX,ECX                       
                14F:004446F4        ADD    EDX,EAX                       
                14F:004446F6        INC    EDI           ;Move to the next byte in the buffer.
                14F:004446F7        DEC    ESI           ;Decrease chunk length counter.
                14F:004446F8        JNZ    004446F0      ;Get back to the top of the loop
                                                         ;if it's not the end of the chunk.
                14F:004446FA        MOV    [EBP-04],EAX  ; SS:0072F9F0
                14F:004446FD        MOV    [EBP-08],EDX  ; SS:0072F9EC
                14F:00444700        POP    ESI                        
                14F:00444701        POP    EDI                       
                14F:00444702        MOV    EAX,[EDI]     ;Get the chunk length counter.
                14f:00444704        ADD    [EBP-0C],EAX  ;Move buffer ptr to the next chunk.
                14F:00444707        MOV    EAX,[EBP-04]  ; SS:0072F9F0
                14F:0044470A        MOV    ECX,[EBP-08]  ; SS:0072F9EC
                14F:0044470F        CDQ
                14F:00444710        IDIV   ECX                      
                14F:00444712        MOV    [EBP-04],EDX               
                14F:00444715        MOV    EAX,[EBP-08]               
                14F:00444718        MOV    ECX,0000FFF1              
                14F:0044471D        CDQ
                14F:0044471E        IDIV   ECX                        
                14F:00444720        MOV    [EBP-08],EDX           
                14F:00444723        TEST   EAX,EAX                     
                14F:00444725        JG     004446C8               
                14F:00444727        MOV    EAX,[EBP-08]              
                14F:0044472A        SHL    EAX,10                      
                14F:0044472D        OR     EAX,[EBP-04]             
                14F:00444730        MOV    [ESI],EAX    ; DS:0072FB40 - CALCULATED CHECKSUM
                                                        ; RESIDES AT THIS DWORD !!!
                                    
This is a part of the routine that calculates the checksum of one file part. It is being called from within a loop for each part of the file. After the last part has been processed, the checksum is left at DS:0072FB40. We will naturally place a bpmd at that address, so that we could track what is happening to the checksum. Eventually, we'll get to the following:
        14F:0045F7B6  MOV  EAX,[EBP+08]                  ;
here => 14F:0045F7B9  XOR  DWORD PTR [EAX-04],F5A3E289   ;The CRC is being XOR-ed and  
                                                         ;it's value-to-be-compared
                                                         ;is now complete.
After this we'll get to the point where this value is being compared with some other value (probably the EXPECTED CRC):
        14F:0045F68B        MOV        EAX,[EBP+08]
        14F:0045F68E        MOV        EAX,[EAX+08]
here => 14F:0045F691        MOV        EAX,[EAX-04]     ;Get the calculated CRC'S
                                                        ;value-to-be-compared
        14F:0045F694        MOV        EDX,[EBP+08]
        14F:0045F697        MOV        EDX,[EDX+08]
        14F:0045F69A        CMP        EAX,[EDX-08]     ;Compare it with expected value
        14F:0045F69D        SETZ       AL               ;Set the flag if they DO !
 													 
We could change the code here to accept any value , but that would disable virus protection and we don't want that. Instead, we will take a closer look at the address where the expected CRC is (it's at 0072FB3C). Whatever happened at that address, happened earlier, so we will have to restart tracing (from the checksum calculation loop for instance, or immediately after it), but, this time with a bmpd set at it. We'll get to:
 
         14F:0045F961        MOV  EAX,[EBP-20]  ;Get the expected CRC's 
		                                        ;original address.
         14F:0045F964        MOV  EAX,[EAX]     ;Get the expected CRC.
         14F:0045F966        XOR  EAX,2A67BE65  ;XOR it and the CRC's
                                                ;value-to-be-compared
here =>  14F:0045F96B        MOV  [EBP-08],EAX  ;is done. So store it 
                                                ;(at 72FB3C)! 
From the above code we can get the expected checksum's address (it's 45F671) and its value (it's 9DEBC2EA). We will replace this value with our own.

Take a look at following expression:
D = A XOR B
In our case it's
EXPECTED_CRC's_VALUE_TO_BE_COMPARED = EXPECTED_CRC XOR 2A67BE65
and
CALCULATED_CRC's_VALUE_TO_BE_COMPARED = CALCULATED_CRC XOR F5A3E289
and in order not to invoke the virus protection mechanism the following must be true:
EXPECTED_CRC's_VALUE_TO_BE_COMPARED = CALCULATED_CRC's_VALUE_TO_BE_COMPARED
Now, take a look at THIS:
A = D XOR B ,
that is
EXPECTED_CRC = EXPECTED_CRC's_VALUE_TO_BE_COMPARED XOR 2A67BE65
In our case, the expected value DOES NOT equal the calculated one, due to EXE file's changes, so we have to MAKE IT equal. We will take the CALCULATED_CRC's_VALUE_TO_BE_COMPARED at the point of comparation and apply it in the above expression as it was EXPECTED_CRC's_VALUE_TO_BE_COMPARED. That way we'll get the new EXPECTED CRC with which we'll replace the original one. The next time the program runs, it will get our NEW expected CRC, XOR it and compare the result with calculated CRC'S value-to-be-compared. Now they DO match (since we took that EXPECTED_CRC's_VALUE_TO_BE_COMPARED equals CALCULATED_CRC's_VALUE_TO_BE_COMPARED when we calculated the new expected CRC) and the program finally works (with ICE beneath).Only thing that remains is to edit EXE file once again and make the permanent CRC change.So we will search for the original EXPECTED CRC, and replace it with our new value:
SEARCH FOR: EA C2 EB 9D (it's found at file offset 5EA71)
REPLACE WITH: 05 C4 70 4C (..for example. Your new CRC will probably differ)
And that's it! You've made it! It wasn't hard after all. The current version of Windows Commander i 3.03 and can be found in a form of patch at WinCmd's home page:
http://www.ghisler.com
This patch won't work on the cracked EXE file, so you'll have to patch it first and then crack it. Or you can (which is even better) try to crack the patcher to accept your new or any CRC !
(c) iNCuBuS++, 1997. All rights reserved.
You are deep inside fravia's page of reverse engineering, choose your way out:

homepage links red anonymity +ORC students' essays tools cocktails
antismut search_forms mailFraVia
is reverse engineering legal?