⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 filter.txt

📁 UPX 源代码
💻 TXT
字号:
This document explains the concept of "filtering" in UPX. Basicallyfiltering is a data preprocessing method which could improve thecompression ratio of the files UPX processes.Currently the filters UPX uses are all based on one very specialalgorithm which is working well on ix86 executable files.This is what upx calls the "naive" implementation. There is also a"clever" method which works only with 32-bit executable file formatsand was first implemented in UPX.Let's start with an example (from this point I assume a 32-bit fileformat). Consider this code fragment:00025970: E877410600                     calln     FatalError00025975: 8B414C                         mov       eax,[ecx+4C]00025978: 85C0                           test      eax,eax0002597A: 7419                           je        file:000259950002597C: 85F6                           test      esi,esi0002597E: 7504                           jne       file:0002598400025980: 89C6                           mov       esi,eax00025982: EB11                           jmps      file:0002599500025984: 39C6                           cmp       esi,eax00025986: 740D                           je        file:0002599500025988: 83C4F4                         add (d)   esp,F40002598B: 68A0A91608                     push      0816A9A000025990: E857410600                     calln     FatalError00025995: FF45F4                         inc       [ebp-0C]Here you can find two calls to a function called "FatalError". As youprobably know the compression ratio is better if the compressor enginefinds longer sequences of repeated strings. In this case the enginesees the following two byte sequences:E877 410600 8B   andE857 410600 FF.So it can find a 3-byte-long match.Now comes the trick. On ix86 near calls are encoded as 0xE8 then a 32bit relative offset to the destination address. Let's see whathappens if the position of the call is added to that offset:0x64177 + 0x25970 = 0x89AE70x64157 + 0x25990 = 0x89AE7E8 E79A0800 8BE8 E79A0800 FFAs you can see now the compressor engine finds a 5-byte-long match.Which means, that we've just saved 2 bytes of compressed data. Not bad.So this is the basic idea (the "naive" implementation). All we have todo is to "filter" the uncompressed data using this method beforecompression, and "unfilter" it after decompression. Simply go over thememory, find 0xE8 bytes and process the next 4 bytes as specifiedabove.Of course there are several possibilities where this scheme could beimproved. First, not only calls could be handled this way - near jumps(0xE9 + 32-bit offset) could work similarly.A second improvement could be if we limit this filtering only for thearea occupied by real code - there is no point in messing with generaldata.Another improvement comes if the byte order of the 32-bit offset isreversed. Why? Here is another call which follows the above fragment:000261FA: E8C9390600                     calln     ErrorF0x639C9 + 0x261FA = 0x89BC3E8 C39B 0800     compare this withE8 E79A 0800As you can see these two functions are quite close together, but thecompressor is not able to utilize this information (2-byte-long matchesare usually not useful) unless the byte order of the offsets arereversed. In this case:E8 0008 9AE7E8 0008 9BC3So, the compressor engine finds a 3-byte-long match here. This is anice improvement - now the engine utilizes the similarity of nearbydestinations too.This is nice, but what happens when we find a "fake" call - ie. an 0xE8which is part of another instruction? Like this:0002A3B1: C745 E8 00000000               mov       [ebp-18],00000000In this case those nice 0x00 bytes are overwritten with some lesscompressible data. This is the disadvantage of the "naive"implementation.So let's be clever and try to detect and process only "real" calls. InUPX a simple method is used to find these calls. We simply check thatthe destinations of these calls are inside the same area as the callsthemselves (so the above code is still a false positive, but it helpsgenerally). A better method would be to actually disassemble the code -contributions are welcome :-)But this is only half of the job. We can not simply process one callthen skip another one - the unfiltering process needs some informationto be able to reverse the filtering.UPX uses the following idea, which works nicely. First we assume thatthe size of the area that should be filtered is less than 16MB. ThenUPX scans over this area and keeps a record of the bytes that arefollowing the 0xE8 bytes. If we are lucky, there will be bytes thatwere not found following 0xE8. These bytes are our candidates to beused as markers.Do you still remember that we assumed that the size of scanned area isless than 16MB? Well, this means that when we process a real call, theresulting offset will be less than 0x00FFFFFF too. So the MSB is always0x00. Which is a nice place to store our marker. Of course we shouldreverse the byte order in the resulting offset - so this marker willappear just after the 0xE8 byte and not 4 bytes after it.That's all. Just go over the memory area, identify the "real" calls,and use this method to mark them. Then the job of the unfilter is veryeasy - it just searches for a 0xE8 + marker sequence and does theunfiltering if it finds one. It's clever, isn't it? :)To tell you the truth it's not this simple in UPX. It can use anadditional parameter ("add_value") which makes things a little bit morecomplicated (for example it can happen that a found marker is proven tobe unusable because of some overflow during an addition).And the whole algorithm is optimized for simplicity on the unfilteringside (as short and as fast assembly as possible - see stub/macros.ash),which makes the filtering process a little more difficult (fcto_ml.ch,fcto_ml2.ch, filteri.cpp).As it can be seen in filteri.cpp, there are lots of variants of thisfiltering implemented - native/clever, calls/jumps/calls&jumps,reversed/unreversed offsets - a sum of 18 slightly different filters(and another 9 variants for 16-bit programs).You can select one of them using the command line parameter "--filter="or try most of them with "--all-filters". Or just let upx use the onewe defined as the default for that executable format.EOF

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -