首页
社区
课程
招聘
[原创]UPX 4.0.2 源码分析
发表于: 2023-8-4 21:08 19713

[原创]UPX 4.0.2 源码分析

2023-8-4 21:08
19713

因为最近一直在参加HW,在红队中学习到了很多新知识。加壳作为一个常用的免杀手段,我经常是知其然不知其所以然,因此打算自顶向下分析一下upx的源码,梳理整个程序运行的机制。本文将以最新版本 upx 4.0.2为基础,对 PE 64位程序加壳流程进行分析。

分析版本:upx-devel 4.0.2
需要压缩的程序:PE 64位程序
先对编译upx源码做一下记录,挺简单的,选择最新版本:
图片描述

图片描述
生成的可执行文件upx在upx/build/release中。

在/doc中目前包含了elf-to-mem.txtfilter.txtloader.txtMakefileselinux.txtupx.pod几项。

我UPX源码都在文件夹/src中,进入该文件夹后我们可以发现其源码由文件夹/src/check/src/compress/src/console/src/filter/src/stub/util和一系列*.h*.cpp文件构成。

我们的分析将会从main.cpp入手,经过work.cpp,最终跳转到对应架构和平台的packer()类中。

在经过不断分析和调整后,可以知道整个源码运行流程如下,我将流程图放在源码分析最前面便于理解框架的整体运行:
图片描述

main.cpp 包含的函数功能:

我们先从主函数入手,可以看到主函数main()的主要作用是调用upx_main()

__acc_cdecl_main 这个函数修饰符是为了确保 main 函数使用正确的调用约定。具体来说不同的编译器可能有不同的默认调用约定:

为了可移植性,UPX 定义了 __acc_cdecl_main 函数修饰符,当编译 UPX 时:

