Windows Commander v4.51
[Reversing essay]
published by Tsehp, July 2000

Subject: Unpacking/ Cracking
Target: Windows Commander v4.51
URL: http://www.ghisler.com
Author: BlackB
Date: 2000-26-07
Tools used: SoftICE, HIEW, IceDump
Difficulty (scale 1-5): 3.5

Before starting!
This essay is for knowledge purposes only!!
Software developers spend much time in making their programs. They live from the money we give them!
Please buy good software!!
I. Introduction

Windows Commander: the beloved tool of us crackers. It's a very powerful 'Norton-Commander'-alike program that outperforms Microsoft Explorer with ease. Admitted, its look and lay-out is somewhat unprofessional-looking.....but the results count, not the look!

This essay will be pretty long in comparison with most of my other essays, so I did a lot of work on it. Enjoy and learn. And oh yea, before you start: note that this essay is not really suitable for real newbies. Some cracking experience is really needed.

II. About the protection
Packed: ASPack - CRC check - Keyfile - Nag
III. Cracking it

I did not try to make a valid keyfile for the program as the wincmd.key file must be 1024 bytes long and is encrypted and organised in a quite complex way.....in other words, I couldn't manage to make a valid keyfile. Maybe you can :-)

So.....I took this approach:

Step1: Manually unpacking
Step2: Finding the instructions that should be cracked
Step3: Patching the file with code insertion

Let the show begin!

Step1: Manually unpacking
*********************

WINCMD is packed with ASPack. Note that it's not ASProtect. In order to avoid problems later, open Procdump PE Editor and set characteristics of the .CODE section to E0000060.
I assume that you are familiar with manually unpacking, so I won't explain everything in great detail. Anyway, first thing to do know is the Image base and Image size. Therefore fire Procdump PE Editor on Wincmd32.exe and note that the Image Base == 400000 and the Image Size == 1A4000.
Now, first of all we have to find the OEP: Orignal program Entry Point, which is always found at the end of the unpacking routine. We won't trace from the start of the unpacker code as this is very time robbing. We want SoftICE to popup as close as possible to the end of the unpacking routine. How to accomplish that?
Run WINCMD, click on Help - About Windows Commander...but before you click on it, first set a breakpoint on MessageboxA in SoftICE. Now click it and SoftICE pops. Press F12 until you're in the main code. Take an offset of an instruction you see there, it doesn't matter what instruction. Keep that offset in your mind, or write it down if you forget easily. In my case it's 004E8562. Don't leave SoftICE yet! First set a bpm on that offset. In my case I should type: bpm 4E8562.
Now leave SoftICE, don't forget to clear your breakpoint on MessageBoxA, and leave WINCMD. Re-run WINCMD and SoftICE should popup.

Now, what has happened? Location 4E8562 is an instruction of the main WINCMD program. As long as the unpacker hasn't finished his job, that location or that instruction does not exist yet! Therefore, if we set a breakpoint on memory access, SoftICE will break on the moment the unpacker unpacks the instruction at location 4E5862.
This method saves us a lot of time as we don't have to trace from the start of the unpacking routine.

When SoftICE has popped, you should see an instruction like "REPZ MOVSD" or something like that. You could now go and trace, trace, trace, trace, avoiding unpacker loopies, etc... . That would work, but again.....it's slow.
We know that we're pretty close to the end of the unpacking routine, so why not scroll down in the code to look for a PUSH followed by a RET instruction...? And indeed, if you do you should see this:

Start partial code

0137:0059A4F3  POPAD
0137:0059A4F4  JNZ       0059A4FE
0137:0059A4F6  MOV       EAX,00000001
0137:0059A4FB  RET       000C
0137:0059A4FE  PUSH      00000000
0137:0059A503  RET

End partial code

Now this is actually kinda strange. The PUSH instruction should push the OEP value on the stack......but it seems that it only pushes 00000000 onto the stack. Well, set a breakpoint on 0059A4F3 POPAD , exit SoftICE, and SoftICE will pop on the POPAD instruction. And now you see this:

Start partial code

0137:0059A4F3  POPAD
0137:0059A4F4  JNZ       0059A4FE     <- This jumps
0137:0059A4F6  MOV       EAX,00000001
0137:0059A4FB  RET       000C
0137:0059A4FE  PUSH      005329B0     <- OEP
0137:0059A503  RET                    <- Leave unpacker code, start main code

End partial code

