Reverse-engineering du fichier T4C Server.exe

De T4C Tech
Aller à la navigation Aller à la recherche

Modification du port d'écoute du serveur T4C

Serveur 1.25

Cette modification de l'exécutable T4C Server permet de modifier le port de connexions au serveur T4C.

Nous savons que par défaut le port de base du serveur T4C est 11677, il suffit ensuite d'ouvrir l'exécutable dans un désassembleur de votre choix (j'utilise IDA Pro), une fois le désassemblage de l'exécutable terminer, il vous suffit de rechercher la valeur 11677 en hexadécimale(0x2D9D) dans votre désassembleur.

Dans cette exemple de reverse, nous trouverons nombreuses fois la valeur 0x2D9D, pour être certain de trouver les bonnes bonne valeurs nous vérifierons que nous assignons bien le RECV_PORT et le SEND_PORT.

Ce qui nous donne :

.text:0043F6C3 loc_43F6C3:                             ; CODE XREF: _main+705�j
.text:0043F6C3                 lea     ecx, [ebp+var_94]
.text:0043F6C9                 call    ??0CString@@QAE@XZ ; CString::CString(void)
.text:0043F6CE                 push    offset aSoftwareVirc_9 ; "Software\\Vircom\\The 4th Coming Server\\N"...
.text:0043F6D3                 push    80000002h       ; hKey
.text:0043F6D8                 lea     ecx, [ebp+var_1B0]
.text:0043F6DE                 mov     byte ptr [ebp+var_4], 9
.text:0043F6E2                 call    ?Open@RegKeyHandler@@QAEHPAUHKEY__@@PBD@Z ; RegKeyHandler::Open(HKEY__ *,char const *)
.text:0043F6E7                 cmp     eax, edi
.text:0043F6E9                 jz      loc_43F79C
.text:0043F6EF                 push    offset Class    ; lpString2
.text:0043F6F4                 push    offset aRecv_ip ; "RECV_IP"
.text:0043F6F9                 lea     ecx, [ebp+var_1B0]
.text:0043F6FF                 call    ?GetProfileStringA@RegKeyHandler@@QAEPBDPBD0@Z ; RegKeyHandler::GetProfileStringA(char const *,char const *)
.text:0043F704                 push    eax
.text:0043F705                 mov     ecx, offset lpString2
.text:0043F70A                 call    ??4CString@@QAEABV0@PBD@Z ; CString::operator=(char const *)
.text:0043F70F                 mov     ecx, lpString2
.text:0043F715                 push    ecx             ; lpString2
.text:0043F716                 push    offset aSend_ip ; "SEND_IP"
.text:0043F71B                 lea     ecx, [ebp+var_1B0]
.text:0043F721                 call    ?GetProfileStringA@RegKeyHandler@@QAEPBDPBD0@Z ; RegKeyHandler::GetProfileStringA(char const *,char const *)
.text:0043F726                 push    eax
.text:0043F727                 mov     ecx, offset dword_51428C
.text:0043F72C                 call    ??4CString@@QAEABV0@PBD@Z ; CString::operator=(char const *)
.text:0043F731                 mov     ecx, offset lpString2
.text:0043F736                 call    ?TrimRight@CString@@QAEXXZ ; CString::TrimRight(void)
.text:0043F73B                 mov     ecx, offset lpString2
.text:0043F740                 call    ?TrimLeft@CString@@QAEXXZ ; CString::TrimLeft(void)
.text:0043F745                 mov     ecx, offset dword_51428C
.text:0043F74A                 call    ?TrimRight@CString@@QAEXXZ ; CString::TrimRight(void)
.text:0043F74F                 mov     ecx, offset dword_51428C
.text:0043F754                 call    ?TrimLeft@CString@@QAEXXZ ; CString::TrimLeft(void)
.text:0043F759                 push    2D9Dh           ; Port du serveur, valeur entière 11677
.text:0043F75E                 push    offset aRecv_port ; "RECV_PORT"
.text:0043F763                 lea     ecx, [ebp+var_1B0]
.text:0043F769                 call    ?GetProfileIntA@RegKeyHandler@@QAEKPBDK@Z ; RegKeyHandler::GetProfileIntA(char const *,ulong)
.text:0043F76E                 push    2D9Dh           ; Port du serveur, valeur entière 11677
.text:0043F773                 push    offset aSend_port ; "SEND_PORT"
.text:0043F778                 lea     ecx, [ebp+var_1B0]
.text:0043F77E                 mov     word ptr hostshort, ax
.text:0043F784                 call    ?GetProfileIntA@RegKeyHandler@@QAEKPBDK@Z ; RegKeyHandler::GetProfileIntA(char const *,ulong)
.text:0043F789                 lea     ecx, [ebp+var_1B0]
.text:0043F78F                 mov     word ptr hostshort+2, ax
.text:0043F795                 call    ?Close@RegKeyHandler@@QAEXXZ ; RegKeyHandler::Close(void)
.text:0043F79A                 jmp     short loc_43F7BD
.text:0043F79C ; ---------------------------------------------------------------------------

Une fois les données trouvées, il vous suffira de développer un petit logiciel de patch ou bien de modifier à l'aide d'un éditeur hexadécimale les 2 valeurs concernants les ports de connexions au serveur.

Exemple d'un patcheur pour cet exécutable en Delphi

(* -------------------------------------------------------------------------- *)
(* T4C Port 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) + 259930);
      Edit_Port.Text := IntToStr(PWord(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)) >= 1200128) 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) + 259930);
              Pword(LongInt(Emplacement))^ := StrToInt(Edit_Port.Text);
              // On Patch a l'offset 003F76F
              Emplacement := Pointer(LongInt(Donnees) + 259951);
              Pword(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 T4C Server.exe 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 T4C Server.exe !');
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.

Serveur 1.50

Comme pour la version 1.25 du serveur T4C, Cette modification de l'exécutable T4C Server permet de modifier le port de connexions au serveur T4C.

Nous savons que par défaut le port de base du serveur T4C est 11677, il suffit ensuite d'ouvrir l'exécutable dans un désassembleur de votre choix (j'utilise IDA Pro), une fois le désassemblage de l'exécutable terminer, il vous suffit de rechercher la valeur 11677 en hexadécimale(0x2D9D) dans votre désassembleur.

Dans cette exemple de reverse, nous trouverons nombreuses fois la valeur 0x2D9D, pour être certain de trouver les bonnes bonne valeurs nous vérifierons que nous assignons bien le RECV_PORT et le SEND_PORT.

La méthode de recherches ainsi que la procédure et identique à la version 1.25 du serveur, la seul différence c'est que le code serveur change legèrement et que les offset sont différent. je vous donne quand même le code ASM pour la version 1.50

Ce qui nous donne :

.text:00445D90 loc_445D90:                             ; CODE XREF: _main+B84�j
.text:00445D90                 push    offset asc_518CE4 ; "-+----------------+-"
.text:00445D95                 push    80h             ; int
.text:00445D9A                 push    edi             ; int
.text:00445D9B                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445DA0                 add     esp, 0Ch
.text:00445DA3                 push    offset aStartingT4cSer ; "Starting T4C server."
.text:00445DA8                 push    80h             ; int
.text:00445DAD                 push    edi             ; int
.text:00445DAE                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445DB3                 add     esp, 0Ch
.text:00445DB6                 push    offset asc_518CE4 ; "-+----------------+-"
.text:00445DBB                 push    80h             ; int
.text:00445DC0                 push    1               ; int
.text:00445DC2                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445DC7                 add     esp, 0Ch
.text:00445DCA                 push    offset aStartingT4cSer ; "Starting T4C server."
.text:00445DCF                 push    80h             ; int
.text:00445DD4                 push    1               ; int
.text:00445DD6                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445DDB                 add     esp, 0Ch
.text:00445DDE                 push    offset asc_518CE4 ; "-+----------------+-"
.text:00445DE3                 push    80h             ; int
.text:00445DE8                 push    2               ; int
.text:00445DEA                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445DEF                 add     esp, 0Ch
.text:00445DF2                 push    offset aStartingT4cSer ; "Starting T4C server."
.text:00445DF7                 push    80h             ; int
.text:00445DFC                 push    2               ; int
.text:00445DFE                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445E03                 add     esp, 0Ch
.text:00445E06                 push    offset asc_518CE4 ; "-+----------------+-"
.text:00445E0B                 push    80h             ; int
.text:00445E10                 push    esi             ; int
.text:00445E11                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445E16                 add     esp, 0Ch
.text:00445E19                 push    offset aStartingT4cSer ; "Starting T4C server."
.text:00445E1E                 push    80h             ; int
.text:00445E23                 push    esi             ; int
.text:00445E24                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445E29                 add     esp, 0Ch
.text:00445E2C                 push    offset asc_518CE4 ; "-+----------------+-"
.text:00445E31                 push    80h             ; int
.text:00445E36                 push    4               ; int
.text:00445E38                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445E3D                 add     esp, 0Ch
.text:00445E40                 push    offset aStartingT4cSer ; "Starting T4C server."
.text:00445E45                 push    80h             ; int
.text:00445E4A                 push    4               ; int
.text:00445E4C                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445E51                 add     esp, 0Ch
.text:00445E54                 push    offset asc_518CE4 ; "-+----------------+-"
.text:00445E59                 push    80h             ; int
.text:00445E5E                 push    ebx             ; int
.text:00445E5F                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445E64                 add     esp, 0Ch
.text:00445E67                 push    offset aStartingT4cSer ; "Starting T4C server."
.text:00445E6C                 push    80h             ; int
.text:00445E71                 push    ebx             ; int
.text:00445E72                 call    ?SaveToLog@CT4CLog@@SAXKGPBDZZ ; CT4CLog::SaveToLog(ulong,ushort,char const *,...)
.text:00445E77                 add     esp, 0Ch
.text:00445E7A                 lea     ecx, [ebp+var_94]
.text:00445E80                 call    ??0CString@@QAE@XZ ; CString::CString(void)
.text:00445E85                 push    offset aSoftwareVir_11 ; "Software\\Vircom\\The 4th Coming Server\\N"...
.text:00445E8A                 push    80000002h       ; hKey
.text:00445E8F                 lea     ecx, [ebp+var_1B0]
.text:00445E95                 mov     byte ptr [ebp+var_4], 0Dh
.text:00445E99                 call    ?Open@RegKeyHandler@@QAEHPAUHKEY__@@PBD@Z ; RegKeyHandler::Open(HKEY__ *,char const *)
.text:00445E9E                 cmp     eax, edi
.text:00445EA0                 jz      loc_445F53
.text:00445EA6                 push    offset Class    ; lpString2
.text:00445EAB                 push    offset aRecv_ip ; "RECV_IP"
.text:00445EB0                 lea     ecx, [ebp+var_1B0]
.text:00445EB6                 call    ?GetProfileStringA@RegKeyHandler@@QAEPBDPBD0@Z ; RegKeyHandler::GetProfileStringA(char const *,char const *)
.text:00445EBB                 push    eax
.text:00445EBC                 mov     ecx, offset lpString2
.text:00445EC1                 call    ??4CString@@QAEABV0@PBD@Z ; CString::operator=(char const *)
.text:00445EC6                 mov     ecx, lpString2
.text:00445ECC                 push    ecx             ; lpString2
.text:00445ECD                 push    offset aSend_ip ; "SEND_IP"
.text:00445ED2                 lea     ecx, [ebp+var_1B0]
.text:00445ED8                 call    ?GetProfileStringA@RegKeyHandler@@QAEPBDPBD0@Z ; RegKeyHandler::GetProfileStringA(char const *,char const *)
.text:00445EDD                 push    eax
.text:00445EDE                 mov     ecx, offset dword_529BAC
.text:00445EE3                 call    ??4CString@@QAEABV0@PBD@Z ; CString::operator=(char const *)
.text:00445EE8                 mov     ecx, offset lpString2
.text:00445EED                 call    ?TrimRight@CString@@QAEXXZ ; CString::TrimRight(void)
.text:00445EF2                 mov     ecx, offset lpString2
.text:00445EF7                 call    ?TrimLeft@CString@@QAEXXZ ; CString::TrimLeft(void)
.text:00445EFC                 mov     ecx, offset dword_529BAC
.text:00445F01                 call    ?TrimRight@CString@@QAEXXZ ; CString::TrimRight(void)
.text:00445F06                 mov     ecx, offset dword_529BAC
.text:00445F0B                 call    ?TrimLeft@CString@@QAEXXZ ; CString::TrimLeft(void)
.text:00445F10                 push    2D9Dh           ; Port du serveur, valeur entière 11677
.text:00445F15                 push    offset aRecv_port ; "RECV_PORT"
.text:00445F1A                 lea     ecx, [ebp+var_1B0]
.text:00445F20                 call    ?GetProfileIntA@RegKeyHandler@@QAEKPBDK@Z ; RegKeyHandler::GetProfileIntA(char const *,ulong)
.text:00445F25                 push    2D9Dh           ; Port du serveur, valeur entière 11677
.text:00445F2A                 push    offset aSend_port ; "SEND_PORT"
.text:00445F2F                 lea     ecx, [ebp+var_1B0]
.text:00445F35                 mov     word ptr hostshort, ax
.text:00445F3B                 call    ?GetProfileIntA@RegKeyHandler@@QAEKPBDK@Z ; RegKeyHandler::GetProfileIntA(char const *,ulong)
.text:00445F40                 lea     ecx, [ebp+var_1B0]
.text:00445F46                 mov     word ptr hostshort+2, ax
.text:00445F4C                 call    ?Close@RegKeyHandler@@QAEXXZ ; RegKeyHandler::Close(void)
.text:00445F51                 jmp     short loc_445F74
.text:00445F53 ; ---------------------------------------------------------------------------

Une fois les données trouvées, il vous suffira de développer un petit logiciel de patch ou bien de modifier à l'aide d'un éditeur hexadécimale les 2 valeurs concernants les ports de connexions au serveur.

Exemple d'un patcheur pour cet exécutable en Delphi

(* -------------------------------------------------------------------------- *)
(* T4C Port Patcher v1.50 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) + 286481);
      Edit_Port.Text := IntToStr(PWord(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)) = 1286240) 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) + 286481);
              Pword(LongInt(Emplacement))^ := StrToInt(Edit_Port.Text);
              // On Patch a l'offset 003F76F
              Emplacement := Pointer(LongInt(Donnees) + 286502);
              Pword(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 T4C Server.exe 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 T4C Server.exe !');
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.

Modification du multiplicateur d'expérience

Serveur 1.25

Cette modification de l'exécutable T4C Server permet de modifier les multiplicateur d'expérience utilisée par le falg XP GOD BOOST (Id 41) du serveur T4C.

Nous savons que par défaut les multiplicateurs d'expérience intervienne lors de deux @Trigger différent, le @OnHit et le @OnDeath, il suffit ensuite d'ouvrir l'exécutable dans un désassembleur de votre choix (j'utilise IDA Pro), une fois le désassemblage de l'exécutable terminer, il vous suffit de rechercher OnHit et OnDeath dans votre désassembleur.

