snippets
Back to the Snippets
UNPACK/UNPROTECT COM FILES USING DEBUG.EXE
(old powerful dos debugging - still useful today)
("An acquarium for your viri")
BY
THE UNDERTAKER -=BANDA=-
more than slightly edited by reverser+, 16 January 1998
Courtesy of Reverser's page of reverse engineering

Most of today's com packers & protectors can be removed using DOS 
debug. Many young crackers seem to have forgotten the mighty power 
of this very small and powerful utility: "the swiss knife of all 
crackers", as Master +ORC wrote.

debug.exe  	20.522 bytes  (MS dos ver. 7, usually inside C:\WINDOWS\COMMAND)
symdeb.exe	37.021 bytes  (Ver. 4, Micro$oft)

This method is very useful, If you don't have tools around you. Also it
works with most of the popular packers & unpackers. 
Actually DEBUG is a very powerful tool when it comes to the handling 
of com files. 
In those almost forgotten days, our bane micro$oft wanted to grab the 
market by producing good programs, an approach they have long forgotten, 
seen that it is now easier to grab the market bankrupting the adversaries. 

Anyway, that way DEBUG came up. 
Once they captured the market, they only cared about the money. 
Finally ending up with stupid programs like micro$oft word, access, VB+ 
and all other junk, useless programs that we are lobotomized with.