Seems like the OEP is written at the end of the unpacking routine. Maybe to mislead us crackers? If it is, it's kinda lame actually :-) Keep in your mind: OEP=5329B0.
Trace over the RET instruction and now you're where the real unpacked WINCMD code starts. Dumping time :-)
In SoftICE type: /dump 400000 1A4000 c:\temp\image.exe
This will dump the whole unpacked file to your harddisk. Clear all breakpoints in SoftICE and open Procdump PE Editor on IMAGE.EXE. First change the Entry Point to 5329B0-400000=1329B0. Then click SECTIONS and repeat following procedure for every section:

1. rightclick on a section
2. click on EDIT SECTION
3. copy VSize over PSize
4. copy RVA over Offset
5. change characteristics to E0000060

Click OK, exit Procdump and try to run IMAGE.EXE.......it works!
You won't need this file anymore. It's just meant to show you how you can unpack. Our final goal is to inline patch WINCMD. That means: patching it without unpacking.

Let's move on to the next step:

Step2: Finding the instructions that should be cracked
*****************************************

In this step the real cracking of Windows Commander is explained. First ask yourself: "What has to be cracked?". If you never used WINCMD, the only thing you can see is a nagscreen with three annoying buttons. But if you're used to it, you know it also has an annoying CRC check and that it displays that it's unregistered.

When I was cracking for a few months (i.e. when I was a newbie), I failed to remove the nagscreen of WINCMD. (I failed the rest too tho). The difficulty is that you don't have to try setting breakpoints on UpdateWindow or ShowWindow or any other of these NAG-applicable API calls, as it won't work. WINCMD first checks if a file named WINCMD.KEY is present, then it will decide whether is should display the nag or not. First make the file WINCMD.KEY. There should be nothing in it.....just make sure the file exists.
Set a breakpoint on ReadFile. Run WINCMD and SoftICE will break. Press F12 until you're in the main code. You should see this:

Start partial code

0137:004CE12B  MOV       EAX,ESI
0137:004CE12D  CALL      0043C02C
0137:004CE132  JMP       004CE13F
0137:004CE134  XOR       EBX,EBX
0137:004CE136  MOV       WORD PTR [EBP-011E],0000
0137:004CE13F  CMP       WORD PTR [EBP-011E],0080
0137:004CE148  SETZ      BYTE PTR [0053E6B9]  <- If valid key a '1' will be set

End partial code

And now it's just a matter of trial-and-error to find the 'jump-to-nag' instruction. Disable all breakpoints and start tracing. Note that pressing F12 about two times will fasten the process. While tracing you can see appear the splashscreen, and after a while you suddenly see that the splashscreen changes into a fixed window with the caption "Windows Commander". Scroll a bit up in the code and......do you see the conditional jump? :) I do........

Start partial code

0137:004CBF8A  TEST      AL,AL                   <- Registered?
0137:004CBF8C  JZ        004CBFB4                <- If registered, don't jump
0137:004CBF8E  MOV       EAX,[005366CC]
0137:004CBF93  MOV       EAX,[EAX+28]
0137:004CBF96  MOV       DL,01
0137:004CBF98  CALL      0041D5CC
0137:004CBF9D  MOV       BYTE PTR [00534C7C],01
0137:004CBFA4  MOV       BYTE PTR [00534C80],01
0137:004CBFAB  MOV       EAX,EBX
0137:004CBFAD  CALL      00418E9C
0137:004CBFB2  JMP       004CBFF2
0137:004CBFB4  MOV       DL,01
0137:004CBFB6  MOV       EAX,[0053E6B0]
0137:004CBFBB  CALL      00416E10
0137:004CBFC0  MOV       EDX,00000001
0137:004CBFC5  MOV       EAX,[EBX+000001B0]
0137:004CBFCB  CALL      0042FFE0                <- If you trace over this call
                                                    , you see the nag appear.

End partial code

It's not difficult: you trace and trace until you trace over the call 004CBFCB CALL 0042FFE0. Then, you see that nag appear which tells you that you're too far. Scroll up and you see this conditional jump 004CBF8C JZ 004CBFB4 .
Set a breakpoint on it, leave SoftICE, leave WINCMD and re-run WINCMD. Now, when SoftICE breaks, reverse the jump so it doesn't jump....and look.....the nag is gone!