Dans cette exemple de reverse, nous trouverons nombreuses fois la valeur OnHit, pour être certain de trouver les bonnes bonne valeurs nous vérifierons que nous trouvons bien la valeur 10 qui est par défaut la valeur de l'expérence boostée avec le flag XP GOD BOOST.

Ce qui nous donne pour le @OnHit :

.text:0044A37C loc_44A37C:                             ; CODE XREF: NPCstructure::OnHit(Unit *,Unit *,Unit *,void *,void *)+102�j
.text:0044A37C                 mov     ecx, esi
.text:0044A37E                 call    ?GetPlayer@Character@@QAEPAVPlayers@@XZ ; Character::GetPlayer(void)
.text:0044A383                 test    eax, eax
.text:0044A385                 mov     [ebp+arg_8], eax
.text:0044A388                 jz      short loc_44A3F7
.text:0044A38A                 mov     ecx, eax
.text:0044A38C                 call    ?GetGodFlags@Players@@QAE_KXZ ; Players::GetGodFlags(void)
.text:0044A391                 and     edx, 200h
.text:0044A397                 xor     eax, eax
.text:0044A399                 or      eax, edx
.text:0044A39B                 jz      short loc_44A3B3
.text:0044A39D                 mov     eax, dword ptr [ebp+var_14+4]
.text:0044A3A0                 push    0
.text:0044A3A2                 push    0Ah             ; Multiplicateur d'expérience @OnHit
.text:0044A3A4                 push    eax
.text:0044A3A5                 push    ebx
.text:0044A3A6                 call    __allmul
.text:0044A3AB                 mov     ebx, eax
.text:0044A3AD                 mov     dword ptr [ebp+var_14], ebx
.text:0044A3B0                 mov     dword ptr [ebp+var_14+4], edx
.text:0044A3B3

