freenrv2[b,d,e] & upx for code snippets ======================================= 1. lz encoding Lempel-Ziv encoding replaces repeated strings with (offset,length) codes. If we cant find duplicated string, we encode character itself. Both string and character in the packed stream are prefixed with one bit of data, indicating what kind of code is it. As such, we lose one bit when we encode character, but we achieve some compression when we replace duplicated string with (offset,length) code. Depending on algorithm modification, offset and/or length can be limited or not, and can be encoded using fixed and/or variable-length codes. The most interesting part of this class of algorithms is that if we will simply encode longest found strings (v1), we will not achieve the best compression ratio. Here is an example (spaces are used just for readability): Lets assume that (offset,length) code uses 1 bit for prefix, 8 bits for offset and 4 bits for length, so it all uses 1+8+4=13 bits, and encoded character uses 1+8=9 bits of data. data length in bits ------------------------------------------- -------------- unpacked: abc bcdefgh abcdefgh 18*8 = 144 packed (v1) abc (1,2#bc#) defgh (0,3#abc#) (5,5#defgh#) 8*9+3*13 = 111 packed (v2) abc (1,2#bc#) defgh a (3,7#bcdefgh#) 9*9+2*13 = 107 As you can see, scanning for longest repeated strings is not the best packing strategy. 2. nrv2[b,d,e] algorithms (used by the UPX, binary distribution) Both algorithms differs by the format of the (offset,length) encoding. While encoding offset/length, numbers are divided into two parts: 1st part is encoded as a fixed length code, 2nd part is encoded as a variable-length code. Also, if current offset matchs previous offset, it is encoded using just two bits. 3. freenrv2b algorithm (see nrv2b.zip && nrv2bde.zip) This is nrv2b-compatible algorithm, difference is only in the compression method. We try to achieve "ideal" compression ratio, not taking into account compression time. Note, that such an ideality is limited by the algorithm and (offset,length)-encoding specifics, and other algorithms can achieve better ratio, for sure. Using kind of optimized brute force, we try all the possible variants. (see v1,v2 in the example above) 4. upx for code snippets (see snupx.zip) Given some code snippet (32-bit x86 code without any headers), we try nrv2b,d,e methods and choose the best one. Then we produce the following code ("p" (packed) option): call X X: pop esi sub esp, ALIGN4(unpacked_data_size) mov edi, esp jmp esp As such, we generate compressed, self-extracting code snippet. When "x" (xored) or "px" (packed+xored) options are specified, we also add "unxor" layer, which hides zero/cr/lf/slash characters. 5. upx for code snippets/special edition (see snupxs.zip) This is the the same tool as previous one, except that only "p" option is supported, and instead of nrv2[b,d,e] algorithms we try their modifications (some of these modifications are equal to nrv2b,d,e algorithms), i.e. we generate (offset,length)-codes format and corresponding decompressor code on-the-fly. This is even more slow, but improves compression ratio, sometimes 10%+ 6. what for? The only idea was to compress some code snippets, such as shellcodes, engines, etc. Since in such a cases data length is only some kilobytes, compression time is not important, even when it grows as N*N, while making shellcode some bytes smaller always looks nice.