Reverse-engineering du fichier Npc Stoneheim.dll
Modification du nombre maximum de renaissances (Serveur 1.25)
Cette modification de la DLL NPC Stoneheim permet de modifier le nombre de renaissances maximum sur un serveur T4C qui par défaut sont limitées à 3.
Nous savons que le nombre de renaissances d'un joueur est stocké dans le flag "Number Of Remorts" (30419).
Il suffit donc d'ouvrir la DLL dans le désassembleur de votre choix, et une fois le désassemblage terminé, de rechercher la valeur 30419 en hexadécimal (0x76D3) dans le listing.
Ce qui nous donne :
.text:10087060 .text:10087060 loc_10087060: ; CODE XREF: sub_10086CD0+387�j .text:10087060 mov edx, [esi] .text:10087062 mov ecx, esi .text:10087064 call dword ptr [edx+194h] .text:1008706A .text:1008706A loc_1008706A: ; CODE XREF: sub_10086CD0+38E�j .text:1008706A push offset aReborn ; "REBORN" .text:1008706F push eax .text:10087070 push 225Bh .text:10087075 call ?GetString@IntlText@@SAPBDKGPBD@Z ; IntlText::GetString(ulong,ushort,char const *) .text:1008707A push eax .text:1008707B lea eax, [ebp+var_14] .text:1008707E push eax .text:1008707F call ?NPCKeyWord@@YA_NABVCString@@PBD@Z ; NPCKeyWord(CString const &,char const *) .text:10087084 add esp, 14h .text:10087087 test al, al .text:10087089 jz loc_100871FE .text:1008708F cmp dword_1012B33C, edi .text:10087095 jnz loc_100871FE .text:1008709B push offset unk_101127D8 .text:100870A0 lea ecx, [ebp+var_10] .text:100870A3 call ??YCString@@QAEABV0@PBD@Z ; CString::operator+=(char const *) .text:100870A8 mov edx, [esi] .text:100870AA push edi .text:100870AB push 76D0h .text:100870B0 mov ecx, esi .text:100870B2 call dword ptr [edx+0Ch] .text:100870B5 cmp eax, 1 .text:100870B8 jnz loc_10087F55 .text:100870BE mov eax, [esi] .text:100870C0 push edi .text:100870C1 push 76D3h .text:100870C6 mov ecx, esi .text:100870C8 call dword ptr [eax+0Ch] .text:100870CB cmp eax, 03h .text:100870CE jl short loc_10087106 .text:100870D0 cmp esi, edi .text:100870D2 jnz short loc_100870DB .text:100870D4 call ?GetDefaultLng@IntlText@@SAGXZ ; IntlText::GetDefaultLng(void) .text:100870D9 jmp short loc_100870E5 .text:100870DB ; ---------------------------------------------------------------------------
Examinons de plus près ce passage :
.text:100870BE mov eax, [esi] .text:100870C0 push edi .text:100870C1 push 76D3h .text:100870C6 mov ecx, esi .text:100870C8 call dword ptr [eax+0Ch] .text:100870CB cmp eax, 03h .text:100870CE jl short loc_10087106
Ici on récupère la valeur du flag 30419 (76d3 hex) du joueur et on la stocke dans le registre eax :
.text:100870BE mov eax, [esi] .text:100870C0 push edi .text:100870C1 push 76D3h ; Flag Number Of Remorts (30419) .text:100870C6 mov ecx, esi .text:100870C8 call dword ptr [eax+0Ch]
Autrement dit, on lit le nombre de renaissances du joueur et on la met de côté.
Ensuite, on compare cette valeur avec un entier fixe, ici 3 (03 hex), et on va brancher le programme à l'adresse virtuelle 10087106 (loc_10087106) dans la section ".text" si cette valeur est moindre (jl, soit jump if less than) ; sinon, on continue le programme :
.text:100870CB cmp eax, 03h ; Max number of remorts .text:100870CE jl short loc_10087106
Autrement dit, on vient de comparer le nombre de renaissances du joueur avec 3, et suivant que ce nombre est moindre ou pas, le programme ne se comportera pas de la même manière. Il ne faut pas être un génie pour comprendre que cette valeur fixe est la valeur qui limite le nombre de renaissances.
L'adresse de cette instruction est l'adresse virtuelle (Virtual Address ou VA) dans la section de l'exécutable nommée ".text". Si vous utilisez le désassembleur IDA, l'offset correspondant dans le fichier est indiqué juste à côté en bas à gauche (merci Ikar pour le tuyau ;)). Sinon, il faut le calculer. Corrigez-moi si je me trompe : (--Sorkvild 17 mars 2008 à 17:06 (MSK))
FileOffset = VirtualAddress - VirtualOffset + RawOffset
- VirtualAddress est notre addresse VA, 0x100870CB + 2 octets (pour l'instruction cmp eax), soit 0x100870CD de la section .text.
- VirtualOffset est l'adresse où démarre notre section .text. Cette valeur se trouve dans les informations de l'en-tête des sections (sections header dans votre désassembleur). Celui-ci nous renseigne : la section .text commence à 0x10001000.
- RawOffset est un décalage arbitraire dans le fichier binaire à partir duquel les sections commencent. Le désassembleur nous renseigne encore : dans notre DLL, il vaut 0x00001000.
Ce qui nous donne :
FileOffset = 0x100870CD - 0x10001000 + 0x00001000 = 0x000870cd = 553165
On récupère donc son offset dans le fichier : 553165.
Vous trouverez davantage d'informations sur le format PE sur cette page.
Une fois cette adresse trouvée, il vous suffira de développer un petit logiciel de patch ou bien de modifier à l'aide d'un éditeur hexadécimal la valeur du nombre de renaissances directement dans le fichier.
Exemple d'un patcheur pour cette DLL en Delphi
(* -------------------------------------------------------------------------- *)
(* T4C Reborn Patcher v1.25 By Mestoph *)
(* -------------------------------------------------------------------------- *)
Unit Patcheur;
Interface
Uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, WinSkinData;
Type
TForm1 = Class(TForm)
SkinData1: TSkinData;
GroupBox1: TGroupBox;
OpenDialog1: TOpenDialog;
Lbl_Port: TLabel;
Edit_Port: TEdit;
Edit_Fichier: TEdit;
Btn_Ouvrir: TButton;
Btn_Patch: TButton;
Btn_Fermer: TButton;
GroupBox3: TGroupBox;
Lbl_Taille: TLabel;
Bevel1: TBevel;
Procedure Btn_OuvrirClick(Sender: TObject);
Procedure Btn_FermerClick(Sender: TObject);
Procedure Btn_PatchClick(Sender: TObject);
Procedure Edit_PortKeyPress(Sender: TObject; Var Key: Char);
Private
{ Déclarations privées }
Public
{ Déclarations publiques }
End;
Var
Form1: TForm1;
Implementation
{$R *.dfm}
(* -------------------------------------------------------------------------- *)
(* Ouverture du fichier *)
(* -------------------------------------------------------------------------- *)
Procedure TForm1.Btn_OuvrirClick(Sender: TObject);
Var Taille_Fichier : LongInt;
Donnees, Emplacement : Pbyte;
Fichier : File;
Begin
If OpenDialog1.Execute = True Then
Begin
Edit_Fichier.Text := OpenDialog1.FileName;
AssignFile(Fichier, OpenDialog1.filename);
Reset(Fichier,1);
Taille_Fichier := FileSize(Fichier);
GetMem(Donnees,Taille_Fichier);
BlockRead(Fichier,Donnees^,Taille_Fichier);
Emplacement := Donnees;
Emplacement := Pointer(LongInt(Emplacement) + 553165);
Edit_Port.Text := IntToStr(PByte(LongInt(Emplacement))^);
FreeMem(Donnees);
Lbl_Taille.Caption := IntToStr(Filesize(Fichier)) + ' Octets';
CloseFile(Fichier);
End;
End;
(* -------------------------------------------------------------------------- *)
(* Fermeture du programme *)
(* -------------------------------------------------------------------------- *)
Procedure TForm1.Btn_FermerClick(Sender: TObject);
Begin
Application.Terminate;
End;
(* -------------------------------------------------------------------------- *)
(* Patche du fichier *)
(* -------------------------------------------------------------------------- *)
Procedure TForm1.Btn_PatchClick(Sender: TObject);
Var Taille_Fichier : LongInt;
Donnees, Emplacement : Pbyte;
Fichier : File;
Begin
If Edit_Fichier.Text <> '' Then
Begin
If Edit_Port.Text <> '' Then
Begin
If (StrToInt(Copy(Lbl_Taille.Caption,1,Length(Lbl_Taille.Caption)-7)) >= 1196100) then
Begin
AssignFile(Fichier,Edit_Fichier.Text);
Reset(Fichier,1);
Taille_Fichier := FileSize(Fichier);
// On alloue une nouvelle zone mémoire
GetMem(Donnees,Taille_Fichier);
// On mets les données du fichier en mémoire
BlockRead(Fichier,Donnees^,Taille_Fichier);
Emplacement := Donnees;
// On Patch a l'offset 003f75a
Emplacement := Pointer(LongInt(Donnees) + 553165);
PByte(LongInt(Emplacement))^ := StrToInt(Edit_Port.Text);
CloseFile(Fichier);
// On fait une copie du fichier en cas de problème.
RenameFile(Edit_Fichier.Text,Edit_Fichier.Text+'.bak');
AssignFile(Fichier,Edit_Fichier.Text);
Rewrite(Fichier,1);
// On mets les données de la mémore dans le fichier
BlockWrite(Fichier,Donnees^,Taille_Fichier);
CloseFile(Fichier);
// On libère la mémoire
FreeMem(Donnees);
ShowMessage('Le fichier : ' + #13 + Edit_Fichier.Text + #13 + 'a été patché avec succès !');
Edit_Port.Text := '';
Edit_Fichier.Text := '';
Lbl_Taille.Caption := '0 Octets';
End
Else ShowMessage('Le fichier ne correspont pas au fichier NPC Stoneheim.dll de base !');
End
Else ShowMessage('Vous n''avez pas rempli de façon correct les informations requises !');
End
Else ShowMessage('Vous devez ouvrir le fichier NPC Stoneheim.dll !');
End;
(* -------------------------------------------------------------------------- *)
(* Detection des touche clavier *)
(* -------------------------------------------------------------------------- *)
Procedure TForm1.Edit_PortKeyPress(Sender: TObject; Var Key: Char);
Begin
If Key In ['0'..'9', Chr(VK_BACK), Chr(VK_DELETE)] Then
Else
Begin
showmessage('Uniquement les chiffres de 0 à 9 sont autorisés !');
Key:=#0;
End;
End;
End.
--Mestoph 17 mars 2008 à 05:04 (MSK)