Phát hiện DOSBox từ bên trong hệ thống: Sử dụng lệnh opcode không hợp lệ để nhận diện trình giả lập
Bài viết này khám phá kỹ thuật tinh vi để phát hiện môi trường DOSBox bằng cách khai thác một lệnh opcode riêng biệt (FE/7) mà trình giả lập này sử dụng nội bộ, thay vì dựa vào các chuỗi ký tự BIOS dễ bị giả mạo.

Nếu bạn là người thường xuyên đọc blog công nghệ, hẳn bạn đã từng nghe đến DOSBox. Đây là một trình giả lập MS-DOS nổi tiếng, đồng thời cũng là một dạng trình giả lập kiến trúc x86. Tuy nhiên, khác với các trình giả lập x86 như 86Box hay QEMU, các thành phần DOS trong DOSBox là một phần không thể tách rời.
Mặc dù có các ngắt BIOS và quy trình POST (Power-On Self-Test), nhưng DOSBox không thực sự sở hữu một BIOS theo nghĩa truyền thống là "một chip ROM được ánh xạ vào bộ nhớ". Thậm chí, nó cũng không thực sự có một hệ điều hành DOS đầy đủ theo nghĩa đen. Tuy nhiên, khi bạn đang chạy bên trong DOSBox, bạn gần như không nhận ra sự khác biệt này. Hầu hết mọi API DOS mà bạn mong đợi đều có sẵn, và các tính năng như Long File Names (tên tệp dài) cũng sẽ không xuất hiện nếu phiên bản bạn báo cáo quá cũ để hỗ trợ nó.
Vậy làm thế nào để phát hiện ra một thứ được thiết kế để "giấu mình"? Hãy cùng tìm hiểu kỹ thuật sâu bên trong.
Hệ thống giả lập
Tại sao các phương pháp truyền thống không đủ tin cậy?
Hầu hết các hệ thống giống MS-DOS khác không phải là bản sao hoàn hảo của MS-DOS, và bạn thường có thể sử dụng các "điểm kỳ lạ" (quirks) hoặc các chức năng thừa để xác định xem mình đang chạy trên nền tảng nào. Nhiều người tưởng tượng DOSBox cũng vậy!
Tuy nhiên, các "điểm kỳ lạ" thường là các lỗi đang chờ được khắc phục. Các lệnh như MOUNT và VER có vẻ như có khả năng "nói chuyện" với thế giới bên ngoài, nhưng liệu có một chức năng ẩn nào đó?
Cách đơn giản nhất mà nhiều người nghĩ đến đầu tiên là lấy chuỗi ký tự tại địa chỉ FE00:0061 — địa chỉ nổi tiếng chứa chuỗi phiên bản BIOS Award — và kiểm tra xem nó có bắt đầu bằng "DOSBox" hay không. Tuy nhiên, cách này rất mong manh (brittle). Người ta hoàn toàn có thể sửa đổi một BIOS không phải của DOSBox để có chuỗi phiên bản đó, hoặc chỉnh sửa DOSBox để hiển thị chuỗi khác.
Chúng ta cần tìm một thứ gì đó là một phần vốn có của trình giả lập, một bằng chứng không thể chối cãi rằng đây là DOSBox.
Mã máy
Đi sâu vào mã máy: Lệnh Opcode bí mật
Hãy quay lại cách DOSBox giao tiếp với thế giới bên ngoài thông qua các lệnh như MOUNT.COM. Các tệp .COM về bản chất là mã máy, nghĩa là ta có thể chạy trực tiếp chúng qua trình tháo gỡ (disassembler).
Khi phân tích MOUNT.COM của DOSBox, chúng ta thấy một đoạn mã lạ. Sau vài lệnh hợp lý, ta bắt gặp các byte dường như là "rác": db 0xfe. Trên một CPU x86 điển hình, byte này sẽ gây ra ngoại lệ Invalid Instruction (Lệnh không hợp lệ).
Tuy nhiên, khi viết một CPU x86 giả lập, người ta có thể tự tạo ra các lệnh riêng! Trong mã nguồn của DOSBox, 0xFE là một nhóm opcode. Trong khi 0x00 là INC và 0x01 là DEC (các lệnh thực trên x86), thì cái cuối cùng 0x07 lại là độc quyền của DOSBox.
Byte ngay sau opcode được dùng để chỉ định "callback" nào sẽ được gọi. Về cơ bản, DOSBox đã định nghĩa một lệnh máy riêng FE /7 dùng để thoát ra khỏi môi trường giả lập và gọi hàm nội bộ.
Khai thác lệnh không hợp lệ để phát hiện
Trên các CPU x86 thực tế (từ 80186 trở đi), các lệnh không hợp lệ sẽ kích hoạt ngoại lệ #UD (Undefined Opcode), hay còn gọi là Ngắt 06h. Đó là chìa khóa của chúng ta.
Chúng ta có thể viết một trình xử lý ngoại lệ cho Ngắt 06h. Nếu lệnh FE 38 00 00 thực thi mà không gây ra crash (nghĩa là DOSBox đã xử lý nó nội bộ như một lệnh callback rỗng), thì chúng ta đang ở trong DOSBox. Nếu nó kích hoạt ngắt 06h, chúng ta đang ở trên phần cứng thật hoặc trình giả lập khác.
Kiểm tra lỗi
Tìm ra lỗi trong 86Box
Để kiểm thử kỹ thuật này, tác giả đã sử dụng 86Box — một trình giả lập khác không phải là DOSBox. Bước qua mã lệnh bằng chương trình DEBUG, một điều kỳ lạ đã xảy ra: DEBUG không hiểu lệnh này, nhưng lại bước qua nó như thể nó hợp lệ?!
Sau nhiều ngày gỡ lỗi, nguyên nhân được tìm ra: 86Box đã kế thừa một lỗi từ PCem, nơi bất kỳ sửa đổi opcode ModR/M nào khác 0 đều được xử lý như FE /1 (lệnh DEC). Điều này có nghĩa là FE /2, FE /4 và cả FE /7 đều hoạt động như lệnh DEC thay vì gây lỗi. Lỗi này sau đó đã được sửa chữa và hợp nhất vào mã nguồn chính của 86Box.
Kết luận
Mặc dù dự án này rất thú vị, mục đích không chỉ để phát hiện DOSBox. Các môi trường khác như NTVDM hay MS-DOS Prompt của Win9x dễ phát hiện hơn nhiều thông qua các cuộc gọi INT 2Fh. DOSEMU, một trình giả lập khác trên Linux, có rất nhiều API callback được triển khai dưới dạng các tệp .COM công khai.
Tuy nhiên, việc sử dụng một lệnh CPU tùy chỉnh là một phương pháp phát hiện khó giả mạo hơn nhiều so với việc thay đổi một chuỗi ký tự BIOS, và nó cho thấy sự thú vị của lập trình cấp thấp.



