1 points par GN⁺ 2024-07-15 | 1 commentaires | Partager sur WhatsApp
  • Le RTS de 2001 Emperor: Battle for Dune avait du mal à s’installer, à se lancer et à fonctionner en ligne sur Windows modernes, et EmperorLauncher est un patch qui le rend à nouveau jouable
  • Les améliorations clés se concentrent sur la prise en charge des hautes résolutions, une limite à 60 FPS, le multijoueur en ligne par IP directe, un mode campagne coopératif et le contournement d’une procédure d’installation cassée
  • L’implémentation repose sur un lanceur de remplacement pour Emperor.exe, une injection de DLL dans Game.exe, des patchs de fonctions via Microsoft Detours, des hooks de rendu Direct3D 7, l’interception de winsock et un serveur WOL minimal
  • Le jeu en ligne tunnelise l’ancienne architecture P2P à ports arbitraires et NAT punching vers une connexion unique client→serveur, de sorte que seul l’hôte du serveur doit gérer la configuration réseau
  • La solution couvre aussi la copie des fichiers du CD d’origine, l’extraction des .cab, l’application du patch officiel v1.09, le contournement de l’enregistrement COM de WOLAPI.DLL et même une interface de lanceur Win32, de l’installation jusqu’au lancement

Là où Emperor: Battle for Dune bloquait

  • Emperor: Battle for Dune est un jeu de stratégie en temps réel créé en 2001 par Westwood Studios, suite de Dune 2000
  • Sur les systèmes modernes, plusieurs problèmes empêchaient d’y jouer
    • impossible de lancer le jeu en haute résolution adaptée aux écrans actuels
    • en multijoueur, la vitesse de simulation n’était pas limitée, rendant le jeu beaucoup trop rapide
    • Westwood Online (WOL) ne fonctionne plus, ce qui complique fortement le multijoueur hors LAN
    • la campagne coopérative est une fonctionnalité réservée au mode en ligne et n’est donc pas utilisable en LAN
    • l’installateur fourni sur le disque est cassé
    • plusieurs effets visuels se dégradent avec les fréquences d’affichage élevées des PC modernes
  • EmperorLauncher est un patch conçu pour résoudre ces problèmes, avec téléchargement et code source publics

Remplacement de Emperor.exe et initialisation de Game.exe

  • Le Emperor.exe du jeu n’est pas l’exécutable principal, mais un simple wrapper qui lance Game.exe
  • Lancer Game.exe directement ne produit rien ; il a donc fallu analyser le processus d’initialisation réalisé par Emperor.exe et le reproduire dans un lanceur de remplacement
  • L’analyse a été faite avec IDA
    • IDA permet de désassembler un exécutable et de décompiler une partie du code sous une forme proche du C
    • dans un binaire dépourvu d’informations de types et de structures, il faut suivre les appels de fonctions et l’usage des API Windows
  • Avant de lancer Game.exe, Emperor.exe crée un mutex et un handle de file mapping anonyme, traite des données lues depuis Emperor.dat, puis les copie dans ce mapping
  • Le processus parent transmet ensuite la valeur du handle de file mapping au thread principal du processus enfant via des messages Windows, à partir de l’ID obtenu avec CreateProcessA
    • un identifiant de message personnalisé 0xBEEF est utilisé
    • les données du file mapping sont les trois chaînes "UIDATA,3DDATA,MAPS", transmises au code de chargement des assets du jeu
  • Au lieu de réimplémenter directement le code de déchiffrement, l’auteur a placé un outil de dump à la place de Game.exe pour enregistrer sur disque les données transmises, puis a reproduit la même séquence dans le lanceur

Injection de DLL et méthode de patch des fonctions

  • Pour appliquer les patchs, il fallait exécuter du code utilisateur à l’intérieur du processus Game.exe, via la méthode CreateRemoteThread + LoadLibrary
  • La procédure d’injection suit cet ordre
    • allocation d’un buffer dans la mémoire du processus cible avec VirtualAllocEx
    • copie de la chaîne du chemin de la DLL avec WriteProcessMemory
    • passage de l’adresse de LoadLibrary et du buffer contenant le chemin de la DLL à CreateRemoteThread pour charger la DLL dans le processus cible
    • exécution de DllMain, qui déclenche le code de patch
  • Le processus est lancé en état suspendu, puis la DLL est injectée pour garantir l’exécution du code avant main
  • Pour modifier les fonctions existantes, le projet utilise Microsoft Detours
    • Detours remplace les premières instructions de la fonction d’origine par un saut redirigeant l’appel vers une fonction de remplacement
    • les instructions écrasées sont copiées dans une autre zone mémoire, puis un wrapper saute vers le reste de la fonction d’origine, ce qui permet encore de l’appeler
  • Les pages mémoire contenant le code des fonctions ne sont pas modifiables en écriture pour des raisons de sécurité ; il faut donc changer les permissions via VirtualProtect, puis appeler FlushInstructionCache après modification