如果操作系统是 Windows 并使用 MSVC 编译器,且定义了_WRITE_ABORT_MSG 和 _CALL_REPORTFAULT 宏,就会执行:
_set_abort_behavior(_WRITE_ABORT_MSG, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
这行代码用来设置 Crash 时的行为,启用 Crash 日志和报告功能。

根据命名我推测这行代是用来码处理 wildcards 参数的,wildcards(Windows wildcards) 指的是 Windows 中的通配符:

总的来说这个函数的功能为:根据通配符acc_wildargv()函数会操作和修改命令行参数
acc_wildargv的声明是在miniacc.h文件中:

宏 ACCLIB_EXTERN 的定义是:

就是简单地展开成一个标准的函数声明:

它的作用是:

这样可以在不泄露实现的情况下,向其他代码提供 AccLib 中函数的接口。
接着往下看main代码,可以看到:

这里是初始化随机数发生器,srand的工作模式为:

如果想看一下这个随机函数用在了哪些地方,具体可以看一下packer.cpp中用到的rand()
第一个用到的地方是用来生成打包文件的随机 ID,这个随机 ID 用来区分不同的打包文件。

第二个地方是在调试模式下随机选择一个压缩方法或过滤器(Filter):

这里提前讲一下 filter,过滤器(Filter)是 UPX 中用于预处理输入文件的数据流的组件,它可以实现一些转换,从而改善输入数据的压缩效果。
过滤器能够预处理输入流从而给予压缩算法更好的输入,Packer 会尝试使用不同的过滤器并选择压缩效果最好的那个。
main函数主要还是去执行upx_main()的,接着看upx_main()。

upx_main() 函数具体流程为:

首先在完成初始化后,调用main_get_options() 解析命令行参数,根据参数设置 opt 全局配置,根据 opt->cmd决定执行哪个操作,默认为压缩。接着开始压缩工作,调用 do_files() 压缩/解压缩传入的文件列表(do_files()具体函数声明在work.cpp中),返回操作结果。
opt->cmd 是 UPX 源码中一个全局变量,表示需要执行的操作。代码定义在 src/options.h 中:

opt -> cmd中opt 是 Options 结构体的一个指针,含义为访问结构体 Options 的成员(成员变量) cmd,cmd成员包含的值如下:

upx_main()函数中,根据解析到的命令行参数会设置opt->cmd为对应的操作值:

接着看开始工作部分,/* start work */代码如下:

do_files() 函数的功能是处理命令行传入的多个文件,函数声明在work.cpp中,相对应work.cpp中有处理单一文件的函数do_one_file(),多文件时使用do_files()先初步处理,然后再让do_one_file()挨个根据opt->cmd 执行不同的操作:

do_one_file() 函数的具体操作流程:

此处pm是PackMaster的对象:

PackMaster 类的定义在 src/packmast.h 中,注释写得十分清楚:"dispatch to a concrete subclass of class Packer; see work.cpp",这个部分后面会讲。

PackMaster 类的主要功能是:

PackMaster 可以在不修改 Packer 子类的情况下,支持不同的文件类型,这也是packmast.cpp的主要功能。虽然 PackMaster 使用了 final 关键字,让这个类不会再有子类,但是 PackMaster 类内部引用了 Packer 类,Packer 类的声明位于 packer.h 中,通过这个 Packer 子类对象再执行不同的操作。

图片描述
packmast.cpp在整个upx加壳流程中起到了分类的作用,其中getPacker()会根据文件格式选择合适的打包器类,比如说检测到PE文件则选择pefile.cpp。下面详细说明packmast.cpp实现的功能:
在packmast.h中声明了PackMaster类,后续work.cpp会用到这个类:

在packmast.cpp中代码一开始使用构造函数PackMaster::PackMaster(InputFile *f, Options *o) noexcept : fi(f)来初始化PackMaster对象的状态。PackMaster类的实现提供了以下功能:

该源码从上到下包含的主要函数有三个:

这个地方的代码主要是根据packmast.cpp提供的PackMaster类构造PackMaster对象,例如下面的代码:

这段代码首先创建了一个 InputFile 对象 fi,并打开了输入文件 iname,接着创建一个名为 fo 的 OutputFile 对象,然后使用这个输入文件 fi 和选项 opt,创建了一个 PackMaster 对象 pm。如果选项 opt 指定的命令是压缩 (CMD_COMPRESS),那么就调用 pm 的 pack 方法,并传入输出文件对象 fo进行压缩操作。
work.cpp与pefile.cpp的关系是什么?

所以 work.cpp 控制总体流程,使用 packmast.cpp 来根据文件类型从 packer.cpp 中选择 PeFile 来实现特定格式PE文件的处理。

packer.cpp 实现了 Packer 抽象类的具体函数,用来提供不同文件格式的打包和解包的基类。Packer 是 PackMaster 的子类,再作为 pefile.cpp 的基类,后续 pefile.cpp 会继承 packer.cpp 的一部分特性进行打包。
图片描述
Packer 抽象类提供了共享的函数,子类可以实现各自的压缩和解压算法。在源码里我们可以看到还有packer_f.cpp、packer_c.cpp、packer_c.cpp,这些文件提供了基于packer.cpp函数的共享函数,例如 packer_c.cpp 提供了的共享函数:

这些函数并不是 packer.cpp 类的内部函数,而是可以被 Packer 子类调用的共享函数。例如下面我们要讲到的 PeFile 可以这样使用:

在packer.cpp中packer_c.cpp提供的isValidCompressionMethod()也是直接使用的:

在压缩过程中最主要的是void Packer::compressWithFilters(),packer.cpp使用重载对这个函数进行了封装:
图片描述
compressWithFilters 函数是用来找到最佳的压缩方法和过滤器,并执行实际的压缩操作的一个核心函数。下面是这个函数的主要步骤:

pefile.cpp 是 packer.cpp 的子类,主要是针对PE文件进行操作。在压缩过程中我们需要注意PeFile::pack0函数。

图片描述
PeFile::pack0 执行步骤总结如下:

这一段代码检查了是否需要精确打包。如果需要,则抛出异常。

这一段代码处理了 PE 文件的导入表,TLS,加载配置,资源,导出表和重定位。

这一段代码调用了一个函数来进行压缩,并使用了过滤器。

这一段代码将处理后的 PE 头部和各个部分写入到输出文件,然后复制文件的覆盖层。 复制覆盖层是指在原始的可执行文件压缩过程中,一些数据并没有被压缩,这部分数据通常被称为覆盖层(overlay)。这可能包括一些附加的未压缩数据,**例如数字签名,不会被压缩。**在解压缩过程中,这部分覆盖层数据需要被直接复制到解压缩的文件中,而不需要进行解压缩处理。

接下来是 p_w64pe_amd64.cpp,p_w64pe_amd64.cpp 实现了针对64位PE文件的压缩打包逻辑,而 pefile.cpp 包含了PE文件格式的通用处理逻辑。p_w64pe_amd64.cpp是对pefile.cpp模板的复用:

对于64位PE,p_w64pe_amd64.cpp中的pack0()会实例化PeFile::pack0<LE64>(),而PeFile32和PeFile64则分别实例化PeFile::pack0<LE32>()和PeFile::pack0<LE64>()

接着分析p_w64pe_amd64.cpp的源码:

在给定的源代码中,定义了类的构造函数、析构函数、成员函数,这些函数的功能为:

主要打包函数在PackW64PeAmd64::pack(OutputFile *fo)

super是访问父类成员的关键字,super::pack0调用了父类PeFile的pack0()模板方法进行实际的压缩工作,所以从这里开始正式执行pefile.cpp模板提供的打包函数PeFile::pack0()
图片描述

filter是 UPX 中用于预处理输入文件的数据流的组件,压缩的程序需要经过过滤器filter处理数据流来便于压缩,它可以实现一些转换,从而改善输入数据的压缩效果。filter的核心思想是转换相对跳转和调用转为绝对地址,以便更好地压缩。
filter.h中公有类:

根据源码可以总结出过滤器运行流程大致为:
initFilter->isValidFilter -> getFilter -> do_filter
初始化filter -> 判断filter是否有效 -> 获取filter的过滤器ID对应的FilterEntry对象 -> 根据方法进行填充
在filter.h中可以看到FilterEntry结构体的定义:

do_filter 和 do_unfilter 是函数指针,它们指向实现过滤和解过滤操作的函数。举个例子来理解filter:
如果过滤器ID是0x01(Fill holes),那么在压缩阶段,filter() 函数将会调用 do_filter() 函数,该函数会用某个字节(如0x00或0xFF)填充输入数据流中的空洞。在解压缩阶段,unfilter()函数将会调用do_unfilter()函数,该函数会从数据流中移除这些填充字节,恢复原始的数据流。
过滤器的种类和功能包括:(0x00代表过滤器ID)

过滤器通过上面的方式能够预处理输入流从而给予压缩算法更好的输入,它位于 UPX 的 Packer 组件之前执行,Packer 默认会尝试使用不同的过滤器并选择压缩效果最好的那个。如果想使用 Remove duplicates,通过命令行参数 --filter 可以指定, 我测试的时候貌似没什么效果。

src/compress/compress.cpp文件中,定义了三个函数:upx_compressupx_decompressupx_test_overlap,分别用来压缩、解压缩、测试解压缩过程中是否有数据覆盖。
upx_compress函数是一个通用的接口,用于根据指定的压缩方法对数据进行压缩。具体的压缩方法包括:LZMA,NRV,UCL,ZSTD等。你可以在以下代码中看到这个函数:

根据指定的压缩方法,upx_compress函数将调用对应的压缩函数:

以上代码中的upx_lzma_compressupx_nrv_compressupx_ucl_compressupx_zstd_compress等函数是具体的压缩函数的调用,它们的具体实现在src/compress/*文件夹中。
图片描述
upx_decompressupx_compress相对应,执行的是与之相反的功能。
upx_test_overlap函数被用来测试在解压缩过程中是否有数据覆盖的发生。数据覆盖是指解压缩的输出会覆盖未解压缩的输入数据,这通常在解压缩的输出和输入共享相同的内存区域并且输出比输入大时发生,这种情况在解压缩过程中是需要避免的。
在UPX中这种情况可能发生,因为UPX的设计目标是使得解压缩可以在原地进行,即解压缩的输出可以覆盖压缩的输入,以节省内存。但是,如果解压缩的输出数据比输入数据大,并且输出和输入的内存区域有重叠,那么就会发生数据覆盖。为了避免这种情况,UPX在解压缩之前会使用upx_test_overlap函数来测试是否会发生数据覆盖。
upx_test_overlap函数接受压缩数据和解压缩数据的内存区域,以及预期的解压缩数据的大小等参数。然后,它会调用相应的*_test_overlap函数(例如upx_lzma_test_overlap、upx_ucl_test_overlap等),来测试给定的解压缩方法是否会导致数据覆盖。如果测试发现会发生数据覆盖,那么upx_test_overlap函数会返回一个错误代码。否则,它会返回一个表示成功的代码。

关于upx压缩算法部分,在src/compress目录下,upx 主要使用了下面几种压缩算法:

这四种算法各有优劣:

在代码中,upx 对算法进行打包,根据 method 参数的不同,选择调用相应压缩算法的实现,例如在src/compress/compress.cpp

什么是字典编码类压缩算法?字典编码类压缩算法是一类基于重复字符串匹配的压缩算法,它的主要思想是:

典型的字典编码类压缩算法有:LZ77、LZ78、LZW、DEFLATE 等。具体的做法是:

这样可以实现压缩的效果:

在这篇文章算法部分不详细展开,后续可能会进行进一步分析。

这里我们要回到p_w64pe_amd64.cpp的源码中进行分析,下面是p_w64pe_amd64中PackW64PeAmd64成员函数buildLoader:

它的主要作用是创建和配置在解压缩UPX压缩文件时使用的加载器,buildLoader函数接受一个Filter类型的指针ft作为参数,这是表示在压缩和解压缩过程中使用的过滤器。
buildLoader函数内主要使用了两个函数,都在packer.cpp中:
initLoader:

addLoader:

这段代码是对addLoader进行重载,addLoader为重载的函数,参数个数不同,用于接收不同个数的const char参数。
#define C#define N 是用定义宏来分别表示 const char
和 nullptr,每个addLoader内部都调用addLoaderVA,将可变参数打包传递给addLoaderVA,addLoaderVA才是实际的实现函数。
addLoaderVA:

addLoaderVA中使用了linker->addLoader(s, ap)将第一个固定参数 s 和可变参数 ap 传递给 linker 的 addLoader 方法,linker 是一个 ElfLinker类的对象,addLoader是ElfLinker类中定义的成员函数,inker->addLoader() 根据传入的第一个字符串参数和可变参数,将这些字符串添加到加载器中。
总结一下,buildLoader函数具体做了以下操作:

标识符在amd64-win64.pe.S中:
图片描述

根据源码我们可以发现 linker 是一个非常关键的类,主要用来构建和管理可执行文件的加载器。它的主要作用有:

对应函数进行分类来分析代码可知:
图片描述

我们可以看到 p_w64pe_amd64.cpp 没有使用 getLoader() 函数,getLoader()是通用的加载器模板代码,而p_w64pe_amd64.cpp 针对具体的amd64 PE平台重写了buildLoader方法直接生成了amd64平台的加载器代码,因此只使用initLoader、addLoader就可以了。

src/stub/src目录下可以看到这种名字为架构-系统.类型.S的汇编代码文件, 例如:amd64-win64.pe.S,这是针对windows 64位PE程序的汇编代码。

这个汇编代码也整理的十分整齐,比如把入口点统一放在一起:

涉及到重定位的放在了一起:

修改PEMAIN01字段的代码就会修改加壳后的入口函数汇编代码,注意栈对齐:
图片描述
图片描述

本文主要是自顶向下分析整个upx打包流程中涉及的源码,在行文过程中学习到了很多知识,也可能存在疏漏,如有问题希望可以多多指出。

UPX源码分析——加壳篇 - i春秋 - 博客园
手动编译UPX并修改Loader
[原创] UPX源码学习和简单修改-加壳脱壳-看雪-安全社区|安全招聘|kanxue.com
upx压缩壳源码分析的一些关键点
运行时压缩(UPX)_upx压缩算法_Mi1k7ea的博客-CSDN博客

git clone https://github.com/upx/upx.git
cd upx
git submodule update --init
make all
git clone https://github.com/upx/upx.git
cd upx
git submodule update --init
make all
.
├── CMakeLists.txt
├── COPYING
├── LICENSE
├── Makefile
├── NEWS
├── README
├── README.SRC
├── compile_flags.txt
├── doc
│   ├── BUGS.txt
│   ├── Makefile
│   ├── THANKS.txt
│   ├── elf-to-mem.txt
│   ├── filter.txt
│   ├── linker.txt
│   ├── selinux.txt
│   ├── upx-doc.html
│   ├── upx-doc.txt
│   ├── upx.1
│   └── upx.pod
├── misc
│   ├── podman
│   ├── scripts
│   └── testsuite
├── src
│   ├── Makefile
│   ├── bele.h
│   ├── bele_policy.h
│   ├── check
│   ├── compress
│   ├── conf.h
│   ├── console
│   ├── except.cpp
│   ├── except.h
│   ├── file.cpp
│   ├── file.h
│   ├── filter
│   ├── filter.cpp
│   ├── filter.h
│   ├── headers.h
│   ├── help.cpp
...
...
.
├── CMakeLists.txt
├── COPYING
├── LICENSE
├── Makefile
├── NEWS
├── README
├── README.SRC
├── compile_flags.txt
├── doc
│   ├── BUGS.txt
│   ├── Makefile
│   ├── THANKS.txt
│   ├── elf-to-mem.txt
│   ├── filter.txt
│   ├── linker.txt
│   ├── selinux.txt
│   ├── upx-doc.html
│   ├── upx-doc.txt
│   ├── upx.1
│   └── upx.pod
├── misc
│   ├── podman
│   ├── scripts
│   └── testsuite
├── src
│   ├── Makefile
│   ├── bele.h
│   ├── bele_policy.h
│   ├── check
│   ├── compress
│   ├── conf.h
│   ├── console
│   ├── except.cpp
│   ├── except.h
│   ├── file.cpp
│   ├── file.h
│   ├── filter
│   ├── filter.cpp
│   ├── filter.h
│   ├── headers.h
│   ├── help.cpp
...
...
int __acc_cdecl_main main(int argc, char *argv[]) {
#if 0 && (ACC_OS_DOS32) && defined(__DJGPP__)
    // LFN=n may cause problems with 2.03's _rename and mkdir under WinME
    putenv("LFN=y");
#endif
#if (ACC_OS_WIN32 || ACC_OS_WIN64) && (ACC_CC_MSC) && defined(_WRITE_ABORT_MSG) &&                 \
    defined(_CALL_REPORTFAULT)
    _set_abort_behavior(_WRITE_ABORT_MSG, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
    acc_wildargv(&argc, &argv);
    // srand((int) time(nullptr));
    srand((int) clock());
 
    // info: main() is implicitly "noexcept", so we need a try block
#if 0
    int r = upx_main(argc, argv);
#else
    int r;
    try {
        r = upx_main(argc, argv);
    } catch (const Throwable &e) {
        printErr("unknown", e);
        std::terminate();
    } catch (...) {
        std::terminate();
    }
#endif
 
#if 0 && defined(__GLIBC__)
    // malloc_stats();
#endif
    return r;
}
int __acc_cdecl_main main(int argc, char *argv[]) {
#if 0 && (ACC_OS_DOS32) && defined(__DJGPP__)
    // LFN=n may cause problems with 2.03's _rename and mkdir under WinME
    putenv("LFN=y");
#endif
#if (ACC_OS_WIN32 || ACC_OS_WIN64) && (ACC_CC_MSC) && defined(_WRITE_ABORT_MSG) &&                 \
    defined(_CALL_REPORTFAULT)
    _set_abort_behavior(_WRITE_ABORT_MSG, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
    acc_wildargv(&argc, &argv);
    // srand((int) time(nullptr));
    srand((int) clock());
 
    // info: main() is implicitly "noexcept", so we need a try block
#if 0
    int r = upx_main(argc, argv);
#else
    int r;
    try {
        r = upx_main(argc, argv);
    } catch (const Throwable &e) {
        printErr("unknown", e);
        std::terminate();
    } catch (...) {
        std::terminate();
    }
#endif
 
#if 0 && defined(__GLIBC__)
    // malloc_stats();
#endif
    return r;
}
#if (ACC_OS_WIN32 || ACC_OS_WIN64) && (ACC_CC_MSC) && defined(_WRITE_ABORT_MSG) &&                 \
    defined(_CALL_REPORTFAULT)
    _set_abort_behavior(_WRITE_ABORT_MSG, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
#if (ACC_OS_WIN32 || ACC_OS_WIN64) && (ACC_CC_MSC) && defined(_WRITE_ABORT_MSG) &&                 \
    defined(_CALL_REPORTFAULT)
    _set_abort_behavior(_WRITE_ABORT_MSG, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
acc_wildargv(&argc, &argv);
acc_wildargv(&argc, &argv);
ACCLIB_EXTERN(void, acc_wildargv) (int*, char***);
ACCLIB_EXTERN(void, acc_wildargv) (int*, char***);
#define ACCLIB_EXTERN(rt,func,args) rt func args
#define ACCLIB_EXTERN(rt,func,args) rt func args
void acc_wildargv(int *argc, char ***argv);
void acc_wildargv(int *argc, char ***argv);
srand((int) clock());
srand((int) clock());
// Create a pseudo-unique program id.
unsigned Packer::getRandomId() const {
    if (opt->debug.disable_random_id)
        return 0x01020304;
    unsigned id = 0;
#if 0 && defined(__unix__)
    // Don't consume precious bytes from /dev/urandom.
    int fd = open("/dev/urandom", O_RDONLY | O_BINARY);
    if (fd < 0)
        fd = open("/dev/random", O_RDONLY | O_BINARY);
    if (fd >= 0) {
        if (read(fd, &id, 4) != 4)
            id = 0;
        close(fd);
    }
#endif
    while (id == 0) {
#if !(HAVE_GETTIMEOFDAY) || ((ACC_OS_DOS32) && defined(__DJGPP__))
        id ^= (unsigned) time(nullptr);
        id ^= ((unsigned) clock()) << 12;
#else
        struct timeval tv;
        gettimeofday(&tv, nullptr);
        id ^= (unsigned) tv.tv_sec;
        id ^= ((unsigned) tv.tv_usec) << 12; // shift into high-bits
#endif
#if HAVE_GETPID
        id ^= (unsigned) getpid();
#endif
        id ^= (unsigned) fi->st.st_ino;
        id ^= (unsigned) fi->st.st_atime;
        id ^= (unsigned) rand();
    }
    return id;
}
// Create a pseudo-unique program id.
unsigned Packer::getRandomId() const {
    if (opt->debug.disable_random_id)
        return 0x01020304;
    unsigned id = 0;
#if 0 && defined(__unix__)
    // Don't consume precious bytes from /dev/urandom.
    int fd = open("/dev/urandom", O_RDONLY | O_BINARY);
    if (fd < 0)
        fd = open("/dev/random", O_RDONLY | O_BINARY);
    if (fd >= 0) {
        if (read(fd, &id, 4) != 4)
            id = 0;
        close(fd);
    }
#endif
    while (id == 0) {
#if !(HAVE_GETTIMEOFDAY) || ((ACC_OS_DOS32) && defined(__DJGPP__))
        id ^= (unsigned) time(nullptr);
        id ^= ((unsigned) clock()) << 12;
#else
        struct timeval tv;
        gettimeofday(&tv, nullptr);
        id ^= (unsigned) tv.tv_sec;
        id ^= ((unsigned) tv.tv_usec) << 12; // shift into high-bits
#endif
#if HAVE_GETPID
        id ^= (unsigned) getpid();
#endif
        id ^= (unsigned) fi->st.st_ino;
        id ^= (unsigned) fi->st.st_atime;
        id ^= (unsigned) rand();
    }
    return id;
}
if (opt->debug.use_random_method && nmethods >= 2) {
    int method = methods[rand() % nmethods];
    ...
}
 
if (opt->debug.use_random_filter && nfilters >= 3 && filters[nfilters - 1] == 0) {
    int filter_id = filters[rand() % (nfilters - 1)];
    ...
if (opt->debug.use_random_method && nmethods >= 2) {
    int method = methods[rand() % nmethods];
    ...
}
 
if (opt->debug.use_random_filter && nfilters >= 3 && filters[nfilters - 1] == 0) {
    int filter_id = filters[rand() % (nfilters - 1)];
    ...
/*************************************************************************
// command line options
**************************************************************************/
 
// main command
enum {
    CMD_NONE,
    CMD_COMPRESS,
    CMD_DECOMPRESS,
    CMD_TEST,
    CMD_LIST,
    CMD_FILEINFO,
    CMD_HELP,
    CMD_LICENSE,
    CMD_VERSION,
};
 
struct Options;
extern Options *opt;      // global options, see class PackMaster for per-file local options
struct Options final {
    int cmd;
 
    // compression options
    int method;
    bool method_lzma_seen;
    bool method_nrv2b_seen;
    bool method_nrv2d_seen;
    bool method_nrv2e_seen;
    int level;  // compression level 1..10
    int filter; // preferred filter from Packer::getFilters()
    bool ultra_brute;
    bool all_methods; // try all available compression methods ?
    int all_methods_use_lzma;
    bool all_filters; // try all available filters ?
    bool no_filter;   // force no filter
    bool prefer_ucl;  // prefer UCL
    bool exact;       // user requires byte-identical decompression
    ....
    }
/*************************************************************************
// command line options
**************************************************************************/
 
// main command
enum {
    CMD_NONE,
    CMD_COMPRESS,
    CMD_DECOMPRESS,
    CMD_TEST,
    CMD_LIST,
    CMD_FILEINFO,
    CMD_HELP,
    CMD_LICENSE,
    CMD_VERSION,
};
 
struct Options;
extern Options *opt;      // global options, see class PackMaster for per-file local options
struct Options final {
    int cmd;
 
    // compression options
    int method;
    bool method_lzma_seen;
    bool method_nrv2b_seen;
    bool method_nrv2d_seen;
    bool method_nrv2e_seen;
    int level;  // compression level 1..10
    int filter; // preferred filter from Packer::getFilters()
    bool ultra_brute;
    bool all_methods; // try all available compression methods ?
    int all_methods_use_lzma;
    bool all_filters; // try all available filters ?
    bool no_filter;   // force no filter
    bool prefer_ucl;  // prefer UCL
    bool exact;       // user requires byte-identical decompression
    ....
    }
enum {
    CMD_NONE,
    CMD_COMPRESS,      // 压缩
    CMD_DECOMPRESS,    // 解压缩
    CMD_TEST,          // 测试
    CMD_LIST,          // 列出文件内容
    CMD_FILEINFO,      // 查看文件信息
    CMD_HELP,          // 显示帮助信息 
    CMD_LICENSE,       // 显示软件许可
    CMD_VERSION,       // 显示版本信息
};
enum {
    CMD_NONE,
    CMD_COMPRESS,      // 压缩
    CMD_DECOMPRESS,    // 解压缩
    CMD_TEST,          // 测试
    CMD_LIST,          // 列出文件内容
    CMD_FILEINFO,      // 查看文件信息
    CMD_HELP,          // 显示帮助信息 
    CMD_LICENSE,       // 显示软件许可
    CMD_VERSION,       // 显示版本信息
};
switch (opt->cmd) { 
case CMD_COMPRESS:
    //...
    break;
case CMD_DECOMPRESS:
    //... 
    break;
// ...
}
switch (opt->cmd) { 
case CMD_COMPRESS:
    //...
    break;
case CMD_DECOMPRESS:
    //... 
    break;
// ...
}
/* start work */
set_term(stdout);
if (do_files(i, argc, argv) != 0)
    return exit_code;
 
if (gitrev[0]) {
    // also see UPX_CONFIG_DISABLE_GITREV in CMakeLists.txt
    bool warn_gitrev = true;
    const char *ee = getenv("UPX_DEBUG_DISABLE_GITREV_WARNING");
    if (ee && ee[0] && strcmp(ee, "1") == 0)
        warn_gitrev = false;
    if (warn_gitrev) {
        FILE *f = stdout;
        int fg = con_fg(f, FG_RED);
        con_fprintf(
            f, "\nWARNING: this is an unstable beta version - use for testing only! Really.\n");
        fg = con_fg(f, fg);
        UNUSED(fg);
    }
}
/* start work */
set_term(stdout);
if (do_files(i, argc, argv) != 0)
    return exit_code;
 
if (gitrev[0]) {
    // also see UPX_CONFIG_DISABLE_GITREV in CMakeLists.txt
    bool warn_gitrev = true;
    const char *ee = getenv("UPX_DEBUG_DISABLE_GITREV_WARNING");
    if (ee && ee[0] && strcmp(ee, "1") == 0)
        warn_gitrev = false;
    if (warn_gitrev) {
        FILE *f = stdout;
        int fg = con_fg(f, FG_RED);
        con_fprintf(
            f, "\nWARNING: this is an unstable beta version - use for testing only! Really.\n");
        fg = con_fg(f, fg);
        UNUSED(fg);
    }
}
void do_one_file(const char *iname, char *oname) {
    int r;
    struct stat st;
    mem_clear(&st);
#if HAVE_LSTAT
    r = lstat(iname, &st);
#else
    r = stat(iname, &st);
...
}
void do_one_file(const char *iname, char *oname) {
    int r;
    struct stat st;
    mem_clear(&st);
#if HAVE_LSTAT
    r = lstat(iname, &st);
#else
    r = stat(iname, &st);
...
}
// handle command - actual work is here
PackMaster pm(&fi, opt);
if (opt->cmd == CMD_COMPRESS)
    pm.pack(&fo);
else if (opt->cmd == CMD_DECOMPRESS)
    pm.unpack(&fo);
else if (opt->cmd == CMD_TEST)
    pm.test();
else if (opt->cmd == CMD_LIST)
    pm.list();
else if (opt->cmd == CMD_FILEINFO)
    pm.fileInfo();
else
    throwInternalError("invalid command");
// handle command - actual work is here
PackMaster pm(&fi, opt);
if (opt->cmd == CMD_COMPRESS)
    pm.pack(&fo);
else if (opt->cmd == CMD_DECOMPRESS)
    pm.unpack(&fo);
else if (opt->cmd == CMD_TEST)
    pm.test();
else if (opt->cmd == CMD_LIST)
    pm.list();
else if (opt->cmd == CMD_FILEINFO)
    pm.fileInfo();
else
    throwInternalError("invalid command");
#pragma once
 
class Packer;
class InputFile;
class OutputFile;
 
/*************************************************************************
// dispatch to a concrete subclass of class Packer; see work.cpp
**************************************************************************/
 
class PackMaster final {
public:
    explicit PackMaster(InputFile *f, Options *o = nullptr) noexcept;
    ~PackMaster() noexcept;
 
    void pack(OutputFile *fo);
    void unpack(OutputFile *fo);
    void test();
    void list();
    void fileInfo();
 
    typedef Packer *(*visit_func_t)(Packer *p, void *user);
    static Packer *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user);
 
private:
    OwningPointer(Packer) packer = nullptr; // owner
    InputFile *fi = nullptr;                // reference
 
    static Packer *getPacker(InputFile *f);
    static Packer *getUnpacker(InputFile *f);
 
    // setup local options for each file
    Options local_options;
    Options *saved_opt = nullptr;
};
 
