目前这个问题下最小的能在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头重叠。