Et pour le @OnDeath :

.text:0044A774 loc_44A774:                             ; CODE XREF: NPCstructure::OnDeath(Unit *,Unit *,Unit *,void *,void *)+1DD�j
.text:0044A774                                         ; NPCstructure::OnDeath(Unit *,Unit *,Unit *,void *,void *)+1ED�j
.text:0044A774                 xor     eax, eax
.text:0044A776                 mov     ecx, esi
.text:0044A778                 mov     dword ptr [ebp+var_30], eax
.text:0044A77B                 mov     [ebp-2Ch], eax
.text:0044A77E                 call    ?GetType@Unit@@QAEDXZ ; Unit::GetType(void)
.text:0044A783                 cmp     al, 1
.text:0044A785                 jnz     short loc_44A7D0
.text:0044A787                 mov     ecx, esi
.text:0044A789                 call    ?GetPlayer@Character@@QAEPAVPlayers@@XZ ; Character::GetPlayer(void)
.text:0044A78E                 mov     ecx, eax
.text:0044A790                 call    ?GetGodFlags@Players@@QAE_KXZ ; Players::GetGodFlags(void)
.text:0044A795                 and     edx, 200h
.text:0044A79B                 xor     eax, eax
.text:0044A79D                 or      eax, edx
.text:0044A79F                 jz      short loc_44A7D0
.text:0044A7A1                 mov     eax, dword ptr [ebp+var_18+4]
.text:0044A7A4                 push    0
.text:0044A7A6                 push    0Ah             ; Multiplicateur d'expérience @OnDeath
.text:0044A7A8                 push    eax
.text:0044A7A9                 push    ebx
.text:0044A7AA                 call    __allmul
.text:0044A7AF                 mov     ecx, [ebp+var_20]
.text:0044A7B2                 add     ecx, eax
.text:0044A7B4                 mov     dword ptr [ebp+var_30], eax
.text:0044A7B7                 mov     eax, [ebp+var_1C]
.text:0044A7BA                 adc     eax, edx
.text:0044A7BC                 push    eax
.text:0044A7BD                 mov     [ebp-2Ch], edx
.text:0044A7C0                 mov     edx, [esi]
.text:0044A7C2                 mov     [ebp+var_20], ecx
.text:0044A7C5                 push    ecx
.text:0044A7C6                 mov     ecx, esi
.text:0044A7C8                 mov     [ebp+var_1C], eax
.text:0044A7CB                 call    dword ptr [edx+50h]
.text:0044A7CE                 jmp     short loc_44A7E3
.text:0044A7D0 ; ---------------------------------------------------------------------------