Restauration des logs de debug

  • Le binaire contenait des appels ressemblant à des logs de debug, mais la fonction cible réelle était vide et ne faisait qu’un ret
  • Il semble que plusieurs fonctions vides aient été fusionnées dans la build release, et que l’une d’elles ait été le logger de debug
  • Au départ, l’auteur a utilisé une heuristique interprétant le premier argument comme un pointeur vers une chaîne, puis vérifiant s’il pointait vers des caractères ASCII imprimables
    • les accès à des pointeurs invalides étaient capturés et ignorés via les exceptions SEH de Windows
    • cette méthode fonctionnait partiellement, mais laissait des faux positifs et des faux négatifs
  • Ensuite, grâce à la fonction de patch d’IDA et à des scripts Python, les sites d’appel de logs ont été déplacés vers une autre fonction vide
    • certains ont été trouvés par heuristique, d’autres via un motif où une constante de chaîne est push avant l’appel
    • les centaines de sites d’appel restants ont été annotés manuellement
  • Les logs restaurés ont aidé au débogage du multijoueur WOL
    • en voyant l’assertion "MyId == INVALID_ID" pendant le traitement de SC_MESSAGE_YOUR_DETAILS, l’auteur a confirmé dans un dump Wireshark que la commande GAMEOPT était envoyée à tort à tous les joueurs

Patch graphique Direct3D 7

  • Emperor repose sur Direct3D 7, dont la prise en charge sur Windows modernes n’est pas complète
  • Le problème de haute résolution était lié à la limite de taille maximale de texture à 2048 dans la couche wrapper Direct3D 7 ; la solution s’appuie sur le code LegacyD3DResolutionHack d’UCyborg
  • Le jeu gère mal les écrans autres qu’au format 4:3
    • le rendu fonctionne, mais l’interface se déforme comme si elle était excessivement agrandie
    • le rendu de la souris dans le jeu présente aussi un décalage dépendant de la distance au centre de l’écran
  • La solution retenue est un letterboxing 4:3
    • en lançant le jeu en mode fenêtré, il devient possible d’utiliser une résolution arbitraire
    • le style de bordure de la fenêtre est supprimé, puis la fenêtre du jeu est reparentée au-dessus d’une fenêtre noire en plein écran
    • une capture de souris est ajoutée pour éviter que le défilement sur les bords ne se casse en multi-écran ou en mode fenêtré
  • La limite de fréquence d’affichage est implémentée en patchant IDirect3DDevice7::EndScene pour viser 60 FPS
    • EndScene étant appelé une fois par frame à la fin du rendu, c’est un bon point pour calculer un délai et faire dormir le thread
    • comme le pointeur de EndScene n’est pas exporté directement, il faut d’abord hooker DirectDrawCreateEx, puis IDirect3D7::CreateDevice, afin de récupérer le pointeur de fonction dans la vtable

Multijoueur en ligne et remplacement de WOL

  • L’objectif était de proposer un multijoueur par IP directe sans infrastructure de lobby ni d’hébergement, uniquement avec une redirection de ports et la saisie d’une IP
  • Le mode LAN fonctionne, mais comme il découvre les serveurs via UDP broadcast, il n’est pas adapté au jeu sur Internet
    • le menu LAN ne permet pas de saisir une IP manuellement
    • au départ, l’auteur a tenté de patcher le chat LAN pour y indiquer une IP, mais a abandonné après avoir constaté que la campagne coopérative était réservée à WOL
  • Pour faire revivre WOL, il fallait deux éléments
    • un faux serveur maître WOL permettant au jeu de savoir où se connecter et quelle partie lancer
    • un proxy capable de faire fonctionner les paquets du jeu au-dessus d’une connexion IP directe
  • L’architecture WOL d’origine comprenait, en plus du master server, un serveur dit “mangler”, qui semblait coordonner le NAT punching
    • le serveur mangler d’origine a disparu, et le jeu se bloque en attendant sa réponse
    • le patch supprime ces appels au mangler
  • Emperor utilise un modèle réseau P2P, ouvrant des connexions bidirectionnelles entre chaque paire de joueurs et choisissant des ports arbitraires
    • comme tous les clients doivent accepter des ports ouverts, cette architecture ne correspond plus à l’Internet moderne
  • La solution consiste à intercepter les fonctions winsock pour tunneliser toutes les connexions vers une connexion unique client→serveur
    • quand un client tente d’envoyer un message au serveur ou à un autre client, le message est intercepté, encapsulé avec un en-tête, puis envoyé sur cette connexion unique
    • un thread côté serveur reçoit ensuite les messages et les redistribue à leur destination
    • le jeu croit toujours fonctionner en P2P, mais en réalité seul l’hôte du serveur doit gérer la configuration réseau
  • Avec cette approche, il a été possible de lancer et de rejoindre des parties de campagne coopérative