/* vim:set ts=4 sw=4 et: */
#pragma once
 
class Packer;
class InputFile;
class OutputFile;
 
/*************************************************************************
// dispatch to a concrete subclass of class Packer; see work.cpp
**************************************************************************/
 
class PackMaster final {
public:
    explicit PackMaster(InputFile *f, Options *o = nullptr) noexcept;
    ~PackMaster() noexcept;
 
    void pack(OutputFile *fo);
    void unpack(OutputFile *fo);
    void test();
    void list();
    void fileInfo();
 
    typedef Packer *(*visit_func_t)(Packer *p, void *user);
    static Packer *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user);
 
private:
    OwningPointer(Packer) packer = nullptr; // owner
    InputFile *fi = nullptr;                // reference
 
    static Packer *getPacker(InputFile *f);
    static Packer *getUnpacker(InputFile *f);
 
    // setup local options for each file
    Options local_options;
    Options *saved_opt = nullptr;
};
 
/* vim:set ts=4 sw=4 et: */
/*************************************************************************
// dispatch to a concrete subclass of class Packer; see work.cpp
**************************************************************************/
 
class PackMaster final {
public:
    explicit PackMaster(InputFile *f, Options *o = nullptr) noexcept;
    ~PackMaster() noexcept;
 
    void pack(OutputFile *fo);
    void unpack(OutputFile *fo);
    void test();
    void list();
    void fileInfo();
 
