简体   繁体   English

如何使用 MinGW 创建微型 PE (Win32) 可执行文件

[英]How to create tiny PE (Win32) executables using MinGW

I have the following C program:我有以下 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. */
}

I compile it with GCC 4.8.2, using the following command line:我使用 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

The generated .exe file is 2048 bytes long.生成的 .exe 文件长 2048 字节。 How can I make it smaller using MinGW, preferably at most 1024 bytes, or (even better) at most 512 bytes?如何使用 MinGW 使其更小,最好最多 1024 个字节,或者(甚至更好)最多 512 个字节?

I'd prefer a solution without writing assembly code, but I'm also interested in assembly solutions.我更喜欢不编写汇编代码的解决方案,但我也对汇编解决方案感兴趣。

I've tried -Wl,-N to decrease the number of sections (segments), but that caused a segfault when running the .exe in Wine.我试过-Wl,-N来减少节(段)的数量,但这会导致在 Wine 中运行 .exe 时出现段错误。

This article suggests that 480 bytes is possible. 本文建议 480 字节是可能的。 It uses the following settings:它使用以下设置:

#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)

Unfortunately these #pragma s don't work with MinGW GCC.不幸的是,这些#pragma不适用于 MinGW GCC。 Are there equivalents?有等价物吗?

In here I was able to find the GCC flags -Wl,--section-alignment,16,--file-alignment,16 which bring down the .exe size to 752 bytes.这里,我能够找到 GCC 标志-Wl,--section-alignment,16,--file-alignment,16将 .exe 大小降低到 752 字节。 The .exe seems to work in Wine. .exe 似乎在 Wine 中工作。

By modifying the linker script I was able to merge .data and .rdata , and go down to 736 bytes.通过修改链接器脚本,我能够合并.data.rdata ,并减少到 736 字节。 I'm using these GCC flags in addition to those above: -Wl,--section-alignment,16,--file-alignment,16,-T,tinygccpe.scr .除了上述那些之外,我还使用这些 GCC 标志: -Wl,--section-alignment,16,--file-alignment,16,-T,tinygccpe.scr

I'm still looking for the MinGW equivalent of /MERGE .我仍在寻找等效于/MERGE的 MinGW 。

This question is similar, but it doesn't attempt to go below 9000 bytes. 这个问题很相似,但它不会尝试低于 9000 字节。

I'm also looking for a strip tool (the strip command in MinGW doesn't reduce the .exe size any further) which can remove the DOS stub (which is between offsets 0x40 and 0x80, it contains This program cannot be run in DOS mode. , we could save 64 bytes).我也在寻找一个strip工具(MinGW 中的strip命令不会进一步减小 .exe 的大小),它可以删除 DOS 存根(位于偏移量 0x40 和 0x80 之间,它包含This program cannot be run in DOS mode. ,我们可以节省 64 个字节)。 This code can remove it, but it also breaks all the absolute offsets in the .exe. 此代码可以删除它,但它也会破坏 .exe 中的所有绝对偏移量。 Unfortunately the linker ld in MinGW isn't able to remove the DOS stub, it's hardcoded in the file bfd/peXXigen.c , just above the NT_SIGNATURE .不幸的是,MinGW 中的链接器ld无法删除 DOS 存根,它被硬编码在文件bfd/peXXigen.c ,就在NT_SIGNATURE

Is it possible to strip more headers from the .exe, ie headers which the loader doesn't use?是否可以从 .exe 中删除更多的头文件,即加载程序不使用的头文件?

This question has extensive online literature, starting from about 1995.这个问题有大量的在线文献,大约从 1995 年开始。

Each version of 32-bit and 64-bit Windows has a different set of rules on what header values they accept in PE .exe executables. 32 位和 64 位 Windows 的每个版本都有一套不同的规则,用于说明它们在 PE .exe 可执行文件中接受哪些标头值。 For example, Windows 7 accepts .exe files with 0 sections, section alignment 4, file alignment 4, and other versions of Windows (eg Windows XP and the most recent Windows 10 in 2020) reject these files.例如,Windows 7 接受具有 0 节、节对齐 4、文件对齐 4 的 .exe 文件,而其他版本的 Windows(例如 Windows XP 和 2020 年的最新 Windows 10)拒绝这些文件。

