使用UPX压缩Windows可执行文件有什么缺点吗?

46

我之前使用过UPX来缩小我的Windows可执行文件的大小,但是我必须承认,我对它可能产生的任何负面影响都很幼稚。这种打包/解包的方法会有什么副作用呢?

在某些情况下,是否有人建议不要将可执行文件压缩使用UPX(例如,在编写DLL、Windows服务或针对Vista或Win7时)?我大多数的代码都是用Delphi编写的,但我也用UPX压缩C/C++可执行文件。

另外一方面,我并不是在尝试通过运行UPX来保护我的exe免受反汇编器的攻击,而仅仅是为了减小可执行文件的大小和防止粗略篡改。


1
不使用UPX的另一个原因是,一些反病毒软件将Delphi+UPX识别为潜在病毒。最好使用我们的LVCL,它可以生成30KB的Delphi exe文件,而不需要压缩(而不是使用常规VCL生成300KB / 800KB的exe文件)。这已经足够用于安装程序,该程序将解压缩其内容。请参见http://synopse.info/forum/viewtopic.php?id=30 您还可以查看KOL,它们更难使用,但也更强大。 - Arnaud Bouchez
13个回答

51

使用EXE压缩器也有缺点,其中最明显的是:

  • 在启动被压缩的EXE/DLL时,所有代码都会在一次内存读取中从磁盘镜像解压到内存中,如果系统内存不足且被迫访问交换文件,则可能导致磁盘抖动。与此相反,未经压缩的EXE/DLL会根据需要(即执行时)由操作系统为代码页分配内存。

  • 多个压缩的EXE/DLL实例会在内存中创建多个代码副本。例如,如果你有一个包含1 MB代码(压缩前)的压缩EXE,并启动了5个实例,则将浪费约4 MB的内存。同样地,如果你有一个1 MB的DLL并且它被5个正在运行的应用程序使用,则将浪费约4 MB的内存。而未压缩的EXE/DLL只需在内存中存储一次代码,在多个实例之间共享。

http://www.jrsoftware.org/striprlc.php#execomp


4
并非所有软件都存在多实例之间的虚拟机页面共享问题,而且一些压缩器允许跳过共享部分。例如,PECompact默认会跳过它们。此外,关于“加载所有内存”的问题是正确的,但如果未使用并需要重新分配到其他位置时,该内存将被换页出去。在某些情况下,这可以创建更快的加载速度,因为存储介质(通常是HDD)开销较低,而且所有内容都可以“一次性”加载并且已经“就绪”。所以,有例外情况,各取所需。免责声明:我是PECompact的作者。 - dyasta
2
Jeremy:共享代码段并不是真正的问题,实际的代码段才是。代码通常是内存映射读取执行的,这意味着如果你启动了10个程序,它们将全部使用相同的、完全相同的物理内存页面,并且物理数据只会从磁盘加载一次。操作系统也可以丢弃包含从未被调用过的代码的页面,如果内存不足,则知道它始终可以轻松地从镜像中重新加载它们。对于来自exe打包器的代码,以上都不适用,相应的页面必须由交换文件支持。 - Damon
3
我同意你的看法,达蒙。那么问题就变成了 - 你的应用程序是否倾向于同时运行多个实例?这是我长期以来一直警告过的一个陷阱之一。当然,代码部分通常都很小 - 和今天的系统相比我们谈论的尺寸非常小。我会认为大多数应用程序更倾向于动态分配更多的内存。 - dyasta
我也预计这会显著减慢启动时间 - 对于单次运行可能不会注意到,但如果你有那种需要重复运行的可执行文件(比如grep),那么它可能会产生相当明显的差异,对吧? - user541686
这是否意味着 upx original.exe -o compressed.exe 压缩然后解压缩 upx -d compressed.exe -o original-decompressed.exe 可执行文件将像 exe 文件一样正常运行,其中操作系统在实例之间共享代码并且没有磁盘抖动? - Phani Rithvij

26