    typedef Packer *(*visit_func_t)(Packer *p, void *user);
    static Packer *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user);
 
private:
    OwningPointer(Packer) packer = nullptr; // owner
    InputFile *fi = nullptr;                // reference
 
    static Packer *getPacker(InputFile *f);
    static Packer *getUnpacker(InputFile *f);
 
    // setup local options for each file
    Options local_options;
    Options *saved_opt = nullptr;
};
 
/* vim:set ts=4 sw=4 et: */
/*************************************************************************
// dispatch to a concrete subclass of class Packer; see work.cpp
**************************************************************************/
 
class PackMaster final {
public:
    explicit PackMaster(InputFile *f, Options *o = nullptr) noexcept;
    ~PackMaster() noexcept;
 
    void pack(OutputFile *fo);
    void unpack(OutputFile *fo);
    void test();
    void list();
    void fileInfo();
 
    typedef Packer *(*visit_func_t)(Packer *p, void *user);
    static Packer *visitAllPackers(visit_func_t, InputFile *f, const Options *, void *user);
 
private:
    OwningPointer(Packer) packer = nullptr; // owner
    InputFile *fi = nullptr;                // reference
 
    static Packer *getPacker(InputFile *f);
    static Packer *getUnpacker(InputFile *f);
 
    // setup local options for each file
    Options local_options;
    Options *saved_opt = nullptr;
};
 
/* vim:set ts=4 sw=4 et: */
    InputFile fi;
    fi.st = st;
    fi.sopen(iname, O_RDONLY | O_BINARY, SH_DENYWR);
 
