Copy Link
Add to Bookmark
Report
29A Issue 03 02 15
JQCODING - Superfast/Supertiny Compression/Encryption library for VXers!
(c) 1998 by Jacky Qwerty/29A.
Contents
1. Description
2. Compiling notes
3. Linking notes
4. API Function reference
4.1. jq_encode
4.2. jq_decode
5. ASM Source code
5.1. Inclusion at assembly time
5.2. Inclusion at link time
6. HLL support
6.1. Static library makefile
6.2. C/C++ Header file
7. Special considerations
8. Examples
8.1. Compression example
8.2. Decompression example
9. Disclaimer
1. Description
This is very powerful super(tiny/fast) compresion/encryption tool for VXers!
It consists of both compressor/encryptor and decompresor/decryptor routines!
Compresor and decompresor routines are only 9Ah/7Ah bytes long respectively!
Compression ratio is much better than RLE and acceptably good compared with
other compresors. Compresion algorithm is not based on LZ dictionaries, nor
huffman coding, nor any other popular algorithm, it rather implements a new
compresion technique which favors code size and both compresion speed/ratio!
Both routines were intended to be included together into an ASM source file
(see 5.1.), but you could prefer to include them at link time into your exe
(see 5.2.), you choose. For this latter purpose there's a makefile provided
(see 6.1.) in order to build the static library. If you intend to use these
functions from a HLL such as C/C++, you need to include such static library
at link time plus the C/C++ header file at compile time (see 6.2). This and
other related files are included too in the file section of the zine. Enjoy!
2. Compiling notes
If you intend to include the JQCODING functions at assembly time, read on.
ASM Compiler must support HLL (High-Level Language) constructions such
as .if, .repeat, .until, etc to implement equivalent ASM control flow
instructions. This improves readability and eliminates the use of many
labels throughout the source. Use MASM 6.xx, although TASM 5.x could
also do the job as well. I actually haven't tested this, though.
3. Linking notes
If you intend to include the JQCODING functions at link time, read on.
Exported functions use 'stdcall' calling convention: Parameters are
pushed from last to first (or right to left if you prefer), callee
(not caller) cleans the stack, function names are case sensitive
starting with underscore and ending with @n where n is the total
number of *bytes* (in decimal) pushed as arguments to the stack.
4. API Function reference
4.1. jq_encode
Compresses 'in_len' bytes from the input buffer pointed by 'in' and
writes the compressed stream to the output buffer pointed by 'out'.
C/C++ syntax:
unsigned long
__stdcall
jq_encode(void *out, /* output stream ptr */
const void *in, /* input stream ptr */
unsigned long in_len, /* input stream length */
void *mem64k); /* work mem ptr */
Parameters:
out Pointer to the output buffer where the compressed
stream will be written to.
in Pointer to the input buffer where the uncompressed
stream will be read from.
in_len Length in bytes of the input stream to be
compressed.
mem64k Pointer to a 64Kb memory buffer, already
allocated prior to the call.
Return value:
Length in bytes of the resulting compressed stream.
X86 specific:
Return value is copied into the EAX register.
All other registers are preserved except EFLAGS and EDX register.
4.2. jq_decode
Decompress 'in_len' bytes from the input buffer pointed by 'in' and
writes the decompressed stream to the output buffer pointed by 'out'.
C/C++ syntax:
unsigned long
__stdcall
jq_decode(void *out, /* output stream ptr */
void *in, /* input stream ptr */
unsigned long in_len, /* input stream length */
void *mem64k); /* work mem ptr */
Parameters:
out Pointer to the output buffer where the decompressed
stream will be written to.
in Pointer to the input buffer where the compressed
stream will be read from.
in_len Length in bytes of the input stream to be
decompressed
mem64k Pointer to a 64Kb memory buffer, already
allocated prior to the call.
Return value:
Length in bytes of the resulting decompressed stream.
X86 specific:
Return value is copied into the EAX register.
All other registers are preserved except the EFLAGS register.
5. ASM Source code
5.1. Inclusion at assembly time
Too add compression/decompression to your ASM projects at assembly time
you need to add JQCODING ASM source code to your own ASM source. Just
copy the following two ASM routines 'jq_encode' and 'jq_decode' and
paste them into your ASM source file or simply include the JQCODING.ASM
file (in the file section of the zine) into your ASM source (see 8.).
Remember however, that both JQCODING functions expect to find parameters
onto the stack, so you should push them in the appropriate order before
calling the functions. Remember also that the return value is copied into
the EAX register and all other registers are preserved, except the EFLAGS
and the EDX register (see 4.). Refer also to 8. for a working example.
***
jq_encode: sub edx, edx ;Encoder/Encryptor
xchg eax, edx
pushad
mov ebp, esp
and ecx, eax
mov edi, [ebp+30h]
cld
mov ch, 40h
push edi
rep stosd
sub edx, 2864E25Ch
mov esi, [ebp+28h]
jnz jq_e0
dec edx
jq_e0: push ecx
sub ax, 0AEB6h
mov edi, [ebp+24h]
pop ebx
stosw
xchg eax, edx
pop ebp
stosd
push edi
xchg eax, edx
push esp
jq_e1: test cl, 7
lodsb
jnz jq_e3
xchg edx, [esp]
adc ah, dl
pop edx
xchg edi, [esp]
ror edx, 1
mov [edi], ah
jc jq_e2
xor edx, 2C047C3Eh
jq_e2: pop edi
mov ah, 0FFh
push edi
xor edx, 76C52B8Dh
inc edi
push edx
jq_e3: cmp al, [ebx+ebp]
jz jq_e5
ror edx, 1
mov [ebx+ebp], al
jnc jq_e4
xor edx, 2C047C3Eh
jq_e4: mov bh, al
xor edx, 5AC157B3h
adc al, dl
stosb
mov al, bh
stc
jq_e5: inc ecx
mov bh, bl
rcl ah, 1
cmp ecx, [esp+34h]
mov bl, al
jc jq_e1
ror ah, cl
pop ebx
add ah, bl
pop esi
mov ebp, esp
sub edi, [ebp+24h]
mov [ebp+14h], edx
xchg ah, [esi]
add [ebp+1Ch], edi
popad
ret 10h
jq_decode: sub eax, eax ;Decoder/Decryptor
pushad
mov ebp, esp
and ecx, eax
mov edi, [ebp+30h]
cld
mov ch, 40h
push edi
rep stosd
mov esi, [ebp+28h]
xchg ebx, eax
add ecx, [ebp+2Ch]
lodsw
mov edi, [ebp+24h]
add ecx,-6
pop ebp
lodsd
xchg eax, edx
jq_d0: test byte ptr [esp+1Ch], 7
jnz jq_d2
ror edx, 1
jecxz jq_d5
jnc jq_d1
xor edx, 2C047C3Eh
jq_d1: lodsb
dec ecx
xor edx, 5AC157B3h
sbb al, dl
mov ah, al
jq_d2: shl ah, 1
inc byte ptr [esp+1Ch]
jnc jq_d4
ror edx, 1
jecxz jq_d5
jc jq_d3
xor edx, 2C047C3Eh
jq_d3: lodsb
dec ecx
xor edx, 76C52B8Dh
sbb al, dl
mov [ebx+ebp], al
jq_d4: mov al, [ebx+ebp]
mov bh, bl
stosb
mov bl, al
jmp jq_d0
dec edx
push ecx
jq_d5: sub edi, [esp+24h]
mov [esp+1Ch], edi
popad
ret 10h
***
5.2. Inclusion at link time
You can also add the JQCODING functions at link time to your ASM or HLL
project. Just add the JQCODING library file (JQCODING.LIB) at the link
command line in order to include the compressor/decompressor functions
to your final exe.
You can build the JQCODING library yourself from the Compressor/
Decompressor source code which is included in the file section of
the zine (JQENCODE.ASM and JQDECODE.ASM). You will also want to take
a look at the makefile JQCODING.MAK that builds the library (see 6.1.).
6. HLL support
You can also call the JQCODING functions from a HLL interface, assuming
that your HLL compiler supports the 'stdcall' convention. This is becoz
the jq_encode and jq_decode functions are implemented in ASM following
the 'stdcall' calling convention, which is also used by the Win32 API.
If you intend to use the JQCODING functions from the C/C++ language,
just add the C/C++ header file (see 6.2.). If you intend to use them
under other HLL's (PASCAL, BASIC, etc.) make sure that the compiler
understands and generates the appropiate call construction for the
functions, according to the 'stdcall' convention. You might also have
to declare both JQCODING functions according to the specific HLL syntax.
6.1. Static library makefile
If you want to create the static JQCODING library, you will need this
makefile, also included in the file section as JQCODING.MAK. It assumes
your assembler is Macro Assembler ML.EXE >6.1x, but it's not mandatory.
However if you use another assembler, you have to modify the makefile.
***
AFLAGS = /nologo /coff
jqcoding.lib : jqencode.obj jqdecode.obj
@if exist $@ del $@
lib /nologo /out:$@ $**
***
6.2. C/C++ Header file
You will need this C/C++ header file, also included in the file section
of the zine, if you intend to use the JQCODING functions right from the
C/C++ language. Simply add the following JQCODING.H header file to your
C/C++ source, this way:
#include "jqcoding.h"
***
unsigned long
__stdcall
jq_encode(void *out, /* output stream ptr */
const void *in, /* input stream ptr */
unsigned long in_len, /* input stream length */
void *mem64k); /* work mem ptr */
unsigned long
__stdcall
jq_decode(void *out, /* output stream ptr */
const void *in, /* input stream ptr */
unsigned long in_len, /* input stream length */
void *mem64k); /* work mem ptr */
***
7. Special considerations
In some unusual cases the compressor may expand data rather than
compressing. This usually happens when dealing with files already
compressed, or files with relatively low data redundancy. This
needs to be considered when allocating memory for the output
buffer, since the output stream may overflow the buffer in such
cases. The worst case occurs when no compression at all is possible
(btw very unlikely) in which the maximum length possible for the
output buffer becomes:
(output buffer max length) = (input stream length) / 8 * 9 + 14
The "output buffer overflow" issue is evidenced much further at
decompression for obvious reasons: the output stream will expand
itself to reach its uncompressed state. So the worst case (or best
if you prefer) occurs when decompressing a data stream previously
compressed at the maximum optimal compression, in which the
maximum length possible for the output buffer becomes:
(output buffer max length) = (input stream length) * 8
However, if you previously saved the original size prior to compression,
which is usually the case, then you don't need to worry at all about
the latter formula.
8. Examples
These examples show you how to use the JQCODING functions to add
compression/decompression to your ASM projects at the assembly
level. First Step is to include the JQCODING.ASM file into your
ASM source:
include jqcoding.asm
Second step is to call the JQCODING functions. Remember that parameters
should be pushed to the stack following the 'stdcall' calling convention
and the return value is copied into the EAX register. All other registers
are preserved, except EFLAGS, EDX and EAX register.
The following examples show you how to compress a data stream (see 8.1.)
and how to DEcompress a compressed data stream (see 8.2.).
8.1. Compression example
(...)
original_input db 'Hello World, Hello World, Watermellon, Watermellon'
original_size db $ - original_input
max_compressed_size equ original_size / 8 * 9 + 14
compressed_output db max_compressed_size dup (?)
real_compressed_size dd ?
work_memory db 10000h dup (?) ;better allocate this with GlobalAlloc API
(...)
push offset work_memory ;work mem ptr
push original_size ;input stream length
push offset original_input ;input stream ptr
push offset compressed_output ;output stream ptr
mov eax, (pi * e * arctanh(3 - j6)) ;optional: seed value (undocumented)
call jq_encode ;Compress/Encrypt
mov [real_compressed_size], eax ;store real compr. size
(...)
8.2. Decompression example
(...)
original_input db 012h, 008h, 037h, 09Eh, 0B9h, 0B6h, 0E9h, 05Dh
0E1h, 026h, 0F6h, 02Fh, 04Eh, 0E0h, 034h, 0BEh
0FEh, 0AAh, 02Ch, 07Dh, 0FCh, 0C6h, 00Fh, 0B2h
001h, 0D7h, 0A1h, 09Fh, 008h, 08Eh, 038h, 0D0h
06Fh, 039h, 0ABh, 048h, 056h
original_size db $ - original_input
max_DEcompressed_size equ original_size * 8
DEcompressed_output db max_DEcompressed_size dup (?)
real_DEcompressed_size dd ?
work_memory db 10000h dup (?) ;better allocate this with GlobalAlloc API
(...)
push offset work_memory ;work mem ptr
push original_size ;input stream length
push offset original_input ;input stream ptr
push offset DEcompressed_output ;output stream ptr
call jq_decode ;DEcompress/Decrypt
mov [real_DEcompressed_size], eax ;store real decompr. size
(...)
If you previously saved the original size of the data after compressing
with 'jq_encode' then you don't need to consider the max_DEcompressed_size
value becoz you already know the real size of the decompressed data, which
btw should match exactly the original size of the data. For instance:
(...)
original_input db 012h, 008h, 037h, 09Eh, 0B9h, 0B6h, 0E9h, 05Dh
0E1h, 026h, 0F6h, 02Fh, 04Eh, 0E0h, 034h, 0BEh
0FEh, 0AAh, 02Ch, 07Dh, 0FCh, 0C6h, 00Fh, 0B2h
001h, 0D7h, 0A1h, 09Fh, 008h, 08Eh, 038h, 0D0h
06Fh, 039h, 0ABh, 048h, 056h
original_size db $ - original_input
DEcompressed_output db real_DEcompressed_size dup (?)
work_memory db 10000h dup (?) ;better allocate this with GlobalAlloc API
(...)
push offset work_memory ;work mem ptr
push original_size ;input stream length
push offset original_input ;input stream ptr
push offset DEcompressed_output ;output stream ptr
call jq_decode ;DEcompress/Decrypt
(...)
9. Disclaimer
This information is provided for educational purposes only. The author is
NOT responsible in any way, for problems it may cause due to improper use!
(c) 1998. Jacky Qwerty/29A.