当前位置: 华文问答 > 数码

Windows 上最小的「HelloWorld.exe」能有多小?

2019-11-29数码

目前这个问题下最小的能在Windows系统上运行的Hello World是

Windows 上最小的「HelloWorld.exe」能有多小? - 潘安仁的回答

,161Bytes,我看了他的答案后深受启发,在他的基础上更进一步,写出了97Bytes的Hello World。

先贴代码,注意在32位Windows系统上运行。(我只在WinXP32上运行通过)

ps:评论区有人指出,只能在WinXP上运行,XP之后的系统库的地址会变。

pss:将本回答最后一幅图片保存,后缀改成 .rar ,解压后也能获得程序。

4D5A0000504500004C01000048656C6C6F2C776F726C6421CC0002000B016AF5E8640C807C6A0CEB07CCCCCC1E000000680C00010050EB0C000001000400000004000000E8A4C0807C90EBFD0400E89AC0807C90EBFDCC00CCCCCC00CCCCCCCC03

————————————————分割线————————————————

为什么我的程序更小?

@潘安仁

的程序输出Hello World用了USER32的MessageBoxA,这个api看上去很好,毕竟'USER32'是6Bytes,而其他的库名如'kernel32'是8Bytes,所以调用USER32中的函数更短?

错。因为载入PE文件时kernel32.dll和ntdll.dll会被自动载入,并且这两个库正常是卸不掉的,你可以试试FreeLibrary(GetModuleHandle("kernel32")),然后再用汇编代码强制调用kernel32的一个函数,正常运行有没有。所以如果调用kernel32的函数可以删去导入表,大大节省空间。

那kernel32有什么输出函数可以输出Hello world?

有。WriteConsoleA(),写控制台。如果是同一系统,这个函数的位置应该是不变的,在Winxp系统中,这个函数地址是7C81C0ED。同时这个函数还要用到控制台输出句柄,用GetStdHandle(),地址7C810C89。这下程序就小了很多。

————————————————分割线————————————————

下面是程序的总体思路:

1.先写汇编代码

Data: db 'Hello world!' push -B call 7C810C89 push 0 push 0 push c push Data push eax call 7C81C0ED

代码好长啊能不能缩短一些呢?我们注意到WriteConsoleA的最后两个参数都对我们无用,一个保存被写入的字节数,一个是保留参数,这种无用的东西就丢掉好了。于是变成了:

Data: db 'Hello world!' push -B call 7C810C89 push c push Data push eax call 7C81C0ED

这样函数还能正常返回吗?当然能,我们没将数据压入栈不代表不能pop出东西来,至于pop出来的是什么、由此而产生栈平衡问题都不关我事。

2.压缩出最小的文件头

我的程序没有其他外部命令,自然不需要导入表和导出表,节表也不需要,直接将代码写在文件头里,只需要DOS头、文件头和可选头。

这里就是

@潘安仁

的神操作了,把PE头和DOS头重叠。

如图,加载32位PE文件时,系统对dos头只读取开头的MZ标志和e_ifanew,中间的值可以随意填写,这样我们把NT头提到地址为4的地方去,将DOS头和NT头重叠。

大家也可以尝试其他的位置,其中4是最小的可行位置。

还有一处要注意的地方是我们的是控制台程序,所以Subsystem设为3。

图中是一个可运行的PE程序,运行后直接死循环。

可以看到这样的程序还有不少空位,我们可以把代码插进去。

3.插入代码

文件头中的空白部分TimeDateStamp;PointerToSymbolTable;NumberOfSymbols;都是无用且可以随意填写的,刚好12个字节,我们把"Hello world!"放进去。

剩下不少空位我们把代码也放进去,如果放不下就放一个jmp短跳,跳到下一个空位。

放好之后如图:

图中我们放入代码后,还有不少空位,我都用0xCCh标记了出来。

4.修剪

这样我们的程序已经很小了,它是124字节,那么怎么把它变成97字节呢?相信你也猜到了,把最后一堆零删掉就好了,PE文件载入时空白内存自动填零,这堆零我们是不需要的。

最后就是这样:

运行效果:

输出了一行朴实无华的hello world,我们的程序还有不少空位,或许还可以把效果做炫酷一点,大家自己尝试。

5.也学着做一个总结:

这个问题是13年提出来的,现在已经是16年了,在浮躁的社会里,这个问题或许早已被遗忘在尘埃里。我看到了这个问题,看到了前面大神的回答,我知道我可以做得比他更小,于是我开始着手尝试。我花了几天来学习PE结构,找出缩小程序的方法,思考余暇,我不禁疑惑,做这种事,做出这种程序有什么意义,我为什么要有一个这么小的Hello world?我硬盘差几字节吗?或许你也有一样的困惑,然而我们追求这一系列极限有什么意义呢?更高更快更强,是要回到狩猎时代吗?我想这一切无关乎这种浅薄的利益,它像是一种证明,证明自己的高度。它竖起了一座里程碑,并鼓励后来人一步一个脚印,继续向上,去到山顶上,去睥睨众生。如此之后,我们浮躁的社会里,或许有了一点没那么浮躁的东西。