Dungeon Siege for the lulz
Introduction
Il y a bien longtemps avec un ami nous jouions à Dungeon Siege, un jeu de role/action développé par Gas Powered Games sorti le 7 mai 2002. Dans cette article je me suis penché sur le fait de me faire un no-cd et etre en mesure de parser leurs fichiers de sauvegarde multijoueur.
No-Cd
Première chose à faire, c'est de regarder si le binaire est packé :
Ici nous avons affaire avec un packer dont je ne connaissais pas l'existance, et en googelant, on tombe sur aucun tutorial pour pouvoir l'unpacker mais on va voir quand fait il n'y a rien de compliqué.
Quand on ouvre le binaire sous OllyDbg, on tombe sur la chose suivante :
007C0000 > /EB 10 JMP SHORT DSLOA.007C0012
007C0002 |0300 ADD EAX,DWORD PTR DS:[EAX]
007C0004 |0000 ADD BYTE PTR DS:[EAX],AL
007C0006 |0200 ADD AL,BYTE PTR DS:[EAX]
007C0008 |0000 ADD BYTE PTR DS:[EAX],AL
007C000A |0000 ADD BYTE PTR DS:[EAX],AL
007C000C |0000 ADD BYTE PTR DS:[EAX],AL
007C000E |0000 ADD BYTE PTR DS:[EAX],AL
007C0010 |0000 ADD BYTE PTR DS:[EAX],AL
007C0012 \55 PUSH EBP
007C0013 E8 00000000 CALL DSLOA.007C0018
007C0018 5D POP EBP
007C0019 81ED 18000000 SUB EBP,18
En voyant la chose suivante, on peut émettre l'hypothèse suivante :
Le packer fait un call à une routine qui vient s'occuper de decompresser l'éxecutable, puis jump ou ret sur le real entry point. Il nous suffit donc de step jusqu'au CALL, placer un hardware breakpoint sur l'acces à la valeur de ESP au moment de cette appel, et nous devrions arriver sur l'entry point.
On run le programme, et après s'etre mangé quelques exceptions dans la tronche que l'on repasse au programme, on arrive ici :
0040A39B 55 DB 55 ; CHAR 'U'
0040A39C 8B DB 8B
0040A39D EC DB EC
0040A39E 6A DB 6A ; CHAR 'j'
0040A39F FF DB FF
0040A3A0 68 DB 68 ; CHAR 'h'
0040A3A1 E8 DB E8
0040A3A2 96 DB 96
0040A3A3 72 DB 72 ; CHAR 'r'
0040A3A4 00 DB 00
0040A3A5 68 DB 68 ; CHAR 'h'
On analyse le code, et on voit la mise en place d'une SEH, un appel à kernel32!GetVersion, GetCommandLineA, ca a l'air de ressembler à notre real OEP. On dump donc notre processus, et on reconstruit l'IAT avec ImportRec :
En fait à partir de là le no-cd est deja fait, car ces boulets cré un processus qui va etre chargé de vérifier si le cd est bien dans le lecteur, si oui on continue la décompression et on lance le jeu, sinon on ExitProcess.
En fait on bypass cette vérification en unpackant à la main en ayant bien mis le cd original dans le lecteur cd et le tour est joué étant donné que la vérification n'aura plus lieu.
I want to cheat
J'étais un peu sur ma faim après cette protection lame.
Je me suis donc mis en tete d'écrire un lecteur / unpacker pour les sauvegardes de types multijoueur (fichier .dsparty). Un logiciel fait deja bien le travail "TankViewer", mais malheuresement, source closed c'est moche.
Je décide donc de regarder d'un peu plus près avec un éditeur hexa :
La première image ressemble typiquement à un header de sauvegarde pour Dungeon Siege, j'ai meme entouré des offset plutot intéressants qu'on retrouve sur la deuxieme image, ou on peut voir le nom de 3 fichiers :
- infos.gas : Informations a propos de la partie en cours ( nom personnage, zone de jeu, etc ... )
- player.gas : Informations relatives aux personnages ( level, or, item dans l'inventaire, etc ... )
- portraite-0.bmp : L'avatar de notre personnage
C'est en googelant des betises que j'ai réussi à tomber sur ce site, qui grace auquel j'ai tout dessuite compris comment était formé les fichiers de sauvegarde.
A partir de là j'étais en mesure de pouvoir ecrire un décodeur fonctionnel.
Il faut savoir que chacun des 3 fichiers est compressé avec zlib. D'ailleurs ca m'a fait pas mal chié de faire marcher cette daube avec MinGW.
Je vais pas éterniser la chose et realease les sources : Reader.c, Reader.h.
Une fois les 3 fichiers récupérés, ils sont tres facile à comprendre ansi qu'à parser, voici un exemple.
On peut trés bien imaginer la suite, créer un éditeur complet de sauvegarde ( cheateur ? ), mais lister tous les objets ca relève de beaucoup de travail et je n'y vois pas vraiment d'amusement.
Voici quand meme un apercu de l'output de mon outil :