  __..,,--^^--,,..____..,,--^^--,,..____..,,--^^--,,..____..,,--^^--,,..__
 |                            ZERO COMPRESSION                            |
 |                             by JAkun '2000                             |
 |__..,,--^^--,,..____..,,--^^--,,..____..,,--^^--,,..____..,,--^^--,,..__|

0. Sorry for my poor English.

Main idea of "zero copression" is that there is a lot of zeros in PE EXE
file. These zeros are not only in big caves, but oft in single instructions,
address etc.
We can use simple RLE compression for these zeros, to gain some space for
our virus.

I. Compression

At the beggining let's look, how the compressor is builded.
The algorithm is very simple:

1. Get one byte from source
2. Is this a zero? - YES: Goto 6.
3. Is this a special byte or sign? - YES: Goto 10.
4. Write readed byte.
5. Goto 1.
6. Count number of zero's from this offset.
7. Is there only one zero? YES: Goto 4.
8. In the place of this zero's write a special byte.
9. Goto 1.
10. Write a sign byte.
11. Goto 4.

I have to explain what are sign and special bytes.

Special byte is a bytes, which tell us how many zeros is here.
We only have to use special bytes which replaces 2,3 or 10 zeros.
I've tested, that these values are best for PE files.
2 and 3 zeros are typical for values in instructions, 10 bytes are good
for compressing big caves.

Sign byte is a byte needed when we have to write byte from source which
we are using in our compression.

Let's look at the simple example. We have to compress this string:
AB000C0D2x

in compression we will use number 3 in the place of zeros
and x as a special sign.

Going by compression algorithm:
1. A is not a 0 and not a special sign. Add it to output string (OS).
 OS: A
2. B is not a 0 and not a special sign. Add it to output string.
 OS: AB
3. Now we have 3 zeros. We are adding 3
 OS: AB3
4. C is not a 0 and not a special sign. Add it to output string.
 OS: AB3C
5. Now we have 1 zero. We are adding it.
 OS: AB3C0
6. D is not a 0 and not a special sign. Add it to output string.
 OS: AB3C0D
7. 2 is a special byte. We have to mark it and add.
 OS: AB3C0Dx2
8. x is a sign byte. We have to mark it and add.
 OS: AB3C0Dx2xx

If a special byte is marked by sign byte, then under decopression we
don't replace it by zeros.

Well, compression is finished and we gain... 0 bytes.
Don't worry, that was only an example. In real PE EXE there is a much more zeros.

Hmm... but there is also much more special bytes and sign bytes.
Because of that we have to use as a special bytes, bytes which have lowest
frequency in file.
If we want to have better compresion, we can count it, but this is not
necessary. I've tested a hundred of megabytes of EXE files and in 95% of files
byte with smallest frequency was A7h. Afterwards 97h, 9Dh and A2h
(we only need 4 bytes: 2,3, 10 and sign).

II. Decompression

So, compression is finished. How to decompress? It's very simple.
I'll write it.

1. Get one byte from source
2. Is this a special byte? YES: Goto 6.
3. Is this a sign byte? YES: Goto 8.
4. Write readed byte.
5. Goto 1.
6. In the place of special byte write 2, 3 or 10 zeros.
7. Goto 1.
8. Get next byte from source
10. Write readed byte.
11. Goto 1.

Decompressing using special bytes and sign used under compression:
AB3C0Dx2xx
step-by-step
1. A
2. AB
3. AB000
4. AB000C
5. AB000C0
6. AB000C0D
6. AB000C0D2
6. AB000C0D2x

III. How good is this compression?

This compression is not very good. In most cases PE file will lost
10%-20% of it's size. This is not much, but enough for viruses purposes.
For example 100kb of PE EXE code can be compressed to 80-90 kb, so we gain
10-20kb of free space, what should be enough for virus.

Zero compression has also some adventages:
1. Befor compression we can count how many bytes we will get.
2. This is the smallest usefull compressor/decompressor (53h/28h bytes!)

The last point need to be explained. We have to decompres in another way,
from end to start.

END to START decompression.
1. Read byte.
2. Is this a sign byte? YES: Goto 6.
3. Is this a special byte? YES: Goto 9.
4. Write this byte.
5. Goto 1.
6. Read byte (there is 2 signs)
7. Write sign byte.
8. Goto 1.
9. Is there a sign byte before? YES: Goto 12.
10. Replace special byte by zeros.
11. Goto 1.
12. Is there a sign byte before? YES: Goto 10.
13. This is a not-coded special byte
14. Write a not-coded special byte and don't care on sign before (read it now)
15. Goto 1.

Using this algorithm we have to know length of compressed and decompressed
data. I haven't tested it, so mayby in some cases bytes decompressed
will overwrite compressed, but I don't think so.
Hmm... all problems should be solved, if sign byte were written after a byte,
not before. It req. another compression procedure, but I have no time now
to write it.

IVa. Compressor - 53h bytes long!

;; At the beginning:
;; ESI - input buffer
;; EDI - output buffer
;; ECX number of bytes 

;; At the end:
;; destroyed: EAX, EBX
;; ESI - end of input buffer
;; EDI - end of compresed data
;; ECX - equal 0

zero_compress:
	lodsb		;; AL is a readed byte
	cmp	al, 0	;; Is this a zero
	je	zero_byte
	cmp	al, 097h	;; Is this a zero?
	je	special_byte
	cmp	al, 09Dh
	je	special_byte
	cmp	al, 0A2h
	je	special_byte
	cmp	al, 0A7h	;; Is this a sign byte?
	je	special_byte
write_byte:
	stosb			;; Write AL to EDI
	loop	zero_compress
	ret			;; Return
zero_byte:
        xor     ebx,ebx           ;; ebx=0
	push	esi
count_zero:
	lodsb		;; AL is a readed byte
	cmp	al, 0	;; Is this a zero
	jne	not_zero
        inc     bl
        cmp     bl,9	;; 4
	jne	count_zero
	pop	esi
        add     esi, ebx
        sub     ecx, ebx
	mov	al, 0A2h;
	jmp	write_byte
not_zero:		        ;; Write special bytes
	cmp	bl,1
	jng	not_4		;; BL<=1
        mov     bl,2
not_4:
	pop	esi
        add     esi, ebx
        sub     ecx, ebx
        add     ebx, offset bytes
        mov     al,byte ptr [ebx]
	jmp	write_byte
special_byte:
	push	ax
	mov	al,0a7h		;; AL = Sign
	stosb			;; Write AL to EDI
	pop	ax
	jmp	write_byte
bytes:
        db 0,097h,09Dh

IVb. Decompressor - 28h bytes long!

;; At the beginning:
;; ESI - input buffer
;; EDI - output buffer 
;; ECX number of bytes to decompress
;;
;; At the end:
;; Destroyed: EAX EDX
;; ESI - end of input buffer
;; EDI - end of decompresed buffer
;; ECX - equal 0

zero_decompress:
	lodsb		;; AL is a readed byte
	mov	dl,1		;; # of zeros
	cmp	al, 0A7h	;; Is this a sign byte?
        je      sign_byte
        cmp     al, 097h        
        je      write_2
	cmp	al, 09Dh
        je      write_3
	cmp	al, 0A2h
        jne     write_decompressed
write_5:
	add	dl,7
write_3:
	inc	dl
write_2:
	inc	dl
	xor	al,al
write_decompressed:
	stosb			;; Write AL to EDI
	dec	dl
	jnz	write_decompressed
	loop	zero_decompress
	ret			;; Return
sign_byte:
	lodsb		;; AL is a readed byte
        dec     ecx
        jmp     write_decompressed
