今天和同事聊到「真搞不懂為什麼某些人那麼容易中毒?」這個話題時,剛好心血來潮,順手示範了一個從前曾拿來整人用的老把戲。
這個把戲就是用 linker 把某個二進制資料嵌入到執行檔中,變成其中一個區段。當然,其中沒什麼高深的學問,只是因為很多人沒有仔細玩過 binutils,不曉得 ld 有這樣的功能。
懶得打字,直接貼程式碼。首先是 Makefile,為了和 MinGW 行為相容,以下所有的執行檔都刻意加上 exe 副檔名,對 Linux 沒影響。(提醒一下,如果要複製去玩的話,別忘了把空白改 tab)
all: wrapper.exe wrapper.exe: payload.exe wrapper.c gcc -c -o wrapper.o wrapper.c ld -Ur -o payload.exe.o -b binary payload.exe gcc -o wrapper.exe wrapper.o payload.exe.o payload.exe: payload.c gcc -o payload.exe -O2 -s payload.c clean: rm *.o *.exe
payload.exe 是什麼並不重要,可以放個最簡單的 Hello World,或者乾脆抽換成圖檔、音樂檔等等。
重點在於 wrapper.c
#include <stdio.h> extern const char _binary_payload_exe_start[]; extern const char _binary_payload_exe_end[]; extern const char _binary_payload_exe_size[]; int main() { const size_t payloadSize = (size_t) ((void*)_binary_payload_exe_size); const char* payload = _binary_payload_exe_start; printf("size=%u \n", payloadSize); FILE* outfile = fopen("qwerty.exe", "wb"); fwrite(payload, sizeof(char), payloadSize, outfile); fclose(outfile); }
剛剛嵌入的二進制檔案,在程式碼中可以透過 _binary_<檔名>_start 等變數存取,檔名中的特殊字元好像會被換成底線。另一點需要注意的是,隨者 toolchain 版本的不同,變數名稱開頭也可能沒有底線,建議先用 objdump 看看實際的 symbol 變成什麼。
上面的 wrapper 程式會把被內嵌的 payload 原封不動寫出成檔案 qwerty.exe,只要具有執行權限就能執行。
應用的情境之一: 用圖檔當成 payload,把 wrapper 換成圖檔的圖示。當使用者執行 wrapper 時,將圖檔解到 temp 資料夾下然後用預設圖片瀏覽器開啟,使用者會誤以為自己確實點選了圖檔,此時 wrapper 可以在背後做其他事.....
題外話,我一直搞不懂這件事:Windows 明明就是用副檔名辨識執行檔,可是為什麼系統偏偏預設隱藏副檔名。真是自己拿石頭砸腳的設計。
應用的情境之二: 將 script 檔和直譯引擎包成單一個執行檔,直譯器可以直接從 char 陣列讀取 script。
當然,還有很多不同的玩法,大家可以發揮創意。至於要拿來整人,還有些細節要處理,這裡就不多說了。