Implémentation d’un serveur WOL minimal

  • Le serveur maître WOL avait une architecture plus proche d’un serveur IRC
  • xwis.net proposait un serveur semblant opéré par des fans et, au moment de l’écriture, semblait aussi avoir accès à l’entrée DNS d’origine du jeu servserv.westwood.com
    • Emperor ne fonctionnait pas parfaitement tel quel avec xwis, mais cela a servi de référence pour la création et la participation aux lobbies
  • L’implémentation publique handle_wol.cpp de pvpgn-server a aussi servi de ressource
  • La raison de développer son propre serveur est qu’aucune garantie n’existe quant à la pérennité d’un serveur tiers
    • l’objectif n’était pas d’exploiter une communauté concurrente, mais de fournir les fonctions minimales nécessaires pour lancer une partie multijoueur
  • WOL mélange comportement IRC standard et mécanismes personnalisés
    • les lobbies de jeu sont des canaux spéciaux
    • les informations de lobby utilisent le topic IRC
    • le chat de lobby utilise PAGE au lieu de PRIVMSG
    • la synchronisation des paramètres de jeu passe par des messages GAMEINFO contenant du contenu non ASCII
  • L’implémentation de base du serveur WOL a été finalisée par tâtonnements ; elle n’est pas robuste hors du chemin nominal, mais elle fonctionne

Programme d’installation et application du patch v1.09

  • L’installateur d’origine étant cassé, il fallait jusqu’ici demander à l’utilisateur de copier le contenu du CD sur le disque dur puis d’écraser le setup par une version de remplacement
  • EmperorLauncher intègre une fonction d’installation qui copie les fichiers depuis le CD d’origine et extrait les fichiers .cab
    • un .cab est un format d’archive proche du zip, et Windows fournit une interface pour l’extraction
  • L’application du dernier patch officiel, v1.09, était encore plus délicate
    • extraire simplement EM109EN.EXE avec 7zip pour récupérer les binaires récents ne fonctionne pas
    • un gros resource contenant ce qui ressemblait à un en-tête d’exécutable a été trouvé dans les ressources Windows
    • les 4 premiers octets de cette ressource correspondent à la taille du fichier, suivie du fichier réel
  • EM109EN.EXE extrait une DLL embarquée dans un fichier temporaire, la charge, puis exécute la fonction RTPatch32@12 à l’intérieur
    • RTPatch était un outil de patch binaire par diff
    • en s’appuyant sur l’outil myRTP, l’auteur a chargé et exécuté directement la DLL embarquée
  • La DLL lisait le chemin d’installation dans le registry au lieu d’utiliser le chemin passé en argument ; le patch a donc été appliqué en créant une fausse clé registry correspondant à ce qu’elle attendait

Gestion des Westwood Online Shared Internet Components

  • L’installation d’origine séparait le jeu principal Emperor des Westwood Online Shared Internet Components
  • Sans ce composant, WOL ne fonctionne pas, et le fichier principal est WOLAPI.DLL
  • WOLAPI.DLL est une bibliothèque de classes COM, et Emperor crée ses objets COM avec CoCreateClass
  • En temps normal, l’enregistrement COM consiste à inscrire le CLSID et le chemin de la DLL dans HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID, à l’échelle du système
    • cette méthode exige des droits administrateur
    • elle a un effet global au-delà du seul processus du jeu
  • Le patch utilise à la place une redirection du registry et OaEnablePerUserTLibRegistration pour effectuer un enregistrement au niveau utilisateur
    • aucune méthode n’a été trouvée pour enregistrer la class library uniquement dans le périmètre d’un seul processus
    • les tentatives d’utiliser directement DllGetClassObject n’ont pas fonctionné