...
     
    // handle command - actual work is here
    PackMaster pm(&fi, opt);
    if (opt->cmd == CMD_COMPRESS)
        pm.pack(&fo);
    else if (opt->cmd == CMD_DECOMPRESS)
        pm.unpack(&fo);
    else if (opt->cmd == CMD_TEST)
        pm.test();
    else if (opt->cmd == CMD_LIST)
        pm.list();
    else if (opt->cmd == CMD_FILEINFO)
        pm.fileInfo();
    else
        throwInternalError("invalid command");
    InputFile fi;
    fi.st = st;
    fi.sopen(iname, O_RDONLY | O_BINARY, SH_DENYWR);
 
...
     
    // handle command - actual work is here
    PackMaster pm(&fi, opt);
    if (opt->cmd == CMD_COMPRESS)
        pm.pack(&fo);
    else if (opt->cmd == CMD_DECOMPRESS)
        pm.unpack(&fo);
    else if (opt->cmd == CMD_TEST)
        pm.test();
    else if (opt->cmd == CMD_LIST)
        pm.list();
    else if (opt->cmd == CMD_FILEINFO)
        pm.fileInfo();
    else
        throwInternalError("invalid command");
isValidCompressionMethod() 
getDefaultCompressionMethods()
getDecompressorSections()
isValidCompressionMethod() 
getDefaultCompressionMethods()
getDecompressorSections()
class PeFile: public Packer {
   void compress() {
       methods = getDefaultCompressionMethods();   // 调用共享函数
       // ...
   }
}
class PeFile: public Packer {
   void compress() {
       methods = getDefaultCompressionMethods();   // 调用共享函数
       // ...
   }
}
int Packer::prepareMethods(int *methods, int ph_method, const int *all_methods) const {
    int nmethods = 0;
    if (!opt->all_methods || all_methods == nullptr || (-0x80 == (ph_method >> 24))) {
        methods[nmethods++] = forced_method(ph_method);
        return nmethods;
    }
    for (int mm = 0; all_methods[mm] != M_END; ++mm) {
        int method = all_methods[mm];
        if (method == M_ULTRA_BRUTE && !opt->ultra_brute)
            break;
        if (method == M_SKIP || method == M_ULTRA_BRUTE)
            continue;
        if (opt->all_methods && opt->all_methods_use_lzma != 1 && M_IS_LZMA(method))
            continue;
        // check duplicate
        assert(Packer::isValidCompressionMethod(method));
        // 此处使用assert检查是否true, 如果false就会抛出assertion failed错误
        // assert 语句仅在调试环境下有效,在发布版本(Release mode)中 assert 语句会被自动忽略
        for (int i = 0; i < nmethods; i++)
            assert(method != methods[i]);
        // use this method
        methods[nmethods++] = method;
    }
    // debug
    if (opt->debug.use_random_method && nmethods >= 2) {
        int method = methods[rand() % nmethods];
        methods[0] = method;
        nmethods = 1;
        NO_printf("\nuse_random_method = %d\n", method);
    }
    return nmethods;
}
int Packer::prepareMethods(int *methods, int ph_method, const int *all_methods) const {
    int nmethods = 0;
    if (!opt->all_methods || all_methods == nullptr || (-0x80 == (ph_method >> 24))) {
        methods[nmethods++] = forced_method(ph_method);
        return nmethods;
    }
    for (int mm = 0; all_methods[mm] != M_END; ++mm) {
        int method = all_methods[mm];
        if (method == M_ULTRA_BRUTE && !opt->ultra_brute)
            break;
        if (method == M_SKIP || method == M_ULTRA_BRUTE)
            continue;
        if (opt->all_methods && opt->all_methods_use_lzma != 1 && M_IS_LZMA(method))
            continue;
        // check duplicate
        assert(Packer::isValidCompressionMethod(method));
        // 此处使用assert检查是否true, 如果false就会抛出assertion failed错误
        // assert 语句仅在调试环境下有效,在发布版本(Release mode)中 assert 语句会被自动忽略
        for (int i = 0; i < nmethods; i++)
            assert(method != methods[i]);
        // use this method
        methods[nmethods++] = method;
    }
    // debug
    if (opt->debug.use_random_method && nmethods >= 2) {
        int method = methods[rand() % nmethods];
        methods[0] = method;
        nmethods = 1;
        NO_printf("\nuse_random_method = %d\n", method);
    }
    return nmethods;
}
// copy back results
this->ph = best_ph;
*parm_ft = best_ft;
 
// Finally, check compression ratio.
// Might be inhibited when blocksize < file_size, for instance.
if (!inhibit_compression_check) {
    if (best_ph.c_len + best_ph_lsize >= best_ph.u_len)
        throwNotCompressible();
    if (!checkCompressionRatio(best_ph.u_len, best_ph.c_len))
        throwNotCompressible();
 
    // postconditions 2)
    assert(best_ph.overlap_overhead > 0);
}
 
// convenience
buildLoader(&best_ft);
// copy back results
this->ph = best_ph;
*parm_ft = best_ft;
 
// Finally, check compression ratio.
// Might be inhibited when blocksize < file_size, for instance.
if (!inhibit_compression_check) {
    if (best_ph.c_len + best_ph_lsize >= best_ph.u_len)
        throwNotCompressible();
    if (!checkCompressionRatio(best_ph.u_len, best_ph.c_len))
        throwNotCompressible();
 
    // postconditions 2)
    assert(best_ph.overlap_overhead > 0);
}
 
