因为最近一直在参加HW,在红队中学习到了很多新知识。加壳作为一个常用的免杀手段,我经常是知其然不知其所以然,因此打算自顶向下分析一下upx的源码,梳理整个程序运行的机制。本文将以最新版本 upx 4.0.2为基础,对 PE 64位程序加壳流程进行分析。
分析版本:upx-devel 4.0.2 需要压缩的程序:PE 64位程序 先对编译upx源码做一下记录,挺简单的,选择最新版本:
生成的可执行文件upx在upx/build/release中。
在/doc中目前包含了elf-to-mem.txt
,filter.txt
,loader.txt
,Makefile
,selinux.txt
,upx.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_compress
、upx_decompress
、upx_test_overlap
,分别用来压缩、解压缩、测试解压缩过程中是否有数据覆盖。upx_compress
函数是一个通用的接口,用于根据指定的压缩方法对数据进行压缩。具体的压缩方法包括:LZMA,NRV,UCL,ZSTD等。你可以在以下代码中看到这个函数:
根据指定的压缩方法,upx_compress
函数将调用对应的压缩函数:
以上代码中的upx_lzma_compress
,upx_nrv_compress
,upx_ucl_compress
,upx_zstd_compress
等函数是具体的压缩函数的调用,它们的具体实现在src/compress/*
文件夹中。upx_decompress
与upx_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:
cd upx
git submodule update --init
make all
git clone https:
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__)
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
)
clock
());
#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__)
#endif
return
r;
}
int
__acc_cdecl_main main(
int
argc,
char
*argv[]) {
#if 0 && (ACC_OS_DOS32) && defined(__DJGPP__)
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
)
clock
());
#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__)
#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
());
unsigned Packer::getRandomId()
const
{
if
(opt->debug.disable_random_id)
return
0x01020304;
unsigned id = 0;
#if 0 && defined(__unix__)
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;
#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;
}
unsigned Packer::getRandomId()
const
{
if
(opt->debug.disable_random_id)
return
0x01020304;
unsigned id = 0;
#if 0 && defined(__unix__)
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;
#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)];
...
}
enum
{
CMD_NONE,
CMD_COMPRESS,
CMD_DECOMPRESS,
CMD_TEST,
CMD_LIST,
CMD_FILEINFO,
CMD_HELP,
CMD_LICENSE,
CMD_VERSION,
};
struct
Options;
extern
Options *opt;
struct
Options final {
int
cmd;
int
method;
bool
method_lzma_seen;
bool
method_nrv2b_seen;
bool
method_nrv2d_seen;
bool
method_nrv2e_seen;
int
level;
int
filter;
bool
ultra_brute;
bool
all_methods;
int
all_methods_use_lzma;
bool
all_filters;
bool
no_filter;
bool
prefer_ucl;
bool
exact;
....
}
enum
{
CMD_NONE,
CMD_COMPRESS,
CMD_DECOMPRESS,
CMD_TEST,
CMD_LIST,
CMD_FILEINFO,
CMD_HELP,
CMD_LICENSE,
CMD_VERSION,
};
struct
Options;
extern
Options *opt;
struct
Options final {
int
cmd;
int
method;
bool
method_lzma_seen;
bool
method_nrv2b_seen;
bool
method_nrv2d_seen;
bool
method_nrv2e_seen;
int
level;
int
filter;
bool
ultra_brute;
bool
all_methods;
int
all_methods_use_lzma;
bool
all_filters;
bool
no_filter;
bool
prefer_ucl;
bool
exact;
....
}
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
;
}
set_term(stdout);
if
(do_files(i, argc, argv) != 0)
return
exit_code;
if
(gitrev[0]) {
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);
}
}
set_term(stdout);
if
(do_files(i, argc, argv) != 0)
return
exit_code;
if
(gitrev[0]) {
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);
...
}
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"
);
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;
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;
InputFile *fi = nullptr;
static
Packer *getPacker(InputFile *f);
static
Packer *getUnpacker(InputFile *f);
Options local_options;
Options *saved_opt = nullptr;
};
#pragma once
class
Packer;
class
InputFile;
class
OutputFile;
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;
InputFile *fi = nullptr;
static
Packer *getPacker(InputFile *f);
static
Packer *getUnpacker(InputFile *f);
Options local_options;
Options *saved_opt = nullptr;
};
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;
InputFile *fi = nullptr;
static
Packer *getPacker(InputFile *f);
static
Packer *getUnpacker(InputFile *f);
Options local_options;
Options *saved_opt = nullptr;
};
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;
InputFile *fi = nullptr;
static
Packer *getPacker(InputFile *f);
static
Packer *getUnpacker(InputFile *f);
Options local_options;
Options *saved_opt = nullptr;
};
InputFile fi;
fi.st = st;
fi.sopen(iname, O_RDONLY | O_BINARY, SH_DENYWR);
...
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);
...
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
;
assert
(Packer::isValidCompressionMethod(method));
for
(
int
i = 0; i < nmethods; i++)
assert
(method != methods[i]);
methods[nmethods++] = method;
}
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
;
assert
(Packer::isValidCompressionMethod(method));
for
(
int
i = 0; i < nmethods; i++)
assert
(method != methods[i]);
methods[nmethods++] = method;
}
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;
}
this
->ph = best_ph;
*parm_ft = best_ft;
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();
assert
(best_ph.overlap_overhead > 0);
}
buildLoader(&best_ft);
this
->ph = best_ph;
*parm_ft = best_ft;
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();
assert
(best_ph.overlap_overhead > 0);
}
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) {
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(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);
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) {
ibuf.dealloc();
ibuf.alloc(isection[2].size);
fi->seek(isection[2].rawdataptr, SEEK_SET);
fi->readx(ibuf, ibufgood = isection[2].size);
}
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);
}
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) {
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);
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;
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]"
);
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) {
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(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);
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) {
ibuf.dealloc();
ibuf.alloc(isection[2].size);
fi->seek(isection[2].rawdataptr, SEEK_SET);
fi->readx(ibuf, ibufgood = isection[2].size);
}
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);
}
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) {
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);
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;
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]"
);
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);
processLoadConf(&loadconfiv);
processResources(&res);
processExports(&xport);
processRelocs();
const
unsigned dllstrings = processImports();
processTls(&tlsiv);
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.
*
/
static const CLANG_FORMAT_DUMMY_STATEMENT
/
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
/
/
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
/
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.
*
/
static const CLANG_FORMAT_DUMMY_STATEMENT
/
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
/
/
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
/
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编辑
,原因: