當前位置: 華文問答 > 數碼

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?我硬碟差幾字節嗎?或許你也有一樣的困惑,然而我們追求這一系列極限有什麽意義呢?更高更快更強,是要回到狩獵時代嗎?我想這一切無關乎這種淺薄的利益,它像是一種證明,證明自己的高度。它豎起了一座裏程碑,並鼓勵後來人一步一個腳印,繼續向上,去到山頂上,去睥睨眾生。如此之後,我們浮躁的社會裏,或許有了一點沒那麽浮躁的東西。