Reverse-engineering du fichier T4C Server.exe
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)