How to code a COM - crypter

12/97 by UniquE
Hello all!
~^~~~~~~^~
This is my very first try to write an article for PAiN,
so check this out!
Maybe you realised that there are already much powerful protectors available
HackStop, Protect!, BinLock, ...).
So why shouldn't you use one of these for your productions? For sure, you
can do this, but there is a large number of unpackers for nearly any
well-known protector around in the net and on boards. With these tools,
any totally incapable pseudo "computer freaks" can "hack" your programs.
Maybe you realised that guys like Dark Destroyer, Beast, Moixa and
others protect their releases with some non-public-available protectors.
I 'll give you the basics for writing your very own COM crypter, featured
with:


           - tiny size (+- 140 bytes)
           - new, UniquE techniques against debugging, tracing,
             analysing, disassembling, heuristic analysing, unpacking
           - encryption (a shitty 16-bit XOR in 65'535 variations)


 o The basic concept
   First, we have to decide how we want to construct our little
   protector. Let's say that we want to prevent automatic unpacking with
   powerful tools like CUP386, common programs like
   the heuristic cleaner TBCLEAN from TBAV and many others. Further,
   we want to encrypt the original program for prevent simple
   changes of copyright notes and other strings. We won't use
   multiple encryption layers (like in my USCC) but we don't
   let the whole decrypter code unencrypted. So we encrypt
   a part of our decrypter who's executed _after_ the main-
   decryption of the original COM file. Anything clear? No.
   Good, just read on!

 o The COM file structure
   We have to know what we are going to do, so let's have a look
   at the structure of a DOS COM program. The main difference between
   a COM and an EXE program is the fact that a COM can only use ONE
   segment for both data and code. Furthermore the stack is
   downgrowing from the top of the same segment:

       Offset        What?
       0000h..00FFh  Program Segment Prefix (PSP), created by DOS. (P)
       0100h..       Code and data area. (C)
            ..       Free space among the heap and the stack. (x)
            ..FFFEh  The program stack, downgrowing to the code. (S)


       Offset 0000F.....0100h...........................FFFFh
       What?  PPPPPPPPPPCCCCCCCCCCCCCCCCCCxxxxxxxxxxxSSSSSSS

   If a COM files becomes executed, DOS reserves a whole segment of
   memory (65'535 dec. / FFFFh). Then it creates a PSP between
   offset 0000h and 00FFh. Next, it loads the data from the
   COM - file 1:1 at offset 0100h in the reserved segment
   (our codesegment CS is our datasegment DS and stacksegement SS,
   so CS = DS = SS and also = ES). DOS puts the instruction pointer
   IP at 0100h, because the code has been loaded at CS:0100h.
   Before DOS gives control to the COM, it
   sets the stack at CS:FFFEh for leaving a maximum of distance
   between the code and the stack. Furthermore, all registers
   (except SI, don't ask why) will be set to zero (0000h).
   We have to ensure that all these conditions are still true
   after our decryptioncode has been processed. If not, some programs
   won't longer run. If you want to try your protector under
   "hard" conditions, then process a program of the 1st coding competition
   of PAiN. These programs take full advantage of the conditions
   discussed right now.


 o The encryption / protection
    1st  Read the file into memory.
    2nd  Save the first three bytes of the COM in the decrypter code.
    3rd  Recalculation of some offsets in the decrypter code.
    4th  We get a random encryption key and store it in the
         decrypter, too (for decryption while running).
    5th  Append the decrypter to the COM who was loaded at "1st".
    6th  Finally, we encrypt the COM with a part of our decrypter.
    7th  We write the funny thing down to disk.

    After the program execution, the segment looks like this:

       Offset 0000F.....0100h...........................FFFFh
       What?  PPPPPPPPPPJJJXXXXXXXXXXXXXXXWWWVVVxxxxxSSSSSSS

          P    PSP
          J    JMP to "New_Entry_Point:"
          X    encrypted COM file
          W    encrypted part of the decryptor
          V    decryption code
          x    free space
          S    Stack

 o The calculation and the encryption of the COM file
     We said that we want to call our code by a JMP instruction
     at the beginning of the file. So we have to save the bytes
     who'll become overwritten by the JMP:


                ; save the first three bytes
                mov     ax, Word Ptr [Offset COM_Start]
                mov     bl, Byte Ptr [Offset COM_Start + 2]
                mov     Saved_Byte_1_2, ax
                mov     Saved_Byte_3, bl

     Now, we put the JMP - opcode E9h at the first position of the
     COM and calculate the location of the start of the decryption
     code where he has to jump:


                ; write a JMP to our code at the begin of the file
                mov     Byte Ptr [Offset COM_Start], 0E9h ; JMP ....
                mov     ax, COM_Size    ; we start at the end of the old COM
                add     ax, (Offset New_Entry_Point - Offset Decrypt_Start) -3
                mov     Word Ptr [Offset COM_Start + 1], ax

     Using the technique of adding the size of the original COM file
     to all operations that access an absolute memory address, we can ensure
     that we won't get wrong positions. After that, a simple routine
     copies the decrypter at the end of the original COM for that
     it can be encrypted with a random number calculated from the
     system time. The encryption loop can be done like this:



                ; encrypt the COM with a poor XOR - loop
                mov     cx, Crypt_Length ; get the length for the encryption
                mov     bp, Offset COM_Start + 3  ; begin _after_ the JMP!
                mov     bx, Decrypt_Key ; get the encryption - key

        Encrypt_Loop:
                xor     Word Ptr[bp], bx ; encrypt NOW!
                add     bp, 02h         ; to the next word...
                loop    Encrypt_Loop    ; encrypt all!


 o A closer look at some parts of the decryption code
    We have to save all registers for the original program. This is
    also simple and important, done in two lines:

    New_Entry_Point:
                pusha                  ; save all registers
                push    es

   After that, we want to fuck up some poor techniques of code
   analysing and debugging and not very versed people with some
   code who was never seen before (I think):
     We switch the keyboard off:

                cli                     ; interrupts OFF
                in      al, 64h         ; keyboard OFF
                or      al, 040h
                out     64h, al

     We amaze the world by writing _code_in_the_keyboard_buffer
     who must be executed for ensuring the proper decryption of
     the program. If you disassemble the keyboardbuffer, you'll
     find these instructions:

                    pop   bx            ; We take the IP in BX...
                    inc   bx            ; ... and adjust the return-point.
                    inc   bx
                    push  bx            ; Back to stack, for IRET
                    sub   bx, COM_Size  ; execute an important operation
                    iret                ; Leave the keyboard buffer

     For bringing this code at this place, we use these lines:

                mov     ax, 0040h       ; BIOS segment address
                mov     es, ax          ; ES = 0040h
                mov     Word Ptr es:[0020h], 0435Bh ; We write CODE in
                mov     Word Ptr es:[0022h], 05343h ; the keyboard buffer!
                mov     Word Ptr es:[0024h], 0EB81h
mov_es_24       db      26h, 0C7h, 06h, 26h, 00h ; (MOV WORD PTR ES:[0024], .)
Length_of_COM   dw      (Offset Division_Position - Offset Decrypt_Start) + 2
                mov     Byte Ptr es:[0028h], 0CFh

     Then we change the interrupt handler of INT 00h (division-by-zero
     interrupt) to our code in the keyboardbuffer:

                xor     ax, ax          ; change the INT-table at 0000:0000
                mov     es, ax
                mov     cx, es:[0000h]  ; save the INT 00h handler
                push    cx
                mov     cx, es:[0002h]
                push    cx

                mov     Word Ptr es:[0002h], 0040h  ; Set segment...
                mov     Word Ptr es:[0000h], 0020h  ; and offset of the new
                                                    ; INT handler.

     This is an old but very useful technique for nuking nearly any
     analysing tool. What's the result of a division by zero? Can
     it fit in a register, if possible? Well, who knows :)...
     _You_ determinated the rules by creating a new interrupt handler.
     This line forces an INT 00h - call. You remember, we wrote
     a handler for this interrupt who executes an "important
     instruction". In our case, we calculate the position of the
     "New_Entry_Point:" (=> see complete source code).

       Division_Position:
                div     ax              ; AX = 0000h => division by ZERO!
                                        ; ==> INT 00h call!!


     So, now there aren't many analysing-tools "alive", we can restore the
     old interrupt and reenable the keyboard. By the way, what would happen
     if you hadn't deactivate the keyboard? Right, if the user (or
     the hacker) had pressed a key before the code in the Keyboardbuffer
     has been executed, the would destroy the "important instructions"
     in the buffer and the program would probably crash. So you have
     to debug without your keyboard (if you aren't using an advanced
     debugger like CUP386).

                pop     cx              ; restore the old INT 00h handler
                mov     es:[0002h], cx
                pop     cx
                mov     es:[0000h], cx

                in      al, 64h         ; keyboard ON
                and     al, 0BFh
                out	64h, al
                sti                     ; interrupts ON

     Now we can begin with the decryption of the program with
     the following code. You can see that we use self-modifying
     code for keeping the codesize as tiny as possible:


    mov_cx          db      0B9h            ; move to CX (MOV CX, ....)
                                            ; the length of the crypted code
    Crypt_Length    dw      (Offset New_Entry_Point - Offset Decrypt_Start) / 2
                    mov     bp, 101h        ; decryption begins at CS:0101h

                    push    bx              ; save the IP for the next RET
                    add     bx, (Offset New_Entry_Point - Offset Decrypt_Start)
    Decrypt_Loop:
    xor_word_ptr_bp db      081h, 076h, 00h ; decrypt (XOR WORD PTR [BP], ...)
    Decrypt_Key     dw      0DEADh          ; ... with the decryption key
                    inc     bp              ; go to the next position
                    inc     bp
                    cmp     bp, bx          ; everything decrypted?
                    jna     Decrypt_Loop    ; no. => continue!

                    ret                     ; jump to "Decrypt_Start:"
      Decrypt_End:

     Cool, we have successfully decrypted the original program. Let's
     restore the first three bytes (remember, they have been replaced
     by a "JMP New_Entry_Point" instruction) and the saved registers:


      Decrypt_Start:
                ; Restore the original COM file
       mov_W_Ptr_100   db      0C7h, 06h, 00h, 01h      ; (MOV WORD PTR [100], ....)
       Saved_Byte_1_2  dw      ?               ; the first two original bytes
       mov_B_Ptr_102   db      0C6h, 06h, 02h, 01h      ; (MOV BYTE PTR [102], ..)
       Saved_Byte_3    db      ?               ; the third original byte

                       pop     es              ; restore all registers
                       popa
                       push    0100h           ; push the original CS:IP to stack
                       ret                     ; return to the original COM now!



So, that's it. I hope you understood the essence of the encryption of
DOS COM program files and some weak techniques of anti debugging. If
you want to learn more about software protection, then download any
version of HackStop or check out some releases done by Dark Destroyer.
But remember: everything you protect by software can be deprotected by
software! It is impossible to code the unhackable protector (or if you
have one just pass it to some elite crackers and you 'll see...).


                                             Regards
                                             UniquE



I want to give my respects to Dark Destroyer and Beast.



You can download this text with the complete source code (Borland Turbo Assembler).


>Back to the project page

Last updated by UniquE on 16.10.1998