ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VS2013下UPX 3.09源码编译与依赖配置实践

VS2013下UPX 3.09源码编译与依赖配置实践 简介提供一套已编译通过的 UPXUltimate Packer for eXecutables集成工程面向需要在 Visual Studio 2013 下使用 UPX 源码进行二次开发或理解其原理的 C/C 开发者。工程以 rar 包发布共 388 个文件主要包括 161 个 .h 头文件、53 个 .cpp 与 37 个 .c 源文件、90 个 obj 中间文件同时带有 VS2013 的工程配置sln/vcxproj、调试信息pdb/idb/ilk及编译日志压缩包仅 11.12MB可在 VS2013 中直接打开并编译运行。通过该工程开发者能系统掌握 UPX 的 API 调用方式、动态链接库引用方法、命令行参数处理逻辑以及可执行文件的压缩与解压实现还能从源码中追踪从底层压缩算法到命令行入口的完整调用链并学习 VS2013 下工程属性配置、链接器设置、断点调试、单步跟踪和错误处理等实用技巧。已有 305 人学习下载对刚接触 UPX 或希望在 VS2013 中集成压缩功能的初学者来说是一份结构清晰、可直接对照实践的参考资料。 UPX这个工具搞软件打包和逆向分析的人应该都熟——给exe、dll套一层压缩壳体积能缩到三分之一甚至更小程序运行的时候自己解压对最终用户完全透明。我最近因为一个老项目维护构建环境被严格锁定在VS2013上需要从源码编译一份自用的UPX本以为就是cmake三连的事结果从版本选择开始就一路踩坑。把整个流程整理出来给同样在老工具链上折腾UPX的朋友做个参考。这篇文章按实操顺序来先讲清楚UPX编译这件事背后的版本约束和依赖关系再逐个编译UCL、zlib、LZMA这几个前置库最后编译UPX主体中间穿插所有我碰到的报错和排查思路。无论你是要完整复现编译过程还是只想确认某个错误怎么解决都可以直接跳到对应章节。1. 项目背景为什么要在VS2013上编译UPX1.1 UPX到底解决了什么问题UPX全称Ultimate Packer for eXecutables是一个开源的“可执行文件压缩器”。它的原理说穿了不复杂把PE、ELF、Mach-O这些可执行格式里的代码和数据做一次压缩然后在文件头部附加一小段解压桩stub。程序启动时stub先执行把压缩内容在内存里还原再跳转到原始入口点。对使用者来说压缩后的文件和原文件没有任何使用上的区别只是体积变小了、启动时多了几毫秒的解压开销。举一个实际例子一个5MB的exe用UPX加壳后可能只剩1.5MB左右。对于要分发给客户的工具集、带附属DLL的绿色软件这个体积缩减非常可观。也正因为它会在内存里动态还原原始代码段很多软件加载完成后进程内存里看到的其实是完整代码所以UPX也常被当作一个“轻量壳”用防止别人直接把exe拖进反编译器里看代码。关键点在于UPX对目标文件格式和平台非常敏感。不同格式要匹配不同的stub所以“能不能编译、编译出来能不能用”直接取决于你手上的UPX源码版本和目标平台。UPX是纯C/C写的跨平台能力不错Linux、Windows、macOS都能编译但在某个具体编译器和系统组合下能不能一次通过版本匹配是决定性因素。1.2 为什么非VS2013不可可能有人会问UPX这么老牌的工具直接去GitHub下Release版exe不就行了何必自己编译。这里有个背景很多企业级老项目尤其是工控、金融、嵌入式上位机这类领域构建环境是被严格锁定的。项目用了VC2013编译的第三方库或者运行目标机器只装了VC12运行时这时候所有新增工具都必须用VS2013重新编译一遍否则可能混入VC2015/2017的运行时依赖拖到现场就是经典的“找不到MSVCP140.dll”。VS2013对应的MSVC版本是VC12编译器版本号是18.00。在C标准支持方面VS2013对C11只完成了部分支持变参模板、右值引用这些能用但另一些特性是半吊子状态。这就直接导致UPX版本选择的硬约束UPX 4.x源码用了一些VS2013不支持的语言特性强行编译会报出成百上千行错误没有任何性价比。所以结论是在VS2013环境下老老实实选UPX 3.09不要碰4.x。UPX版本发布时间构建方式VS2013兼容性说明3.082016传统Makefile可编译老项目里比较常见3.092018CMake Makefile兼容性最好建议直接选这个4.0.x2023CMake不兼容要求C11完整支持CMake版本也更新选择3.09而不是3.08的原因很简单3.09是3.x系列的收尾版本修了不少问题而且引入了CMake构建文件用VS2013配合老版本CMake可以直接生成工程。3.08虽然也能编但需要手动处理Makefile麻烦得多。2. 依赖库的角色分工与版本配对2.1 三个前置库分别是干什么的UPX源码编译不是孤立的它依赖几个外部压缩库每个库的角色不一样zlib提供DEFLATE压缩算法UPX在部分场景下会用到它做处理。这个库大家应该很熟几乎所有跨平台C/C项目都绕不开它。UCL提供NRVNot Really Vanished系列压缩算法是UPX早期最核心的压缩器。UCL是一个纯C语言的极小库编译难度几乎为零但压缩率不如LZMA。LZMA SDK提供LZMA/LZMA2算法UPX 3.x开始把LZMA作为主力压缩算法压缩率高解压速度也还可以。补充说明一个容易混淆的点UPX的压缩流程不是“整个exe压成一个包”那么简单。它会把原始代码分段先用LZMA或NRV做一次压缩再用自定义的过滤器做优化最后生成带stub的解压代码。zlib不是UPX的核心路径但在某些构建配置里会被用到。实际使用中三个库都准备齐是最省心的。2.2 版本配比建议我实测下来最稳的一套组合是UPX 3.09源码zlib 1.2.11UCL 1.03LZMA SDK 19.00兼容性较好的一版这里要特别强调一句不要拿zlib 1.3.x或者zlib-ng分支去配VS2013。虽然理论上也能编译但它们的CMake配置会要求比较新的特性或者产生大量噪音警告完全没必要给自己找麻烦。老工具链就配老依赖这是我在折腾老环境时最深的体会。3. 环境准备VS2013和CMake的坑3.1 VS2013安装时容易漏掉的东西如果你机器上还没装VS2013或者装的是精简版用之前一定要确认两件事是否安装了VC编译工具集。VS2013默认安装不会把所有C组件都装上特别是“Visual C”和“Windows SDK”这两项很多人装完发现C项目报找不到cl.exe就是这里漏了。安装时在功能选择里手动勾上。是否打了Update 5补丁。这个补丁修复了大量标准库和编译器的bug建议装上不然编译某些模板代码可能出莫名其妙的递归实例化错误。操作上建议直接用“VS2013 x86 Native Tools Command Prompt”VS2013本机工具命令提示符。如果坚持用普通cmd需要手动把cl.exe和nmake.exe所在目录加进PATH位置一般在C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE但手动配PATH容易漏掉rc.exe资源编译器和一堆DLL依赖还是用官方命令提示符最省心。3.2 CMake版本选择不能用太新的另一个容易翻车的点CMake版本太新的时候连“Visual Studio 12 2013”这个generator都不认识了或者直接标成deprecated。我用的CMake 3.16.5配置VS2013非常顺利。如果你机器上已经装了CMake 3.25以上建议另装一个绿色版的3.16或3.18专门用于这个项目。判断CMake支不支持VS2013在命令行执行cmake --help看generator列表里有没有“Visual Studio 12 2013”这一项即可。没有就说明版本太新。4. 依赖库编译实操UCL和zlib4.1 编译UCL 1.03UCL源码包解压后根目录就有CMakeLists.txt直接用CMake配就行。打开“VS2013 x86 Native Tools Command Prompt”进入源码目录执行mkdir build cd build cmake -G Visual Studio 12 2013 -A Win32 -DCMAKE_INSTALL_PREFIXD:/deps/ucl .. cmake --build . --config Release --target install几点说明-A Win32指定生成32位库。UPX主要使用场景是32位PE文件压缩依赖统一用32位最稳妥。如果目标平台是64位也可以全部改x64。如果configure阶段报“Could NOT find C compiler”多半是你没用VS2013命令提示符CMake找不到cl.exe。UCL编译完会在D:/deps/ucl下生成include和lib目录里面是ucl.lib静态库和头文件。UCL编译非常快基本几秒钟就完事。4.2 编译zlib 1.2.11zlib在Windows下最省事的办法不是CMake而是官方源码自带的VS工程文件。进入zlib-1.2.11/contrib/vstudio/vc12/打开zlibvc.sln这个工程就是给VS2012/2013准备的。用VS2013打开选Release Win32生成。编译后输出在contrib/vstudio/vc12/x86/Release Zlib/之类的目录主要关注两种文件zlib.lib静态链接库zlibwapi.dll和zlibwapi.lib动态链接版本我编译UPX时用的是静态库在解决方案里右键zlibstatic项目单独生成然后把include目录zlib.h、zconf.h和zlib.lib拷贝到D:/deps/zlib下目录结构保持D:/deps/zlib/include/zlib.h D:/deps/zlib/lib/zlib.lib这里有个容易卡住的点zlib生成时默认采用/MD运行时库Release配置UPX默认也是/MD所以能对上。如果出现运行时库不一致的链接错误LNK2038回zlib工程改成和UPX一致的/MD重新编译即可。5. UPX 3.09主体编译实战5.1 CMake配置和生成依赖就绪后解压UPX 3.09源码在源码根目录下执行mkdir build cd build cmake -G Visual Studio 12 2013 -A Win32 -DCMAKE_PREFIX_PATHD:/deps/ucl;D:/deps/zlib -DCMAKE_INSTALL_PREFIXD:/deps/upx ..CMAKE_PREFIX_PATH是给CMake的find_package用的查找路径两个依赖库用分号分隔。如果你的LZMA SDK也是单独编译的可以一起加进来cmake -G Visual Studio 12 2013 -A Win32 -DCMAKE_PREFIX_PATHD:/deps/ucl;D:/deps/zlib;D:/deps/lzma -DCMAKE_INSTALL_PREFIXD:/deps/upx ..UPX 3.x对LZMA SDK的查找方式比较随缘。有的环境能自动找到有的不行。如果你的configure阶段提示找不到LZMA又实在不想单独编译SDK可以加一个参数把LZMA支持关掉cmake -G Visual Studio 12 2013 -A Win32 -DCMAKE_PREFIX_PATHD:/deps/ucl;D:/deps/zlib -DUPX_USE_LZMAOFF ..只保留UCL也能完成编译代价是压缩率会明显下降。我个人建议还是保留LZMA毕竟这是UPX最重要的压缩路径。配置成功后目录下会生成UPX.sln。直接用VS2013打开配置改成Release Win32生成解决方案。也可以继续在命令行构建msbuild UPX.sln /p:ConfigurationRelease /p:PlatformWin32编译完成后src/Release目录下会出现upx.exe。5.2 三个真实会踩的编译报错第一个是error C2039: snprintf: is not a member of std。老VC的cstdio没有把snprintf注入std命名空间UPX 3.09部分代码用了std::snprintf。解决方案在报错源文件顶部加#include cstdio然后把std::snprintf改成_snprintf或者define一个兼容宏。实际UPX 3.09官方修过这个问题但某些分支版本还是会触发改起来不麻烦。第二个是链接报错类似LNK2019: unresolved external symbol _ucl_nrv2b_decompress_8。这基本是UCL库没链接上或者链接的是Debug版、架构不对的库。检查CMake配置时CMAKE_PREFIX_PATH有没有指向UCL安装目录UCL是不是Release x86编译的。反复出现时直接用Dependencies工具查看UPX项目实际链接了哪个库文件十有八九是路径指向了旧版本。第三个是LNK2038: mismatch detected for _MSC_VER。意思是某个依赖库不是用VS2013编译的。比如机器上之前用VS2017编过一个zlib库VS2013在链接时检测到编译器版本不匹配直接拒绝。这种没有捷径所有依赖必须统一用VS2013重编。这也是老项目最坑的地方依赖库来源一多就乱最好建一个专门的D:/deps目录统一管理。5.3 编译成功后的快速验证在命令行执行upx --version如果输出类似“UPX 3.09 Markus Laszlo ver.”的字样说明编译成功。再找个测试exe试一下upx -9 C:\test\demo.exeUPX会显示压缩前后的体积对比和压缩率。5MB左右的exe压到1.5MB是很常见的效果。6. 常见问题排查速查表现象可能原因处理办法CMake报Generator not foundCMake版本太新Visual Studio 12 2013被移出换用CMake 3.16到3.20找不到zlib.h或ucl.hCMAKE_PREFIX_PATH没配置或依赖目录结构不对检查include和lib子目录层级链接时一堆unresolved externalUCL或zlib库没找到或架构不匹配检查库路径统一Release x86LNK2038 _MSC_VER不匹配依赖库由其他VS版本编译所有库统一用VS2013重编生成的upx.exe运行报0xc000007b32位exe在64位系统缺少VC运行时安装VS2013的VC12 Redistributable x86加壳后的exe被杀软误报UPX壳特征明显无解只能数字签名或换方案压缩后的程序启动变慢解压需要时间尤其大文件换更低压缩级别或评估是否值得加壳另外提醒一句UPX本质是压缩不是加密。不要把它当安全壳用。用upx -d就能解开壳还原原始代码所以涉及安全需求的项目必须换真正的加壳或加密方案。UPX的定位就是纯粹的体积优化。7. 最后再分享一点个人体会折腾完整个流程我最大的感受是老工具链编译开源项目最大的成本不是编译本身而是版本的“对账”。UPX 3.09配UCL 1.03配zlib 1.2.11这一个组合我是验证过可以用VS2013一路编译通过的。如果你也被老环境锁死这个组合可以直接抄。实际使用中还有一个技巧编译好的upx.exe可以不用只当手工工具。在VS2013工程的后构建事件Post-build event里加一行命令$(SolutionDir)tools\upx.exe -9 $(TargetPath)这样每次Release编译完成就自动压缩输出文件对交付体积敏感的项目非常实用。不过Debug阶段别开这个压缩过会拖慢调试。老项目维护这件事很多时候就是靠这些不起眼的小流程优化一点点把效率找回来的。本文还有配套的精品资源点击获取
返回列表