- Đây là một trò chơi Snake chạy trên Windows, Linux và trình duyệt chỉ bằng một tệp đơn kích thước 13KiB, hỗ trợ cả ba nền tảng từ cùng một mã nguồn
- Trò chơi tuân theo luật Snake cổ điển: điều khiển rắn ăn mồi và tránh đâm vào tường, có kèm điểm số, cấp độ và cấu trúc mê cung
- Mỗi nền tảng được triển khai bằng C (WinAPI/X11) và JavaScript (HTML5 Canvas), mỗi bản đều được nén và gộp xuống mức khoảng 3~5KiB
- Windows chạy bằng header PE bất thường và cơ chế apphelp, Linux dùng nén
lzma và shell dropper, còn trình duyệt tận dụng các mẹo HTML/CSS để thực thi
- Kết quả ghép ba phần triển khai là một tệp thực thi đơn 13.312 byte, cho thấy tiềm năng thử nghiệm của cấu trúc tệp thực thi đa nền tảng
Thử nghiệm đa nền tảng lấy cảm hứng từ Cosmopolitan libc
- Cosmopolitan libc là bộ công cụ có thể biên dịch mã nguồn C thành một binary đơn chạy được trên nhiều hệ điều hành
- Hỗ trợ chạy native trên nhiều OS như Windows, Linux, BSD
- Do không hỗ trợ GUI và bị giới hạn bởi kích thước binary lớn, tác giả chọn thử thách tự làm một trò chơi video dưới 16KiB
- Mục tiêu là tạo một trò chơi dựa trên một mã nguồn duy nhất, chạy được trên Windows, Linux và trình duyệt
Cấu trúc và luật chơi
- Trò chơi là Snake tiêu chuẩn, điều khiển bằng phím mũi tên hoặc WASD
ESC để thoát, R để đặt lại, P để tạm dừng, phím cách để bắt đầu
- Mỗi lần ăn mồi, điểm số sẽ tăng: mồi thường được 10 điểm, mồi màu vàng (xác suất 15%) được 20 điểm
- Mồi sẽ biến mất sau một khoảng thời gian nhất định; thời gian này được quyết định theo tốc độ của rắn, vốn tỉ lệ với chiều dài của nó
- Ăn 10 mồi sẽ sang cấp tiếp theo, bố cục tường được thay đổi ngẫu nhiên
- Mê cung được tạo sao cho luôn tồn tại đường đi tới mồi
- Vị trí khởi đầu của rắn cũng là ngẫu nhiên, nhưng được đặt theo hướng có ít nhất 5 ô trống
- Kích thước tệp game hoàn chỉnh là 13.772 byte
Cách triển khai trên từng nền tảng
- Phiên bản Windows được viết bằng mã C dựa trên WinAPI, bao gồm script nén và stub giải nén
- Tận dụng các byte có thể điều khiển tự do sau chữ ký
MZ của header PE để nhúng shell script
- Nhờ vậy, tệp này vừa là file thực thi Windows vừa đồng thời hợp lệ như một shell script trên Linux
- Ở lần chạy đầu tiên có thể xuất hiện lỗi “The application was unable to start correctly (0xc0000005)”, nhưng chạy lại sẽ hoạt động bình thường
- Phiên bản Linux dùng nén
lzma, với một shell dropper nhỏ để giải nén và chạy binary ELF64 đã nén
- Được cấu hình để chạy trong khi bỏ qua một số phần đầu và cuối của tệp
- Phiên bản HTML tận dụng đặc tính trình duyệt bỏ qua phần đầu không cần thiết của tệp và vẫn xử lý HTML
- CSS được dùng để che phần dữ liệu không cần thiết khỏi màn hình
Ghép thành một tệp duy nhất và cấu trúc của nó
- Các tệp cho ba nền tảng được nối (concatenate) theo một thứ tự nhất định, để mỗi môi trường chỉ thực thi phần phù hợp với mình
- Windows nhận diện section PE, Linux nhận diện section ELF, trình duyệt nhận diện section HTML
- Kích thước tệp cuối cùng là 13.312 byte, tạo thành một binary polyglot chạy được ở cả ba môi trường
- Bên trong tệp lần lượt chứa header PE của Windows, mã nén LZMA cho Linux, và mã HTML/CSS/JavaScript
- Trong khối mã ví dụ có thể thấy lần lượt chữ ký
MZ, marker ks, thẻ <html>, khối <script>...
Ý nghĩa kỹ thuật
- Hiện thực hóa cấu trúc thực thi hỗ trợ đồng thời Windows, Linux và trình duyệt chỉ bằng một tệp duy nhất
- Phân nhánh đường chạy theo từng nền tảng thông qua việc lồng ghép các định dạng PE, ELF và HTML
- Một thành tựu thử nghiệm kết hợp nén, hack định dạng và thực thi đa nền tảng trong kích thước cực nhỏ chỉ 13KiB
1 bình luận
Ý kiến Hacker News
Tôi đã trích xuất file thực thi Linux và khá ngạc nhiên khi readelf và objdump không đọc được đúng cách
Tìm hiểu thêm thì hóa ra nó nhét cưỡng ép tên trình liên kết động vào các trường không dùng trong PT_DYNAMIC header để tiết kiệm dung lượng
Nhưng tôi nghĩ việc cố tình làm hỏng định dạng như thế này là một kiểu tiết kiệm vô nghĩa
Nó cũng có thể xung đột với các tính năng bảo mật như antivirus hay DEP, nên tôi không muốn đụng vào những file thực thi kiểu này
Tôi ngạc nhiên vì kích thước file của game Zelda gốc nhỏ đến vậy
Thật kỳ diệu khi chỉ với chừng đó code và dữ liệu mà họ tạo ra được cảm xúc và độ nhập vai lớn đến thế
The Legend of Zelda (Wikipedia)
40 năm sau người ta mới trích xuất bản đồ ra, chỉ riêng PNG đã hơn 800KB
Xem bản đồ game
Các nhà thiết kế đã sắp xếp chúng như một câu đố trong giới hạn không gian
Tham khảo cấu trúc bản đồ Zelda
Kết quả thử nghiệm của tôi
.htmllà chạy đượclzma, cài góixzvào thì chạy được.comhoặc.exethì gặp lỗi (0xc0000005), nhưng sau khi tắt cài đặt DEP thì chạy đượcchmod +x snake.comrồi chạy thì Mono sẽ thử mở và thất bạiThay vào đó, chạy bằng
bash snake.comthì hoạt động tốt. Tôi dùng Debian 13Điều thú vị là file này thực chất là ba file thực thi được gộp làm một
Vì vậy về lý thuyết bạn cũng có thể nhét vào các chương trình hoàn toàn khác nhau cho từng nền tảng
Nó làm tôi nhớ tới game FPS 96KB tên kkrieger
Chất lượng đồ họa của nó hồi đó thực sự đáng kinh ngạc
kkrieger (archive)
Tôi muốn biết trên Mac có cách nào chạy ngoài trình duyệt không
Tôi gặp lỗi
cannot execute binary fileTôi tò mò liệu có thể phát triển cơ chế này thành một universal binary thực sự hay không
Ví dụ viết code bằng Haxe rồi biên dịch sang C++ để tạo bản Win32 và Linux,
sau đó chuyển sang JavaScript để chạy được dưới dạng HTML,
rồi ghép cả ba kết quả thành một file duy nhất
Tôi thích ý tưởng ứng dụng thực thi đơn có thể chạy ở mọi nơi chỉ với một file
Tôi cũng đang nghiên cứu hướng này khi xây dựng một nền tảng serverless
Chỉ với một file
.htmlduy nhất có thể tạo ứng dụng hướng dữ liệu, với web-components được nạp từ xaKhả năng mở trực tiếp HTML bằng giao thức
file://rất mạnh, nhưng thường bị xem nhẹTức là không chỉ đơn giản mở một phiên bản trình duyệt, mà đúng hơn là đã tạo ra hai ứng dụng native hoàn chỉnh
Thật lạ khi polyglot file lại xuất hiện muộn như thế này
Về mặt kỹ thuật thì có vẻ như 10–20 năm trước cũng đã làm được rồi
EICAR.COM hiện lên trong đầu như một ví dụ rất sớm của file vừa là văn bản vừa là file thực thi
Nó khiến tôi nhớ tới các cuộc thi webgame siêu nhỏ như js13kGames