M
Myrdinn
Invité
Tracer les Dlls -- Post 3 LA routine de calcul des dégats des bases.
Les posts précédents donnant les bases permettant une meilleure compréhension sont ICI et LA
Je dois commencer par un exemple détaillé de ce que l'on peut obtenir. Dans le prochain post nous ferrons le trace de cette partie, pour eviter de partir de rien, je vais décrire la fonction en détail même si pour l'instant vous n'avez pas forcement les moyens de savoir d'ou je tire mes informations (Vous le verrez par la suite) Bref la fonction de calcul des dégats physique.
Pour commencer une explication du format du post.
Le code est représenté en citation
La première colonne représente l'adresse du code dans la dll(Offset)
Le deuxième l'instruction assembleur en hexa
La troisième la même instruction en OpCode
LA fonction commence en 0007DC40 (par commodité j'oublierais les OOO par la suite). C'est le code de la 1.09B pour la 1.09D le code commence en 0007E140
C'est le moment de voir une nouvelle instruction NOP
NOP=No Operation. Demande au processeur de ne rien faire .
En réalité c'est l'alias d'une fonction qui ne fait rien. A quoi ca sert? A faire du remplissage dans les Dlls. Si vous voulez faire un mod ou tout les degats physiques sont annulés vous n'avez qu'à remplacer tout ce que l'on va voir dans ce post par des NOP.Tout ca pour vous dire que blizzard entoure fréquemment ces fonctions par des NOPs pour les délimiter, c'est d'ailleurs très gentil de leur part.
Venons en à la fonction. On voit une modification du pointeur de la pile on lui retire 8 (Donc on laisse 2Dwords supplémentaires) et on pousse 4 registres !!! Il faut ce souvenir que cette fonction comme toutes les autres est appelé par le programme principal qui utilise également les registres. Ce qui se passe ici c'est une sauvegarde de ces registres sur la pile pour récupération à la fin de cette fonction. D'ailleurs si vous regardez la fin de la fonction vous avez exactement le contraire et les NOPs après qui servent de remplissage.
Au passage vous remarquerez que les regsitres en X ne sont jamais sauvegardés. Si leur contenu est important il faut toujours le sauvegarder autre part. C'est ce que fait DiabloII on aura l'occasion de le voir.
La on commence vraiment la fonction.
Il faut que je vous parle du pipelining des processeurs. Les processeurs modernes ne traitent pas les instructions assembleur ligne par ligne, mais plusieurs instructions en même temps en parrallèle (dans des "pipelines") si celle ci ne dépendent pas du résultat de la précedente.
Par exemple
MOV EAX,64
ADD EBX,EAX
ne bénéficie pas du pipeline car le résultat dans EBX dépend de l'instruction précédente qui charge 100 dans EAX.
Bref pour des questions de gain de rapidité vous verrez souvent des mélanges de lignes pour des raison d'optimisation. C'est le cas ici (Pour ESI), vous avez 2 Morceaux de code distinct.
MOV ESI,ECX
MOV EDI,EDX
La fonction récupère les paramètres passés dans ECX et EDX.
Désormais
ESI=Pointeur sur l'objet Attaquant
EDI=Flag combat
Dans le quatrième post je vous expliquerais comment savoir que ce sont ces paramètres qui sont passés. Ne vous inquiétez pas je vous donne beaucoup d'info mais vous verrez quand ce sera possible d'ou elles viennent
et
PUSH 0
PUSH ESI
CALL D2Common.dll 10434
Une explication des appels de fonctions Exportées. Lorsque l'on appelle une fonction exportée on passe les paramètres sur la pile (Les PUSH). Sinon pour les appel internes à la dll les paramètres sont passés dans les registres (C'est le cas juste au dessus, les 2 MOV) Ensuite on appelle la fonction avec un CALL (C'est notre outil qui vous donnera le numéro de la fonction appelée). Le résultat sera toujours renvoyé dans EAX.
Que fait cette fonction?
Cette fonction retourne dans EAX le pointeur sur l'Arme dans la main Paramètre 1 de l'unité (Param2) ou 0 si l'unité est à main nue.
Ici
Paramètres 1. 0= principal (1=Secondaire)
Paramètre 2. Pointeur sur l'UNIT concernée (Ici l'attaquant).
Bref EAX=Pointeur sur l'arme Active[/red]
On pourrait tracer cette fonction, mais j'ai fait le choix de ne jamais sortir de la fonction principale. Sinon on ne reveindrait dans le sujet de ce post que dans une dizaine de millier de lignes de codes . Donc on se contetera ici et par la suite de regarder le résultat dans EAX
CONCLUSION de la section 1
Cette partie est extremement commune, on verra l'équivalent dans la plupart des fonctions. Ce qu'il faut retenir pour la suite c'est
ESI=Pointeur sur l'unit Attaquant
EDI= Flag. Je n'en dit pas plus exprès pour vous donnez la démarche a suivre.
EAX=Pointeur sur l'objet Arme.
Les posts précédents donnant les bases permettant une meilleure compréhension sont ICI et LA
Je dois commencer par un exemple détaillé de ce que l'on peut obtenir. Dans le prochain post nous ferrons le trace de cette partie, pour eviter de partir de rien, je vais décrire la fonction en détail même si pour l'instant vous n'avez pas forcement les moyens de savoir d'ou je tire mes informations (Vous le verrez par la suite) Bref la fonction de calcul des dégats physique.
Pour commencer une explication du format du post.
Le code est représenté en citation
La première colonne représente l'adresse du code dans la dll(Offset)
Le deuxième l'instruction assembleur en hexa
La troisième la même instruction en OpCode
Code:
Section 1 Initialisation de la fonction
0007DC38 90 NOP
0007DC39 90 NOP
0007DC3A 90 NOP
0007DC3B 90 NOP
0007DC3C 90 NOP
0007DC3D 90 NOP
0007DC3E 90 NOP
0007DC3F 90 NOP
0007DC40 83EC 08 SUB ESP,8
0007DC43 53 PUSH EBX
0007DC44 55 PUSH EBP
0007DC45 56 PUSH ESI
0007DC46 57 PUSH EDI
C'est le moment de voir une nouvelle instruction NOP
NOP=No Operation. Demande au processeur de ne rien faire .
En réalité c'est l'alias d'une fonction qui ne fait rien. A quoi ca sert? A faire du remplissage dans les Dlls. Si vous voulez faire un mod ou tout les degats physiques sont annulés vous n'avez qu'à remplacer tout ce que l'on va voir dans ce post par des NOP.Tout ca pour vous dire que blizzard entoure fréquemment ces fonctions par des NOPs pour les délimiter, c'est d'ailleurs très gentil de leur part.
Venons en à la fonction. On voit une modification du pointeur de la pile on lui retire 8 (Donc on laisse 2Dwords supplémentaires) et on pousse 4 registres !!! Il faut ce souvenir que cette fonction comme toutes les autres est appelé par le programme principal qui utilise également les registres. Ce qui se passe ici c'est une sauvegarde de ces registres sur la pile pour récupération à la fin de cette fonction. D'ailleurs si vous regardez la fin de la fonction vous avez exactement le contraire et les NOPs après qui servent de remplissage.
Au passage vous remarquerez que les regsitres en X ne sont jamais sauvegardés. Si leur contenu est important il faut toujours le sauvegarder autre part. C'est ce que fait DiabloII on aura l'occasion de le voir.
Code:
0007DC47 8BF1 MOV ESI,ECX
0007DC49 6A 00 PUSH 0
0007DC4B 8BFA MOV EDI,EDX
0007DC4D 56 PUSH ESI
0007DC4E E8 F5710400 CALL 000C4E48(D2Common.Dll Fonction exportée 10434)
Il faut que je vous parle du pipelining des processeurs. Les processeurs modernes ne traitent pas les instructions assembleur ligne par ligne, mais plusieurs instructions en même temps en parrallèle (dans des "pipelines") si celle ci ne dépendent pas du résultat de la précedente.
Par exemple
MOV EAX,64
ADD EBX,EAX
ne bénéficie pas du pipeline car le résultat dans EBX dépend de l'instruction précédente qui charge 100 dans EAX.
Bref pour des questions de gain de rapidité vous verrez souvent des mélanges de lignes pour des raison d'optimisation. C'est le cas ici (Pour ESI), vous avez 2 Morceaux de code distinct.
MOV ESI,ECX
MOV EDI,EDX
La fonction récupère les paramètres passés dans ECX et EDX.
Désormais
ESI=Pointeur sur l'objet Attaquant
EDI=Flag combat
Dans le quatrième post je vous expliquerais comment savoir que ce sont ces paramètres qui sont passés. Ne vous inquiétez pas je vous donne beaucoup d'info mais vous verrez quand ce sera possible d'ou elles viennent
et
PUSH 0
PUSH ESI
CALL D2Common.dll 10434
Une explication des appels de fonctions Exportées. Lorsque l'on appelle une fonction exportée on passe les paramètres sur la pile (Les PUSH). Sinon pour les appel internes à la dll les paramètres sont passés dans les registres (C'est le cas juste au dessus, les 2 MOV) Ensuite on appelle la fonction avec un CALL (C'est notre outil qui vous donnera le numéro de la fonction appelée). Le résultat sera toujours renvoyé dans EAX.
Que fait cette fonction?
Cette fonction retourne dans EAX le pointeur sur l'Arme dans la main Paramètre 1 de l'unité (Param2) ou 0 si l'unité est à main nue.
Ici
Paramètres 1. 0= principal (1=Secondaire)
Paramètre 2. Pointeur sur l'UNIT concernée (Ici l'attaquant).
Bref EAX=Pointeur sur l'arme Active[/red]
On pourrait tracer cette fonction, mais j'ai fait le choix de ne jamais sortir de la fonction principale. Sinon on ne reveindrait dans le sujet de ce post que dans une dizaine de millier de lignes de codes . Donc on se contetera ici et par la suite de regarder le résultat dans EAX
CONCLUSION de la section 1
Cette partie est extremement commune, on verra l'équivalent dans la plupart des fonctions. Ce qu'il faut retenir pour la suite c'est
ESI=Pointeur sur l'unit Attaquant
EDI= Flag. Je n'en dit pas plus exprès pour vous donnez la démarche a suivre.
EAX=Pointeur sur l'objet Arme.