我很惊讶这一点还没有被提到,但使用UPX压缩的可执行文件也增加了启发式杀毒软件产生误报的风险,因为统计数据显示许多恶意软件也使用了UPX。


2
UPX不是用于保护的,只是打包。 - John Smith
抱歉,我说话有点口齿不清。现在已经更改措辞了。 - Oliver Giesen
3
完全同意。在Free Pascal中,我们每年会收到一些这样的报告(发行版中的某个二进制文件被某种病毒软件触发)。因此,我们停止了例行的升级处理。 - Marco van de Voort
@JohnSmith 你说得没错。但是顺便提一下,你实际上可以(相当容易地)移动UPX压缩文件的入口点,这样默认的“标准”UPX解包方法就无法解包它,从而使其受到保护(寻找UPX Scrambler,它可以自动完成此操作)。 - user257319

18

以下是三个缺点:

  1. 在虚拟内存中,整个代码都将完全未经压缩地加载进来,而在正常的EXE或DLL中,只有实际使用的代码才会被加载到内存中。如果每次运行时只使用EXE/DLL中的一小部分代码,则这一点尤其重要。
  2. 如果多个实例同时运行您的DLL和EXE,则它们的代码无法在各个实例之间共享,因此您将使用更多的内存。
  3. 如果您的EXE/DLL已经在缓存中,或者在非常快速的存储介质上,或者您正在运行的CPU速度很慢,那么您将会遇到启动速度减慢的问题,因为仍然需要进行解压缩,并且您不会从减小的文件大小中获益。对于多次重复调用的EXE来说,这点尤其正确。

因此,如果您的EXE或DLL包含大量资源,则上述缺点可能更加突出,但否则,在实践中它们可能不是太大的问题,考虑到可执行文件的相对大小和可用内存,除非你谈论的是被许多可执行文件(如系统DLL)使用的DLL。

为了纠正其他答案中的一些错误信息:

  • UPX不会影响您在启用DEP保护的计算机上运行的能力。
  • UPX不会影响主要杀毒软件的能力,因为它们支持UPX压缩的可执行文件(以及其他可执行文件压缩格式)。
  • UPX现在已经能够使用LZMA压缩(7zip的压缩算法),使用--lzma开关。

11
唯一需要考虑大小的时候是从互联网下载时。如果您使用UPX,实际上性能会比使用7-zip差(根据我的测试,7-Zip的性能是UPX的两倍)。然后,当它实际上在目标计算机上保持压缩状态时,性能会降低(请参见Lars的答案)。因此,UPX不是文件大小的好解决方案。只需将整个文件压缩为7zip即可。
至于防止篡改,UPX也是一个失败的解决方案。UPX支持解压缩。如果有人想修改EXE,则会看到它是使用UPX压缩的,然后取消压缩。您可能会减慢的潜在破解者的百分比并不能证明努力和性能损失是值得的。
更好的解决方案是使用二进制签名或至少只使用哈希。一个简单的哈希验证系统是对您的二进制文件和一个秘密值(通常是GUID)进行哈希。只有您的EXE知道秘密值,因此在重新计算用于验证的哈希时,它可以再次使用它。这并不完美(秘密值可以被检索)。理想情况下,应使用证书和签名。

2
USB闪存驱动器的大小很重要。便携应用程序是其中一个情况,这时候大容量的USB会更有意义。 - niXar
2
考虑到当前多GB内存棒的价格,我不确定在那方面大小是否还重要。当然,除非你存储高清视频。 - RBerteig
UPX压缩算法至少与LZMA相当,用于压缩单个可执行文件。如果您有多个exe/dll需要同时压缩,那么仅使用7-zip会更好:在这种情况下,7-zip将同时压缩所有这些文件,从而获得一定的压缩比例。 - Arnaud Bouchez
3
当使用网络共享或外部硬盘上的文件时,文件大小也很重要。 - Pieter B
1
Docker 容器的大小也很重要。 - MikeSchinkel

6

现在,磁盘上可执行文件的最终大小已经不再重要。你的程序可能会加载得更快一些毫秒,但一旦开始运行,差异是无法区分的。