// convenience
buildLoader(&best_ft);
template <typename ht, typename LEXX, typename ord_mask_t>
void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, bool set_oft) {
    // infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(),
    // objs);
 
    handleStub(fi, fo, pe_offset);
    if (ih.filealign == 0)
        throwCantUnpack("unexpected value in the PE header");
 
    const unsigned iobjs = ih.objects;
    const unsigned overlay =
        file_size_u -
        ALIGN_UP(isection[iobjs - 1].rawdataptr + isection[iobjs - 1].size, ih.filealign);
    checkOverlay(overlay);
 
    ibuf.alloc(ph.c_len);
    obuf.allocForDecompression(ph.u_len);
    fi->seek(isection[1].rawdataptr - 64 + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET);
    fi->readx(ibuf, ibufgood = ph.c_len);
 
    // decompress
    decompress(ibuf, obuf);
    unsigned skip = get_le32(obuf + (ph.u_len - 4));
    unsigned take = sizeof(oh);
    SPAN_S_VAR(byte, extra_info, obuf);
    extra_info = obuf.subref("bad extra_info offset %#x", skip, take);
    // byte * const eistart = raw_bytes(extra_info, 0);
 
    memcpy(&oh, extra_info, take);
    extra_info += take;
    skip += take;
    unsigned objs = oh.objects;
 
    if ((int) objs <= 0 || (iobjs > 2 && isection[2].size == 0))
        throwCantUnpack("unexpected value in the PE header");
    Array(pe_section_t, osection, objs);
    take = sizeof(pe_section_t) * objs;
    extra_info = obuf.subref("bad extra section size at %#x", skip, take);
    memcpy(osection, extra_info, take);
    extra_info += take;
    skip += take;
    rvamin = osection[0].vaddr;
 
    if (iobjs > 2) {
        // read the noncompressed section
        ibuf.dealloc();
        ibuf.alloc(isection[2].size);
        fi->seek(isection[2].rawdataptr, SEEK_SET);
        fi->readx(ibuf, ibufgood = isection[2].size);
    }
 
    // unfilter
    if (ph.filter) {
        Filter ft(ph.level);
        ft.init(ph.filter, oh.codebase - rvamin);
        ft.cto = (byte) ph.filter_cto;
        OCHECK(obuf + (oh.codebase - rvamin), oh.codesize);
        ft.unfilter(obuf + (oh.codebase - rvamin), oh.codesize);
    }
 
    // FIXME: ih.flags is checked here because of a bug in UPX 0.92
    if (ih.flags & IMAGE_FILE_RELOCS_STRIPPED) {
        oh.flags |= IMAGE_FILE_RELOCS_STRIPPED;
        ODADDR(PEDIR_RELOC) = 0;
        ODSIZE(PEDIR_RELOC) = 0;
    }
 
    rebuildImports<LEXX>(extra_info, ord_mask, set_oft);
    rebuildRelocs(extra_info, sizeof(ih.imagebase) * 8, oh.flags, oh.imagebase);
    rebuildTls();
    rebuildExports();
 
    if (iobjs > 3) {
        // read the resource section if present
        ibuf.dealloc();
        ibuf.alloc(isection[3].size);
        fi->seek(isection[3].rawdataptr, SEEK_SET);
        fi->readx(ibuf, ibufgood = isection[3].size);
    }
 
    rebuildResources(extra_info, isection[ih.objects - 1].vaddr);
 
    // FIXME: this does bad things if the relocation section got removed
    //  during compression ...
    // memset(eistart, 0, ptr_udiff_bytes(extra_info, eistart) + 4);
 
    // fill the data directory
    ODADDR(PEDIR_DEBUG) = 0;
    ODSIZE(PEDIR_DEBUG) = 0;
    ODADDR(PEDIR_IAT) = 0;
    ODSIZE(PEDIR_IAT) = 0;
    ODADDR(PEDIR_BOUND_IMPORT) = 0;
    ODSIZE(PEDIR_BOUND_IMPORT) = 0;
 
    setOhHeaderSize(osection);
    oh.chksum = 0;
 
    // write decompressed file
    if (fo) {
        unsigned ic = 0;
        while (ic < objs && osection[ic].rawdataptr == 0)
            ic++;
 
        ibuf.dealloc();
        ibuf.alloc(osection[ic].rawdataptr);
        ibuf.clear();
        infoHeader("[Writing uncompressed file]");
 
        // write header + decompressed file
        fo->write(&oh, sizeof(oh));
        fo->write(osection, objs * sizeof(pe_section_t));
        fo->write(ibuf, osection[ic].rawdataptr - fo->getBytesWritten());
        for (ic = 0; ic < objs; ic++)
            if (osection[ic].rawdataptr)
                fo->write(obuf + (osection[ic].vaddr - rvamin),
                          ALIGN_UP(osection[ic].size, oh.filealign));
        copyOverlay(fo, overlay, obuf);
    }
    ibuf.dealloc();
}
template <typename ht, typename LEXX, typename ord_mask_t>
void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, bool set_oft) {
    // infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(),
    // objs);
 
    handleStub(fi, fo, pe_offset);
    if (ih.filealign == 0)
        throwCantUnpack("unexpected value in the PE header");
 
    const unsigned iobjs = ih.objects;
    const unsigned overlay =
        file_size_u -
        ALIGN_UP(isection[iobjs - 1].rawdataptr + isection[iobjs - 1].size, ih.filealign);
    checkOverlay(overlay);
 
    ibuf.alloc(ph.c_len);
    obuf.allocForDecompression(ph.u_len);
    fi->seek(isection[1].rawdataptr - 64 + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET);
    fi->readx(ibuf, ibufgood = ph.c_len);
 
    // decompress
    decompress(ibuf, obuf);
    unsigned skip = get_le32(obuf + (ph.u_len - 4));
    unsigned take = sizeof(oh);
    SPAN_S_VAR(byte, extra_info, obuf);
    extra_info = obuf.subref("bad extra_info offset %#x", skip, take);
    // byte * const eistart = raw_bytes(extra_info, 0);
 
    memcpy(&oh, extra_info, take);
    extra_info += take;
    skip += take;
    unsigned objs = oh.objects;
 
    if ((int) objs <= 0 || (iobjs > 2 && isection[2].size == 0))
        throwCantUnpack("unexpected value in the PE header");
    Array(pe_section_t, osection, objs);
    take = sizeof(pe_section_t) * objs;
    extra_info = obuf.subref("bad extra section size at %#x", skip, take);
    memcpy(osection, extra_info, take);
    extra_info += take;
    skip += take;
    rvamin = osection[0].vaddr;
 
    if (iobjs > 2) {
        // read the noncompressed section
        ibuf.dealloc();
        ibuf.alloc(isection[2].size);
        fi->seek(isection[2].rawdataptr, SEEK_SET);
        fi->readx(ibuf, ibufgood = isection[2].size);
    }
 
    // unfilter
    if (ph.filter) {
        Filter ft(ph.level);
        ft.init(ph.filter, oh.codebase - rvamin);
        ft.cto = (byte) ph.filter_cto;
        OCHECK(obuf + (oh.codebase - rvamin), oh.codesize);
        ft.unfilter(obuf + (oh.codebase - rvamin), oh.codesize);
    }
 
    // FIXME: ih.flags is checked here because of a bug in UPX 0.92
    if (ih.flags & IMAGE_FILE_RELOCS_STRIPPED) {
        oh.flags |= IMAGE_FILE_RELOCS_STRIPPED;
        ODADDR(PEDIR_RELOC) = 0;
        ODSIZE(PEDIR_RELOC) = 0;
    }
 
    rebuildImports<LEXX>(extra_info, ord_mask, set_oft);
    rebuildRelocs(extra_info, sizeof(ih.imagebase) * 8, oh.flags, oh.imagebase);
    rebuildTls();
    rebuildExports();
 
    if (iobjs > 3) {
        // read the resource section if present
        ibuf.dealloc();
        ibuf.alloc(isection[3].size);
        fi->seek(isection[3].rawdataptr, SEEK_SET);
        fi->readx(ibuf, ibufgood = isection[3].size);
    }
 
    rebuildResources(extra_info, isection[ih.objects - 1].vaddr);
 
    // FIXME: this does bad things if the relocation section got removed
    //  during compression ...
    // memset(eistart, 0, ptr_udiff_bytes(extra_info, eistart) + 4);
 
    // fill the data directory
    ODADDR(PEDIR_DEBUG) = 0;
    ODSIZE(PEDIR_DEBUG) = 0;
    ODADDR(PEDIR_IAT) = 0;
    ODSIZE(PEDIR_IAT) = 0;
    ODADDR(PEDIR_BOUND_IMPORT) = 0;
    ODSIZE(PEDIR_BOUND_IMPORT) = 0;
 
    setOhHeaderSize(osection);
    oh.chksum = 0;
 
    // write decompressed file
    if (fo) {
        unsigned ic = 0;
        while (ic < objs && osection[ic].rawdataptr == 0)
            ic++;
 
        ibuf.dealloc();
        ibuf.alloc(osection[ic].rawdataptr);
        ibuf.clear();
        infoHeader("[Writing uncompressed file]");
 
        // write header + decompressed file
        fo->write(&oh, sizeof(oh));
        fo->write(osection, objs * sizeof(pe_section_t));
        fo->write(ibuf, osection[ic].rawdataptr - fo->getBytesWritten());
        for (ic = 0; ic < objs; ic++)
            if (osection[ic].rawdataptr)
                fo->write(obuf + (osection[ic].vaddr - rvamin),
                          ALIGN_UP(osection[ic].size, oh.filealign));
        copyOverlay(fo, overlay, obuf);
    }
    ibuf.dealloc();
}
if (opt->exact)
    throwCantPackExact();
if (opt->exact)
    throwCantPackExact();
const unsigned dllstrings = processImports();
processTls(&tlsiv); // call before processRelocs!!
processLoadConf(&loadconfiv);
processResources(&res);
processExports(&xport);
processRelocs();
const unsigned dllstrings = processImports();
processTls(&tlsiv); // call before processRelocs!!
processLoadConf(&loadconfiv);
processResources(&res);
processExports(&xport);
processRelocs();
callCompressWithFilters(ft, filter_strategy, ih.codebase);
callCompressWithFilters(ft, filter_strategy, ih.codebase);
fo->write(&oh, sizeof(oh));
fo->write(osection, sizeof(osection[0]) * oobjs);
...
copyOverlay(fo, overlay, obuf);
fo->write(&oh, sizeof(oh));
fo->write(osection, sizeof(osection[0]) * oobjs);
...
copyOverlay(fo, overlay, obuf);
/* p_w64pe_amd64.cpp --
 
   This file is part of the UPX executable compressor.
 
 */
 
#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "pefile.h"
#include "p_w64pe_amd64.h"
#include "linker.h"
 
static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/amd64-win64.pe.h"
 
/*************************************************************************
//
**************************************************************************/
 
PackW64PeAmd64::PackW64PeAmd64(InputFile *f) : super(f) { use_stub_relocs = false; }
 
PackW64PeAmd64::~PackW64PeAmd64() noexcept {}
 
const int *PackW64PeAmd64::getCompressionMethods(int method, int level) const {
    bool small = ih.codesize + ih.datasize <= 256 * 1024;
    return Packer::getDefaultCompressionMethods_le32(method, level, small);
}
 
const int *PackW64PeAmd64::getFilters() const {
    static const int filters[] = {0x49, FT_END};
    return filters;
}
 
Linker *PackW64PeAmd64::newLinker() const { return new ElfLinkerAMD64; }
 
/*************************************************************************
// pack
**************************************************************************/
 
bool PackW64PeAmd64::canPack() {
    if (!readFileHeader())
        return false;
    checkMachine(ih.cpu);
    if (ih.cpu != IMAGE_FILE_MACHINE_AMD64)
        return false;
    return true;
}
 