Exemple d'un patcheur pour cet exécutable en Delphi

(* -------------------------------------------------------------------------- *)
(* T4C XPCoef v1.25 By Mestoph *)
(* -------------------------------------------------------------------------- *)
unit Patcheur;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, WinSkinData, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    SkinData1: TSkinData;
    GroupBox1: TGroupBox;
    OpenDialog1: TOpenDialog;
    Edit_Fichier: TEdit;
    Btn_Ouvrir: TButton;
    Btn_Patch: TButton;
    Btn_Fermer: TButton;
    GroupBox3: TGroupBox;
    Lbl_Taille: TLabel;
    Bevel1: TBevel;
    Liste_Coef: TComboBox;
    Label1: TLabel;
    procedure Btn_OuvrirClick(Sender: TObject);
    procedure Btn_FermerClick(Sender: TObject);
    procedure Btn_PatchClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  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 Fichier : File;
Begin
  If OpenDialog1.Execute = True Then
    Begin
      Edit_Fichier.Text := OpenDialog1.FileName;
      AssignFile(Fichier, OpenDialog1.filename);
      Reset(Fichier,1);
      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 Liste_Coef.ItemIndex <> -1 Then
        Begin
          If (StrToInt(Copy(Lbl_Taille.Caption,1,Length(Lbl_Taille.Caption)-7)) >= 1200128) 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 le premier offset.
              Emplacement := Pointer(LongInt(Emplacement) + 304035);
              Pbyte(LongInt(Emplacement))^ := Liste_Coef.ItemIndex + 1;
              Emplacement := Donnees;
              Emplacement := Pointer(LongInt(Emplacement) + 305063);
              Pbyte(LongInt(Emplacement))^ := Liste_Coef.ItemIndex + 1;
              CloseFile(Fichier);
              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 !' + #13#13 + 'N''oubliez pas d''ajouter le flag 41 sur vos comptes joueurs !');
              Edit_Fichier.Text := '';
              Lbl_Taille.Caption := '0 Octets';
            End
          Else ShowMessage('Le fichier ne correspont pas au fichier T4C Server.Exe de base !');
        End
      Else ShowMessage('Vous devez choisir le multiplicateur d''expériences !');
    End
  Else ShowMessage('Vous devez ouvrir le fichier T4C Server.Exe !');
End;
(* -------------------------------------------------------------------------- *)
(* Liste des coef d'xp *)
(* -------------------------------------------------------------------------- *)
Procedure TForm1.FormCreate(Sender: TObject);
  Var I : Byte;
Begin
  For I := 1 To 255 Do Liste_Coef.Items.Add(IntToStr(I));
End;

End.

Serveur 1.50

Cette modification de l'exécutable T4C Server permet de modifier les multiplicateur d'expérience utilisée par le falg XP GOD BOOST (Id 41) du serveur T4C.

Nous savons que par défaut les multiplicateurs d'expérience intervienne lors de deux @Trigger différent, le @OnHit et le @OnDeath, il suffit ensuite d'ouvrir l'exécutable dans un désassembleur de votre choix (j'utilise IDA Pro), une fois le désassemblage de l'exécutable terminer, il vous suffit de rechercher OnHit et OnDeath dans votre désassembleur.

Dans cette exemple de reverse, nous trouverons nombreuses fois la valeur OnHit, pour être certain de trouver les bonnes bonne valeurs nous vérifierons que nous trouvons bien la valeur 10 qui est par défaut la valeur de l'expérence boostée avec le flag XP GOD BOOST.

Ce qui nous donne pour le @OnHit :

.text:004509F1 loc_4509F1:                             ; CODE XREF: NPCstructure::OnHit(Unit *,Unit *,Unit *,void *,void *)+E7�j
.text:004509F1                 mov     ecx, esi
.text:004509F3                 call    ?GetPlayer@Character@@QAEPAVPlayers@@XZ ; Character::GetPlayer(void)
.text:004509F8                 test    eax, eax
.text:004509FA                 mov     [ebp+arg_8], eax
.text:004509FD                 jz      short loc_450A62
.text:004509FF                 mov     ecx, eax
.text:00450A01                 call    ?GetGodFlags@Players@@QAE_KXZ ; Players::GetGodFlags(void)
.text:00450A06                 and     edx, 200h
.text:00450A0C                 xor     eax, eax
.text:00450A0E                 or      eax, edx
.text:00450A10                 jz      short loc_450A21
.text:00450A12                 push    0
.text:00450A14                 push    0Ah             ; Multiplicateur d'expérience @OnHit
.text:00450A16                 push    ebx
.text:00450A17                 push    edi
.text:00450A18                 call    __allmul
.text:00450A1D                 mov     edi, eax
.text:00450A1F                 mov     ebx, edx
.text:00450A21

Et pour le @OnDeath :

.text:00450CDD loc_450CDD:                             ; CODE XREF: NPCstructure::OnDeath(Unit *,Unit *,Unit *,void *,void *)+1AB�j
.text:00450CDD                                         ; NPCstructure::OnDeath(Unit *,Unit *,Unit *,void *,void *)+1BB�j ...
.text:00450CDD                 xor     eax, eax
.text:00450CDF                 mov     ecx, esi
.text:00450CE1                 mov     dword ptr [ebp+var_18], eax
.text:00450CE4                 mov     [ebp-14h], eax
.text:00450CE7                 call    ?GetType@Unit@@QAEDXZ ; Unit::GetType(void)
.text:00450CEC                 cmp     al, 1
.text:00450CEE                 jnz     short loc_450D36
.text:00450CF0                 mov     ecx, esi
.text:00450CF2                 call    ?GetPlayer@Character@@QAEPAVPlayers@@XZ ; Character::GetPlayer(void)
.text:00450CF7                 mov     ecx, eax
.text:00450CF9                 call    ?GetGodFlags@Players@@QAE_KXZ ; Players::GetGodFlags(void)
.text:00450CFE                 and     edx, 200h
.text:00450D04                 xor     eax, eax
.text:00450D06                 or      eax, edx
.text:00450D08                 jz      short loc_450D36
.text:00450D0A                 push    0
.text:00450D0C                 push    0Ah             ; Multiplicateur d'expérience @OnDeath
.text:00450D0E                 push    ebx
.text:00450D0F                 push    edi
.text:00450D10                 call    __allmul
.text:00450D15                 mov     ecx, [ebp+var_20]
.text:00450D18                 add     ecx, eax
.text:00450D1A                 mov     dword ptr [ebp+var_18], eax
.text:00450D1D                 mov     eax, [ebp+var_1C]
.text:00450D20                 adc     eax, edx
.text:00450D22                 push    eax
.text:00450D23                 mov     [ebp-14h], edx
.text:00450D26                 mov     edx, [esi]
.text:00450D28                 mov     [ebp+var_20], ecx
.text:00450D2B                 push    ecx
.text:00450D2C                 mov     ecx, esi
.text:00450D2E                 mov     [ebp+var_1C], eax
.text:00450D31                 call    dword ptr [edx+50h]
.text:00450D34                 jmp     short loc_450D46
.text:00450D36 ; ---------------------------------------------------------------------------

Exemple d'un patcheur pour cet exécutable en Delphi