Before we start this approach, let's check how com files are organized.

 Com files are memory images that do not have any relocatable item. 
 They can only fit into one segment (SS,ES,DS = CS).

 Com files has a size of 64k. 
 Maximum amount one offset can vary between 0000 - FFFF

 Com files are slightly faster than the exe files, when it comes to 
 execution (bet you didn't know it).

 Com files do not have a header nor a checksum, like exe files.

 Com files should start at 100h, immediatly above the PSP.

 At the time of loading the com file. ES,DS,SS,CS are pointed to PSP. 
 And SP is pointed as high as possible in memory, minus 2 bytes 
 (SP = FFFE). In fact MS-DOS pushes a zero word on the stack before 
 entry.

Well, the information above will help you to handle com files. 
Ok, let us start the work. You'll see how you can UNPACK a 
file packed using an unknown packer using old silly debug!!

First, using a packer, compress your com file
I use pklite to pack a target com file of mine (CO.COM) 
Then load your packed com file using DEBUG.EXE

DEBUG CO.COM

Now type (r) to check the register contains.

-r
AX=0000  BX=0000  CX=0F6D  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=20AB  ES=20AB  SS=20AB  CS=20AB  IP=0100   NV UP EI PL NZ NA PO NC
20AB:0100 B80524        MOV     AX,2405
-

Check the CX register. 
This contains the program size. If you convert the value inside the CX 
register to decimal you will get the actual file size, here it is
0xF6D = 3949 bytes

Once you uncompress the program you will have to "adjust" the value of 
CX, of course.

To uncompress the file just use the command go (g) and exit from the program. 
Then you simply land inside the debugger.

-g

After having exited the program you will see the following message:

Program terminated normally
-

Ok, Now you have got in memory the original(Uncompressed) file (of course: the 
point of any compressor is to compress a file UNTIL IT MUST RUN, duh). 
To Verify that you have an uncompressed program, you can chek the Unassembly 
listing by typing (u).

-u100
20AB:0100 E9B903        JMP     04BC
20AB:0103 0A434F        OR      AL,[BP+DI+4F]
20AB:0106 2031          AND     [BX+DI],DH
20AB:0108 2E            CS:
20AB:0109 3020          XOR     [BX+SI],AH
20AB:010B 286329        SUB     [BP+DI+29],AH
20AB:010E 2031          AND     [BX+DI],DH
20AB:0110 3938          CMP     [BX+SI],DI
20AB:0112 37            AAA
20AB:0113 205A69        AND     [BP+SI+69],BL
20AB:0116 66            DB      66
20AB:0117 66            DB      66
20AB:0118 20436F        AND     [BP+DI+6F],AL
20AB:011B 6D            DB      6D
20AB:011C 6D            DB      6D
20AB:011D 756E          JNZ     018D
20AB:011F 69            DB      69
-

Now we have to give the new size to our program. 
This is (a bit) the difficult part.
First we have to locate were the program ends. 
To do that we have check the program's terminating 
functions, and there are quite a lot of them. 
Like ...

        FUNCTION                SEARCH STRING
        --------                -------------

        1. RET          -->     C3
        2. INT  20H     -->     CD 20
        3. MOV  AH,4C  
           INT  21H     -->     4C CD 21
        4. MOV  AX,4C00 
           INT  21H     -->     4C CD 21
        5. INT  27H     -->     CD 27    (For TSR's)
        6. MOV  AX,3100
           INT  21H     -->     31 CD 21 (For TSR's)

Now we have to search our program for the above strings. 
You may of course write a short C program in order to do it, 
or you may do it 'per hand'. I'll show you how:
Lets start searching our program using the most common 
terminate function (AX=4C, INT 21).

-s cs:100 ffff 4c cd 21
20AB:0646
-

Aha.. found it at 0646. 
Now convert 0646 into decimal (1606) and then check if the 
value you have written down is smaller than this.

Oh...No! The previous value (0xF6D) is greater than this. 
This happens because this program's exit function is in the 
middle of the program, not at the end. 

In this case we have 2 options.
 1) Feel the code

 1. Search the program starting from 100h to the end of the code & data. 
    Some times after the code starts all the data. 
    This is easy to detect: dump the program (d) starting from 100h and 
    just have a look at the code. 
    Once the program code & data are over, you can identify the rest
    of the area easyly. 
    In fact, once the program is over, you will see some path settings & 
    Ascii strings. Now write down the offset address were
    the program ends. 
    (To use this methods you need a good expriance of X86 assembly 
    OPCODES. By the way, getting this experience you will learn how to 
    detect patterns)

Lets say that we think that the program ends at offset 1105. 
Now he have to take off the starting 100h, because all com programs start 
from 100h. To do that we can use a Debug command of course...

-h 1105 100
1205  1005
-

Yes: answer is 0x1005. Now we have to set this value in CX register. 
We also have to give a new name to our 'new' uncompressed 
program...

-rcx
CX 0F6D
:1005           -- Our calculated program length.
-n test.com     -- New name for uncompressed file. (TEST.COM)
-w              -- Write test com into the disk.

Now you got an uncompressed file called TEST.COM. 

Ok, feeling the end of the code was not really difficult... 
Lets pass over to option 2.

 2. If you can't find the program end use a simple trick.  
    Multiply the compressed file size by 2. This will double the 
    compressed file size. Now put the new file size into the CX 
    register and give a name to the uncompressed file and save 
    it.


    NOTE :- Multification factor depend on the packer. 
    Some packers are able to pack files more efficiently
    than others.

Ok... Now using debug -as you can see- you can get the unpacked file. 
In fact debug is the most effective generic unpacker for com files. 
This is fairly known, and therefore many packed files may contain various 
INT 3 instructions(0xCC) inside their packed code. 
These int 3 have been put there ON PURPOSE, in order to "gasp" debug. 
These instructions will in fact cause the debugger to breakpoint. 

Do not worry: you could not care less!
Once you gasp with debug onto an INT 3 instruction just do the following 
steps...

Check the value in IP register and increment IP register by 1. Then 
countinue the program (g) that's all.

-rip      --- asking for ip
IP 016D   --- say this is the answer
:016E     --- then you just type 16E
-g        --- let's go on!

If you find more than one INT 3 in the packed code just repeat the above 
steps. If there are too many INT 3, just use your hexeditor's replace 
function and nop every single one of them.

This is just the start of course...
Keep on experimenting with com files. 
You will find lot of things inside com files. 
Also we can use this method to remove com viruses from our files
per hand (who said you should trust Norton?). 

"AN ACQUARIUM FOR YOUR VIRI" C'mon, make yourself a very nice present! Buy for next to nothing an old 'almost thrown away' 80286 computer in some shoddy second hand shop and use it to create your own VIRUS ACQUARIUM. You'll use it in order to experiment all sort of assembly viri/antiviri tricks, without any risk of having them biting your 'real' programs... and you'll moreover be able to use its screen for a real 'winice' session on your main computer if needs be... if you never used the ALTSCR ON command when you work with winice (using a second monitor for output) you don't know what you are loosing! Anyway, once you have bought for the price of two pizzas your 80286, go fishing on the web and fish some interesting viri (with assembly source code) out of the net. Now handle evrything with care and pour them into your acquarium, infecting all the fake files you have installed there. Now try first to see if you can identify and remove them from your infected (fake) files using only debug WITHOUT and your 'feeling', without looking at their source code! (You wont, at least not at the beginning :-) Now study their source code, watch them 'alive', eating your files, kill them with YOUR OWN probes (and not with Norton's cram) You'll learn an incredible amount of assembly doing this! It's fun, great fun! (Yet keep a copy of F-prot handy :-)
Learn assembly! Use assembly! Donate something to the world to destroy this gasthly micro$oft's dictature... HuH Huh... Readers can write me... undertakerd(at)hotmail(point)com A final thank goes to all the +HCU's guys and to master +ORC. Keep up the good work Guys!!. Also Special thank goes to Reverser+ and +gthorne for their great contribution to our small reversing world. The Undertaker -=BANDA=- //SRI LANKA//

snippets
Back to the snippets