It's possible to create working .exe files smaller than 2048 bytes though.不过,可以创建小于 2048 字节的有效 .exe 文件。 Example:例子:

  • hh6d.golden.exe (584 bytes) is portable: it works on all Win32 implementations released by Microsoft and also on Wine.hh6d.golden.exe (584 字节)是可移植的:它适用于 Microsoft 发布的所有 Win32 实现,也适用于 Wine。 (Tested on Windows NT 3.1, Windows 95, Windows XP and Windows 10, Wine 1.6.2, Wine 5.0.) (在 Windows NT 3.1、Windows 95、Windows XP 和 Windows 10、Wine 1.6.2、Wine 5.0 上测试。)
  • hh2d.golden.exe (408 bytes) works on Windows 95 and up.hh2d.golden.exe (408 字节)适用于 Windows 95 及更高版本。 (Tested on Windows 95, Windows XP, Windows 7 and Windows 10). (在 Windows 95、Windows XP、Windows 7 和 Windows 10 上测试)。
  • hh1.golden.exe (268 bytes) doesn't work on Windows XP, it works on Windows 7, it doesn't work on Windows 10.hh1.golden.exe (268 字节)不适用于 Windows XP,适用于 Windows 7,不适用于 Windows 10。
  • Assembly source code included in the pts-tinype repo. pts-tinype 存储库中包含的汇编源代码。

Here is why it is unlikely that a portable Win32 PE .exe hello-world shorter than 584 bytes will be released:这就是为什么不太可能发布小于 584 字节的便携式 Win32 PE .exe hello-world 的原因:

  • Program code must be put to an executable section, and the earliest file byte offset for that is 512 because SectionAlignment must be at least 512, and the section can't start at the beginning of the file.程序代码必须放在一个可执行的节中,并且最早的文件字节偏移量是512,因为SectionAlignment 必须至少为512,并且节不能从文件的开头开始。
  • Minimum hello-world printer i386 Win32 machine code size is 36 bytes.最小 hello-world 打印机 i386 Win32 机器代码大小为 36 字节。 (This excludes the message size, because the message can be stored in the header, within the first 512 bytes.) (这不包括消息大小,因为消息可以存储在头中的前 512 个字节内。)
  • Minimum size of IMAGE_IMPORT_DESCRIPTORS is 20 bytes, for KERNEL32.DLL.对于 KERNEL32.DLL,IMAGE_IMPORT_DESCRIPTORS 的最小大小为 20 字节。 Windows 95 doesn't allow this inside the header, so it must be put after file byte offset 512. Windows 95 不允许在头文件中这样做,所以它必须放在文件字节偏移 512 之后。
  • Minimum size of IAT (import address table) is 16 bytes: 4 bytes for GetStdHandle, 4 bytes for WriteFile, 4 bytes for ExitProcess, 4 bytes for end-of-list. IAT(导入地址表)的最小大小为 16 字节:GetStdHandle 4 字节,WriteFile 4 字节,ExitProcess 4 字节,列表结尾 4 字节。 Since the loader modifies these addresses, they can't be put to the header (which is read-only), so they must be put after file byte offset 512.由于加载器修改了这些地址,所以不能放到header(只读)中,所以必须放到文件字节偏移512之后。
  • If we add these up, we get 512 + 36 + 20 + 16 = 584 bytes.如果我们把这些加起来,我们得到 512 + 36 + 20 + 16 = 584 个字节。

The .exe files smaller than 268 bytes work only on Windows versions earlier than Windows XP, and they don't work on 64-bit Windows systems.小于 268 字节的 .exe 文件仅适用于早于 Windows XP 的 Windows 版本,它们不适用于 64 位 Windows 系统。

Related literature:相关文献:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM