Reverse-engineering du fichier Npc Stoneheim.dll

De T4C Tech
Aller à la navigation Aller à la recherche

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)