(* -------------------------------------------------------------------------- *)
(* T4C XpCoef v1.50 By Mestoph *)
(* -------------------------------------------------------------------------- *)
unit Patcheur;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, WinSkinData, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    Edit1: TEdit;
    Edit2: TEdit;
    Label2: TLabel;
    Label3: TLabel;
    GroupBox2: TGroupBox;
    Btn_Ouvrir: TButton;
    Edit_Fichier: TEdit;
    GroupBox3: TGroupBox;
    Lbl_Taille: TLabel;
    Bevel1: TBevel;
    GroupBox4: TGroupBox;
    Liste_Coef: TComboBox;
    Label1: TLabel;
    Btn_Patch: TButton;
    Btn_Fermer: TButton;
    OpenDialog1: TOpenDialog;
    SkinData1: TSkinData;
    procedure Btn_OuvrirClick(Sender: TObject);
    procedure Btn_FermerClick(Sender: TObject);
    procedure Btn_PatchClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure EditKeyPress(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);
      Lbl_Taille.Caption := IntToStr(Filesize(Fichier)) + ' Octets';
      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 le premier offset.
      Emplacement := Pointer(LongInt(Emplacement) + 330261);
      Edit1.text := inttostr(Pbyte(LongInt(Emplacement))^);
      Emplacement := Donnees;
      Emplacement := Pointer(LongInt(Emplacement) + 331021);
      Edit2.text := inttostr(Pbyte(LongInt(Emplacement))^);
      // On libère la mémoire
      FreeMem(Donnees);
      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 Liste_Coef.ItemIndex <> -1 Then
        Begin
          If (StrToInt(Copy(Lbl_Taille.Caption,1,Length(Lbl_Taille.Caption)-7)) >= 1200128) 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 le premier offset.
              Emplacement := Pointer(LongInt(Emplacement) + 330261);
              Pbyte(LongInt(Emplacement))^ := Liste_Coef.ItemIndex + 1;
              Emplacement := Donnees;
              Emplacement := Pointer(LongInt(Emplacement) + 331021);
              Pbyte(LongInt(Emplacement))^ := Liste_Coef.ItemIndex + 1;
              CloseFile(Fichier);
              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 !' + #13#13 + 'N''oubliez pas d''ajouter le flag 41 sur vos comptes joueurs !');
              Edit_Fichier.Text := '';
              Lbl_Taille.Caption := '0 Octets';
            End
          Else ShowMessage('Le fichier ne correspont pas au fichier T4C Server.Exe de base !');
        End
      Else ShowMessage('Vous devez choisir le multiplicateur d''expériences !');
    End
  Else ShowMessage('Vous devez ouvrir le fichier T4C Server.Exe !');