If we would apply this patch now, we would get a CRC error. Therefore we won't patch yet, just keep the location we have to patch in mind. Just change a little unimportant byte to trigger the CRC error. As you can see, the CRC error appears when WINCMD is loaded for about 30 seconds. I set a breakpoint on the MessageboxA that appears, set a breakpoint on readfile, etc etc.... but nothing worked. It was very strange that there was no ReadFile anymore, as if a program CRC checks itself, it has to open itself with ReadFile.
This could only mean one thing: the CRC is calculated in the beginning, stored, and compared later. This makes finding the CRC a lot harder. We'll use the API CreateFileA to break on, as they are called a lot more then ReadFile thus more likely that the CRC check 's close to a CreateFileA breakpoint then to a ReadFile breakpoint.
There are about 10 CreateFileA's executed. 7 before the nag is drawn, and three when the nag is drawn. I first checked out the three when the nag is drawn, and they are of no importance. So I took the seventh CreateFileA break....why? No idea, I thought it would be a good time to calculate a CRC just before the nag is drawn. Set a breakpoint on CreateFileA, re-run WINCMD and when SoftICE has popped 7 times press F12 until you get in the main code. Should look like this:

Start partial code

0137:00451E41  MOV       [EBP-10],EAX
0137:00451E44  CALL      00403508
0137:00451E49  CMP       DWORD PTR [EAX+0000000C],00
0137:00451E50  JZ        00451E65
0137:00451E52  LEA       EAX,[EBP-0124]

End partial code

Start tracing (F10) and notice how many loops are executed, and how many MOVZX instructions are executed. Looks suspicious! Keep on tracing and avoid loops by placing a breakpoint just outside them (like you would avoid loops when tracking an unpacker) until you see this......and the XOR makes it feel suspicious:

Start partial code

0137:00451EF1  MOV       EAX,[EBP-18]
0137:00451EF4  MOV       EAX,[EAX]
0137:00451EF6  XOR       EAX,2A67BE65 <- CRC gets encoded
0137:00451EFB  MOV       [EBP-08],EAX <- Encoded CRC into [EBP-08]
0137:00451EFE  PUSH      EBP
0137:00451EFF  CALL      00451D68     <- This call calculates the CRC the file now has

End partial code

If you look at EAX it should contain the value: F5BE0EFF
Now let's trace into CALL 00451D68

Start partial code

0137:00451D8E  MOV       EDX,[EBP+08]
0137:00451D91  MOV       EDX,[EDX+08]
0137:00451D94  MOV       [EDX],EAX
0137:00451D96  MOV       EAX,[EBP+08]
0137:00451D99  XOR       DWORD PTR [EAX-04],F5A3E289 <- Encoded CRC into [EAX-04]

End partial code

I found out that the CRC in [EBP-08] is the fixed CRC as calculated when the file is not patched. The fixed CRC at [EBP-08] gets compared with the CRC at [EAX-04]. This is the newly calculated CRC. If the file is patched, [EAX-04] will contain another value then [EBP-08]. To patch, we will move the fixed CRC (=F5BE0EFF) into [EAX-04].
We do this by replacing the instruction:

XOR DWORD PTR [EAX-04],F5A3E289 by
MOV DWORD PTR [EAX-04], F5BE0EFF

Keep in mind that you can't apply the patch yet as WINCMD is packed. I will deal with that at the end. You can see the comparisons if you set a bpm on EAX-04.

Last thing to patch is the "UNREGISTERED" string on the splashscreen and the Windows Commander caption. I did this by making WINCMD believe our keyfile is valid. Of course it is not valid, we just make WINCMD believe it is ;-)
I did this by experimenting with some jumps when the keyfile is processed. Set a breakpoint on ReadFile, run WINCMD, SoftICE breaks, press F12 until you're in the main code and.....remember this code snippet?

Start partial code

0137:004CE13F  CMP       WORD PTR [EBP-011E],0080
0137:004CE148  SETZ      BYTE PTR [0053E6B9]      <- If valid key a '1' will be set
0137:004CE14F  MOV       BYTE PTR [EBP-011F],01
0137:004CE156  TEST      BL,BL
0137:004CE158  JZ        004CE448                 <- If valid key then jump

End partial code

Changing this JZ into a JMP will do the job.

If you're new to code insertion it may be a good time now to read my essay on ACDSee. (if you're not on my page, this link won't work....first go to my page: http://blackb.tsx.org You can find the essay in the "Essays" section.) However, if you have experience doing this stuff or if you just FEEL like you can do this, read on...

Step3: Patching the file with code insertion
********************************

Here's a basic scheme on how code insertion works and how we will apply it:

