1 điểm bởi GN⁺ 2024-07-15 | 1 bình luận | Chia sẻ qua WhatsApp
  • Tựa RTS năm 2001 Emperor: Battle for Dune gặp trục trặc ở cả chạy, cài đặt và chơi online trên Windows hiện đại, và EmperorLauncher là bản vá đưa game trở lại trạng thái có thể chơi được
  • Các cải tiến chính tập trung vào hỗ trợ độ phân giải cao, giới hạn 60 FPS, chơi mạng nhiều người trực tiếp qua IP, chế độ chiến dịch co-op, và vượt qua quy trình cài đặt bị hỏng
  • Cách triển khai gồm launcher thay thế Emperor.exe, tiêm DLL vào Game.exe, vá hàm bằng Microsoft Detours, hook kết xuất Direct3D 7, chặn winsock, và một máy chủ WOL tối giản
  • Chơi online được chuyển từ cấu trúc P2P cũ với cổng ngẫu nhiên và NAT punching sang đường hầm kết nối một chiều client→server, để chỉ máy chủ host cần quan tâm cấu hình mạng
  • Công cụ xử lý từ khâu cài đặt tới chạy game: sao chép file từ CD gốc, giải nén .cab, áp dụng bản vá chính thức v1.09, bỏ qua đăng ký COM của WOLAPI.DLL, và cả UI launcher Win32

Điểm nghẽn của Emperor: Battle for Dune

  • Emperor: Battle for Dune là game chiến thuật thời gian thực do Westwood Studios phát hành năm 2001, là phần tiếp theo của Dune 2000
  • Trên hệ thống hiện đại, nhiều vấn đề vẫn cản trở việc chơi game
    • Không thể chạy ở độ phân giải cao phù hợp với màn hình hiện đại
    • Trong multiplayer, tốc độ mô phỏng game không bị giới hạn nên chạy quá nhanh
    • Westwood Online(WOL) không còn hoạt động, khiến multiplayer ngoài LAN trở nên khó khăn
    • Chiến dịch co-op là tính năng chỉ dành cho online nên không dùng được trong LAN
    • Trình cài đặt đi kèm đĩa bị hỏng
    • Nhiều hiệu ứng hình ảnh bị lỗi ở mức framerate cao trên PC hiện đại
  • EmperorLauncher là bản vá nhằm giải quyết các vấn đề này, và cả file tải về lẫn mã nguồn đều được công khai

Thay thế Emperor.exe và khởi tạo Game.exe

  • Emperor.exe của game không phải file thực thi game chính mà chỉ là một wrapper mỏng để khởi chạy Game.exe
  • Nếu chạy trực tiếp Game.exe thì không có gì xảy ra, nên cần phân tích và tái hiện quy trình khởi tạo mà Emperor.exe vốn thực hiện bằng launcher thay thế
  • IDA được dùng để phân tích
    • IDA có thể disassemble file thực thi và decompile một phần mã sang dạng C
    • Với binary đã mất thông tin kiểu và struct, cần lần theo lời gọi hàm và cách dùng Windows API
  • Trước khi chạy Game.exe, Emperor.exe tạo mutex và handle file mapping ẩn danh, xử lý dữ liệu đọc từ Emperor.dat, rồi sao chép vào vùng mapping
  • Tiến trình cha gửi giá trị handle file mapping bằng Windows message tới thread chính của tiến trình con lấy từ CreateProcessA
    • ID message tùy biến dùng là 0xBEEF
    • Dữ liệu file mapping là ba chuỗi "UIDATA,3DDATA,MAPS", được truyền cho mã nạp tài nguyên của game
  • Thay vì tự hiện thực lại mã giải mã, tác giả đặt một công cụ dump vào vị trí Game.exe để ghi dữ liệu được truyền xuống đĩa, rồi để launcher thực hiện lại đúng chuỗi thao tác đó

