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.