Bonjour et bonne année à tous
Bien que je m'interesse à d'autres sujets en ce moment, je reviens toujours de temps en temps à Diablo2 pour entretenir mon habitude de debogage.
J'ai décidé de faire un sujet sur le MF.
Pourquoi ce sujet ? Parce que mon post Anatomie d'1 drop est assez vieux et obsolète.
L'autre raison est que c'est l'un des mécanismes les plus connus (ca nous aidera) et en même temps sur lequel il nous reste le plus de chose à découvrir.
Un sujet en LIVE
Je pourrais donner les résultats tout fait, mais ils sont déjà pour la plupart très connus.
J'essayerais dans ce post de vous montrer comment je fais pour débugger diablo2. Comment je raisonne, comment je progresse et mes méthodes d'analyse.
J'avancerais le sujet petit à petit tant que ca interessera quelqu'un
je vous ferais participer si possible.
Par ou commencer
Déjà quelque soit le logiciel à debugger on ne peut pas partir de zéro.
Il faut énormément de connaissance pour trouver quelque chose à quoi se raccrocher.
Pour le moment mieux vaut connaitre les MPQs (pour l'hypothèse de départ) et bien le jeu ca aide à trouver des repères pour les hypothèses futures.
Pour ne pas vous faire peur j'aborderais les éléments que vous ne pouvez pas deviner au fur et à mesure.
Ne vous inquiétez pas. Si vous ne connaissez pas ces éléments ca viendra assez vite. Pour le moment je suis la pour vous les transmettre.
Par ou commencer- connaitre l'environnement
Je sais 2 choses sur le MF qui sont assez remarquables pour être possible à trouver dans le code.
1) Le diminishing return commence à 110. 0x6E en hexa
2) L'itemStatCost du MagicFind est Item_MAgicBonus = 80 0x50 en Héxa.
Je ne reviendrais pas sur le fonctionnement de ollydbg (je le décrit déjà ici avec photo d'écran à l'appui)
Si vous avez des questions ou blocage vous êtes les bienvenus.
Après avoir commencer une partie de D2
Dans ollydbg Vous avez fait File/Attach
ensuite vous avez selectionner Diablo2.Exe
Dans View/Exécutable module vous avez double cliquer sur D2Game.dll
Vous avez maintenant ollydbg ouvert sur D2 Avec comme fenetre
En haut à gauche D2Game.dll
En haut à droite Les registres
En bas à gauche Les zones mémoires
En bas à droite la pile.
Première méthode Je cherche dans D2Game.dll les occurences de 6E notre 110 du diminishing return
On cherche toutes les occurences de 6E dans D2Game.dll
Pour cela on fait CTRL+B puis on tape 6E
Et la surprise il y en a plein.
Vous ne croyez quand même pas que ce serait si facile.
A la place on va faire CTRL+F pour choisir une instruction et on va essayer les registre courant.
je cherche d'abord CMP EAX,6E puis CMP EBX,6E puis CMP ECX,6E et pour finir CMP EDX,6E.
Vous n'en trouverez pas beaucoup de chaque mais assez pour vous repoussez.
En réalité c'est par la que je suis passé.
Commente savoir lequel est le bon. Déjà on regarde les instructions autour
(niveau avancé) et on élimine les routines qui semblent le moins correspondent.
Après avoir Comparé avec 6E on s'attend à une formule de diminishing return.
Ensuite quand il reste 3-4 routines on peut tester.
On met un breakpoint sur chaque CMP Register,6E (en appuyant sur F2) et on va tuer du monstre. Au moment du drop vous vous
arrêterez sur le bon.
J'ai dit 3-4 mais vous voyez que vous pouvez faire la manip même avec 20 occurences de CMP Register,6E sans trop d'éffort
Mais bon c'est trop compliqué
Méthode numéro 2.
La deuxième consiste à Trouver PUSH 50 Dans D2Game.dll.
En hypothèse j'espère que l'intérrogation du MF se fait en passant les paramètres sur la pile (le PUSH)
Vous devriez en trouver 6
La première
00960D1D 6A 50 PUSH 50
00960D1F 56 PUSH ESI
00960D20 E8 6B99FDFF CALL <JMP.&D2Common.#10890>
00960D25 85C0 TEST EAX,EAX
00960D27 0F85 24010000 JNZ D2Game.00960E51 |
Ca pourrait coller on passe 50 en paramètre.
LA deuxième
0097B0FB 6A 50 PUSH 50
0097B0FD 53 PUSH EBX
0097B0FE E8 8DF5FBFF CALL <JMP.&D2Common.#10890>
0097B103 85C0 TEST EAX,EAX
0097B105 ^ 0F84 7AFFFFFF JE D2Game.0097B085 |
Meme routine appelé (on la verra bientot
)
OK Possible.
LA troisième
009FEB2E CC INT3
009FEB2F CC INT3
009FEB30 85F6 TEST ESI,ESI
009FEB32 74 33 JE SHORT D2Game.009FEB67
009FEB34 8B06 MOV EAX,DWORD PTR DS:[ESI]
009FEB36 85C0 TEST EAX,EAX
009FEB38 7C 2D JL SHORT D2Game.009FEB67
009FEB3A 83F8 01 CMP EAX,1
009FEB3D 7F 28 JG SHORT D2Game.009FEB67
009FEB3F 57 PUSH EDI
009FEB40 6A 00 PUSH 0
009FEB42 6A 50 PUSH 50
009FEB44 56 PUSH ESI
009FEB45 E8 0CBCF3FF CALL <JMP.&D2Common.#10537>
009FEB4A 8BCE MOV ECX,ESI
009FEB4C 8BF8 MOV EDI,EAX
009FEB4E E8 EDA1FAFF CALL D2Game.009A8D40
009FEB53 85C0 TEST EAX,EAX
009FEB55 74 0C JE SHORT D2Game.009FEB63
009FEB57 6A 00 PUSH 0
009FEB59 6A 50 PUSH 50
009FEB5B 50 PUSH EAX
009FEB5C E8 F5BBF3FF CALL <JMP.&D2Common.#10537>
009FEB61 03F8 ADD EDI,EAX
009FEB63 8BC7 MOV EAX,EDI
009FEB65 5F POP EDI
009FEB66 C3 RETN
009FEB67 33C0 XOR EAX,EAX
009FEB69 C3 RETN
009FEB6A CC INT3
009FEB6B CC INT3 |
Elle est double et c'est une petite routine délimitée par les INT3 (c'est pourquoi je la met en entier)
00A0BED7 6A 50 PUSH 50
00A0BED9 50 PUSH EAX
00A0BEDA E8 83E5F2FF CALL <JMP.&D2Common.#10415>
00A0BEDF 8B4424 14 MOV EAX,DWORD PTR SS:[ESP+14]
00A0BEE3 56 PUSH ESI
00A0BEE4 50 PUSH EAX
00A0BEE5 E8 9EE6F2FF CALL <JMP.&D2Common.#10795> |
Ca colle encore.
00A1A5F6 6A 50 PUSH 50
00A1A5F8 6A 24 PUSH 24
00A1A5FA 57 PUSH EDI
00A1A5FB E8 3EFEF1FF CALL <JMP.&D2Common.#10590>
00A1A600 53 PUSH EBX |
LA c'est pas terrible. Dans mes connaissance du MF je ne vois pas du tout de paramètres semblables à 24 ??
A voir si le reste ne donne rien
En réalité et vous le comprendrez vite par la suite. 1 seul des PUSH 50 a le bon format.
Mais je préfère vous laissez le découvrir.
Retour au jeu
Contrairement à la légende. je ne fais pas que lire le code.
Je joue systématiquement au jeu pour confirmer mes hypothèses. OK ici c'est juste pour activer le breakpoint
Bon 5 F2 (sur chaque PUSH 50) a faire c'est pas la mort
Retournons jouer (en appuyant sur F9) avec nos 5 Breakpoints.
Premier monstre que je tue Rien. En même temps pas de drop.
Vous me direz ce que ca donne, j'ai pas trop de chance, tous les monstres tués ne m'ont rien donner, sauf des carquois (qui je le sais n'active pas le MF)
Par contre j'ai soulevé un rocher dans la première map et la je m'arrete sur la routine que j'ai mise en entier.
Si c'est du MF Alors c'est que le MF fonctionne sur les rochers CQFD
OK Je met le breakpoint au début de la routine pour l'avoir en entier.
Je retourne jouer (F9)
Au passage le rocher m'a dropé des bottes (bref objet ou le MF à 1 sens)
OK UN zombie me donne enfin un arrêt après moult corps (qui ne m'ont pas donné d'objet à MF, j'ai eut des potions or ...)
Au premier coup d'oeil vous devriez avoir une confirmation que c'est probablement le MF associé au drop que nous calculons.
En regardant précisement la pile.
Moi je vois ceci
0012F4E0 009FF047 RETURN to D2Game.009FF047 from D2Game.009FEB30
0012F4E4 0012FA4C
0012F4E8 00000000
0012F4EC 0000008C
0012F4F0 065DE200
0012F4F4 00000000
0012F4F8 02CD5274 ASCII "boot2"
0012F4FC 00A024E1 RETURN to D2Game.00A024E1 from D2Game.009FEF60 |
Outre les 2 adresse des procédures appelantes (qui nous servirons plus tard je vois ASCII "boot2"
J'ai comme l'impression que je suis en train de dropper des bottes
Le format Boot2 vous est peut être inconnu. Nous verrons bien un jour ou l'autre ce qu'il signifie
Le code
OK Il est temps de commencer l'analyse de la routine
009FEB2E CC INT3
009FEB2F CC INT3
009FEB30 85F6 TEST ESI,ESI
009FEB32 74 33 JE SHORT D2Game.009FEB67 |
Ce que ous devez savoir avant de commencer
1) LEs unités
Ce qu'est une unité au sens D2. Et ce qu'est le PtUNIT (le pointeur vers l'unité) Si vous ne le savez pas aller lire Tracer les DLLS dans mes fixes.
Pour faire rapide. Une unité est un ensemble de données (une fiche signalétique ) qui concerne un élément du jeu (PErsonnage Jeu Missiles Coffres ...) Par exemple pour un personnage joeur on trouvera Sa classe, son équipement, ses skills, son état (en train de courir frapper ...) ...
Le pointeur indique le début de l'unité. Pt+XX indique la position relative XX dans la fiche.
2) un peu d'assembleur.
PAs de problème je suis la pour le traduire pour le moment. Ensuite si vous retenez les opérations archi communes, l'apprentissage sera facile.
3) un peu d'ollydbg.
Pour aller de ligne en ligne sans rentrer dans les sous appel. C'est la touche F8
L'analyse
1) L'instruction
On test ESI avec lui même associé avec la fonction EQUAL (Jump if Equal)
Retenez le c'est équivalent de ESI = 0 ou ESI Existe ? C'est un test très fréquent de validation
Si ESI n'existe pas (<=> vaut 0 <=> condition Vraie pour EQUAL) alors on sort de la routine (en fait on saute (JUMP) à la fin de la routine.
2) L'unité
Je fais bouton droit sur ESI dans la partie registres puis Follow in Dump
Et dans la zone mémoire je regarde la tête du pointeur.
moi j'obtient cela
$ ==> >00 00 00 00 04 00 00 00 00 00 00 00 01 00 00 00 ..............
$+10 >07 00 00 00 00 FA 5D 01 00 00 00 00 00 62 35 02 ....ú].....b5
$+20 >C5 E4 9D 38 5E C2 25 54 7B 34 3E D1 00 5C 5D 01 Åä8^Â%T{4>Ñ.\]
$+30 >00 00 00 00 00 00 00 00 00 00 00 00 23 01 00 00 ............#.. |
Ca ressemble à un pointeur d'unité (Cf. mes autres topic)
Dans ce cas
PT+00 = 00 00 00 00 dans mon cas = 0 = Unit Type. 0 = Joueur
PT+04 = 04 00 00 00 = 4 = Unit Class = Barbare ca tombe bien c'est ce que je joues.
PT+08 = 0 On verra une autre fois c'est pas utilisable pour nous
PT+0C = 1 Unit ID. Je suis le premier joueur de la partie.
Le PT+04 associé à divers personnage vous permettra de savoir les codes pour les Classes (ici 4= Barbare. Sinon les MPQ sont la pour vous le dire
). Comme quoi on peut découvrir des choses et construire des listes.
C'est ce qu'il faut faire à la moindre occasion. (Et c'est encore meilleur quand on s'aperçoit après qu'un fichier TXT confirme notre hypthèse)
OK je pourrais vérifier la suite mais la structure du tueur (c'est à dire moi) mais ca suffira pour le moment.
Si je jouais avec un suivant (squelette ...) et qu'il était le tueur alors
J'aurais
PT+00 = 1 Unit Type = Monster
PT+04 = XX ou XX est l'ID du suivant dans Monstat.txt (Exemple 290 --> en hexa 0x122 pour le BloodGolem)
PT+0C L'ordre de summon depuis le début de la partie.
OK Si le tueur n'est pas une unité identifié on laisse tomber.
Je dis le tueur mais c'est spéculatif. Comme c'est moi et que je suis le tueur du zombie j'en déduis que le pointeur est celui du tueur. Chose que je vérifie à l'aide de mon merco ou de mon nécro et sa horde.
Il faut faire des hypothèse pour avancer et les tester.
Si elles sont mauvaises on le verra très rapidement.
Le pointeur aurait été un zombie j'aurais dit La cible et j'aurais vérifier avec d'autres monstres.
On continue
009FEB34 8B06 MOV EAX,DWORD PTR DS:[ESI] |
Ce que ous devez savoir avant de commencer
Il faut bien comprendre le pointeur (le début de l'unité) et les déplacement du pointeur relatif au début de l'unité les
Pt+XX
Il faut ensuite comprendre la structure de l'unité. Je la connais pour vous, nous verrons plus tard comment la construire ou la compléter.
L'analyse
EAX = PtUNIT +00
Dans EAX on copie l'information en position 0 de l'unité.
Je l'ai déjà dit en +00 on a l'information Unit Type
0 si l'unité est un joueur
1 si l'unité est un monstre
009FEB36 85C0 TEST EAX,EAX
009FEB38 7C 2D JL SHORT D2Game.009FEB67
009FEB3A 83F8 01 CMP EAX,1
009FEB3D 7F 28 JG SHORT D2Game.009FEB67 |
Ce que ous devez savoir avant de commencer
Rien de plus
L'analyse
On a déjà vu TEST EAX couplé a EQUAL ?? Ici c'est ouplé à Jump if Less (saute si plus petit)
Quand on test EAX avec lui même on s'attend à 3 résultats
EAX est négatif. Active le Jump if LEss (JL)
EAX = 0 (n'existe pas) Active le JE
EAX est positif. Active le JG (Jump if Greater)
La deuxième instruction est COMPARE (CMP) EAX avec 1. De même on s'attend au résultat <, Nul ou >
Couplé avec JG on test si EAX > 1
Conclusion
Si l'unité type <0 ou si unittype>1 alors on saute à la fin de la routine (que nous verrons plus tard)
Bref pour continuer il faut que l'unité soit une Joueur ou un Monstre
009FEB3F 57 PUSH EDI
009FEB40 6A 00 PUSH 0
009FEB42 6A 50 PUSH 50
009FEB44 56 PUSH ESI
009FEB45 E8 0CBCF3FF CALL <JMP.&D2Common.#10537> |
Ce que ous devez savoir avant de commencer
Les Appels de procédure en assembleur.
On verra plus tard les différents Types.
Souvent les paramètres sont passés par la pile. C'est les PUSH Avant le CALL
Mais on a vu en commançant que ce n'est pas toujours le cas. Notre ESI
Le résultat d'une routine est généralement placé dans EAX.
L'analyse
LE PUSH EDI est une sauvegarde de EDI pour plus tard. c'est pas évident mais on le comprend plus loin.
Bref on fait appel à la sous routine 10537 (0,50,ESI)
Pas besoin d'être savant pour deviner
Les renseignement donnés sont
0 Indevinable Ici
50 (on espère le itemstatcost ID Du MF)
ESI = Pointeur du Tueur chez qui on va mesurer le MF
J'ai débuggé la routine pour vous mais on l'abordera plus tard.C'est ce qu'il faudrait faire à chaque fois pour être sur.
Pour le moment ce serait trop complexe.
Regarder le résultat dans EAX (qui apparait en rouge) au retour de la routine). Théoriquement c'est votre MF ou celui de votre merco ou skelette...
Faites l'essai avec plusieurs personnages.
Dans tous les cas EAX = Le MF du tueur.
Cela doit vous suffire pour le moment comme preuve.
P.S.
Concernant le 0 c'est pour indiquer le type de bonus que l'on cherche BASE. BASE + Equipement. Equipement SEul ...
009FEB4A 8BCE MOV ECX,ESI
009FEB4C 8BF8 MOV EDI,EAX |
Ce que ous devez savoir avant de commencer
Rien MOV A,B C'est Copier B dans A <=> A = B
L'analyse
OK à la première ligne quand vous arrivez avec le F8 C'est la que vous pouvez lire le résultat de la routine qui calcul le MF du tueur dans EAX.
OK ECX contient maintenant l'UNITE Tueuse
EDI = Son MF (c'est pourquoi on avait sauvegardé EDI (le PUSH EDI de tout à l'heure))
009FEB4E E8 EDA1FAFF CALL D2Game.009A8D40
009FEB53 85C0 TEST EAX,EAX
009FEB55 74 0C JE SHORT D2Game.009FEB63 |
Ce que ous devez savoir avant de commencer
La fonction D2GaME.009 ... sinon il faudrait la tracer.
Sinon le TEST suivi de JE (jump if Equal)
Vous le connaissez par coeur
L'analyse
Théoriquement on devrait tracer la routine 9A8... pour la comprendre.
Nous ne le ferons pas, nous ferons F8 pour passer la ligne sans entrer dans la sous routine D2Game.009A8D40
Pourquoi?
Cette routine est trop complexe si vous ne connaissez pas extrememnt bien les unités. Il faut maitriser la naissance des unités pour comprendre que le flag testé est Est ce que le tueur à un maitre Si oui EAX = PtUNIT du Maitre sinon EAX = 0
Désolé. Croyez moi sur parole.
Par contre vous pouvez faire le test empirique en plaçant le breakpoint a cet endroit et en rejouant.
Si c'est vous EAX vaudra 0
Si le tueur est l'un de vos suivant. EAX = le Pointeur sur votre personnage.
Ca doit vous suffire comme preuve.
D'ailleurs en général avant d'aller vois en détail (même si je l'ai fait ici) une sous routine,
faire des tests empiriques est parfois la solution suffisante. (bis repetitam)
OK Le TEST EAX C'est Est ce que le tueur à 1 maitre? Non (EQUAL 0 Alors Jump)
009FEB57 6A 00 PUSH 0
009FEB59 6A 50 PUSH 50
009FEB5B 50 PUSH EAX
009FEB5C E8 F5BBF3FF CALL <JMP.&D2Common.#10537> |
Ce que ous devez savoir avant de commencer
Rien vous connaissez déjà cet appel et les arguments.
Au passage c'est une validation que le PUSH EDI de l'autre appel était un élément externe et bien une sauvegarde de EDI.
L'analyse
OK si le maitre existe alors EAX = MF du maitre
]009FEB61 03F8 ADD EDI,EAX
009FEB63 8BC7 MOV EAX,EDI
009FEB65 5F POP EDI
009FEB66 C3 RETN |
Ce que ous devez savoir avant de commencer
rien sauf ADD = Addition
<=> ici à EDI = EDI + EAX
Analyse
EDI = MF du Suivant + MF du Maitre
C'est la fin du MF Normal
On sort de la routine avec EAX = MF
Vous voyez quand on sort d'une routine le résultat est donné dans EAX. Encore une confirmation ici
009FEB67 33C0 XOR EAX,EAX
009FEB69 C3 RETN
009FEB6A CC INT3 |
Ce que ous devez savoir avant de commencer
XOR Registre avec lui même <=> Registre = 0
Analyse
C'est la fin en échec. LA ou mène tous les jump du début
<=> MF = 0 (EAX) Si un des tests précédents à échouer.
On retourne dans tous les cas ici quand on éxécute la fonction RETN (Retour à la fonction appelante)
009FF03C 8BEB MOV EBP,EBX
009FF03E 896C24 1C MOV DWORD PTR SS:[ESP+1C],EBP
009FF042 E8 E9FAFFFF CALL D2Game.009FEB30
Ici pour être exact
009FF047 8BD8 MOV EBX,EAX
009FF049 85DB TEST EBX,EBX
009FF04B 74 11 JE SHORT D2Game.009FF05E |
C'est la que nous continuerons la prochaine fois.
MAintenant que nous savons arrêter le jeu quand le MF est utilisé il sera facile de découvrir tous les mécanismes 1 à 1 pour n'importe quel type de drop ou génération d'objet
(d'ailleurs c'est l'occasion de tester avec les paris, les achats, les quêtes LE coffre de mephisto...) Attention en cas de résultat négatif, rien ne prouve que le MF ne soit pas calculé ailleurs
La prochaine fois nous verrons la routine (pas trop longue rassurez vous) qui appelle le calcul du MF du tueur.
C'est facile à deviner mais je le prouverais. C'est celle qui determine la qualité du drop et elle se résumera à bien plus que la séquence UNIQUE SET ... comme vous auriez pu le croire.
Remarques
Je pense que le niveau pous suivre est assez élevé. N'hésitez pas si vous vous sentez prêt à accrocher à me poser les questions même les plus basiques possibles, ce n'est pas moi qui vous le reprocherai.
Comment faire sans tous les Ce qu'il faut ... ???
Honnetement ce n'est pas ce que j'arriverais à vous apprendre tout de suite. La le niveau est beaucoup plus élevé. Mais on abordera cette partie petit à petit.
Avant de terminer sur de la publicité.
Reprenons quelques autres méthodes de recherche et pourquoi ne pas avoir commencer par elle.
On peut mettre des breakpoint dans le code mais également en mémoire.
Si je sais ou se trouve TreasureClassEX.bin en mémoire. Je peux mettre un breakpoint sur le TC de Méphisto.
OK Mais ca demande trop de connaissance préalable pour commencer par la.
De même si je sais ou se trouve mon unité joueur, je peux mettre un breakpoint sur l'une de mes propriété/valeurs (mon MF par exemple) De même c'est extrêmement complexe et en plus à cause des raffraichissement il faudrait utiliser une astuce que l'on verra plus tard.
Sinon ici j'ai été dirigiste (je veux le MF et rien d'autre) MAis quand j'ai commencé et qu'il n'y avait pas de CE qu'il faut savoir avant de commencer je cherchais des petites routines, je plaçais un Breakpoint, je jouais et si l'arret sur le routine me semblait spécifique je débugait
La première chose que j'ai trouvé c'est l'expérience (pour la petite histoire IIRC)
Pulicité
Cette routine de quelques lignes n'a l'air de rien mais elle permet de répondre à des questions auxquelles quasiment personne au monde ne saurait répondre de façon certaine.
Exemples ??
Si on met un breakpoint sur
]009FEB30 85F6 TEST ESI,ESI |
Le début
Alors on peut répondre aux question suivantes.
Si un monstre meurt tout seul du poison. Est ce que le MF s'applique ?
Simple Regarder si ESI Contient le tueur ou si ESI est un missile ou autre chose (le poison)
Si je lance orage avec un objet et que je déséquippe l'objet. Est ce que mon MF s'applique ???
Idem ESI Contient Vous ou un missile d'orage ???
On peut également répondre à
Si je transforme un objet +MF en golem d'acier. celui ci a-t-il du MF quand il tue les monstres ???
Facile. Je vous laisse deviner ou mettre le breakpoint.
Quand les monstres meurent avec le boss. Est ce que mon MF s'applique ???
Toutes ces questions on me les a posées. Avec un seul F2 et en lisant une valeur unique (Unité dans ESI), vous en saurez plus que beaucoup et adieu les rumeurs battlenetiques
Si c'est une raison suffisante pour vous pour continuer alors il nous reste plusieurs millers de ligne à lire sur le MF
A suivre .. si vous le voulez bien
En attendant si quelqu'un veut bien répondre aux questions publicitaires 
Message édité par Myrdinn le 14-01-2006 à 11:44:06
---------------
Le savoir ne vaut que s'il est partagé :Lien vers les posts techniques Diablo2/LOD