void PackW64PeAmd64::buildLoader(const Filter *ft) {
    // recompute tlsindex (see pack() below)
    unsigned tmp_tlsindex = tlsindex;
    const unsigned oam1 = ih.objectalign - 1;
    const unsigned newvsize = (ph.u_len + rvamin + ph.overlap_overhead + oam1) & ~oam1;
    if (tlsindex && ((newvsize - ph.c_len - 1024 + oam1) & ~oam1) > tlsindex + 4)
        tmp_tlsindex = 0;
 
    // prepare loader
    initLoader(stub_amd64_win64_pe, sizeof(stub_amd64_win64_pe), 2);
    addLoader("START");
    if (ih.entry && isdll)
        addLoader("PEISDLL0");
    if (isefi)
        addLoader("PEISEFI0");
    addLoader(isdll ? "PEISDLL1" : "", "PEMAIN01",
              icondir_count > 1 ? (icondir_count == 2 ? "PEICONS1" : "PEICONS2") : "",
              tmp_tlsindex ? "PETLSHAK" : "", "PEMAIN02",
              // ph.first_offset_found == 1 ? "PEMAIN03" : "",
              M_IS_LZMA(ph.method)    ? "LZMA_HEAD,LZMA_ELF00,LZMA_DEC20,LZMA_TAIL"
              : M_IS_NRV2B(ph.method) ? "NRV_HEAD,NRV2B"
              : M_IS_NRV2D(ph.method) ? "NRV_HEAD,NRV2D"
              : M_IS_NRV2E(ph.method) ? "NRV_HEAD,NRV2E"
                                      : "UNKNOWN_COMPRESSION_METHOD",
              // getDecompressorSections(),
              /*multipass ? "PEMULTIP" */ "", "PEMAIN10");
    addLoader(tmp_tlsindex ? "PETLSHAK2" : "");
    if (ft->id) {
        const unsigned texv = ih.codebase - rvamin;
        assert(ft->calls > 0);
        addLoader(texv ? "PECTTPOS" : "PECTTNUL");
        addLoader("PEFILTER49");
    }
    if (soimport)
        addLoader("PEIMPORT", importbyordinal ? "PEIBYORD" : "", kernel32ordinal ? "PEK32ORD" : "",
                  importbyordinal ? "PEIMORD1" : "", "PEIMPOR2", isdll ? "PEIERDLL" : "PEIEREXE",
                  "PEIMDONE");
    if (sorelocs) {
        addLoader(soimport == 0 || soimport + cimports != crelocs ? "PERELOC1" : "PERELOC2",
                  "PERELOC3", big_relocs ? "REL64BIG" : "", "RELOC64J");
        if (0) {
            addLoader(big_relocs & 6 ? "PERLOHI0" : "", big_relocs & 4 ? "PERELLO0" : "",
                      big_relocs & 2 ? "PERELHI0" : "");
        }
    }
    if (use_dep_hack)
        addLoader("PEDEPHAK");
 
    // NEW: TLS callback support PART 1, the callback handler installation - Stefan Widmann
    if (use_tls_callbacks)
        addLoader("PETLSC");
 
    addLoader("PEMAIN20");
    if (use_clear_dirty_stack)
        addLoader("CLEARSTACK");
    addLoader("PEMAIN21");
 
    if (ih.entry && isdll)
        addLoader("PEISDLL9");
    if (isefi)
        addLoader("PEISEFI9");
    addLoader(ih.entry || !ilinker ? "PEDOJUMP" : "PERETURN");
 
    // NEW: TLS callback support PART 2, the callback handler - Stefan Widmann
    if (use_tls_callbacks)
        addLoader("PETLSC2");
 
    addLoader("IDENTSTR,UPX1HEAD");
}
 
bool PackW64PeAmd64::needForceOption() const {
    // return true if we need `--force` to pack this file
    bool r = false;
    r |= (ih.opthdrsize != 0xf0); // optional header size is 0xF0 in PE32+ files
    r |= ((ih.flags & IMAGE_FILE_EXECUTABLE_IMAGE) == 0);
    r |= ((ih.flags & IMAGE_FILE_32BIT_MACHINE) != 0); // 32 bit machine flag may not be set
    r |= (ih.coffmagic != 0x20b);                      // COFF magic is 0x20B in PE32+ files
    r |= (ih.entry == 0 && !isdll);
    r |= (ih.ddirsentries != 16);
    return r;
}
 
void PackW64PeAmd64::defineSymbols(unsigned ncsection, unsigned upxsection, unsigned sizeof_oh,
                                   unsigned ic, unsigned s1addr) {
    const unsigned myimport = ncsection + soresources - rvamin;
 
    // patch loader
    linker->defineSymbol("original_entry", ih.entry);
    if (use_dep_hack) {
        // This works around a "protection" introduced in MSVCRT80, which
        // works like this:
        // When the compiler detects that it would link in some code from its
        // C runtime library which references some data in a read only
        // section then it compiles in a runtime check whether that data is
        // still in a read only section by looking at the pe header of the
        // file. If this check fails the runtime does "interesting" things
        // like not running the floating point initialization code - the result
        // is a R6002 runtime error.
        // These supposed to be read only addresses are covered by the sections
        // UPX0 & UPX1 in the compressed files, so we have to patch the PE header
        // in the memory. And the page on which the PE header is stored is read
        // only so we must make it rw, fix the flags (i.e. clear
        // IMAGE_SCN_MEM_WRITE of osection[x].flags), and make it ro again.
 
        // rva of the most significant byte of member "flags" in section "UPX0"
        const unsigned swri = pe_offset + sizeof_oh + sizeof(pe_section_t) - 1;
        // make sure we only touch the minimum number of pages
        const unsigned addr = 0u - rvamin + swri;
        linker->defineSymbol("swri", addr & 0xfff); // page offset
        // check whether osection[0].flags and osection[1].flags
        // are on the same page
        linker->defineSymbol(
            "vp_size", ((addr & 0xfff) + 0x28 >= 0x1000) ? 0x2000 : 0x1000); // 2 pages or 1 page
        linker->defineSymbol("vp_base", addr & ~0xfff);                      // page mask
        linker->defineSymbol("VirtualProtect", ilinkerGetAddress("kernel32.dll", "VirtualProtect"));
    }
    linker->defineSymbol("start_of_relocs", crelocs);
 
    if (ilinker) {
        if (!isdll)
            linker->defineSymbol("ExitProcess", ilinkerGetAddress("kernel32.dll", "ExitProcess"));
        linker->defineSymbol("GetProcAddress", ilinkerGetAddress("kernel32.dll", "GetProcAddress"));
        linker->defineSymbol("kernel32_ordinals", myimport);
        linker->defineSymbol("LoadLibraryA", ilinkerGetAddress("kernel32.dll", "LoadLibraryA"));
        linker->defineSymbol("start_of_imports", myimport);
        linker->defineSymbol("compressed_imports", cimports);
    }
 
    if (M_IS_LZMA(ph.method)) {
        linker->defineSymbol("lzma_c_len", ph.c_len - 2);
        linker->defineSymbol("lzma_u_len", ph.u_len);
    }
    linker->defineSymbol("filter_buffer_start", ih.codebase - rvamin);
 
    // in case of overlapping decompression, this hack is needed,
    // because windoze zeroes the word pointed by tlsindex before
    // it starts programs
    linker->defineSymbol("tls_value",
                         (tlsindex + 4 > s1addr) ? get_le32(obuf + tlsindex - s1addr - ic) : 0);
    linker->defineSymbol("tls_address", tlsindex - rvamin);
 
    linker->defineSymbol("icon_delta", icondir_count - 1);
    linker->defineSymbol("icon_offset", ncsection + icondir_offset - rvamin);
 
    const unsigned esi0 = s1addr + ic;
    linker->defineSymbol("start_of_uncompressed", 0u - esi0 + rvamin);
    linker->defineSymbol("start_of_compressed", esi0);
 
    if (use_tls_callbacks) {
        linker->defineSymbol("tls_callbacks_ptr", tlscb_ptr - ih.imagebase);
        linker->defineSymbol("tls_module_base", 0u - rvamin);
    }
 
    linker->defineSymbol("START", upxsection);
}
 
void PackW64PeAmd64::setOhHeaderSize(const pe_section_t *osection) {
    // SizeOfHeaders
    oh.headersize = ALIGN_UP(pe_offset + sizeof(oh) + sizeof(*osection) * oh.objects, oh.filealign);
}
 
