Le piratage des consoles: un parcours d'apprentissage (partie 4.5)
(Ceci est un article explicatif sur les consoles de piratage: un voyage d’apprentissage, partie 4)
Introduction:
Bonjour et bienvenue dans ce premier «bonus» de mon parcours d’apprentissage. La dernière fois, nous avons pu prendre le contrôle de Patapon 2 et exécuter l’instruction de sortie du jeu, mais je n’en ai pas expliqué beaucoup. Eh bien, si vous voulez des explications, vous êtes au bon endroit! Aujourd’hui, nous allons couvrir l’exploit de bout en bout, mais au lieu de détailler comment nous l’avons fait, nous verrons pourquoi cela a fonctionné.
Partie 1, l'accident:
Commençons au début, allons-nous? La toute première chose que nous avons faite pour lancer cet exploit a été de planter le jeu avec un fichier de sauvegarde falsifié. Mais pourquoi cela a-t-il fonctionné et pourquoi l'avons-nous fait? Eh bien, un exemple que j'aime utiliser pour expliquer ceci est celui d'un livre de jeu de rôle.
Techniquement, ce qui a provoqué le crash du jeu s'appelle un «débordement de tampon». Ce type de problème se produit lorsqu'un programme s'attend à ce qu'une liste d'éléments ait une certaine taille. Ainsi, tout ce qui est écrit et dépassant cette taille aboutit à un endroit indésirable.
Pour cet exemple, utilisons un jeu inventé: L’appel de Patapon.
Lorsque vous ouvrez ce livre de jeu de rôle, vous êtes accueilli par une question: quel est votre nom, aventurier?
La fente de réponse a une certaine taille et vous ne pouvez écrire que trop, mais que se passerait-il si vous en écriviez trop? Où cela finirait-il?
Eh bien, si vous écrivez trop, le livre devient illisible et vous ne pouvez plus continuer. Mais, si vous regardez de plus près, vous pouvez voir que notre lettre majuscule Q (en bleu) vient juste de remplacer la page à laquelle le lecteur devrait accéder par la suite… Je me demande si nous pourrions en tirer quelque chose? Si le lecteur est assez stupide pour aller à la page que nous écrivons au-dessus de «2», peut-être pourrions-nous en tirer quelque chose?
Partie 2, manipuler la console:
Après avoir chuté le jeu et réalisé que nous avions en fait le contrôle de la suite des matchs, nous devions faire quelque chose à partir de cela. Malheureusement, comme nous ne pouvons pas simplement modifier le code du jeu de cette manière, nous devons trouver un endroit où écrire nos instructions. Il doit y avoir un moyen d'entrer, non?
Eh bien, voyez-vous, chaque fois qu'un jeu enregistre des données, il les écrit dans un fichier externe. Le problème, c’est que le jeu doit charger à nouveau ce fichier pour pouvoir lire tout ce qu’il contient. La grande chose dans tout cela est que le fichier de sauvegarde est quelque chose sur lequel nous avons un contrôle, dans une certaine mesure.
En admettant que nous puissions déchiffrer le fichier (nous avons utilisé Savegame Deemer pour le faire), puis le rechiffrer, nous pouvons faire en sorte que le jeu charge n'importe quoi dans sa mémoire. Normalement, ce n’est pas un problème, car le jeu n’y lit jamais les instructions, mais il ya une différence: nous disons au jeu quelle partie de sa mémoire exécuter ensuite.
En utilisant PSPLink dans le dernier message, nous pourrions avoir une lecture de la mémoire du jeu telle qu’elle était au moment du crash. En cherchant dans celle-ci, nous avons constaté que notre sauvegarde était chargée telle quelle quelque part, ce qui signifiait que tout ce que nous pouvions écrire dans celle-ci serait également chargé. Afin de savoir où diriger le jeu, nous avions besoin de connaître l'adresse exacte où ce que nous avons écrit se retrouverait dans la mémoire.
Donc, pour résumer: puisque le fichier de sauvegarde est placé dans la mémoire, nous devons écrire quelque chose dans celui-ci, trouver son adresse exacte, puis placer cette adresse au bon endroit du débordement de mémoire tampon initial.
En termes plus techniques, nous manipulons la variable $ ra (Adresse de retour) du processeur afin de l’obliger à exécuter du code non signé, ce qu’il ne ferait normalement pas. Cette variable $ ra serait le numéro de page, dans notre exemple de livre de jeu de rôle. En soi, la variable $ ra est ce qui indique au programme où lire des choses à la fin de ses activités. Si nous le contrôlons, nous pouvons pointer l'exécution vers n'importe où au lieu du point de retour prévu.
Maintenant, vous vous demandez peut-être: qu'écrivons-nous maintenant? Que pouvons-nous mettre dans le fichier de sauvegarde qui sera compris et exécuté par le processeur?
Vous voyez, lorsque vous créez un programme, vous devez écrire le code correspondant, puis le compiler. La compilation d'un programme se fait automatiquement, et une des choses qu'il fait est d'importer des fonctions.
Dans un programme, les fonctions sont des morceaux de code qui seront réutilisés, parfois très rarement, mais parfois presque partout. Une fonction, par exemple, affichera le texte que vous lui aurez donné (cette fonction, en C par exemple, est appelée cout).
Mais écrire cette page encore et encore chaque fois que vous créez un programme serait sans fin. Et il en serait de même d'écrire une fois par programme et de l'appeler chaque fois que vous l'utiliserez. En fait, lorsque vous écrivez un programme, vous souhaiterez importer des fonctions de différentes bibliothèques (des ensembles de fonctions utiles que vous pouvez appeler sans avoir à écrire à nouveau).
Si vous voulez un homologue du monde réel, prenez une voiture. Vous pouvez fabriquer une voiture par vous-même, créer quatre roues, un moteur, des clignotants, des composants électroniques et tout le reste, mais la solution la plus simple consiste à commander les pneus et le moteur à quelqu'un, peut-être de prendre des ampoules de rechange pour les clignotants et de faire le contrôle électronique. toi même. Eh bien, vous pouvez considérer les magasins de pneus et de moteurs comme des bibliothèques, à partir desquelles vous appelez les fonctions de pneus et de moteurs à inclure dans votre programme. C'est le concept derrière les importations de fonctions.
Maintenant, la raison pour laquelle je parle de compilateurs et d’importations de fonctions est que c’est essentiel pour nous: comme nous ne pouvons pas vraiment faire ce que nous voulons, car nous sommes toujours pris au piège dans Patapon 2, nous ne pouvons utiliser que la fonction importée par ce jeu. quand il a été compilé. La PSP a beaucoup plus à offrir, mais nous sommes limités à ceux-là. Pour savoir quelles fonctions sont importées et où elles se trouvent, utilisez un petit outil appelé prxtool. Si nous l’utilisons tel quel, nous ne connaîtrons pas exactement le nom des fonctions, mais uniquement les bibliothèques auxquelles elles appartiennent; c’est là que le fichier XML entre en jeu, puisqu’il s’agit du traducteur, nous devons identifier des fonctions spécifiques.
Partie 3, écriture du code à exécuter:
Et maintenant, nous plongeons (un peu) dans l’Assemblée. La dernière fois, j'ai terminé le message en disant que je pouvais appeler la fonction pour fermer le jeu (ce qui n’était pas la même chose qu’un crash, car j’étais sorti correctement), et je ne l’ai pas expliqué à ce moment-là. Eh bien, c’est l’occasion!
Cela peut paraître décevant pour certains, et très attendu, pour ceux qui connaissent l’Assemblée, mais nous n’allons pas écrire 1 et 0 nous-mêmes. L’assemblage est plus ou moins le plus bas que vous puissiez faire lors de la programmation car il est le plus proche de la machine, mais il a toujours une syntaxe et est toujours très lisible.
L’assemblée a une liste relativement restreinte d’instructions possibles, du fait de son niveau si bas, mais nous n’aurons pas besoin de beaucoup. Puisque nous avons déjà l'adresse de nombreuses fonctions utilisées par Patapon 2, nous pouvons en appeler une comme preuve de concept. Le plus clair à voir, et celui que j'ai choisi, s'appelle sceKernelExitGameet se trouve à l'adresse 0x08A884D4 dans la mémoire du jeu. Par conséquent, si nous pouvons dire au jeu d'y aller, cette fonction sera appelée.
Heureusement, nous n’aurons pas besoin de faire une édition de fichier de sauvegarde hexadécimale étrange pour que le programme accède à l’adresse, car nous avons déjà le contrôle sur ce qui est exécuté. Il suffit d’utiliser l’instruction «jal 0x08A884D4» pour que le jeu se termine.
Ce que jal fait, c’est qu’il dit à la machine d’exécuter ce qui est là, et quand cela est fait, revenez à l’instruction juste après celle-ci. Curieusement, nous utilisons exactement ce que nous avons exploité précédemment: ici, la variable $ ra est l'endroit où est stockée l'instruction juste après jal.
Ainsi, lorsque nous disons «jal 0x08A884D4», nous disons en réalité «allez à cet endroit, lisez du code et revenez ici après». C'est juste que le code lui-même est la routine de sortie du jeu.
(On m'a dit de mettre ".set noat" et ".set noreorder" avant mon code, mais je ne sais pas ce qu'il fait. N'hésitez pas à m'éclairer.)
Partie 4, compiler le code et le planter:
Génial, nous avons maintenant un peu de code pour jouer. Le seul problème, cependant, est que nous devons le placer dans le fichier de sauvegarde. Nous pourrions le formuler comme ceci, mais la machine ne pourrait pas le lire sous forme de code et nous nous retrouverions avec le même crash que nous avions eu auparavant.
Si nous voulons que la machine puisse lire notre code, nous devons le compiler. La compilation est, en bref, l’acte de traduire le code en langage machine, en binaire. Les commandes pour cela sont très simples et utilisent les outils présents dans le SDK PSP minimaliste:
psp-as loader.s
psp-objcopy -O binaire a.out a.bin
Maintenant, il nous reste un petit fichier nommé “a.binary”. Ceci est notre code prêt pour la machine, et si nous l'ouvrons simplement dans notre éditeur hexadécimal préféré à côté du fichier de sauvegarde déchiffré, nous pouvons utiliser un peu de magie.
Nous devons mettre le contenu de une poubelle juste à la place du motif que nous avons choisi dans le fichier de sauvegarde. Veillez à écraser ce qui est là et à ne rien insérer avant, car nous ne voulons pas que la taille du fichier change ni que le contenu soit déplacé. Chaque fois que cela est fait, il ne vous reste plus qu’à lancer Patapon 2, à afficher notre nom à l’écran comme d’habitude et à savourer le fait qu’au lieu d’un écran figé, le jeu se ferme bien.
Conclusion:
Eh bien, c'était difficile à expliquer. Je suis heureux d’avoir tout mis au point et si vous avez des questions, n'hésitez pas à envoyer un message à @ theoct0 comme toujours. J'aimerais recevoir toutes les critiques que vous pourriez avoir, tous les trucs et astuces, tout ce que vous aimeriez faire. Mais pour l'instant, jusqu'au prochain post, adieu!