...unpacker code....
end_of_unpacker_code: jmp insertion
RET

insertion:
patch_instructions_in_memory
PUSH OEP
RET

Let's take a look the end of our unpacking routine:

Start partial code

0137:0059A4F3  POPAD
0137:0059A4F4  JNZ       0059A4FE
0137:0059A4F6  MOV       EAX,00000001
0137:0059A4FB  RET       000C
0137:0059A4FE  PUSH      005329B0
0137:0059A503  RET

End partial code

The JNZ 0059A4FE and all instructions below will suite perfectly to put a jump to our insertion code. Let's find the location of that jump in the packed .exe: open Wincmd32.exe in a hex editor and search for following hex values: 6800000000C3. These are the hex values for the instruction PUSH 0000000 (=6800000000) and RET (=C3)......I hope you do remember that it's PUSH 00000000 we're looking for and not PUSH 005329B0, because that is only added at the end of the unpacking routine!
Found it? Offset value should be 83CFE. Now that 's the location of the PUSH, but we want the location of the JNZ. So let's look if you see hex value '75' somewhere in the neighbourhood.....and oh yea, at offset 83CF4 we see a 75.
Little check: 4FE-4F4= A = 10 bytes before the push is the JNZ. And if you check with your hex editor that's okay.

Next thing to do is finding an empty loaction we can use to insert our patching code. In most cases I always find much zero marked space at the end of the file. I decided to use offset location 8BC10 to insert our code. Open up HIEW, load Wincmd32.exe into it, press F4, then F3 to go to assembly mode, press F5(=goto offset) and type 83CF4. Enter.
You should be right at the JNZ now. Press F3 to start editing, then press F2 to insert an assembly instruction. Remove the instruction you see appear (backspace) and type: JMP 8BC10, then press enter. Now type: 9090 to remove unneeded parts of instructions.

So far so good. Before we start inserting our code, we gotta know:
1. What locations we have to patch
2. What data we have to move into that location

[Locations to patch]

004CBF8C JZ 004CBFB4
00451D99 XOR DWORD PTR [EAX-04],F5A3E289
004CE158 JZ 004CE448

to

004CBF8C JNZ 004CBFB4
00451D99 MOV DWORD PTR [EAX-04],F5BE0EFF
004CE158 JMP 004CE448

In order to move the right data to these locations, we have to know with what instructions we have to deal with: A jump instruction can be short jump and then the hex code will be EB, but a jump can also be a long jump and then it will start with hex value 0F. A short jump will be only 2 bytes and a long jump will be 4 to 6 bytes, etc... .
As we have manually unpacked Windows Commander, we can use the unpacked file to make the changes. That way you will be able to see what values should be changed. So open HIEW and the unpacked IMAGE.EXE and apply the patch.

[Data changes]

74 becomes 75 at 4CBF8C
8170FC89E2A3F5 becomes C740FCFF0EBEF5 at 00451D99
0F84EA020000
becomes E9EB02000090 at 004CE158

Note that the 90 in E9EB02000090 is to NOP out an unneeded byte. The change from JZ to JMP caused a decrease of 1 byte. If you don't NOP that out, Windows Commander may crash, or do unexpected things.

All righty....goto your code insertion location in the Wincmd32.exe: 8BC10 and insert following code:

mov b, [004CBF8C], 075
mov d, [00451D99], 0FFFC40C7
mov w, [00451D9D], 0BE0E
mov b, [00451D9F], 0F5
mov d, [004CE158], 00002EBE9
mov b, [004CE15D], 090
push 005329B0
ret

Some explanation and comments:

C740FCFF0EBEF5 is 7 bytes long. And we can only move a maximum of 4 bytes at a time. Thats why I first move 4 bytes (d= double word= 4 bytes) then I move 2 bytes (w= word= 2 bytes) and then finally the last 1 byte (b= byte= 1 byte). Also notice that the order of bytes is reversed, as the last byte will get stored first. So if you want to store EA10 into a certain location you type: mov w, [location], 010EA
The 'zero' in front is obliged by HIEW. Otherwise it will refuse.

That's it. If you did everything right, Windows Commander should run fine, without a nag and just like you're really registered.

IV. In the end

Questions, bugs, other comments about this essay are welcome at cracking@softhome.net
Greets goto Risc, C_DKnight and Kwazy Webbit :-)

greets

The Blackbird

Endnote:
Essay written by The Blackbird 1999-2000
This essay can be freely distributed/ published/ printed etc... as long as no modifications are made.