void PackW64PeAmd64::pack(OutputFile *fo) {
    unsigned mask = (1u << IMAGE_SUBSYSTEM_WINDOWS_GUI) | (1u << IMAGE_SUBSYSTEM_WINDOWS_CUI) |
                    (1u << IMAGE_SUBSYSTEM_EFI_APPLICATION) |
                    (1u << IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) |
                    (1u << IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) | (1u << IMAGE_SUBSYSTEM_EFI_ROM);
    super::pack0(fo, mask, 0x0000000140000000ULL);
}
 
/* vim:set ts=4 sw=4 et: */
/* p_w64pe_amd64.cpp --
 
   This file is part of the UPX executable compressor.
 
 */
 
#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "pefile.h"
#include "p_w64pe_amd64.h"
#include "linker.h"
 
static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/amd64-win64.pe.h"
 
/*************************************************************************
//
**************************************************************************/
 
PackW64PeAmd64::PackW64PeAmd64(InputFile *f) : super(f) { use_stub_relocs = false; }
 
PackW64PeAmd64::~PackW64PeAmd64() noexcept {}
 
const int *PackW64PeAmd64::getCompressionMethods(int method, int level) const {
    bool small = ih.codesize + ih.datasize <= 256 * 1024;
    return Packer::getDefaultCompressionMethods_le32(method, level, small);
}
 
const int *PackW64PeAmd64::getFilters() const {
    static const int filters[] = {0x49, FT_END};
    return filters;
}
 
Linker *PackW64PeAmd64::newLinker() const { return new ElfLinkerAMD64; }
 
/*************************************************************************
// pack
**************************************************************************/
 
bool PackW64PeAmd64::canPack() {
    if (!readFileHeader())
        return false;
    checkMachine(ih.cpu);
    if (ih.cpu != IMAGE_FILE_MACHINE_AMD64)
        return false;
    return true;
}
 
void PackW64PeAmd64::buildLoader(const Filter *ft) {
    // recompute tlsindex (see pack() below)
    unsigned tmp_tlsindex = tlsindex;
    const unsigned oam1 = ih.objectalign - 1;
    const unsigned newvsize = (ph.u_len + rvamin + ph.overlap_overhead + oam1) & ~oam1;
    if (tlsindex && ((newvsize - ph.c_len - 1024 + oam1) & ~oam1) > tlsindex + 4)
        tmp_tlsindex = 0;
 
    // prepare loader
    initLoader(stub_amd64_win64_pe, sizeof(stub_amd64_win64_pe), 2);
    addLoader("START");
    if (ih.entry && isdll)
        addLoader("PEISDLL0");
    if (isefi)
        addLoader("PEISEFI0");
    addLoader(isdll ? "PEISDLL1" : "", "PEMAIN01",
              icondir_count > 1 ? (icondir_count == 2 ? "PEICONS1" : "PEICONS2") : "",
              tmp_tlsindex ? "PETLSHAK" : "", "PEMAIN02",
              // ph.first_offset_found == 1 ? "PEMAIN03" : "",
              M_IS_LZMA(ph.method)    ? "LZMA_HEAD,LZMA_ELF00,LZMA_DEC20,LZMA_TAIL"
              : M_IS_NRV2B(ph.method) ? "NRV_HEAD,NRV2B"
              : M_IS_NRV2D(ph.method) ? "NRV_HEAD,NRV2D"
              : M_IS_NRV2E(ph.method) ? "NRV_HEAD,NRV2E"
                                      : "UNKNOWN_COMPRESSION_METHOD",
              // getDecompressorSections(),
              /*multipass ? "PEMULTIP" */ "", "PEMAIN10");
    addLoader(tmp_tlsindex ? "PETLSHAK2" : "");
    if (ft->id) {
        const unsigned texv = ih.codebase - rvamin;
        assert(ft->calls > 0);
        addLoader(texv ? "PECTTPOS" : "PECTTNUL");
        addLoader("PEFILTER49");
    }
    if (soimport)
        addLoader("PEIMPORT", importbyordinal ? "PEIBYORD" : "", kernel32ordinal ? "PEK32ORD" : "",
                  importbyordinal ? "PEIMORD1" : "", "PEIMPOR2", isdll ? "PEIERDLL" : "PEIEREXE",
                  "PEIMDONE");
    if (sorelocs) {
        addLoader(soimport == 0 || soimport + cimports != crelocs ? "PERELOC1" : "PERELOC2",
                  "PERELOC3", big_relocs ? "REL64BIG" : "", "RELOC64J");
        if (0) {
            addLoader(big_relocs & 6 ? "PERLOHI0" : "", big_relocs & 4 ? "PERELLO0" : "",
                      big_relocs & 2 ? "PERELHI0" : "");
        }
    }
    if (use_dep_hack)
        addLoader("PEDEPHAK");
 
    // NEW: TLS callback support PART 1, the callback handler installation - Stefan Widmann
    if (use_tls_callbacks)
        addLoader("PETLSC");
 
    addLoader("PEMAIN20");
    if (use_clear_dirty_stack)
        addLoader("CLEARSTACK");
    addLoader("PEMAIN21");
 
    if (ih.entry && isdll)
        addLoader("PEISDLL9");
    if (isefi)

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2023-8-5 14:25 被bwner编辑 ,原因:
收藏
免费 14
支持
分享
最新回复 (15)
雪    币: 2575
活跃值: (502)
能力值: ( LV2,RANK:85 )
在线值:
发帖
回帖
粉丝
2
这个解析的细,赞!
顺便问下,64位程序代码都是相对地址寻址,不知其上面的重定表主要是重定位哪些内容?
2023-8-5 08:09
0
雪    币: 2153
活跃值: (5248)
能力值: ( LV7,RANK:150 )
在线值:
发帖
回帖
粉丝
3
wyfe 这个解析的细,赞! 顺便问下,64位程序代码都是相对地址寻址,不知其上面的重定表主要是重定位哪些内容?
你指的是pefile部分还是linker部分?
2023-8-5 14:15
0
雪    币: 2575
活跃值: (502)
能力值: ( LV2,RANK:85 )
在线值:
发帖
回帖
粉丝
4
bwner 你指的是pefile部分还是linker部分?
没加壳前的重定位表,代码段的数据应该不需要重定位了
2023-8-6 10:22
0
雪    币: 3297
活跃值: (5395)
能力值: ( LV6,RANK:92 )
在线值:
发帖
回帖
粉丝
5
赞一个,顺带问一下程序流程图是用什么软件画的?
2023-8-6 11:35
0
雪    币: 825
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
学到了,感谢楼主的无私奉献精神!
2023-8-6 11:43
0
雪    币: 14517
活跃值: (17538)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
7
感谢分享
2023-8-6 18:10
0
雪    币: 2153
活跃值: (5248)
能力值: ( LV7,RANK:150 )
在线值:
发帖
回帖
粉丝
8

...

最后于 2023-8-6 19:39 被bwner编辑 ,原因:
2023-8-6 19:37
0
雪    币: 2153
活跃值: (5248)
能力值: ( LV7,RANK:150 )
在线值:
发帖
回帖
粉丝
9
0xC5 赞一个,顺带问一下程序流程图是用什么软件画的?
https://app.diagrams.net/,一个在线画图的
2023-8-6 19:38
0
雪    币: 3059
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2023-8-6 20:54
1
雪    币: 7048
活跃值: (3527)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
11

upx 4.02有个大坑, 给linux so加壳有问题, 要换到devel4分支才行:
https://github.com/upx/upx/issues/689

最后于 2023-8-7 10:00 被bxc编辑 ,原因:
2023-8-7 09:59
2
雪    币: 2153
活跃值: (5248)
能力值: ( LV7,RANK:150 )
在线值:
发帖
回帖
粉丝
12
bxc upx&nbsp;4.02有个大坑,&nbsp;给linux&nbsp;so加壳有问题,&nbsp;要换到devel4分支才行:https://github.com/u ...
感谢提醒
2023-8-7 10:15
0
雪    币: 233
活跃值: (2292)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
bxc upx&nbsp;4.02有个大坑,&nbsp;给linux&nbsp;so加壳有问题,&nbsp;要换到devel4分支才行:https://github.com/u ...
4.1.0修复了吗
2023-8-9 15:50
0
雪    币: 233
活跃值: (2292)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
bxc upx&nbsp;4.02有个大坑,&nbsp;给linux&nbsp;so加壳有问题,&nbsp;要换到devel4分支才行:https://github.com/u ...
有无开源好用的upx图形化加壳工具
2023-8-9 15:50
0
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
最近用到了upx,今天就看到源码分析。困了就有人递枕头,感觉真好
2023-8-10 17:08
0
雪    币: 1763
活跃值: (4725)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
赞一个,太牛了。
2023-8-12 00:49
0
游客
登录 | 注册 方可回帖
返回
//