如何使用 MinGW 创建微型 PE (Win32) 可执行文件
How to create tiny PE (Win32) executables using MinGW
我有以下 C 程序:
#include <windows.h>
void __cdecl mainCRTStartup() {
DWORD bw;
HANDLE hfile = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(hfile, "Hello, World!\r\n", 15, &bw, 0);
ExitProcess(0); /* Needed for successful (0) exit. */
}
我用 GCC 4.8.2 编译它,使用以下命令行:
i686-w64-mingw32-gcc -s -Os -fno-ident -fno-stack-protector -fomit-frame-pointer \
-fno-unwind-tables -fno-asynchronous-unwind-tables -falign-functions=1 \
-mpreferred-stack-boundary=2 -falign-jumps=1 -falign-loops=1 -mconsole \
-nostdlib -nodefaultlibs -nostartfiles -o h.exe h.c -lkernel32
生成的.exe文件长2048字节。我如何使用 MinGW 使其更小,最好最多 1024 字节,或者(甚至更好)最多 512 字节?
我更喜欢不编写汇编代码的解决方案,但我也对汇编解决方案感兴趣。
我已经尝试 -Wl,-N
减少部分(段)的数量,但是当 运行 Wine 中的 .exe 时,这导致了段错误。
这 article 表明 480 字节是可能的。它使用以下设置:
#pragma comment(linker, "/FILEALIGN:16")
#pragma comment(linker, "/ALIGN:16")// Merge sections
#pragma comment(linker, "/MERGE:.rdata=.data")
#pragma comment(linker, "/MERGE:.text=.data")
#pragma comment(linker, "/MERGE:.reloc=.data")
#pragma optimize("gsy", on)
不幸的是,这些 #pragma
不适用于 MinGW GCC。有等价物吗?
在 here 中,我找到了 GCC 标志
-Wl,--section-alignment,16,--file-alignment,16
将 .exe 大小减小到 752 字节。 .exe 似乎在 Wine 中工作。
通过 modifying the linker script 我能够合并 .data
和 .rdata
,并减少到 736 字节。除了上述那些之外,我还使用了这些 GCC 标志:-Wl,--section-alignment,16,--file-alignment,16,-T,tinygccpe.scr
.
我仍在寻找 /MERGE
的 MinGW 等价物。
This question 类似,但不会尝试低于 9000 字节。
我也在寻找一个 strip
工具(MinGW 中的 strip
命令不会进一步减小 .exe 的大小),它可以删除 DOS 存根(在偏移量之间0x40 和 0x80,它包含 This program cannot be run in DOS mode.
,我们可以节省 64 个字节)。 This code 可以删除它,但它也会破坏 .exe 中的所有绝对偏移量。不幸的是,MinGW 中的链接器 ld
无法删除 DOS 存根,它被硬编码在文件 bfd/peXXigen.c
中,就在 NT_SIGNATURE
.
之上
是否可以从 .exe 中删除更多 headers,即加载程序不使用的 headers?
这个问题有大量的网络文献,大约从 1995 年开始。
32 位和 64 位的每个版本 Windows 对它们在 PE .exe executable 中接受的 header 值有一套不同的规则。例如,Windows 7 接受具有 0 个节、节对齐 4、文件对齐 4 和 Windows 的其他版本(例如 Windows XP 和最新的 Windows 10 in 2020) 拒绝这些文件。
虽然可以创建小于 2048 字节的工作 .exe 文件。示例:
- hh6d.golden.exe(584 字节)来自table:它适用于 Microsoft 发布的所有 Win32 实现,也适用于 Wine。 (在 Windows NT 3.1、Windows 95、Windows XP 和 Windows 10、Wine 1.6.2、Wine 5.0 上测试。)
- hh2d.golden.exe(408 字节)适用于 Windows 95 及更高版本。 (在 Windows 95、Windows XP、Windows 7 和 Windows 10 上测试)。
- hh1.golden.exe(268 字节)不适用于 Windows XP,它适用于 Windows 7,它不适用于 Windows 10.
- pts-tinype 存储库中包含的汇编源代码。
这就是为什么不太可能发布小于 584 字节的 portable Win32 PE .exe hello-world 的原因:
- 程序代码必须放在一个executable段,最早的文件字节偏移量是512,因为SectionAlignment必须至少是512,而且该段不能从开头开始文件。
- 最小 hello-world 打印机 i386 Win32 机器代码大小为 36 字节。 (这不包括消息大小,因为消息可以存储在 header 的前 512 个字节内。)
- 对于 KERNEL32.DLL,IMAGE_IMPORT_DESCRIPTORS 的最小大小为 20 字节。 Windows 95 不允许在 header 中使用它,因此它必须放在文件字节偏移量 512 之后。
- IAT(导入地址 table)的最小大小为 16 字节:GetStdHandle 4 字节,WriteFile 4 字节,ExitProcess 4 字节,end-of-list 4 字节。由于loader修改了这些地址,所以不能放到header(也就是read-only),所以必须放在文件字节偏移量512之后。
- 如果我们将这些加起来,我们得到 512 + 36 + 20 + 16 = 584 字节。
小于 268 字节的 .exe 文件仅适用于 Windows XP 之前的 Windows 版本,它们不适用于 64 位 Windows 系统。
相关文献:
- https://www.codejuggle.dj/creating-the-smallest-possible-windows-executable-using-assembly-language/
- http://pferrie.host22.com/misc/tiny/pehdr.htm
- https://drakopensulo.wordpress.com/2017/08/06/smallest-pe-executable-x64-with-every-byte-executed/
- http://www.phreedom.org/research/tinype/
- http://www.pouet.net/topic.php?which=9565
- http://www.crinkler.net/
- https://code.google.com/archive/p/corkami/wikis/PE.wiki
我有以下 C 程序:
#include <windows.h>
void __cdecl mainCRTStartup() {
DWORD bw;
HANDLE hfile = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(hfile, "Hello, World!\r\n", 15, &bw, 0);
ExitProcess(0); /* Needed for successful (0) exit. */
}
我用 GCC 4.8.2 编译它,使用以下命令行:
i686-w64-mingw32-gcc -s -Os -fno-ident -fno-stack-protector -fomit-frame-pointer \
-fno-unwind-tables -fno-asynchronous-unwind-tables -falign-functions=1 \
-mpreferred-stack-boundary=2 -falign-jumps=1 -falign-loops=1 -mconsole \
-nostdlib -nodefaultlibs -nostartfiles -o h.exe h.c -lkernel32
生成的.exe文件长2048字节。我如何使用 MinGW 使其更小,最好最多 1024 字节,或者(甚至更好)最多 512 字节?
我更喜欢不编写汇编代码的解决方案,但我也对汇编解决方案感兴趣。
我已经尝试 -Wl,-N
减少部分(段)的数量,但是当 运行 Wine 中的 .exe 时,这导致了段错误。
这 article 表明 480 字节是可能的。它使用以下设置:
#pragma comment(linker, "/FILEALIGN:16")
#pragma comment(linker, "/ALIGN:16")// Merge sections
#pragma comment(linker, "/MERGE:.rdata=.data")
#pragma comment(linker, "/MERGE:.text=.data")
#pragma comment(linker, "/MERGE:.reloc=.data")
#pragma optimize("gsy", on)
不幸的是,这些 #pragma
不适用于 MinGW GCC。有等价物吗?
在 here 中,我找到了 GCC 标志
-Wl,--section-alignment,16,--file-alignment,16
将 .exe 大小减小到 752 字节。 .exe 似乎在 Wine 中工作。
通过 modifying the linker script 我能够合并 .data
和 .rdata
,并减少到 736 字节。除了上述那些之外,我还使用了这些 GCC 标志:-Wl,--section-alignment,16,--file-alignment,16,-T,tinygccpe.scr
.
我仍在寻找 /MERGE
的 MinGW 等价物。
This question 类似,但不会尝试低于 9000 字节。
我也在寻找一个 strip
工具(MinGW 中的 strip
命令不会进一步减小 .exe 的大小),它可以删除 DOS 存根(在偏移量之间0x40 和 0x80,它包含 This program cannot be run in DOS mode.
,我们可以节省 64 个字节)。 This code 可以删除它,但它也会破坏 .exe 中的所有绝对偏移量。不幸的是,MinGW 中的链接器 ld
无法删除 DOS 存根,它被硬编码在文件 bfd/peXXigen.c
中,就在 NT_SIGNATURE
.
是否可以从 .exe 中删除更多 headers,即加载程序不使用的 headers?
这个问题有大量的网络文献,大约从 1995 年开始。
32 位和 64 位的每个版本 Windows 对它们在 PE .exe executable 中接受的 header 值有一套不同的规则。例如,Windows 7 接受具有 0 个节、节对齐 4、文件对齐 4 和 Windows 的其他版本(例如 Windows XP 和最新的 Windows 10 in 2020) 拒绝这些文件。
虽然可以创建小于 2048 字节的工作 .exe 文件。示例:
- hh6d.golden.exe(584 字节)来自table:它适用于 Microsoft 发布的所有 Win32 实现,也适用于 Wine。 (在 Windows NT 3.1、Windows 95、Windows XP 和 Windows 10、Wine 1.6.2、Wine 5.0 上测试。)
- hh2d.golden.exe(408 字节)适用于 Windows 95 及更高版本。 (在 Windows 95、Windows XP、Windows 7 和 Windows 10 上测试)。
- hh1.golden.exe(268 字节)不适用于 Windows XP,它适用于 Windows 7,它不适用于 Windows 10.
- pts-tinype 存储库中包含的汇编源代码。
这就是为什么不太可能发布小于 584 字节的 portable Win32 PE .exe hello-world 的原因:
- 程序代码必须放在一个executable段,最早的文件字节偏移量是512,因为SectionAlignment必须至少是512,而且该段不能从开头开始文件。
- 最小 hello-world 打印机 i386 Win32 机器代码大小为 36 字节。 (这不包括消息大小,因为消息可以存储在 header 的前 512 个字节内。)
- 对于 KERNEL32.DLL,IMAGE_IMPORT_DESCRIPTORS 的最小大小为 20 字节。 Windows 95 不允许在 header 中使用它,因此它必须放在文件字节偏移量 512 之后。
- IAT(导入地址 table)的最小大小为 16 字节:GetStdHandle 4 字节,WriteFile 4 字节,ExitProcess 4 字节,end-of-list 4 字节。由于loader修改了这些地址,所以不能放到header(也就是read-only),所以必须放在文件字节偏移量512之后。
- 如果我们将这些加起来,我们得到 512 + 36 + 20 + 16 = 584 字节。
小于 268 字节的 .exe 文件仅适用于 Windows XP 之前的 Windows 版本,它们不适用于 64 位 Windows 系统。
相关文献:
- https://www.codejuggle.dj/creating-the-smallest-possible-windows-executable-using-assembly-language/
- http://pferrie.host22.com/misc/tiny/pehdr.htm
- https://drakopensulo.wordpress.com/2017/08/06/smallest-pe-executable-x64-with-every-byte-executed/
- http://www.phreedom.org/research/tinype/
- http://www.pouet.net/topic.php?which=9565
- http://www.crinkler.net/
- https://code.google.com/archive/p/corkami/wikis/PE.wiki