Cách tiêm DLL và vá hàm

  • Để áp dụng bản vá, cần chạy mã người dùng bên trong tiến trình Game.exe, và cách CreateRemoteThread + LoadLibrary được sử dụng
  • Quy trình tiêm diễn ra theo thứ tự sau
    • Cấp phát buffer trong bộ nhớ tiến trình đích bằng VirtualAllocEx
    • Sao chép chuỗi đường dẫn DLL bằng WriteProcessMemory
    • Truyền địa chỉ LoadLibrary và buffer đường dẫn DLL cho CreateRemoteThread để nạp DLL trong tiến trình đích
    • Khi DllMain của DLL chạy, mã vá sẽ được thực thi
  • Tiến trình được khởi động ở trạng thái suspended rồi mới tiêm DLL để đảm bảo có thể chạy mã trước khi main bắt đầu
  • Để sửa các hàm sẵn có, dự án dùng Microsoft Detours
    • Detours thay các lệnh đầu của hàm gốc bằng lệnh nhảy để chuyển lời gọi sang hàm thay thế
    • Các lệnh gốc bị ghi đè được chép sang vùng nhớ khác, rồi tạo wrapper nhảy tiếp tới phần còn lại của hàm gốc, nhờ đó vẫn có thể gọi lại hàm ban đầu
  • Vì page chứa mã hàm không cho ghi vì lý do bảo mật, cần đổi quyền bằng VirtualProtect và gọi FlushInstructionCache sau khi sửa

Khôi phục log debug

  • Trong binary có các lời gọi trông giống log debug, nhưng hàm đích thực tế chỉ là một hàm rỗng chứa ret
  • Có vẻ ở bản build release, nhiều hàm rỗng đã bị gộp vào cùng một đoạn mã, và một trong số đó vốn là logger debug
  • Ban đầu, tác giả dùng heuristic giải thích đối số đầu tiên là con trỏ chuỗi và kiểm tra xem có phải ký tự ASCII có thể in được hay không
    • Truy cập con trỏ sai được bắt và bỏ qua bằng SEH exception của Windows
    • Cách này hoạt động phần nào nhưng vẫn còn false positive và false negative
  • Sau đó, dùng tính năng patch của IDA và script Python để chuyển các điểm gọi log sang một hàm rỗng riêng biệt
    • Một phần được tìm bằng heuristic, một phần bằng mẫu push hằng chuỗi rồi gọi hàm
    • Hàng trăm điểm gọi còn lại được chú thích thủ công
  • Log được khôi phục giúp ích cho việc debug multiplayer WOL
    • Khi thấy log assert "MyId == INVALID_ID" trong quá trình xử lý SC_MESSAGE_YOUR_DETAILS, tác giả xác nhận qua dump Wireshark rằng lệnh GAMEOPT đang bị gửi sai tới mọi người chơi

Bản vá đồ họa Direct3D 7

  • Emperor là game dựa trên Direct3D 7, trong khi hỗ trợ Direct3D 7 trên Windows hiện đại không còn đầy đủ
  • Vấn đề độ phân giải cao liên quan tới giới hạn kích thước texture tối đa 2048 của lớp wrapper Direct3D 7, và được giải quyết bằng cách tận dụng mã từ LegacyD3DResolutionHack của UCyborg
  • Game không xử lý tốt màn hình ngoài tỷ lệ 4:3
    • Bản thân việc render vẫn chạy, nhưng UI bị lỗi như thể bị phóng quá mức
    • Độ lệch của việc render chuột trong game cũng sai tùy theo khoảng cách tới tâm màn hình
  • Giải pháp là letterbox 4:3
    • Chạy game ở chế độ cửa sổ cho phép dùng độ phân giải tùy ý
    • Bỏ kiểu viền cửa sổ, rồi gán lại cửa sổ game làm con của một cửa sổ nền đen toàn màn hình
    • Thêm mouse capture để edge scrolling không bị lỗi trên hệ nhiều màn hình hoặc ở chế độ cửa sổ
  • Giới hạn framerate được thực hiện bằng cách vá IDirect3DDevice7::EndScene để khóa ở 60 FPS
    • EndScene được gọi một lần ở cuối mỗi frame nên phù hợp để tính thời gian chờ và cho thread sleep
    • Vì con trỏ EndScene không được export trực tiếp, cần hook tuần tự các lời gọi DirectDrawCreateExIDirect3D7::CreateDevice để lấy con trỏ hàm từ vtable