End;
(* -------------------------------------------------------------------------- *)
(* Liste des coef d'xp *)
(* -------------------------------------------------------------------------- *)
Procedure TForm1.FormCreate(Sender: TObject);
  Var I : Byte;
Begin
  For I := 1 To 255 Do Liste_Coef.Items.Add(IntToStr(I));
End;

procedure TForm1.EditKeyPress(Sender: TObject; var Key: Char);
begin
  Key := #0;
end;

End.

--Mestoph 17 mars 2008 à 05:03 (MSK)

Modification de la position d'arrivée en jeu à la création d'un personnage

NOTE: Cette position ne doit pas être confondue avec la position de sanctuaire par défaut du personnage, qui se modifie dans la DLL GameOps.

Serveur 1.25

Tout d'abord, nous savons que la position d'arrivée en jeu d'un nouveau personnage est, jusqu'à ce qu'il revienne à son sanctuaire, la valeur que l'on retrouve dans la base de données lorsque ce personnage déconnecte aussitôt après la création. Cette position est : 2944,1059,0.

Il suffit donc d'ouvrir l'exécutable dans le désassembleur de votre choix (comme par exemple IDA Pro) et, une fois le désassemblage de l'exécutable terminé, de rechercher la valeur 2944 en hexadécimal (0x0B80) dans votre désassembleur.

A un certain moment nous tombons sur :

.rdata:004DE63C                 align 10h
.rdata:004DE640 dword_4DE640    dd 0B80h
.rdata:004DE644 dword_4DE644    dd 423h
.rdata:004DE648 dword_4DE648    dd 0

Ici, nous ne sommes pas dans la section .text, donc pas dans le programme proprement dit, mais dans la section .rdata, où sont stockées les constantes d'initialisation. Nous voyons trois valeurs successives, qui sont :

  • 0x0b80 (2944 décimal)
  • 0x0423 (1059 décimal)
  • 0x0000 (0 décimal)

Il s'agit des coordonnées que nous recherchons. Ce sont donc ces valeurs qu'il faut changer.

L'adresse de ces valeurs est l'adresse virtuelle (Virtual Address ou VA) correspondante à ces lignes dans la section de l'exécutable nommée ".rdata". Si vous utilisez le désassembleur IDA, l'offset correspondant dans le fichier est indiqué juste à côté en bas à gauche de votre écran. Sinon, il faut le calculer. Corrigez-moi si je me trompe : (--Sorkvild 17 mars 2008 à 17:06 (MSK))

FileOffset = VirtualAddress - VirtualOffset + RawOffset
  • VirtualAddress est nos trois addresses VA, respectivement 0x004DE640, 0x004DE644 et 0x004DE648 de la section .rdata.
  • VirtualOffset est l'adresse où démarre notre section .rdata. 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 .rdata commence à 0x004DE000.
  • 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 0x000DE000.

Ce qui nous donne :

FileOffsetX = 0x004DE640 - 0x004DE000 + 0x000DE000 = 0x000DE640 = 910912
FileOffsetY = 0x004DE644 - 0x004DE000 + 0x000DE000 = 0x000DE644 = 910916
FileOffsetW = 0x004DE648 - 0x004DE000 + 0x000DE000 = 0x000DE648 = 910920

On récupère donc les offsets dans le fichier :

  • 910912 pour la coordonnée x.
  • 910916 pour la coordonnée y,
  • 910920 pour la coordonnée w,

Vous trouverez davantage d'informations sur le format PE sur cette page.

Une fois les offsets trouvés, il vous suffira de développer un petit logiciel de patch ou bien de modifier à l'aide d'un éditeur hexadécimal les 3 valeurs des coordonnées de la position d'arrivée en jeu des nouveaux personnages.

Exemple d'un patcher pour cet exécutable en C

#include <stdio.h>

int main (int argc, char **argv)
{
   // simplest patcher ever for the T4C Server.exe file to set new players' start coordinates

   FILE *fp;
   unsigned long start_x;
   unsigned long start_y;
   unsigned long start_w;

   // verify usage
   if (argc != 4)
   {
      printf ("Usage: %s start_x start_y start_w\n", argv[0]);
      return (1); // on incorrect usage, print an error message and quit
   }

   // collect arguments
   start_x = atoi (argv[1]);
   start_y = atoi (argv[2]);
   start_w = atoi (argv[3]);

   // open T4C Server.exe file for binary update
   fp = fopen ("T4C Server.exe", "r+");
   if (fp == NULL)
   {
      printf ("Error: Unable to open T4C Server.exe for binary update\n");
      return (1); // if we can't open file, print an error message and quit
   }

   fseek (fp, 910912, SEEK_SET); // seek at X start position
   fwrite (start_x, 1, sizeof (unsigned long), fp); // write new X start position
   fseek (fp, 910916, SEEK_SET); // seek at Y start position
   fwrite (start_y, 1, sizeof (unsigned long), fp); // write new Y start position
   fseek (fp, 910920, SEEK_SET); // seek at W start position
   fwrite (start_w, 1, sizeof (unsigned long), fp); // write new W start position

   fclose (fp); // finished, close file and print a success message
   printf ("New players' start position has been set to %d,%d,%d\n", start_x, start_y, start_w);
   return (0); // and return a success error code
}

Suppression de l'algorithme d'encryption du fichier ELNG

Cette modification permet au serveur T4C de lire les textes des PNJ dans un fichier ELNG au format texte (non crypté), rendant ainsi le travail des techniciens et animateurs du serveur beaucoup plus commode en cas de modification à apporter à ceux-ci.

Serveur 1.25

Nous savons que la longueur du tableau d'encryption des fichiers ELNG fait 7823 octets. Il suffit donc d'ouvrir l'exécutable dans le désassembleur de votre choix (comme par exemple IDA), et une fois le désassemblage de l'exécutable terminé, de rechercher la valeur 7823 en hexadécimal (0x1E8F) dans votre désassembleur.

Nous la trouvons deux fois, en deux endroits peu éloignés. C'est le signe que nous avons tapé dans le mille.

Examinons successivement ces deux occurences :

.text:0043CB12 loc_43CB12:                             ; CODE XREF: sub_43CAF0+40�j
.text:0043CB12                 push    0FFh
.text:0043CB17                 push    0
.text:0043CB19                 mov     ecx, offset unk_515E34
.text:0043CB1E                 call    ??RRandom@@QAEHHH@Z ; Random::operator()(int,int)
.text:0043CB23                 mov     byte_512350[esi], al
.text:0043CB29                 inc     esi
.text:0043CB2A                 cmp     esi, 1E8Fh
.text:0043CB30                 jl      short loc_43CB12
.text:0043CB32                 pop     esi
.text:0043CB33                 retn
.text:0043CB33 sub_43CAF0      endp

Ici, on aligne 0 et 0xFF sur la pile, et on appelle une fonction de random. Le fait de pousser des valeurs sur la pile signifie qu'on passe des arguments à cette fonction. On appelle donc une fonction Random (0, 255) laquelle, c'est assez explicite, va nous retourner un nombre entre 0 et 255. Mais ce n'est pas tout :

Une fois que la fonction a retourné ce nombre, on l'enregistre dans un tableau à l'index donné par le registre esi. Registre que l'on incrémente aussitôt, et en le comparant avec 1E8F en hexadécimal, soit avec 7823 en décimal, si esi est moindre on recommence l'opération (jl loc_43CB12) en appelant à nouveau la fonction Random.

Ainsi, on voit que cette fonction remplit un tableau de 7823 octets avec des valeurs aléatoires. C'est, très précisément, la clé de cryptage que nous avons décrite ici.

Etant donné que nous savons que ce cryptage s'effectue à l'aide d'un Ou exclusif (XOR), une technique très simple pour s'en débarrasser pourrait être de modifier ce bout de code de façon à ne remplir la clé de cryptage qu'avec des zéros. Car le XOR d'une valeur avec zéro ne change pas cette valeur. Mais il y a plus propre : il suffit de supprimer ce XOR, tout bêtement.

Examinons la suite :

.text:0043CB70 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
.text:0043CB70
.text:0043CB70
.text:0043CB70 sub_43CB70      proc near               ; CODE XREF: IntlText::ToChar(IntlText::EncParse &,char,char const *,ushort &)+1E�p
.text:0043CB70                                         ; IntlText::ToChar(IntlText::EncParse &,char,char const *,ushort &)+54�p ...
.text:0043CB70                 push    ebx
.text:0043CB71                 push    esi
.text:0043CB72                 mov     esi, ecx
.text:0043CB74                 mov     eax, [esi+8]
.text:0043CB77                 push    eax             ; FILE *
.text:0043CB78                 call    ds:fgetc
.text:0043CB7E                 mov     ebx, eax
.text:0043CB80                 add     esp, 4
.text:0043CB83                 cmp     ebx, 0FFFFFFFFh
.text:0043CB86                 jnz     short loc_43CB8D
.text:0043CB88                 pop     esi
.text:0043CB89                 or      eax, eax
.text:0043CB8B                 pop     ebx
.text:0043CB8C                 retn
.text:0043CB8D ; ---------------------------------------------------------------------------
.text:0043CB8D
.text:0043CB8D loc_43CB8D:                             ; CODE XREF: sub_43CB70+16�j
.text:0043CB8D                 mov     ecx, [esi+8]
.text:0043CB90                 push    ecx             ; FILE *
.text:0043CB91                 call    ds:ftell
.text:0043CB97                 cdq
.text:0043CB98                 mov     ecx, 1E8Fh
.text:0043CB9D                 idiv    ecx
.text:0043CB9F                 add     esp, 4
.text:0043CBA2                 pop     esi
.text:0043CBA3                 movsx   eax, byte_512350[edx]
.text:0043CBAA                 movsx   edx, bl
.text:0043CBAD                 xor     eax, edx
.text:0043CBAF                 pop     ebx
.text:0043CBB0                 retn
.text:0043CBB0 sub_43CB70      endp
.text:0043CBB0
.text:0043CBB0 ; ---------------------------------------------------------------------------
.text:0043CBB1                 align 10h
.text:0043CBC0

Ici, nous avons carrément une fonction complète. Que fait-elle, à première vue ?

  • Elle appelle la fonction fgetc() pour lire un caractère dans un fichier.
  • Elle fait une comparaison de ce caractère avec 0xFFFFFFFF (n'oublions pas que cette valeur équivaut à -1 en décimal signé), et si c'est différent elle continue plus loin, sinon, elle retourne à l'appelant. Coïncidence ? La valeur EOF, selon les headers de Microsoft, vaut elle aussi -1.
  • En continuant plus loin, la fonction appelle ftell() pour savoir à quelle position elle est rendue dans le fichier.
  • Elle divise ensuite cette valeur par notre 1E8F en hexadécimal, soit 7823 en décimal.
  • Elle va alors chercher une valeur dans un tableau d'octets à l'index correspondant au reste de cette division (byte_512350[edx]), et met cette valeur dans eax.
  • Et pour finir, elle fait un XOR (Ou exclusif) avec cette valeur.

De toute évidence, il s'agit de notre XOR d'encryption. Tout colle : la clé de 7823 octets que l'on masque octet par octet jusqu'à ce que le total soit lu.

L'opération d'annihilation de cet algorithme consistera donc à faire sauter ce XOR. Avec quoi pouvons-nous le patcher ?

.text:0043CBAD                 xor     eax, edx

Cette instruction assembleur fait un Ou exclusif entre eax et edx et stocke le résultat dans eax. Pour s'en débarrasser, il faut donc juste transférer le contenu de edx dans eax.

Elle tient, en hexadécimal dans le fichier, sur deux octets : 0x33 0xC2. 33 hexadécimal pour xor eax, et C2 hexadécimal pour edx. Il faut donc remplacer le premier octet (xor eax) par une instruction où on prend un registre pour le mettre dans un autre. Une telle instruction existe en langage assembleur, il s'agit de l'instruction mov, qui se note 8B en hexadécimal.

L'adresse à modifier est l'adresse virtuelle (Virtual Address ou VA) correspondante à ces lignes dans le listing du désassembleur. Si vous utilisez le désassembleur IDA, l'offset correspondant dans le fichier est indiqué juste à côté en bas à gauche de votre écran. Sinon, il faut le calculer. Nous commençons à avoir l'habitude :

FileOffset = VirtualAddress - VirtualOffset + RawOffset

VirtualAddress est notre adresse VA, soit 0x0043CBAD dans 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 à 0x00401000. RawOffset est un décalage arbitraire dans le fichier binaire à partir duquel les sections commencent. Le désassembleur nous renseigne encore : dans notre exécutable, il vaut 0x00001000. Ce qui nous donne :

FileOffset = 0x0043CBAD - 0x00401000 + 0x00001000 = 0x0003CBAD = 248749

On récupère donc l'offset dans le fichier où coller notre instruction mov.

Note: vous trouverez davantage d'informations sur le format PE sur cette page.

Une fois l'offset trouvé, il vous suffira de développer un petit logiciel de patch comme ceci :

Exemple d'un patcher pour cet exécutable en C

#include <stdio.h>

int main (void)
{
   // simplest patcher ever for the T4C Server.exe file to wipe the ELNG encryption

   FILE *fp;

   // open T4C Server.exe file for binary update
   fp = fopen ("T4C Server.exe", "r+");
   if (fp == NULL)
   {
      printf ("Error: Unable to open T4C Server.exe for binary update\n");
      return (1); // if we can't open file, print an error message and quit
   }

   fseek (fp, 248749, SEEK_SET); // seek at the wanted offset
   fputc (0x8B, fp); // write a 'mov' instead of a 'xor'
   fclose (fp); // finished, close file and print a success message

   printf ("File patched successfully.\n");
   return (0); // and return a success error code
}

...ou bien de modifier à l'aide d'un éditeur hexadécimal l'octet à cette position en le remplaçant par 0x8B et votre serveur pourra lire des fichiers ELNG au format texte, non cryptés. Elle est pas belle, la vie ?


--Sorkvild 17 mars 2008 à 21:10 (MSK)