有些人可能会因为你的可执行文件使用了UPX压缩而更加怀疑它。根据你的最终用户,这可能或可能不是一个重要的考虑因素。


2
+1 我总是对打包的可执行文件/DLL持怀疑态度,特别是如果我无法解包它们。 - Hugh Allen
2
在Docker容器中,大小很重要。 - MikeSchinkel
如果你在读取慢闪存储器中的数据,尤其是在嵌入式系统中,大小就很重要。 - Aaron Wright

2

没有缺点。

但需要说明的是,UPX存在一个非常普遍的误解,即--

资源不仅仅被压缩了

实际上,您正在构建一个具有"加载器"职责的新可执行文件,而"真正"的可执行文件将被削减部分并压缩,作为加载器可执行文件的二进制数据资源(无论原始可执行文件中的资源类型如何)。

使用反向工程方法和工具进行教育目的或其他目的,将向您显示有关"加载器可执行文件"的信息,而不是有关原始可执行文件的可变信息。

executable uncompressed by UPX

executable compressed by UPX


7
“没有缺点”这个说法是错误的,它并不准确。 - kobik
@kobik 尽管“no drawbacks”可能应该改为“没有实际缺点”,但对于现代架构中的实际开发人员来说,这本质上意味着相同的含义。我决定不进行编辑,以换取更清晰、更现实的答案。请接受 :) - user257319
1
作为UPX的经典竞争对手PECompact的作者,我可以说确实存在实际缺点。列举其中三个:误报、潜在的未来互操作性问题以及操作系统无法按需从磁盘加载特定页面(仅适用于非常大的模块)。 - dyasta
2
你关于压缩方式的解释也有点偏差。本地PE压缩实际上是进行“就地”解压缩。所以,它不像你描述的那样,只是从压缩资源中解压缩整个原始EXE,然后运行该文件。相反,操作系统实际上正在运行压缩模块。加载器在位解压虚拟页面。现在,确实存在一些糟糕/差劲的压缩器会像你描述的那样运作,而.NET程序集压缩器就是这样的,但通常并非如此。我很抱歉对你的解释提出批评,只是想提供准确性。 - dyasta

2
上次我尝试在受管程序集上使用它时,结果混淆得很严重,导致运行时无法加载它。那是唯一一个我认为你不会想使用它的情况(实际上,自那以来已经过了很长时间,现在情况可能会更好)。我过去广泛地在所有类型的非托管二进制文件上使用过它,从未遇到问题。

2
如果您只关心减小可执行文件的大小,那么是否尝试过将带有运行时包和不带运行时包的可执行文件大小进行比较呢?虽然您还需要将包的总体大小与可执行文件一起考虑,但如果您有多个使用相同基础包的可执行文件,则可以节省大量空间。
另一个要注意的是程序中使用的图形/字形。通过将它们合并到全局数据模块中包含的单个Timagelist中而不是在每个表单上重复使用,可以节省相当多的空间。我认为每个图像都以十六进制存储在表单资源中,这意味着每个字节占用两个字节……您可以通过使用TResourceStream从RCData资源加载图像来缩小它。

1

UPX被误报的原因是因为其开放许可使恶意软件作者可以毫无顾虑地使用和修改它。当然,这个问题在整个行业中都是固有的,但不幸的是,伟大的UPX项目也受到了这个问题的困扰。

更新:请注意,随着Taggant项目的完成,假阳性问题将得到改善,前提是UPX支持它(或其他任何东西)。


1

在我看来,经常使用UPX是毫无意义的,但原因已经在上面解释过了,主要是内存比磁盘更昂贵。

Erik:LZMA stub可能会更大。即使算法更好,也不总是有净增益。


在现代硬件上,似乎无论如何内存和磁盘空间都不会受到太大影响,这使得使用UPX只是增加了额外的复杂性。根据我的经验,在Mac上特别是dylib文件不被支持的情况下,我只能从一个3MB的应用程序中节省约500KB的空间。 - Beejor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接