Interface du lanceur et résultat final

  • La dernière étape consistait à créer une interface simple de lanceur pour saisir une IP et modifier quelques paramètres de base
  • L’interface a été écrite avec des contrôles Win32 bruts
    • cela suffisait pour une UI statique et simple, mais l’expérience de construction manuelle d’une interface Win32 a été assez rude
  • Au final, EmperorLauncher devient un outil couvrant l’exécution sur systèmes modernes, la haute résolution, la limite à 60 FPS, le multijoueur par IP directe, la campagne coopérative, ainsi que l’installation et l’application des patchs

1 commentaires

 
GN⁺ 2024-07-15
Avis de Hacker News
  • Ce jeu a une importance assez considérable pour tout le genre de la stratégie en temps réel. Quand on pense généralement à la stratégie en temps réel, on imagine une structure où des paysans récoltent des ressources et où il faut les protéger ; le RTS Dune en était très proche du prototype
    Cela dit, cette structure vient aussi du roman original. Sans cela, le genre aurait pu prendre une direction complètement différente. Par exemple, on aurait pu extraire les ressources de la base elle-même, l’adversaire harcelant les bâtiments, et la récompense du contrôle de la carte aurait pu prendre une autre forme que l’accès aux ressources

    • Pour être précis, cet article ne porte pas sur Dune 2, mais sur Emperor: Battle for Dune
    • Comme quelqu’un d’autre l’a souligné, c’est probablement la suite de Dune 2, celle à laquelle on pense
      Dune 1 a aussi posé des bases. Au début, c’est plutôt proche d’un point-and-click d’aventure, mais vers la fin, le jeu se transforme avec gestion des ressources et extraction, production de troupes, combats et terraformation
      Je n’ai jamais vraiment compris la fin. L’objectif semblait être de reverdir la planète, mais quand une zone devenait verte, elle ne produisait plus de spice. Or l’empereur continuait à exiger toujours plus de livraisons de spice, et si on ne tenait pas les quotas, c’était game over
      Il est fort possible que je ne l’aie pas bien compris parce que j’y jouais enfant. Il faudrait que je le relise, mais selon les standards actuels, il a probablement mal vieilli ou demande pas mal de temps pour être terminé
      Modification : je ne savais pas que Dune 1 et 2 étaient sortis la même année. Dans ce cas, soit le développement de Dune 2 était déjà en cours, soit ils ont fait le moteur et le jeu en un an. Même aujourd’hui, avec l’indé et des outils plus rapides, c’est difficile à imaginer
    • J’ai toujours considéré The Settlers(https://en.wikipedia.org/wiki/The_Settlers_(1993_video_game)) comme le premier jeu de stratégie en temps réel
      Il est sorti en juin 1993, donc quelques mois après Dune, mais si les deux jeux étaient en développement en même temps, cela pourrait être un cas où plusieurs « inventeurs » sont arrivés à la même idée. The Settlers aurait été influencé par des « god games » comme Populous(https://en.wikipedia.org/wiki/Populous_(video_game)), où le joueur dispose de pouvoirs divins, comme modifier le terrain, mais ne contrôle pas directement les unités
    • Dawn of War est un bon exemple de RTS qui s’écarte du paradigme classique de l’extraction de minerai
    • Peut-être que les choses seraient de toute façon allées dans cette direction. L’histoire s’appuie beaucoup sur l’archétype « les paysans sont la base des ressources, le dirigeant est le stratège »
      Je pense que ce n’est pas propre à Dune, mais que cela vaut dans une certaine mesure pour toute l’histoire
  • Bon article et excellent travail. J’ai fait quelque chose de similaire il y a une dizaine d’années, sur Tiberian Sun, pour patcher le code réseau
    Se plonger ainsi dans le code de quelqu’un d’autre donne une forme de connexion partagée. J’ai découvert avec horreur qu’il existait une pile entièrement séparée pour le jeu par modem. Ce n’était pas simplement du TCP/IP envoyé par-dessus un modem
    Quelqu’un a dû passer des mois à écrire du code sur mesure pour le framing, la synchronisation, la gestion des erreurs, quoi faire quand la connexion se coupe et qu’il faut rappeler, etc. Et pourtant, au moment de la sortie du jeu, ce code était déjà quasiment inutile

    • Je pense que c’était pour une autre raison. Le but n’était probablement pas de se connecter à Internet par accès commuté, mais d’appeler directement un autre modem
      Je ne l’ai jamais utilisé moi-même, mais beaucoup de vieux jeux proposaient ce genre d’option
  • Sympa. Ce passage de l’article m’a interpellé :
    « Westwood Online (WOL) ne fonctionne plus, il n’est donc plus possible de jouer en multijoueur hors LAN »
    J’adorais Command & Conquer enfant, et je connais un peu Westwood Online côté client
    Si mes souvenirs sont bons, après la fermeture de WOL, XWIS.net a assuré une grande partie du support. L’auteur aurait intérêt à contacter leur petite communauté de développeurs. Cela dit, elle est peut-être vraiment en train de disparaître
    Le travail des gens de XWIS a même été reconnu par EA, et il me semble qu’il a beaucoup aidé à maintenir le support WOL de C&C Renegade
    Il y a aussi le projet FreeRA, qui est en quelque sorte l’ancêtre direct des rééditions récentes de C&C sorties sur Steam et ailleurs. Eux aussi pourraient peut-être aider à faire revivre WOL
    Comme WOL était intégré via sa propre bibliothèque, il est très probable qu’un remplacement de bibliothèque soit beaucoup plus simple que de refaire du reverse engineering de toute la pile WOL
    Modification : en continuant à lire l’article, je vois qu’il a aussi corrigé les composants WOL. Encore mieux

  • Excellent article. L’auteur a l’air d’être quelqu’un d’assez drôle et intelligent pour qu’on ait envie d’aller boire un verre avec lui le soir
    J’ai vraiment aimé les petites explications dépliables, qui sont aussi très utiles. En lisant l’article, j’avais l’impression de jouer à une sorte de RPG d’aventure à embranchements, et c’était une expérience assez nouvelle
    Au passage, à propos de « CS:GO n’a pris sa retraite qu’en 2023 », je pensais que CS:GO avait été rebaptisé CS2 ; je me trompe ?

    • CS2 est considéré par certains comme une régression par rapport à CS:GO. C’est aussi mon avis
      J’ai entendu dire que même des PC de compétition n’arrivaient pas à maintenir un framerate correct dans CS2, alors que le même matériel faisait tourner CS:GO à merveille. Beaucoup de témoignages d’utilisateurs rapportent des résultats similaires, même sur des PC haut de gamme
      Valve voulait que CS2 soit perçu comme la continuité de CS:GO, mais au lieu de créer un meilleur jeu qui l’aurait remplacé naturellement, ils ont imposé ce changement à la base de joueurs. Comme CS:GO était un excellent jeu, moi et d’autres resterons amers pendant un moment
    • CS2 a été publié avec le même ID d’application que CS:GO, mais c’est un jeu entièrement refait avec un nouveau moteur. Ce n’est pas un rebranding
    • Contrairement à l’ancien CS:GO basé sur Source 1, CS2 utilise le moteur Source 2 avec un renderer DX11 ou Vulkan
  • Je trouve vraiment amusant ce schéma où les productions modernes gavées de pubs et pay-to-win se font dépasser par de vieux classiques
    Avec l’aide d’un seul hacker, le public peut chasser ces jeux poubelles. Dans les médias durables, les bonnes choses du passé semblent finir par l’emporter sur les choses médiocres du présent

  • Excellent article et excellent effort. Il y a peut-être moyen d’intégrer cela d’une manière ou d’une autre à notre travail chez CnCNet. Ce serait bien de venir en discuter avec nous sur CnCNet

  • « Il a un modem 28,8 BPS »
    C’est de la matrice active. Un million de couleurs psychédéliques

    • L’architecture RISC va tout changer
  • Article très intéressant et approfondi. J’ai vraiment apprécié la quantité de détails et de connaissances partagés sur la manière de faire du reverse engineering et de patcher ce type de jeu abandonné
    J’ai vu ce jeu dans une ressourcerie du quartier, mais comme je n’avais joué qu’au RTS Dune II, je l’ai laissé. Maintenant, je compte bien le récupérer

    • Je me demande s’il tourne sous Wine
  • À ce sujet, il existe sur Steam un jeu de stratégie en temps réel Dune moderne
    https://store.steampowered.com/app/1605220/Dune_Spice_Wars/

    • C’est un peu exagéré d’appeler ça un jeu de stratégie en temps réel. C’est plus proche d’un jeu 4X, avec un rythme lent et des combats pas si importants
    • Indépendamment du fait que d’autres qualifient Spice Wars de 4X, ce qui s’est le plus rapproché d’un véritable RTS Dune moderne, c’était Homeworld: Deserts of Kharak
  • « La conception d’interface utilisateur est ma passion »
    Excellent. Ce type d’écriture me manque. À bien des égards, cela me rappelle les billets de blog de Steve Yegge