Multiplayer online và thay thế WOL

  • Mục tiêu là multiplayer trực tiếp qua IP chỉ cần port forwarding và nhập IP, không cần hạ tầng lobby hay hosting
  • Chế độ LAN có hoạt động nhưng dùng UDP broadcast để tìm server nên không phù hợp cho chơi qua Internet
    • Menu LAN không có chức năng nhập IP thủ công
    • Ban đầu từng thử vá chat LAN để chỉ định IP, nhưng dừng lại sau khi xác nhận chiến dịch co-op là tính năng chỉ có trên WOL
  • Để hồi sinh WOL cần hai thành phần
    • Một WOL master server giả để game biết phải kết nối tới đâu và khởi chạy trận nào
    • Một proxy để gói tin game hoạt động trên kết nối IP trực tiếp
  • Trong kiến trúc WOL cũ, ngoài master server còn có server “mangler”, có vẻ dùng để điều phối NAT punching
    • Server mangler gốc đã biến mất, và game bị treo khi chờ phản hồi từ đó
    • Bản vá loại bỏ lời gọi tới mangler
  • Emperor dùng mô hình mạng P2P, mở kết nối hai chiều cho từng cặp người chơi và chọn cổng ngẫu nhiên
    • Cấu trúc này yêu cầu mọi client đều phải nhận được cổng mở nên không phù hợp với Internet hiện đại
  • Giải pháp là chặn các hàm winsock để tunnel mọi kết nối qua một kết nối client→server duy nhất
    • Tin nhắn mà client định gửi tới server hay client khác sẽ bị chặn, bọc thêm header rồi gửi qua kết nối duy nhất đó
    • Một thread phía server nhận tin nhắn và phân phối tới đúng đích
    • Game vẫn tin rằng nó đang chạy theo kiểu P2P, nhưng thực tế chỉ host server mới cần xử lý cấu hình mạng
  • Với cấu hình này, đã có thể tạo và tham gia trận chiến dịch co-op

Triển khai máy chủ WOL tối giản

  • WOL master server có cấu trúc gần với một IRC server
  • Trên xwis.net có một server có vẻ do fan vận hành, và tại thời điểm viết bài dường như vẫn có quyền truy cập tới DNS gốc của game là servserv.westwood.com
    • Emperor không hoạt động hoàn toàn nguyên trạng trên xwis, nhưng vẫn hữu ích để tham khảo cách tạo và tham gia lobby
  • Triển khai WOL công khai handle_wol.cpp của pvpgn-server cũng được dùng làm tài liệu tham khảo
  • Lý do tự viết server là vì không có gì đảm bảo server bên ngoài sẽ tồn tại lâu dài
    • Mục tiêu không phải vận hành một cộng đồng cạnh tranh mà là cung cấp tối thiểu chức năng cần thiết để chạy trận multiplayer
  • WOL pha trộn giữa IRC chuẩn và hành vi tùy biến
    • Lobby game là các kênh đặc biệt
    • Thông tin lobby dùng IRC topic
    • Chat trong lobby dùng PAGE thay vì PRIVMSG
    • Đồng bộ thiết lập game dùng thông điệp GAMEINFO chứa nội dung không phải ASCII
  • Phần server WOL cơ bản được hoàn thiện bằng trial and error; không quá vững nếu lệch khỏi luồng chuẩn, nhưng có thể hoạt động

Trình cài đặt và áp dụng bản vá v1.09

  • Trình cài đặt gốc bị hỏng, nên trước đây cần cách vòng là để người dùng chép nội dung CD sang ổ cứng rồi ghi đè bằng setup thay thế
  • EmperorLauncher tích hợp chức năng cài đặt để sao chép file từ CD gốc và giải nén các file .cab
    • .cab là một định dạng archive tương tự zip và Windows có sẵn giao diện để giải nén
  • Việc áp dụng bản vá chính thức cuối cùng v1.09 còn rắc rối hơn
    • Cách giải nén đơn thuần EM109EN.EXE bằng 7zip để lấy binary mới nhất không hiệu quả
    • Tác giả phát hiện một resource lớn trong Windows resource có chứa header file thực thi
    • 4 byte đầu của resource đó là kích thước file, phần sau mới là file thực sự
  • EM109EN.EXE giải nén DLL nhúng ra file tạm rồi nạp nó, sau đó gọi hàm RTPatch32@12 bên trong DLL
    • RTPatch là công cụ vá binary diff
    • Tác giả tham khảo công cụ myRTP để nạp và chạy trực tiếp DLL nhúng
  • DLL không đọc đường dẫn cài đặt từ đối số truyền vào mà lấy từ registry, nên cần tạo khóa registry giả đúng như nó mong đợi để áp dụng bản vá

