Je ne sais pas trop avec quelle version de Securom est packé D2 LOD ...

GameCopyWorld dit v2, ulysse me dit v4, des detecteurs me disent qu'il n'est pas protege par securom, d'autre enfin me disent : Version 5.03.13

j'ai tendance à croire les derniers en effet si on scanne la memoire de l'exe unpacké on trouve ceci :

005333B0  41 64 64 44 03 00 00 00 35 2E 30 33 2E 31 33 00  AddD....5.03.13.
005333C0  30 31 39 30 0C 1A B2 1D 10 1E B4 2E 48 2F 5A 33  0190........H/Z3

Apres la version n'est pas tres importante ...

Tout ce que je sais c'est que cette version n'utilise pas de triggers ou autre trucs simpa offert par securom, je tends à penser que les premieres version de D2 LOD etaient packées avec une version de securom inferieur ce qui explique que les devellopeurs n'aient pas utilisé le SDK de securom ....

Il n'y a pas d'anti debugg qui ne se passe avec HideDebugger, pour être tout a fait honnete je n'en ai vu aucun et je n'en ai pas cherché

Le seul "anti X" que j'ai vu était un couple de timeGetTime qui vérifiait le temps que passé securom à autentifier le CD et un autre qui vérifie le temps que passe Securom à trouver l'addresse de l'api dans les calls redirigés.

Pour arriver a l'eop j'ai fait un petit break point sur GetDriveTypeA et ensuite j'ai tout simplement tracé (le jmp pour arriver à l'eop est un jmp eax).

Arrivé a l'oep (0x0401224) on doit maintenant reconstruire tout les call redirigés par securom.

Pour se faire j'avais commencé par coder un petit olly script mais la reconstruction était longue et la programmation d'un tel script fastidieuse j'ai donc opté sur les conseils de Kaine pour un call fixer injecté sous la forme d'une dll qui va se charger de hook au bon endroit les routines de securom et qui reconstruira les calls.

Les calls redirigés sont de 2 types :

Les "call [Securom]" et les "call Securom" (0xFF15 XXXXXXXX et 0xE8 XXXXXXXX)

Ces 2 types de calls pointent soit sur une zone memoire allouée soit sur une routine situé dans une des sections de Securom

Dans les deux cas, ces calls utilisent 2 routines fixent (situés dans une section de Securom) qui sont alors faciles à hooker.

La première routine se termine par un jmp eax, eax contenant l'addresse de l'API, en 04174B7, il suffit donc de remplacer ce jmp eax par un jmp HOOK1, HOOK1 se chargeant alors de chercher l'addresse de l'api dans l'IAT de Diablo 2 LOD pour remplacer le "call [Securom]" par un "call [addrApi]"

La deuxieme routine est appellée par plusieur "call Securom" differents, elle renvoit une valeure dans eax qui doit etre transformée pour trouver l'addresse réelle de l'API. Seulement, suivant les calls, les operations effectuées sur la valeur de retour de cette routines sont differentes:

00499C1C   CALL CreatePreAddress
00499C21   ADD ESP,10
00499C24   MOV DWORD PTR SS:[EBP-10],EAX
00499C27   MOV EDX,DWORD PTR SS:[EBP-10]
00499C2A   MOV EAX,DWORD PTR DS:[EDX*8+51E6AC]
00499C31   XOR EAX,10     <- ici securom fait un xor 0x10 sur la valeure
00499C34   XOR EAX,DWORD PTR SS:[EBP-4]
00499F1D   CALL CreatePreAddress
00499F22   ADD ESP,10
00499F25   MOV DWORD PTR SS:[EBP-10],EAX
00499F28   MOV EDX,DWORD PTR SS:[EBP-10]
00499F2B   MOV EAX,DWORD PTR DS:[EDX*8+51E6AC]
00499F32   XOR EAX,40      <- ici c'est un xor 0x40
00499F35   XOR EAX,DWORD PTR SS:[EBP-4]

il y a comme ca 6 calls differents qui font respectivement un xor 0x10 / 0x20 / 0x30 / 0x40 / 0x50 et 0x60

