Introduction:
On va reverse un jeu, faire nos triches, et coder un trainer.
Dead Space est un jeu vidéo de tir à la troisième personne dans le genre survival horror dans un univers de science-fiction.
C'est développé par EA game et le jeu est sorti en 2008.
Personnellement, j'ai apprécié y jouer, et c'est aussi avec ce jeu que j'ai découvert Sigur Rós, notamment avec l'utilisation de Dauðalagið dans le trailer du jeu.
Les outils:
OllyDbg v2.01 (http://Ollydbg.de/)
Cheat Engine (https://www.cheatengine.org/downloads.php)
Dead Space v1.0.0.222
I.a: Trouver un pointeur pour les munitions:
Lancez Dead Space, puis allez dans votre partie.
En parallèle, lancez Cheat Engine, puis sélectionnez le processus de Dead Space et cliquez sur Open.
Une fois le processus sélectionné, on va lancer un premier scan avec notre valeur actuelle pour les munitions qu'on a pour l'arme. Pour moi c'est '10'
On retourne dans le jeu pour tirer une balle, puis on retourne dans Cheat Engine pour insérr notre nouvelle valeur (9, pour moi) et on clique sur Next scan. Après le scan, la liste des valeurs a diminué, mais ce n’est pas encore assez dégrossi, on va donc recommencer.
Maintenant il ne reste plus qu'une seule adresse !
On double clique dessus pour envoyer notre valeur dans le volet de la table de triche.
On double clique sur notre valeur (8) dans le volet de la table de triche.
Une fenêtre d'édition s'ouvre, on remplace notre valeur par '1337' pour voir si ça change quelque chose dans le jeu.
On retourne dans le jeu et effectivement, on peut voir que notre manipulation de la valeur a fonctionné
Maintenant qu'on a la bonne adresse, il faut trouver d'où est ce qu'elle est lu/qui l'écrit.
Clic droit sur notre adresse et on clique sur "Find out what accesses this address"
On pourrait aussi faire "find out what writes to this address" mais on va déjà voir si ça donne quelque chose comme ça.
Une fenêtre nous prévient que l'on va attacher le débogueur au processus.
On clique sur Yes.
Ce qu'il y a de bien avec le débogueur de Cheat Engine c'est qu'on peut le détacher sans devoir terminer la cible avec comme dans Olly.
La fenêtre des opcodes apparaît.
On constate que 004F9101 semble y accéder avec insistance.
004F9101 - 8B 88 84060000 - mov ecx,[eax+00000684]
Si on retourne dans le jeu et que l'on passe en mode visée pour mettre en joue, d'autres adresses apparaissent:
004F88A3 - 8B 86 84060000 - mov eax,[esi+00000684]
004F8E9A - 8B 87 84060000 - mov eax,[edi+00000684]
011BA650 - 8B 86 84060000 - mov eax,[esi+00000684]
004F8EF9 - 83 BF 84060000 00 - cmp dword ptr [edi+00000684],00
004F8F29 - 8B 87 84060000 - mov eax,[edi+00000684]
004F88A3 semble y accéder pendant toute la durée du maintien de notre clique droit dans le jeu.
Le reste est accédé une seule fois pendant l'action.
Si on met le jeu en pause, toutes les adresses arrêtent de lire le pointeur.
004F9101 semble digne d'intérêt pour nous vu que le code passe tout le temps dessus.
On pourrait réécrire notre pointeur à cet endroit, comme ça, même si on tire des munitions, cette boucle de lecture va remettre notre valeur dans le compteur.
Ok, maintenant qu'on a les adresses là on click sur le bouton Stop, puis on reclique sur le même bouton qui et devenu 'Close'
I.b: Faire un codecave pour les munitions
On parle souvent de codecave dans le game-hacking, car contrairement au cracking où généralement les remplacements sont minimes en taille et consistent juste à changer un JUMP ou un registre.
Dans le game-hacking on a souvent besoin de rajouter du code "en plus", et on n’a pas la place sans écraser les instructions dans dessous.
Du coup, on redirige ailleurs, où il y a de la place, et on revient de la procédure à la fin.
Enfin, vous aller voir.
Fermez Cheat Engine et ouvrez Ollydbg, puis attachez-le au process de Dead Space.
Olly met en pause le processus une fois celui-ci attaché.
Allez dans la fenêtre des modules de l'exécutable (ALT+E), sélectionnez Dead Space.exe, click droit et cliquez sur follow entry.
On arrive maintenant sur la plage d'offset 011F, on va chercher un endroit en prévision pour poser notre code.
Donc la c'est à vous de voir, soit vous scrollez directement en bas avec le désassembleur pour voir si y'a de la place.
Soit vous pouvez aussi chercher des "zéros" en faisant un clic droit>search for binary string (CTRL+B)
Pour ma part, je vais établir un codecave sur cette zone:
Il y a de la place, et cette zone mémoire ne semble pas être protégée/réécrite par dessus.
On note l'offset pour s'en rappeler plus tard: 011F631D
Revenons à nos munitions, on a trouvé avec Cheat Engine l'adresse 004F9101, allons-y faire un tour avec Olly. (CTRL+G)
MOV ECX,DWORD PTR DS:[EAX+684]
On a un pointeur (EAX+684) son contenue est placé dans ECX.
Si on pose un breakpoint dessus et qu'on lance, on rebreak directement sur notre instruction, on presse F8 un coup et on remarque que ECX contient le nombre de munitions.
On a juste à remplacer l'adresse du pointeur par une valeur fixe, mais on n’a pas la place en terme de taille pour faire ça.
C'est pour ça qu'on a chercher un peu plus tôt 'une zone libre' on remplace l'instruction MOV ECX,DWORD PTR DS:[EAX+684] par un JMP sur notre zone.
Juste la place pour! ça n'aurait pas été un problème si on avait tapé dans la ligne du CMP (on l'aurait juste déplacé dans notre cave).
Maintenant pour le code cave c'est simple:
MOV DWORD PTR DS:[EAX+684],539
On place 539 (1337) dans le pointeur.
MOV ECX,DWORD PTR DS:[EAX+684]
C'est l'instruction originale en 004F9101, on place le contenu du pointeur dans ECX.
JMP 004F9107
On resaute pour revenir en 004F9107 (sur CMP ECX,DWORD PTR DS:[EAX+68C])
Une fois ça fait on et bon, y'a plus qu'a enlever notre breakpoint, appuyez sur F9 pour relancez l'exécution et testez notre modification.
II.a: Trouver le pointeur pour la vie
On ne connait pas notre valeur de santé, rien ne nous l'indique dans le jeu.
On a juste des jauges posées dans la partie dorsale de notre armure.
De manière générale dans les jeux, la valeur de la santé a tendance à être en float, on va partir du principe qu'on a la vie pleine donc à 100, on lance un premier scan avec 100 en valeur.
Ensuite on va prendre des dégâts dans le jeu, et on relance un scan avec 'Decreased value'
Il y en a de trop, on recommence, plus que 20, on continue.
Plus que 11 résultats.
Vu que les valeurs qui restent semblent toutes descendre, on va essayer de monter.
Hop, on va se soigner dans le jeu avec un medikit puis on rescanne, mais avec Increased value.
Plus que 5:
Bon, à ce stade, soit vous continuez, où soit vous commencez à regarder manuellement la valeur des adresses.
Pour ma par je l'ai refait un coup avec Decreased Value, puis j'ai regardé ce que j'avais.
Une valeur avec "55"
Une autre avec -2
Une autre à zéro.
La plus probable étant celle avec la valeur de 55, je l'ai juste remis a 100 et regardé ce qu'il se passait dans le jeu.
Ma jauge de santé et revenue à 100%, c'est bien la bonne valeur.
Si on n’avait rien eu: peut-être que notre valeur initiale n'était pas à 100.
Dans ce cas on aurait refait le premier scanne, mais avec 'Unknown initial value' puis reappliqué la stratégie Decreased/Increased value pour les scannes suivants.
Il faut trouver qui lit/écrit dans cette adresse maintenant.
On fait pareil que tout à l'heure pour les munitions et on attache le débogueur de Cheat Engine pour savoir qui accède au pointeur.
On a:
00526FE9 - 0F2F 80 20010000 - comiss xmm0,[eax+00000120]
Qui semble lire constamment.
Et les autres, qui lisent (ou pas) selon vos actions:
00525971 - 0F2F 86 20010000 - comiss xmm0,[esi+00000120]
00527026 - D9 80 20010000 - fld dword ptr [eax+00000120]
0045D393 - 0F2F 80 20010000 - comiss xmm0,[eax+00000120]
II.b: Faire le codecave pour la vie
On n'a plu qu'a mettre 1337 (c'est un float donc en hexa: 44A72000) dans le pointeur et voir ce que ça donne.
On a déjà un emplacement pour la cave, autant continuer derrière notre dernière modification de code.
Aller hop, on reattache Olly, on va sur 00526FE9 avec CTRL+G, et on remplace l'instruction qui lit le plus pour l'envoyer sur notre cave à la place.
Assurez-vous que le jeu soit en pause ou que le processus soit en pause avant de faire ça, sinon il va partir directement sur votre jump et crasher dans le champ des 000000
Ensuite un peu de code:
MOV DWORD PTR DS:[EAX+120],44A72000
COMISS XMM0,DWORD PTR DS:[EAX+120]
JMP 00526FF0
Il n'y a plus qu'à tester:
III: Faire un trainer
On aurait pu utiliser Cheat Engine qui a une fonctionnalité pour générer un trainer, mais c'est toujours mieux d'avoir du code pour comprendre comment ça marche.
Du coup voilà une solution bricolée en assembleur.
Celui-là possède un peu plus d'options que simplement munitions illimitées et vies illimitées, mais les offsets sont tout aussi simples à trouver que ce qu'on a fait.
Pour le code il y a de la gestion de touches de raccourcie, et on écrit le processus en regardant le titre de la fenêtre et ça classe. (histoire de ne pas écrire dans l’explorateur Windows bêtement si le titre de la fenêtre contient 'Dead Space')
trainer.asm:
.486 .model flat, stdcall option casemap :none ; case sensitive include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc include \masm32\include\comctl32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\comctl32.lib DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD TrainerEngine PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD .const IDD_DIALOG equ 1000 IDB_HEALTH equ 1001 IDB_AMMO equ 1002 IDB_KILL equ 1003 IDB_MONEY equ 1004 IDB_NODES equ 1005 IDB_SHIPLIFE equ 1006 IDB_VELOCITY equ 1007 IDC_STATIC1005 equ 1008 IDC_STATIC1006 equ 1009 IDC_STATIC1007 equ 1010 IDC_STATIC1008 equ 1011 IDC_STATIC1009 equ 1012 IDC_STATIC1010 equ 1013 IDC_STATIC1011 equ 1014 IDC_STATIC1012 equ 1015 .data ;Dialog details szTitle db "Dead Space v1.0.0.222 Trainer +7",0 szIDBCheatON db "[ENABLE]",0 szIDBCheatOFF db "[DISABLE]",0 szErrorCaption db "ERROR", 0 szErrorMessage db "Game is not Running",0ah db "You need to run the game",0ah db "So You can Use the trainer",0 szHotkeyHelp db "Hotkeys", 0 szHotkeyF12 db "F12 - Unlimited health and stasis", 0 szHotkeyF11 db "F11 - Unlimited Ammo", 0 szHotkeyF10 db "F10 - One Hit Kill", 0 szHotkeyF9 db "F9 - Unlimited credits", 0 szHotkeyF8 db "F8 - Unlimited nodes", 0 szHotkeyF7 db "F7 - Unlimited Ship health", 0 szHotkeyF6 db "F6 - Super speed", 0 ;Game patching details ;Tested with Dead.Space-RELOADED GameCaption db "Dead Space",0 ; The window name of game GameClass db "DeadSpaceWndClass",0 ;game class Offset1 dd 4F9101h ; Unlimited ammunition bytes2write1 db 0E9h,017h,0D2h,0CFh,000h,090h ; JMP 011F631D bytes2Restore1 db 08Bh,088h,084h,006h,000h,000h ; MOV ECX,DWORD PTR DS:[EAX+684] Offset2 dd 011F631Dh ; Unlimited ammunition (codecave) bytes2write2 db 0C7h,080h,084h,006h,000h,000h,039h,005h,000h,000h, ;MOV DWORD PTR DS:[EAX+684],539 08Bh,088h,084h,006h,000h,000h, ;MOV ECX,DWORD PTR DS:[EAX+684] 0E9h,0D5h,02Dh,030h,0FFh ;JMP 004F9107 Offset3 dd 526FE9h ; Unlimited health bytes2write3 db 0E9h,044h,0F3h,0CCh,000h,090h,090h ; JMP 011F6332 bytes2Restore3 db 00Fh,02Fh,080h,020h,001h,000h,000h ; COMISS XMM0,DWORD PTR DS:[EAX+120] Offset4 dd 011F6332h ; Unlimited health (codecave) bytes2write4 db 0C7h,080h,020h,001h,000h,000h,000h,020h,0A7h,044h, ;MOV DWORD PTR DS:[EAX+120],44A72000 00Fh,02Fh,080h,020h,001h,000h,000h, ;COMISS XMM0,DWORD PTR DS:[EAX+120] 0E9h,0A8h,00Ch,033h,0FFh ;JMP 00526FF0 Offset5 dd 005808E7h ; Unlimited credits bytes2write5 db 0E9h,05Ch,05Ah,0C7h,000h,090h ; JMP 011F6348 bytes2Restore5 db 08Bh,082h,0E4h,00Ch,000h,000h ; MOV EAX,DWORD PTR DS:[EDX+CE4] Offset6 dd 011F6348h ; Unlimited credits (codecave) bytes2write6 db 0C7h,082h,0E4h,00Ch,000h,000h,0F9h,0C7h,004h,000h, ;MOV DWORD PTR DS:[EDX+CE4],4C7F9 08Bh,082h,0E4h,00Ch,000h,000h, ;MOV EAX,DWORD PTR DS:[EDX+CE4] 0E9h,090h,0A5h,038h,0FFh ;JMP 005808ED Offset7 dd 00521610h ; Unlimited nodes bytes2write7 db 0E9h,048h,04Dh,0CDh,000h,090h ; JMP 011F635D bytes2Restore7 db 08Bh,081h,094h,005h,000h,000h ; MOV EAX,DWORD PTR DS:[ECX+594] Offset8 dd 011F635Dh ; Unlimited nodes (codecave) bytes2write8 db 0C7h,081h,094h,005h,000h,000h,0F9h,0C7h,004h,000h, ;MOV DWORD PTR DS:[ECX+594],04C7F9 08Bh,081h,094h,005h,000h,000h, ;MOV EAX,DWORD PTR DS:[ECX+594] 0E9h,0A4h,0B2h,032h,0FFh ;JMP 00521616 ; Used only in Chapter 4 with the Asteroid Defense System cannon shooting/minigame ; No codecave here, just a dumb patch. Offset9 dd 005BE9ACh ; Unlimited Ship health Offset10 dd 005BE9F6h ; Unlimited Ship health (offset2) bytes2write0910 db 090h,090h,090h,090h,090h,090h,090h,090h ; NOPS bytes2Restore0910 db 0F3h,00Fh,011h,083h,094h,007h,000h,000h ; MOVSS DWORD PTR DS:[EBX+794],XMM0 Offset11 dd 004518DEh ; one hit kill bytes2write11 db 0E9h,07Ah,04Ah,0DAh,000h,090h,090h,090h ; JMP 011F635D bytes2Restore11 db 0F3h,00Fh,010h,087h,020h,001h,000h,000h ; MOVSS XMM0,DWORD PTR DS:[EDI+120] Offset12 dd 011F6389h ; one hit kill (codecave) bytes2write12 db 0C7h,087h,020h,001h,000h,000h,000h,000h,000h,000h, ;MOV DWORD PTR DS:[EDI+120],0 0F3h,00Fh,010h,087h,020h,001h,000h,000h, ;MOVSS XMM0,DWORD PTR DS:[EDI+120] 0E9h,072h,0B5h,025h,0FFh ;JMP 004518E6 Offset13 dd 005471C4h ; Unlimited Stasis bytes2write13 db 0E9h,0A9h,0F1h,0CAh,000h,090h,090h,090h ; JMP 011F6372 bytes2Restore13 db 0F3h,00Fh,010h,089h,028h,001h,000h,000h ; MOVSS XMM1,DWORD PTR DS:[ECX+128] Offset14 dd 011F6372h ; Unlimited Stasis (codecave) bytes2write14 db 0C7h,081h,028h,001h,000h,000h,000h,000h,0C8h,042h, ;MOV DWORD PTR DS:[ECX+128],42C80000 0F3h,00Fh,010h,089h,028h,001h,000h,000h, ;MOVSS XMM1,DWORD PTR DS:[ECX+128] 0E9h,043h,00Eh,035h,0FFh ;JMP 005471CC Offset15 dd 004644A0h ; Super speed bytes2write15 db 0E9h,0FBh,01Eh,0D9h,000h,090h ; JMP 011F63A0 Offset16 dd 011F63A0h ; Super speed (codecave) bytes2write16 db 0C7h,081h,080h,000h,000h,000h,000h,000h,000h,040h, ;MOV DWORD PTR DS:[ECX+80],50000000 0D9h,081h,080h,000h,000h,000h, ;FLD DWORD PTR DS:[ECX+80] 0E9h,0F1h,0E0h,026h,0FFh ;JMP 004644A6 ;Normal speed is bytes2write17 bytes2write17 db 0C7h,081h,080h,000h,000h,000h,000h,000h,080h,03Fh, ;MOV DWORD PTR DS:[ECX+80],3F800000 0D9h,081h,080h,000h,000h,000h, ;FLD DWORD PTR DS:[ECX+80] 0E9h,0F1h,0E0h,026h,0FFh ;JMP 004644A6 .data? hInstance dd ? windhand dd ? ; Window handle hwnddlg dd ? ; Window handle phandle dd ? ; Process handle of game pid dd ? ; Process id of game status1 dd ? ; on/off status2 dd ? ; on/off status3 dd ? ; on/off status4 dd ? ; on/off status5 dd ? ; on/off status6 dd ? ; on/off status7 dd ? ; on/off .code start: invoke GetModuleHandle, NULL invoke DialogBoxParam, hInstance, IDD_DIALOG, 0, ADDR DlgProc, 0 invoke ExitProcess, eax ; ----------------------------------------------------------------------- DlgProc proc hWin :DWORD, uMsg :DWORD, wParam :DWORD, lParam :DWORD .if uMsg == WM_INITDIALOG pushad ; Saving registry is needed... the program will crash if you omit this ; Set the dialog controls texts. Done here in the code instead of resource ; file to reduce the required bytes (strings in the rc file are UNICODE not ANSI) invoke SetWindowText,hWin,ADDR szTitle ; Set the window title text invoke SetDlgItemText,hWin,IDB_HEALTH,ADDR szIDBCheatON invoke SetDlgItemText,hWin,IDB_AMMO,ADDR szIDBCheatON invoke SetDlgItemText,hWin,IDB_KILL,ADDR szIDBCheatON invoke SetDlgItemText,hWin,IDB_MONEY,ADDR szIDBCheatON invoke SetDlgItemText,hWin,IDB_NODES,ADDR szIDBCheatON invoke SetDlgItemText,hWin,IDB_SHIPLIFE,ADDR szIDBCheatON invoke SetDlgItemText,hWin,IDB_VELOCITY,ADDR szIDBCheatON invoke SetDlgItemText,hWin,IDC_STATIC1005,ADDR szHotkeyHelp invoke SetDlgItemText,hWin,IDC_STATIC1006,ADDR szHotkeyF12 invoke SetDlgItemText,hWin,IDC_STATIC1007,ADDR szHotkeyF11 invoke SetDlgItemText,hWin,IDC_STATIC1008,ADDR szHotkeyF10 invoke SetDlgItemText,hWin,IDC_STATIC1009,ADDR szHotkeyF9 invoke SetDlgItemText,hWin,IDC_STATIC1010,ADDR szHotkeyF8 invoke SetDlgItemText,hWin,IDC_STATIC1011,ADDR szHotkeyF7 invoke SetDlgItemText,hWin,IDC_STATIC1012,ADDR szHotkeyF6 mov hwnddlg, eax invoke SetTimer,hWin,0,90, 0 ;set the timer for monitoring action keystrokes mov status1,0 mov status2,0 mov status3,0 mov status4,0 mov status5,0 mov status6,0 mov status7,0 .elseif uMsg == WM_COMMAND ; Did the user press a button .if wParam == IDB_HEALTH @HEALTH: ; Find the game window invoke FindWindow,addr GameClass,addr GameCaption ; The game is running .if eax != NULL .if status1 == 1 mov status1,0 ; Disable invoke TrainerEngine,offset GameCaption,Offset3,offset bytes2Restore3,NULL,NULL,7 ;stasis: invoke TrainerEngine,offset GameCaption,Offset13,offset bytes2Restore13,NULL,NULL,8 invoke SetDlgItemText,hWin,IDB_HEALTH,ADDR szIDBCheatON invoke Beep,1000,30 .else mov status1,1 ; Enable invoke TrainerEngine,offset GameCaption,Offset3,offset bytes2write3,NULL,NULL,7 invoke TrainerEngine,offset GameCaption,Offset4,offset bytes2write4,NULL,NULL,22 ;stasis! invoke TrainerEngine,offset GameCaption,Offset13,offset bytes2write13,NULL,NULL,8 invoke TrainerEngine,offset GameCaption,Offset14,offset bytes2write14,NULL,NULL,23 invoke SetDlgItemText,hWin,IDB_HEALTH,ADDR szIDBCheatOFF invoke Beep,5000,30 .endif ;If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hWin,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif .endif .if wParam == IDB_AMMO @AMMO: ; Find the game window invoke FindWindow,addr GameClass,addr GameCaption ; The game is running .if eax != NULL .if status2 == 1 mov status2,0 ; Disable invoke TrainerEngine,offset GameCaption,Offset1,offset bytes2Restore1,NULL,NULL,6 invoke Beep,1000,30 invoke SetDlgItemText,hWin,IDB_AMMO,ADDR szIDBCheatON .else mov status2,1 ; Enable invoke TrainerEngine,offset GameCaption,Offset1,offset bytes2write1,NULL,NULL,6 invoke TrainerEngine,offset GameCaption,Offset2,offset bytes2write2,NULL,NULL,21 invoke SetDlgItemText,hWin,IDB_AMMO,ADDR szIDBCheatOFF invoke Beep,5000,30 .endif ;If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hWin,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif .endif .if wParam == IDB_KILL @KILL: ; Find the game window invoke FindWindow,addr GameClass,addr GameCaption ; The game is running .if eax != NULL .if status3 == 1 mov status3,0 ; Disable invoke TrainerEngine, offset GameCaption, Offset11, offset bytes2Restore11, NULL, NULL,8 invoke SetDlgItemText,hWin,IDB_KILL,ADDR szIDBCheatON invoke Beep,1000,30 .else mov status3,1 ; Enable invoke TrainerEngine, offset GameCaption, Offset11, offset bytes2write11, NULL, NULL,8 invoke TrainerEngine, offset GameCaption, Offset12, offset bytes2write12, NULL, NULL,23 invoke SetDlgItemText,hWin,IDB_KILL,ADDR szIDBCheatOFF invoke Beep,5000,30 .endif ;If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hWin,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif .endif .if wParam == IDB_MONEY @MONEY: ; Find the game window invoke FindWindow,addr GameClass,addr GameCaption ; The game is running .if eax != NULL .if status4 == 1 mov status4,0 ; Disable invoke TrainerEngine,offset GameCaption,Offset5,offset bytes2Restore5,NULL,NULL,6 invoke SetDlgItemText,hWin,IDB_MONEY,ADDR szIDBCheatON invoke Beep,1000,30 .else mov status4,1 ; Enable invoke TrainerEngine,offset GameCaption,Offset5,offset bytes2write5,NULL,NULL,6 invoke TrainerEngine,offset GameCaption,Offset6,offset bytes2write6,NULL,NULL,21 invoke SetDlgItemText,hWin,IDB_MONEY,ADDR szIDBCheatOFF invoke Beep,5000,30 .endif ;If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hWin,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif .endif .if wParam == IDB_NODES @NODES: ; Find the game window invoke FindWindow,addr GameClass,addr GameCaption ; The game is running .if eax != NULL .if status5 == 1 mov status5,0 ; Disable invoke TrainerEngine,offset GameCaption,Offset7,offset bytes2Restore7,NULL,NULL,6 invoke SetDlgItemText,hWin,IDB_NODES,ADDR szIDBCheatON invoke Beep,1000,30 .else mov status5,1 ; Enable invoke TrainerEngine,offset GameCaption,Offset7,offset bytes2write7,NULL,NULL,6 invoke TrainerEngine,offset GameCaption,Offset8,offset bytes2write8,NULL,NULL,21 invoke SetDlgItemText,hWin,IDB_NODES,ADDR szIDBCheatOFF invoke Beep,5000,30 .endif ;If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hWin,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif .endif .if wParam == IDB_SHIPLIFE @SHIPLIFE: ; Find the game window invoke FindWindow,addr GameClass,addr GameCaption ; The game is running .if eax != NULL .if status6 == 1 mov status6,0 ; Disable invoke TrainerEngine,offset GameCaption,Offset9,offset bytes2Restore0910,NULL,NULL,8 invoke TrainerEngine,offset GameCaption,Offset10,offset bytes2Restore0910,NULL,NULL,8 invoke SetDlgItemText,hWin,IDB_SHIPLIFE,ADDR szIDBCheatON invoke Beep,1000,30 .else mov status6,1 ; Enable invoke TrainerEngine,offset GameCaption,Offset9,offset bytes2write0910,NULL,NULL,8 invoke TrainerEngine,offset GameCaption,Offset10,offset bytes2write0910,NULL,NULL,8 invoke SetDlgItemText,hWin,IDB_SHIPLIFE,ADDR szIDBCheatOFF invoke Beep,5000,30 .endif ;If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hWin,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif .endif .if wParam == IDB_VELOCITY @VELOCITY: ; Find the game window invoke FindWindow,addr GameClass,addr GameCaption ; The game is running .if eax != NULL .if status7 == 1 mov status7,0 ; Disable invoke TrainerEngine,offset GameCaption,Offset16,offset bytes2write17,NULL,NULL,21 invoke SetDlgItemText,hWin,IDB_VELOCITY,ADDR szIDBCheatON invoke Beep,1000,30 .else mov status7,1 ; Enable invoke TrainerEngine,offset GameCaption,Offset15,offset bytes2write15,NULL,NULL,6 invoke TrainerEngine,offset GameCaption,Offset16,offset bytes2write16,NULL,NULL,21 invoke SetDlgItemText,hWin,IDB_VELOCITY,ADDR szIDBCheatOFF invoke Beep,5000,30 .endif ;If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hWin,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif .endif .elseif uMsg == WM_TIMER invoke GetAsyncKeyState, VK_F12 ; Was F12 pressed? .if eax != 0 ; If yes jmp @HEALTH .endif invoke GetAsyncKeyState, VK_F11 ; Was F11 pressed? .if eax != 0 ; If yes jmp @AMMO .endif invoke GetAsyncKeyState, VK_F10 ; Was F10 pressed? .if eax != 0 ; If yes jmp @KILL .endif invoke GetAsyncKeyState, VK_F9 ; Was F9 pressed? .if eax != 0 ; If yes jmp @MONEY .endif invoke GetAsyncKeyState, VK_F8 ; Was F8 pressed? .if eax != 0 ; If yes jmp @NODES .endif invoke GetAsyncKeyState, VK_F7 ; Was F7 pressed? .if eax != 0 ; If yes jmp @SHIPLIFE .endif invoke GetAsyncKeyState, VK_F6 ; Was F6 pressed? .if eax != 0 ; If yes jmp @VELOCITY .endif .elseif uMsg == WM_CLOSE invoke AnimateWindow,hWin,300,AW_BLEND+AW_HIDE invoke EndDialog,hWin,0 .endif xor eax,eax ret DlgProc endp TrainerEngine PROC lpWindCap:DWORD, lpAdress:DWORD, lpNewValue:DWORD, nAdd:DWORD, lpBuffer:DWORD, _byteSize:DWORD ; Find the game window (again) invoke FindWindow,addr GameClass,lpWindCap ; The game is running .if eax != NULL ; Move the handle to windhand mov windhand, eax ; Get the process ID and save it to pid invoke GetWindowThreadProcessId, windhand, offset pid ; Open the process invoke OpenProcess, PROCESS_ALL_ACCESS,NULL, pid ; Move our process handle to phandle mov phandle, eax ; Prepare the ground for writing invoke VirtualProtectEx, phandle, lpAdress, _byteSize, PAGE_EXECUTE_READWRITE, 00 ; Write the new value invoke WriteProcessMemory,phandle, lpAdress, lpNewValue, _byteSize, NULL ; Close handle invoke CloseHandle, phandle ; If game is not running .else invoke Beep,100,30 ; Show the error message invoke MessageBox,hwnddlg,addr szErrorMessage,addr szErrorCaption,MB_ICONERROR .endif ret ; Return TrainerEngine ENDP end start
base.rc:
;This Resource Script was generated by WinAsm Studio. #define IDD_DIALOG 1000 #define IDB_HEALTH 1001 #define IDB_AMMO 1002 #define IDB_KILL 1003 #define IDB_MONEY 1004 #define IDB_NODES 1005 #define IDB_SHIPLIFE 1006 #define IDB_VELOCITY 1007 #define IDC_STATIC1005 1008 #define IDC_STATIC1006 1009 #define IDC_STATIC1007 1010 #define IDC_STATIC1008 1011 #define IDC_STATIC1009 1012 #define IDC_STATIC1010 1013 #define IDC_STATIC1011 1014 #define IDC_STATIC1012 1015 IDD_DIALOG DIALOGEX 0,0,227,129 FONT 8,"Tahoma" STYLE 0x80c80880 EXSTYLE 0x00000000 BEGIN CONTROL "",IDB_HEALTH,"Button",0x50010000,163,22,61,13,0x00000000 CONTROL "",IDB_AMMO,"Button",0x50010000,163,37,61,13,0x00000000 CONTROL "",IDB_KILL,"Button",0x50010000,163,52,61,13,0x00000000 CONTROL "",IDB_MONEY,"Button",0x50010000,163,68,61,13,0x00000000 CONTROL "",IDB_NODES,"Button",0x50010000,163,83,61,13,0x00000000 CONTROL "",IDB_SHIPLIFE,"Button",0x50010000,163,98,61,13,0x00000000 CONTROL "",IDB_VELOCITY,"Button",0x50010000,163,114,61,13,0x00000000 CONTROL "",IDC_STATIC1005,"Static",0x50000000,10,6,44,10,0x00000000 CONTROL "",IDC_STATIC1006,"Static",0x50000000,23,22,134,10,0x00000000 CONTROL "",IDC_STATIC1007,"Static",0x50000000,23,37,134,10,0x00000000 CONTROL "",IDC_STATIC1008,"Static",0x50000000,23,52,134,10,0x00000000 CONTROL "",IDC_STATIC1009,"Static",0x50000000,23,68,134,10,0x00000000 CONTROL "",IDC_STATIC1010,"Static",0x50000000,23,83,134,10,0x00000000 CONTROL "",IDC_STATIC1011,"Static",0x50000000,23,98,134,10,0x00000000 CONTROL "",IDC_STATIC1012,"Static",0x50000000,23,114,134,10,0x00000000 END
make.bat:
@echo off \masm32\bin\rc /v base.rc \masm32\bin\ml.exe /c /coff /Cp /nologo trainer.asm \masm32\bin\link.exe /SUBSYSTEM:WINDOWS /RELEASE /VERSION:4.0 /OUT:DS_Trainer.exe trainer.obj base.res del base.res del trainer.obj pause
Une fois compilé, ça ressemble à ça:
Vu que le code du jeu passe très rapidement sur certaines boucles, il peut arriver que le trainer écrit pendant que le code passe dessus, cela ferra crasher l’exécutable du jeu.
Vu que les boucles sont à l’arrêt quand le jeu et en pause, c’est mieux de le mettre en pause, d’activer nos options et de reprendre la partie.
IV: Idées de hack:
Il est possible de faire beaucoup d'autres choses dans le jeu, voici quelques idées.
- Enlever la collision avec les ennemies quand ils vous touchent pour que le personnage ne bronche pas: Il faut désactiver le segment de code qui détecte que l'on a pris un coup.
- Enlever le recule quand on tir avec une arme: Désactiver le segment qui fait le recule après tir.
- Oxygene illimité : Désactiver le segment qui gère le timer quand le personnage entre dans une zone sans atmosphère respirable.
- Droper par terre des objets en x1337 exemplaires : Trouver le segment qui gère la quantité des objets.
- Geler les ennemis : Désactiver le segment de code qui détermine quand les monstres se déplacent. Ou bien trouver la variable qui affecte si un monstre a vu le joueur ou non, modifiez le code autour de cela afin que les monstres ne se mettent jamais en état d'alerte.
- Tuer les monstres en 1 coup: Trouvez la ligne de code qui soustrait la santé du monstre, faites-lui soustraire un registre différent avec une grosse valeur à la place, ou bien remplacez leur compteur de vie à zéro.
Parfois certains jeux possèdent aussi des registres partagés, où circule dans le registre votre vie puis celle des ennemies (cyberpunk 2077 par exemple). Du coup, trouver un point de référence fixe dans le registre pour votre joueur au moment du segment partagé, et faites une comparaison avec cette valeur dans la cave pour savoir si la boucle en cours concerne votre joueur ou l'ennemie, en général c'est facile de faire un 'one hit kill' couplé d'un cheat vies illimitées pour le joueur une fois qu'on a nos références.
- Méga dommages: Trouvez la ligne qui fait les dégâts de votre arme et augmentez-là.
- Fly hack/coordonnées du joueur: Pour un flyhack, trouvez le registre qui donne la hauteur de votre personnage dans l'espace et augmentez-là.
(increase/decrease en se déplaçant dans un escalier par exemple pour trouver le pointeur)
- Wallhack: en général quand le personnage entre en collision avec un objet, on a affaire à un booléen, la méthode commune consiste a rentrer dans un mur, mettre le jeu en pause cherchez 0, ou 1, sortir du mur et répétez.
Parfois il faut aussi trouver la valeur de collision avec la caméra, car notre personnage peut traverser le mur, mais c'est encore un autre booléen pour la caméra qui ne traversera pas le mur.
Voilà, il y a encore d'autres trucs rigolos à faire auxquels je n'ai surement pas pensé avec ce jeu.
Xylitol, 02/05/2021