Xử lý Westwood Online Shared Internet Components

  • Bản cài gốc có thêm Westwood Online Shared Internet Components tách biệt với phần chính của Emperor
  • Nếu thiếu thành phần này thì WOL không hoạt động, và file quan trọng nhất là WOLAPI.DLL
  • WOLAPI.DLL là một COM class library, và Emperor tạo các đối tượng COM trong DLL bằng CoCreateClass
  • Cách đăng ký COM thông thường là đăng ký CLSID và đường dẫn DLL trên toàn hệ thống dưới HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
    • Cách này cần quyền quản trị
    • Nó cũng ảnh hưởng toàn hệ thống chứ không chỉ tiến trình game
  • Bản vá xử lý việc này bằng registry redirect và OaEnablePerUserTLibRegistration để đăng ký theo người dùng
    • Tác giả không tìm ra cách đăng ký class library chỉ trong phạm vi một tiến trình
    • Cách thử gọi trực tiếp DllGetClassObject đã không hoạt động

UI launcher và kết quả cuối cùng

  • Bước cuối cùng là một UI launcher đơn giản để nhập IP và thay đổi các thiết lập cơ bản
  • UI được viết bằng Win32 controls thuần
    • Với một UI tĩnh và đơn giản thì như vậy là đủ, dù trải nghiệm tự làm UI Win32 khá vất vả
  • Cuối cùng, EmperorLauncher trở thành công cụ bao gồm khả năng chạy trên hệ thống hiện đại, độ phân giải cao, giới hạn 60 FPS, multiplayer trực tiếp qua IP, chiến dịch co-op, cùng cả cài đặt và áp dụng bản vá