Pour savoir avec quelle valeure on doit "xorer" le resultat retourné par la routine il y a plusieurs solutions, soit on se base sur l'addresse du call, soit on recherche la valeur à chaque fois.

En effet, si vous avez regardé atentivement les deux portions de code ci dessus vous avez du remarquer que le xor est precede par un "MOV EAX,DWORD PTR DS:[EDX*8+51E6AC]" il suffit donc de scanner le code a partir de la valeur de retour du call (recuperée dans la pile étant donnée que l'on modifie le retn de la routine par un jmp HOOK2) à la recherche du DWORD 0x51E6AC et de prendre la valeure du byte situé 3 byte plus loin.

Je résume donc ce que va faire notre Call Fixer :

  1. Il hook le jmp eax situé en 0x04174B7 qui correspond aux calls de type 1
  2. Il hook le retn de la routine qui se charge de faire la pre-génération de l'addresse de l'api pour les calls de type 2 en 0x0499CD4
  3. Il scann la section .text de D2 a la recherche de "call [Securom]" ou de "call Securom" (on les reconnait en étudiant l'addresse sur la quelle pointent ces calls (en dehors de la section .text) et grace au premier byte de la fonction qui doit être egal à 0x55 (push ebp))
  4. A chaque call vers une fonction Securom detectée, il saute sur ce call
  5. Normalement, grace aux hooks, on récupère soit directement l'addresse de l'api (call de type1) soit on recupere une "pre-addresse" qu'il faut modifiée pour recupérer l'addresse finale.
  6. Dans le cas ou on obtient une "pre-addresse", on scann le code à la sortie du call CreatePreAddress à la recherche du DWORD 0x51E6AC, une fois qu'on l'a trouvé, on récupère le byte situé un peu plus loin et on fait les opérations necessaires pour retrouver l'addresse de l'api

Maintenant que nous avons l'addresse de l'api, nous devons reconstruire le call.

Si nous avons affaire à un "call [Securom]" c'est tres simple, il suffit de scanner l'iat de D2 à la recherche de l'addresse de l'API, une fois que nous avons cette addresse il suffit de remplacer le "call [Securom]" par un "call [API]"

Dans le cas où le call redirigé est de type "call Securom" c'est un tout petit peu plus compliqué, en effet un "call Addresse" prend un byte de moin qu'un "call [Addresse]" ce byte est donc comblé avec une instruction "nop-like" ou une instruction qui modifie juste eax au cas où le byte se trouve avant le call redirigé (comme une api modifie tout le temps eax , ca n'a pas d'importance de le modifier avant le call).

Il nous faut donc repérer cette instruction afin de savoir si elle se situe avant ou après le call redirigé pour pouvoir le reconstruire correctement (sinon on risque d'écraser du code).

Une fois l'instruction repérée, il n'y a plus qu'à reconstruire le call de la même facon que pour les calls de type 1

Une fois que tous les calls ont été reconstruit il ne nous reste plus qu'a dumper et reconstruire les imports avec Imprec (addr de l'IAT : 0x9000 , size : 0x200)

On lance l'exe unpacké et ..... ca ne marche pas !

On a un message nous priant d'insérer le CD de D2 LOD !

Pas d'affolement, cette fois ci il ne s'agit que d'un cd-check basique effectué dans storm.dll (tient tient comme starcraft :p) qui se patche relativement facilement, la routine se trouve a l'offset 0x11C20 de storm.dll et elle se patche de plusieurs facons.

Enfin il faut copier les fichiers D2X*.mpq du cd au répertoire d'installation de D2 afin qu'il puisse fonctionner correctement.

vous avez alors un No-CD tout ce qu'il y a de plus fonctionnel, le seul inconvénient étant qu'il ne permet pas (pour le momment) de se connecter à Battlenet, un crc étant effectuer pour vérifier la version du jeux (et son integritée :p).

Je ne sais pas encore si je vais faire cette ultime modification ....

Vous trouverez ICI mon call fixer ainsi que sa source et le plugin pour olly que j'ai fait qui permet de l'injecter.

Voili voilou, j'espère que ce petit texte vous aura apporter quelque chose et qu'il est assez clair ;)