1 bình luận

 
GN⁺ 2024-07-15
Ý kiến trên Hacker News
  • Trò chơi này có ý nghĩa khá lớn đối với toàn bộ thể loại chiến thuật thời gian thực. Khi nhắc đến RTS, người ta thường nghĩ đến mô hình nông dân đi khai thác tài nguyên rồi bảo vệ chúng, và Dune RTS là một trong những hình mẫu gần với nguyên bản đó nhất
    Tuy nhiên, cấu trúc đó cũng là do tiểu thuyết gốc. Nếu không, có lẽ cả thể loại đã đi theo một hướng hoàn toàn khác. Ví dụ, có thể tài nguyên được khai thác ngay từ căn cứ, còn đối thủ thì quấy rối công trình để kìm hãm, và phần thưởng cho việc kiểm soát bản đồ cũng có thể mang hình thức khác thay vì chỉ là khả năng tiếp cận tài nguyên

    • Nói chính xác thì bài viết này không nói về Dune 2 mà là về Emperor: Battle for Dune
    • Như người khác đã chỉ ra, đây có lẽ là phần tiếp theo của Dune 2 mà bạn đang nghĩ tới
      Dune 1 cũng đã đặt nền móng. Ban đầu nó gần với một game phiêu lưu point-and-click hơn, nhưng về cuối lại biến thành một trò chơi có quản lý tài nguyên và khai thác, sản xuất quân, chiến đấu, và cả cải tạo địa hình
      Phần cuối thật sự rất khó hiểu. Mục tiêu có vẻ là biến hành tinh trở lại xanh tươi, nhưng khi đã phủ xanh thì lại không còn spice nữa. Trong khi đó hoàng đế vẫn liên tục đòi giao thêm spice, và nếu không đạt chỉ tiêu thì game over
      Cũng có thể là vì tôi chơi nó khi còn nhỏ nên không hiểu đúng. Có lẽ nên đọc lại, nhưng theo tiêu chuẩn bây giờ thì nó hoặc là đã cũ không còn hợp thời, hoặc sẽ tốn khá nhiều thời gian để chơi đến hết
      Sửa: Tôi không biết Dune 1 và 2 ra cùng một năm. Nếu vậy thì hoặc là Dune 2 đã được phát triển từ trước, hoặc họ đã làm xong cả engine lẫn game trong chưa đầy một năm. Ngày nay dù indie và công cụ có nhanh hơn thì vẫn khó tưởng tượng nổi
    • Tôi luôn nghĩ The Settlers(https://en.wikipedia.org/wiki/The_Settlers_(1993_video_game)) mới là game chiến thuật thời gian thực đầu tiên
      Nó phát hành vào tháng 6 năm 1993 nên muộn hơn Dune vài tháng, nhưng nếu cả hai được phát triển song song thì đây có thể là một ví dụ về việc nhiều “nhà phát minh” cùng chạm tới một ý tưởng. Người ta nói The Settlers chịu ảnh hưởng từ những “god game” như Populous(https://en.wikipedia.org/wiki/Populous_(video_game)). Đây là kiểu game mà người chơi có sức mạnh như thần, chẳng hạn thay đổi địa hình, nhưng không điều khiển trực tiếp đơn vị
    • Dawn of War là một ví dụ hay về RTS thoát khỏi mô hình khai thác quặng điển hình
    • Có lẽ cuối cùng mọi thứ rồi cũng sẽ đi theo hướng đó. Lịch sử dựa khá nhiều vào mô thức “nông dân là nền tảng tài nguyên, người thống trị là nhà chiến lược”
      Không chỉ riêng Dune, mà cả lịch sử nói chung cũng phần nào như vậy
  • Bài viết rất hay và đây là một công trình tuyệt vời. Khoảng 10 năm trước tôi cũng từng làm việc tương tự, với đối tượng là Tiberian Sun, cụ thể là vá mã mạng
    Lao vào code của người khác như thế này tạo cảm giác như có một mối kết nối được chia sẻ. Điều kinh khủng là tôi phát hiện ra stack dành cho chơi bằng modem hoàn toàn tách biệt. Họ không đơn giản là gửi TCP/IP qua modem
    Hẳn đã có ai đó dành vài tháng viết mã tùy biến cho framing, đồng bộ hóa, xử lý lỗi, và cả chuyện phải quay số lại thế nào khi mất kết nối. Vậy mà đến lúc game phát hành thì đoạn code đó gần như đã lỗi thời và vô dụng

    • Chắc là vì một lý do khác. Mục đích có lẽ không phải để kết nối vào Internet quay số mà là gọi trực tiếp sang modem khác
      Tôi chưa từng tự dùng, nhưng nhiều game cũ có tùy chọn như vậy
  • Hay đấy. Trong bài có đoạn này làm tôi chú ý:
    “Westwood Online(WOL) không còn hoạt động nữa nên không thể chơi multiplayer ngoài LAN”
    Hồi nhỏ tôi rất thích Command & Conquer, và cũng biết chút ít về Westwood Online ở phía client
    Nếu tôi nhớ không lầm, sau khi WOL ngừng hoạt động thì XWIS.net đã hỗ trợ rất nhiều. Tác giả có thể thử liên hệ với cộng đồng phát triển nhỏ bên đó. Dù vậy, có thể giờ nơi đó cũng đang dần biến mất thật rồi
    Những gì phía XWIS làm từng được EA công nhận, và theo tôi nhớ thì điều đó đã giúp khá nhiều trong việc tiếp tục hỗ trợ WOL cho C&C Renegade
    Ngoài ra còn có dự án FreeRA, vốn là tổ tiên trực tiếp của các bản tái phát hành C&C gần đây trên Steam và nơi khác. Có khi họ cũng có thể giúp trong việc khôi phục WOL
    Vì WOL được nhúng theo dạng thư viện riêng, nên thay thế thư viện có lẽ sẽ dễ hơn nhiều so với việc đảo ngược lại toàn bộ stack WOL
    Sửa: Đọc tiếp bài thì thấy họ cũng đã sửa luôn cả thành phần WOL. Càng tốt

  • Bài viết rất xuất sắc. Tác giả nghe có vẻ là kiểu người thú vị và thông minh đến mức tôi muốn ra ngoài uống một ly với họ vào buổi tối
    Tôi thật sự rất thích các phần giải thích mở rộng đáng yêu đó, mà chúng cũng rất hữu ích. Trong lúc đọc tôi có cảm giác như đang chơi một kiểu RPG phiêu lưu chọn lựa, và đó là một trải nghiệm khá mới mẻ
    Tiện thể, về đoạn “CS:GO chỉ nghỉ hưu vào năm 2023”, tôi tưởng CS:GO chỉ được đổi thương hiệu thành CS2, hay là tôi hiểu sai?

    • CS2 bị một số người xem là bản hạ cấp so với CS:GO. Tôi cũng nghĩ vậy
      Tôi từng nghe nói ngay cả trên PC thi đấu, CS2 cũng không giữ được mức khung hình/giây ổn định như mong muốn. Cùng dàn máy đó lại chạy CS:GO rất tốt. Cũng có rất nhiều báo cáo tương tự từ người dùng dùng cả PC cấu hình cao
      Valve muốn CS2 được nhìn như sự tiếp nối của CS:GO, nhưng thay vì tạo ra một trò chơi tốt hơn để tự nhiên thay thế, họ đã ép cộng đồng người chơi phải chấp nhận sự thay đổi. Vì CS:GO từng là một game tuyệt vời, tôi và nhiều người khác có lẽ sẽ còn thấy cay đắng về chuyện này trong một thời gian
    • CS2 được phát hành dưới cùng application ID với CS:GO, nhưng là một trò chơi được làm lại hoàn toàn trên engine mới. Không phải chỉ là đổi thương hiệu
    • CS2 dùng engine Source 2 với renderer DX11 hoặc Vulkan, khác với CS:GO cũ chạy trên Source 1
  • Thật thú vị khi thấy những game hiện đại đầy quảng cáo, tối ưu hóa kiếm tiền và pay-to-win lại thua trước các tác phẩm kinh điển cũ
    Chỉ cần một hacker giúp một tay cũng có thể quét sạch khán giả khỏi đám game rác đó. Với những phương tiện có tính bền vững, dường như những thứ hay của quá khứ cuối cùng sẽ đánh bại những thứ tầm thường của hiện tại

  • Bài viết rất hay và nỗ lực này cũng rất đáng nể. Biết đâu có thể tích hợp nó theo cách nào đó với công việc bên CnCNet của chúng tôi. Rất mong tác giả ghé CnCNet để cùng trao đổi

  • “Nó có modem 28.8 BPS gắn sẵn”
    Ma trận chủ động đấy. Một triệu màu sắc psychedelic

    • Kiến trúc RISC sẽ thay đổi tất cả
  • Bài viết rất thú vị và có chiều sâu. Tôi thật sự thích lượng chi tiết và kiến thức được chia sẻ về cách đảo ngược và vá những trò chơi bị bỏ quên như thế này
    Tôi từng thấy game này ở một cửa hàng đồ cũ trong vùng nhưng đã để lại vì chỉ từng chơi Dune II RTS. Giờ thì tôi nhất định sẽ mua nó

    • Không biết có chạy được trên Wine không
  • Nhân tiện, trên Steam có một game Dune chiến thuật thời gian thực hiện đại
    https://store.steampowered.com/app/1605220/Dune_Spice_Wars/

    • Gọi nó là RTS thì hơi gượng ép. Nó gần với game 4X hơn, nhịp độ chậm và chiến đấu cũng không quá quan trọng
    • Bỏ qua chuyện người khác gọi Spice Wars là 4X, thứ gần nhất với một game Dune RTS hiện đại thật sự có lẽ là Homeworld: Deserts of Kharak
  • “Thiết kế UI là đam mê của tôi”
    Tuyệt thật. Tôi nhớ kiểu văn phong như thế này. Ở nhiều khía cạnh nó gợi tôi nhớ đến các bài blog của Steve Yegge