[PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Des soucis pour hacker votre jeu ? C'est ici qu'il faut exposer votre problème.
Avatar de l’utilisateur
AtelierFlamel
Messages : 5
Inscription : 28 juil. 2020, 13:41
[PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par AtelierFlamel »

Bonjour tout le monde,

Je suis relativement nouveau dans le milieu, mais j'ai décidé de me lancer dans la traduction du jeu Atelier Ayesha ~ The Alchemist of Dusk ~ (la version PS3 originale) de l'anglais vers le français.
J'ai décompressé l'iso et trouvé que le texte du jeu était soit stocké dans des fichiers .ebm (texte correspondant aux dialogues apparaissant dans les évènements faisant progresser l'histoire, soit le texte principal), soit dans le fichier eboot.bin (décrypté) lui-même (texte lié aux objets/combats/)
Après quelques recherches, j'ai trouvé des outils (HyoutaTools) qui me permettent de modifier facilement les .ebm en les convertissant en fichiers .sqlite.
Par contre, je bloque vraiment sur la façon d'abord le texte dans le SELF/eboot.bin, et c'est pour ça que je viens solliciter votre aide : quelqu'un pourrait-t-il me recommander des ressources/tutoriels/guides pour comprendre comment extraire et injecter du texte dans un fichier SELF de PS3?
Les vagues ressources que je trouve en ligne concernent seulement des consoles des vieilles générations (PS/PS2 au mieux), et je n'ai quasiment rien trouvé pour comprendre comment désassembler les SELF et les manipuler dans tous les cas de figures.
Je ne suis pas opposé à une manipulation directe dans un éditeur hexadécimal du fichier, mais je n'ai également trouvé nulle part de méthode vraiment claire pour trouver les tables de pointeurs vers les lignes de texte dans des (S)ELF alors que beaucoup de ressources en parlent, et puis il y a le problème d'encodage de caractères spéciaux français que je ne vois pas non plus comment traiter.
Je sais que c'est difficile, je possède une certaine pratique de la programmation (due à mes études notamment) et je suis prêt à investir énormément de temps - c'est juste que je ne trouve absolument pas de ressources (moderne) intéressante de rétro-ingénierie/assembleur/romhacking qui pourraient m'aider à aborder ce problème sous le bon jour. N'importe quelle information/ressource me sera d'une grande aide!

Je vous remercie d'avoir pris le temps de lire ce pavé un peu long :-) , je suis un peu désespéré et j'ai vu qu'il y a beaucoup de gens qui s'y connaissent beaucoup ici, c'est pour ça que j'ai posté.

Avatar de l’utilisateur
Maybach
Nouveau Floodeur
Messages : 28
Inscription : 26 mars 2017, 14:17
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par Maybach »

Bonsoir,
Alors premièrement je m'y connais très peu en PS3 ou programmation, mais comme toi j'ai été confronté à ce problème.
Pour une traduction, j'avais tenté ma chance sur des EBOOT.BIN de PS Vita qui sont en fait des fichiers SELF/ELF (je ne sais pas faire la différence).
Il faut savoir que la PS Vita et la PS3 partagent énormément de chose niveau encodage, j'ai parfois pu appliquer des algos de PS3 à des jeux PSV

En fait dans mon cas, les EBOOT.BIN étaient des fichiers "FSELF" c'est à dire avec une couche de cryptage avant d'être convertis en ELF. Bien heureusement on peut se servir de la console pour les décrypter, et parfois même depuis un PC si les développeurs ont hacké cette partie.
Après une brève recherche, je crois qu'il faut TrueAncestor SELF Resigner v1.98 cependant je n'ai aucune expérience et je ne l'ai jamais testé.

Un jour j'avais trouvé de bons outils PS3, ça s'appelait PS3_Tools by Aldo : http://www.logic-sunrise.com/news-88942 ... nible.html
Je ne sais pas à quel point c'est obsolète. De mon côté, j'ai réussi à décompiler le EBOOT.BIN avec Ghidra en suivant ce tuto (pour PSVita) : https://forum.devchroma.nl/index.php?topic=88.0
Mais le code obtenu était trop difficile pour moi même si j'ai pu trouver des chaines de caractères

Avatar de l’utilisateur
Inexpugnable
Dieu Suprême du flood
Messages : 960
Inscription : 30 avr. 2010, 22:11
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par Inexpugnable »

Plus qu'un type de fichier précis, il s'agit d'une compétence clef en rétro-ingénierie, l'analyse.

Comprendre suffisamment la structure d'une archive/ image/compression/etc. pour pouvoir implémenter de quoi l'extraire/désarchiver/réinsérer/recompiler.

Ça ne s'apprend pas en un claquement de doigts et je n'ai malheureusement pas réussi moi-même à atteindre cette étape... Les outils magiques n'existent pas en rétro-ingénierie de jeux-vidéos si ce n'est le debugger d'émulateurs assez avancés, mais encore faut-il savoir les utiliser.
Chaméléon. Est-ce que ne rien prendre. Ah oui mon coeur c'est vrai. Qu'est-ce que ça, qu'est-ce que c'est ? Donnez-moi ton argent. Donnez-moi ton fromage. Je donne tu mon amour et je allume ton chauffage.

https://www.youtube.com/watch?v=9eYAyYo5638

Avatar de l’utilisateur
RyleFury
Maître Suprême Floodeur
Messages : 494
Inscription : 16 janv. 2012, 12:48
Contact :
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par RyleFury »

EDIT : Je viens de voir que la PS3 utilisait le Big endian, attention à ça.
La PS3 utilise aussi une architecture 64 bits à l'inverse de la PS Vita qui utilise du 32 bits. Ca veut dire que tous les pointeurs seront stockés sur 8 octets au lieu de 4. Il faudra t'adapter par rapport à ce que j'ai mis plus bas.
Cette page devrait t'aider pour les headers du fichier ELF sur PS3 légèrement structurés différemment :
https://www.psdevwiki.com/ps3/SELF_-_SPRX#fSELF

Ca fait un moment que je voulais répondre à ce sujet, désolé pour le retard mais fallait que je me prépare psychologiquement à faire un Lyan (pavé) :D.

Pour commencer, je n'ai jamais touché à la PS3 mais la PS Vita devrait être très similaire. Il pourrait donc y avoir quelques légères différences.

Je vois que tu as "décrypté" (on dit "déchiffré" quand il s'agit d'informatique ;)) l'eboot, donc tu devrais avoir le fichier SELF en clair contenant lui-même le fichier ELF. C'est lui qui nous importe ici car il comporte un header standard qui va nous permettre de comprendre sa structure. C'est toujours la première chose à comprendre.

Commence déjà par supprimer les 0x1000 premiers octets correspondant au header du fichier SELF qui nous sont complètement inutiles. Ils ne sont importants que pour le reconstruire. Ca nous donne un fichier ELF standard. Tu devrais alors avoir les octets "7F 45 4C 46" ou " ELF". Si ce n'est pas le cas, c'est que ton fichier SELF n'est pas déchiffré. Nous avons donc devant nous le fichier exécutable principal du jeu comportant la programmation principale du jeu ainsi que ses variables et constantes générales. Il comporte aussi souvent les textes des menus et textes système comme c'est souvent le cas pour les jeux GUST.

Tu as d'attaché à ce message le Graal te permettant de décoder entièrement ce fichier : "ELF_Format.pdf". Pour te mettre sur la piste, les structures importantes sont le "ELF Header", le "Program Header" et le "Section Header" (ce dernier devrait être absent je pense).

Les informations importantes du "ELF Header" sont celles-ci :

- e_phoff : L'adresse du "Program Header" dans le fichier (habituellement 0x34). Il est placé juste après le "ELF Header".
- e_phnum : Le nombre d'entrées du "Program Header".

Une entrée du "Program Header" définit une zone du fichier ELF copiée directement dans la RAM au lancement du jeu. Cela va nous permettre d'obtenir les zones effectives et l'offset entre l'adresse dans le fichier ELF et dans la RAM. Ca va donc nous permettre de trouver les pointeurs des textes et de les modifier. C'est en vérité un peu plus compliqué, mais chaque chose en son temps, tu t'es attaqué à quelque chose de bien complexe pour ton premier projet. Certaines de ces zones sont spéciales et servent à un autre but que d'être copiés dans la RAM.

C'est le moment de décoder le "Program Header" afin d'obtenir les différentes zones copiées dans la RAM. Chaque entrée du header fait 0x20 octets.
Les informations importantes du "Program Header" sont celles-ci :

- p_type : Tu devrais normalement toujours avoir 0x01 (PT_LOAD). Cela signifie que la zone est copiée dans la RAM au lancement du jeu.
- p_offset : L'adresse dans le fichier de la zone à copier dans la RAM.
- p_vaddr : L'adresse dans la RAM où la zone sera copiée. (p_vaddr - p_offset) donne l'offset entre l'adresse de la RAM et du fichier permettant de trouver et modifier les pointeurs.
- p_filesz : La taille de la zone copiée dans la RAM.
- p_memsz : La taille de la zone réservée dans la RAM. Si elle est nulle, rien n'est copié dans la RAM.
Si elle est égale à p_filesz, la zone réservée fait la même taille que ce qui est copié dans la RAM. Ce sera très certainement une zone Read-only.
Si elle est supérieure à p_filesz, cela signifie que le reste de la zone dans la RAM contient une section contenant des variables non initialisées (et donc non présentes dans le fichier ELF). Ce sera donc forcément une zone où on a la permission d'écrire.
- p_flags :

PF_X : 0x1 : Execute
PF_W : 0x2 : Write
PF_R : 0x4 : Read
PF_MASKPROC : 0xF0000000 : Unspecified

"Execute" signifie que la zone contient une partie ASM, la première zone est censée contenir tout l'ASM principal du jeu mais pas que.
"Write" signifie que la zone contient des données modifiées pendant le jeu.
"Read" signifie que la zone contient des données lues pendant le jeu.

Ce sont des flags, ça fonctionne donc en masquant la valeur. Par exemple, si tu as la valeur 0x05, on effectue les opération suivantes :
0x05 & 0x01 = 1 (Execute présent)
0x05 & 0x02 = 0 (Write absent)
0x05 & 0x04 = 1 (Read présent)

& étant un ET. Donc ici les flags "Execute" et "Read" sont actifs.

Jusque-là tout va bien. Je vais te donner un exemple avec l'eboot de Ciel Nosurge sur PS Vita où ça devrait être assez similaire.

Je commence à le "ELF Header", j'obtiens ces valeurs :

- e_phoff : 0x34
- e_phnum : 0x05

On aura donc 5 zones du fichier ELF dont les 2 premières copiées dans la RAM (p_type = PT_LOAD). Passons à la première entrée du "Program Header" :

- p_type : 0x01 (PT_LOAD). La zone sera bien copiée dans la RAM.
- p_offset : 0xE0. La zone commence à cette adresse du fichier.
- p_vaddr : 0x81000000. Adresse typique de la RAM de la PS Vita. L'offset ELF-RAM sera donc 0x80FFFF20.
- p_filesz : 0x53CE8C. Taille de la zone dans le fichier ELF.
- p_memsz : 0x53CE8C. Taille de la zone dans la RAM. Ce sera très certainement une zone Read-only.
- p_flags : 0x05. Cette zone contient les flags "Execute" et "Read" mais pas "Write". Cette zone est donc Read-only et contient entre autre du code ASM toujours placé au début. Il peut très bien contenir autre chose à la suite du code ASM, dont le texte à traduire.

La zone à l'adresse 0xE0 avec une taille de 0x53CE8C sera donc copiée dans la RAM et il sera interdit d'écrire dans cette zone en jeu. A la moindre écriture, il y aura une erreur. Afin de trouver les pointeurs pour cette zone, il faudra prendre l'adresse dans le fichier du premier caractère du texte et ajouter l'offset ELF-RAM.

Voyons voir la seconde entrée du "Program Header" qui est à la suite :

- p_type : 0x01 (PT_LOAD).
- p_offset : 0x53CF80.
- p_vaddr : 0x8153D000. L'offset ELF-RAM sera donc 0x81000080 (différent de l'offset de l'autre zone, il faut donc faire attention).
- p_filesz : 0x1F36C. Taille de la zone dans le fichier ELF.
- p_memsz : 0x348AB8. Taille de la zone dans la RAM. Le reste des données est donc non initialisé. Cette zone sera forcément modifiée en jeu.
- p_flags : 0x06. Cette zone contient les flags "Write" et "Read" mais pas "Execute". Cette zone est donc modifiée en jeu et ne contient pas de code ASM. Le texte peut aussi être ici, mais il est plus souvent dans des zones Read-only.

Ici, on a fait la partie la plus simple... La partie compliquée commence maintenant. Suite dans un autre message.
Pièces jointes
ELF_Format.rar
(116.46 Kio) Téléchargé 34 fois
Dernière modification par RyleFury le 21 août 2020, 08:28, modifié 11 fois.

Avatar de l’utilisateur
RyleFury
Maître Suprême Floodeur
Messages : 494
Inscription : 16 janv. 2012, 12:48
Contact :
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par RyleFury »

Maintenant qu'on a toutes les infos sur les zones écrites dans la RAM, il va nous falloir trouver chaque pointeur de chaque texte afin de les modifier. On va prendre un texte en exemple avec Ciel Nosurge une fois de plus.

Je trouve un texte à l'adresse 0x4B0EC8 du fichier. Le texte fait donc partie de la première zone copiée dans la RAM qui je le rappelle commence à l'adresse 0xE0 avec une taille de 0x53CE53 octets. On ajoute l'offset ELF-RAM afin de trouver son adresse dans la RAM et on aura donc la valeur du pointeur :
0x4B0EC8 + 0x80FFFF20 = 0x814B0DE8.

Maintenant, il faut savoir qu'il existe deux façons de pointer une donnée :
- Soit par un pointeur standard. Il suffit de chercher la valeur en little endian (E8 0D 4B 81).
- Soit c'est pointé directement en ASM, auquel cas c'est plus compliqué à trouver.

Si le texte n'est pas trouvé en cherchant "E8 0D 4B 81", c'est donc que c'est directement pointé en ASM. Et ici c'est le cas.

Avant d'aller plus loin, il faut que j'aborde quelque chose de primordial. Si c'est comme sur PS Vita, les zones du fichier ne sont pas copiées à une adresse absolue de la RAM. Cela veut dire que les zones peuvent être copiées n'importe où dans la RAM. Cependant, nous avons ici des adresses absolues pour ces zones : 0x81000000 et 0x8153D000.

C'est là où ça devient compliqué. Avant de copier ces zones, le jeu va fournir une adresse de la RAM à laquelle ces zones vont être copiées. Par exemple, au lieu que ce soit copié à l'adresse 0x81000000, ça pourrait très bien être copié à l'adresse 0x81900000 à la place. Cette adresse change à chaque lancement de jeu. Cependant, le fichier ELF contient des pointeurs absolus standards et des pointeurs absolus définis en ASM. Ce que va effectuer le système, c'est remplacer TOUS ces pointeurs absolus par les nouveaux fournis. Le pointeur 0x81000000 sera donc réécrit 0x81900000, 0x81100000 sera réécrit 0x81A00000, etc.

Les pointeurs absolus de base ne sont jamais lus et sont toujours écrasés en début de jeu, il est donc inutile de les modifier (sauf si on veut faire quelque chose de vraiment propre). Mais alors, comment le système sait où trouver ces pointeurs pour les modifier ? Il va utiliser une table spécifique à la fin du fichier ELF appelée "Dynamic reallocation table". Il y a une table par zone effective, donc deux ici. Ce sont ces tables qu'il va falloir modifier à chaque fois qu'on cherchera à modifier le pointeur d'un texte.

Bien que cette table peut être chiante à décoder, elle est en vérité extrêmement pratique car elle évite d'avoir à trouver les pointeurs ASM nous-mêmes.

Sur PS Vita, ces tables sont définis dans le "Program Header", et elles sont prises en compte par e_phnum (comme on déchiffre le fichier proprement). Elles sont normalement définies juste après les entrées effectives du Program Header. Sur Ciel Nosurge, nous en avons bien deux autres après les entrées de base. Allons décoder la première entrée :

- p_type : 0x60000000 (PT_LOOS). Cette valeur indique un type spécifique au système. Ici ça veut dire que c'est une "Dynamic reallocation table". Je ne suis pas sûr que ce soit la même valeur sur PS3.
- p_offset : 0x5605D0.
- p_vaddr : 0. Cette table n'existe que dans l'ELF et ne sera pas copiée dans la RAM.
- p_filesz : 0xD5504. Taille de la table.
- p_memsz : 0.
- p_flags : 0.

La table de réallocation de la première zone est donc stockée à l'adresse 0x5605D0 du fichier ELF avec une taille de 0xD5504 octets. C'est tout ce qui nous intéresse. Tous les pointeurs à modifier dans la première zone seront donc dans cette table.

Ensuite, il faut comprendre comment est structurée cette table, ce qui n'est pas forcément simple. Néanmoins, si c'est juste pour changer des pointeurs de texte, on n'aura pas à s'en soucier car les valeurs seront normalement écrites en clair.

Reprenons le pointeur que nous cherchions dans notre exemple : 0x814B0DE8.
Ce pointeur est absolu car nous avons rajouté l'adresse de la zone qui est 0x81000000. Cependant, nous avons dit tout à l'heure que l'adresse de la zone 0x81000000 est inutile car la zone peut être copiée n'importe où dans la RAM. Reprenons donc l'adresse dans le fichier ELF du texte : 0x4B0EC8.

Ici, nous devons obtenir l'adresse relative du texte dans la zone. C'est celle qui sera inscrite dans la "Dynamic reallocation table". La zone commençant à l'adresse 0xE0 dans l'ELF, nous allons donc effectuer l'opération suivante :
0x4B0EC8 - 0xE0 = 0x4B0DE8.

Au final, l'offset ELF-RAM est inutile ici, nous allons plutôt définir un offset ELF-Zone qui sera -0xE0.

On cherche dans la "Dynamic reallocation table" cette valeur en little endian : E8 0D 4B 00. On la trouve un endroit et cela définit un pointeur ASM.

Il peut très bien y avoir qu'une seule valeur ou plusieurs. Il faut modifier toutes ces valeurs quand on souhaite modifier le pointeur. Si c'est comme sur PS Vita, la valeur à 4 octets juste après définit l'adresse relative dans la zone où ce pointeur est stocké et sera écrasé. Si c'est dans la zone ASM, cela modifiera un pointeur ASM, sinon ce sera un pointeur standard. Les 4 octets d'avant définissent le type de pointeur, si c'est ASM, standard ou même une table de pointeurs.

Dans tous les cas, seules les valeurs définissant les pointeurs sont importantes ici.

J'espère que c'est similaire à la PS Vita dans tous les cas. Je t'ai fourni toutes les pistes nécessaires pour que tu puisses appréhender sainement le fichier SELF, maintenant c'est à toi de jouer. J'accepte bien sûr toutes les questions, mais je ne ferai pas le travail à ta place.

Etant moi-même fan des Atelier, je te souhaite bon courage ;)

EDIT : J'ai modifié un peu les valeurs que j'avais mises car j'avais pris une version dumpée par Mai Dump Tool, ce qui n'est pas une référence. Maintenant les nouvelles valeurs et explications correspondent à un fichier déchiffré proprement avec exactement les bonne valeurs. Je rajoute aussi le fichier eboot.elf pour ceux qui veulent vérifier avec le tuto :

http://rylelafurie.free.fr/Ciel_nosurge_eboot.rar
Dernière modification par RyleFury le 05 août 2020, 20:48, modifié 3 fois.

Avatar de l’utilisateur
Maybach
Nouveau Floodeur
Messages : 28
Inscription : 26 mars 2017, 14:17
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par Maybach »

Quelle réponse incroyable, j'ai honte de répondre par si peu de caractères et de ne pas me prosterner devant tant de connaissance, mais merci car je tenterai d'appliquer cette méthode à mon projet en cours sur PS Vita.

Avatar de l’utilisateur
AtelierFlamel
Messages : 5
Inscription : 28 juil. 2020, 13:41
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par AtelierFlamel »

Je voulais tout d'abord te remercier une fois de plus RyleFury pour ton gigantesque tutoriel super détaillé :D , sans toi je n'aurais même pas su où commencer. Après une longue lecture, relecture et tests, sur Ciel Nosurge ainsi que sur Atelier Ayesha lui-même, j'ai quelques nouvelles questions :

1) Tout d'abord une question presque purement mathématique par rapport au déchiffrage des Program Header. Tu dis que l'entrée p_flags des Program Header nous permet de savoir si le bloc de programme correspondant sera Execute/Write/Read en faisant successivement (et respectivement les opérations 0xXX|0x01, 0xXX|0x02 et 0xXX|0x04 (où p_flags = 0xXX) et en regardant si on obtient 1 (présent) ou O (absent). Or, en reprenant ton exemple avec p_flag = 0x05 et une calculatrice scientifique, de type celle intégrée à Windows, j'obtiens pour ma part :
0x05|0x01 = 0x05
0x05|0x02 = 0x02
0x05|0x04 = 0x04
Ce qui ne correspond pas du tout à ce que tu avais indiqué ! J'ai testé d'autres opérations standard (AND, XOR, XAND, etc.), mais la table logique ne correspondait jamais à ce que tu obtenais. J'ai quand même fait le lien avec les permission RWE sous Linux, et surtout j'ai vu qu'en décomposant en binaire, on obtient 0x05 = 0b101, et en identifiant le bit de poids faible avec la permission en écriture (E), le deuxième avec l'écriture (W) et le bit de poids fort avec la lecture (R), j'obtenais ton résultat - ça se vérifiait pour le deuxième exemple, 0x06 = 0b110, qui était bien en R et W seulement.
Du coup j'étais juste curieux de savoir comment avec un OU logique tu obtenais ces résultats ??

2) J'ai décidé de suivre à la lettre ton tutoriel, et après avoir lu les bonnes parties du fichier ELF_Format.pdf, ainsi qu'un autre qui décrit les ELF en 64 bits (lien trouvé sur https://www.psdevwiki.com/ps3/SELF_-_SPRX#fSELF), je me suis lancé à l'assaut de l'EBOOT décrypté d'Atelier Ayesha. J'a donc identifié les informations importantes suivantes :

Elf Header :

e_phoff = 0x40
e_phentsize = 0x38
e_phnum = 0x08

Il y avait donc huit blocs de programme, je suis allé voir ce qu'ils donnaient, les cinq premiers avaient comme caractéristique p_type = 0x01, donc PT_LOAD. Et sur ces cinq premiers, seuls les deux premiers n'avait pas p_filesz = p_memsz = 0x00, et seul le premier avait pour valeur de p_flags = 0x05, les autres avaient soit 0x06, soit 0x04. Les informations intéressantes de ces deux premiers Program Header sont donc les suivantes :

Phdr 1:
p_flags = 0x00400005
p_offset = 0x00
p_vaddr = 0x10000
p_filesz = 0xFC6BE8
p_memsz = 0xFC6BE8

Ptdr 2:
p_flags = 0x00600006
p_offset = 0xFD0000
p_vaddr = 0xFE0000
p_filzsz = 0x4D81DC
p_memsz = 0x817828

(De ce que j’ai compris, le mot de 4 octets de poids fort pour les p_flags sont réservés à une utilisation spécifique environnement et processeur, donc ne semblent pas forcément intéressant ici. Par ailleurs, comme je ne sais pas si j’ai pu rater des informations importantes que je ne maîtrise pas, dans le dossier uploadé le fichier texte Header.txt contient le ELF Header et tous les Program Header en entier que j'ai décodé.)

Le premier bloc de programme me semblait le plus prometteur, je suis donc allé chercher un texte. L’exemple de texte que je prends se situe à l’adresse 0x00C98A24, donc dans le premier bloc de programme (je fournis l’ELF juste en bas, et l’encodage est de l’UTF-8 pour voir correctement le texte), voici une capture du texte que j’ai choisi d’éditer dans l’éditeur hexadécimal ainsi que ce qui apparaît dans le jeu lui-même (si l'image n'apparaît pas, il s'agit de Fig1.jpeg dans le dossier zip que j'ai upload)
Fig1.jpeg
Fig1.jpeg (291.96 Kio) Consulté 682 fois
Puisque p_offset est nul, il suffit en fait simplement d’ajouter p_vaddr pour obtenir la valeur de pointeur à chercher, soit 0x00C98A24 + 0x10000 = 0x00CA8A24. Mon éditeur hexadécimal est bien configuré en Big Endian, je cherche donc « CA 8A 24 » et le miracle s’opère, je trouve cette valeur exactement une fois, à l’adresse 0x00C9EF51 - donc déjà, c’est un pointeur standard, c’est la version facile. (Fig2.png dans le dossier)
Fig2.png
Fig2.png (222.07 Kio) Consulté 682 fois
C’est maintenant que je commence à bloquer : tout d’abord, pour tester, j’ai juste modifié le texte au même endroit (donc sans changer ledit pointeur) tout en le gardant de la même taille mais en rajoutant des caractères spéciaux - le jeu s’est lancé sans problèmes, et m’a affiché le texte correctement.

Les choses se sont corsées déjà lorsque j’ai voulu faire un texte un peu plus long : le texte suivant commence à l’adresse 0x00C98A84, et j’ai également trouvé le pointeur 0x00CA8A84 = (0x00C98A84 + 0x10000) à l’adresse 0x00C9EF69… et entre les deux adresses, il ne semble pas y avoir d’autres pointeurs, donc les textes comme leurs pointeurs sembles consécutifs. Plus précisément, entre les deux pointeurs (et un peu avant) j’ai les octets suivants :

00000000
00000132
>> 00CA8A24
00B637FC
00B637FC
00B637FC
FFFFFFFFF
00000001
>> 00CA8A84

Puisque entre la fin du premier texte à l’adresse 0x00C98A7C et le début du texte suivant (à l’adresse 0x00C98A84), il n’y avait que des 0x00, je me suis donc dit que c’était propre et que je pouvais les utiliser. Sauf que lorsque je l’ai fait, ce qui s’est affiché dans le jeu a été mon nouveau texte, collé à la fin avec le texte suivant de l’EBOOT ! De plus, il y a un saut à la ligne non naturel vers la fin du texte, vers la zone où le texte initial se terminait.
Pour être plus clair, voici une capture d’écran du texte modifié dans l’éditeur hexadécimal ainsi que ce qui est apparu dans le jeu : (Fig3.jpeg dans le dossier)
Fig3.jpeg
Fig3.jpeg (274.34 Kio) Consulté 682 fois
Je ne comprends pas vraiment comment c’est possible, j’ai même cherché toutes les valeurs des offsets des 0x00 + 0x10000 et je n’ai rien trouvé, rien ne semble pointer là-dessus ! Que penses-tu puisse en être la cause ?

Je ne comprends pas vraiment dans tous les cas comment sont codés les pointeurs. Tu disais que sur la Vita, il y avait une structure claire avec 4 octets juste avant pour le type et 4 juste après pour l’adresse relative, ici la structure ne semble pas aussi symétrique (et même pas sur 8 octets avant et 8 après). De plus, les octets devraient être codés sur 16 octets et non 8 à cause de l’architecture 64 bits, mais je ne comprends pas vraiment le sens de la ligne d’avant pour les deux octets que j’ai (soit 0x00000132 et 0x00000001).
Par ailleurs, j’ai aussi noté que les valeurs 0x00000132 précédant le premier pointeurs semblent incrémentées de 0x05 tous les deux pointeurs, alors que pour l’autre (0x00000001), ça reste identique.
Est-ce que tu pourrais peut-être s’il te plaît m’indiquer une référence pour mieux comprendre le codage et stockage des pointeurs (surtout de texte) pour les ELF ??

3) De manière plus générale, comment fait-t-on pour trouver de l’espace libre dans la ROM ou bien pour en insérer proprement ? Typiquement, entre la fin du premier bloc de programme à 0x00FC6BE8 et le début du second à 0X00FD0000 il n’y a que des 0x00 (beaucoup !), et il ne semble pas y avoir d’overlap avec d’autres bloc de programmes (les autres Program Header sont soit vides et à la fin du fichier, soit semblent superposés avec ces deux premiers, cf. fichier Headers.txt). Cependant, comme on est en dehors du premier bloc, la règle de offset + 0x10000 ne marche pas pour construire des pointeurs utilisables (j’ai eu simplement du vide à la place du texte dans le jeu quand j’ai tenté de le faire), donc je sais pas trop quoi faire. De même, j’imagine qu’insérer simplement des 0x00 au milieu nécessiterait de recoder TOUS les pointeurs, et je ne peux pas juste rajouter des 0x00 à la toute fin de l’EBOOT parce qu’il semble y avoir une section finale qui ne soit pas faite de 0x00 et qui sert peut-être pour les sommes de contrôles (dont celles pour la taille du fichier j'imagine).

4) De manière plus générale, faudra-t-il modifier tous les pointeurs à la main, ou bien est-il possible d’optimiser le processus ? Recommandes-tu de travailler avec certain programmes particuliers (désassembleurs, débiteurs, etc.) ??

Voici le lien vers le dossier zippé (mot de passe "AtelierFlamel" (sans les guillemets)), qui contient :
- L'ELF déchiffré.
- Header.txt avec le ELF Header et les Program Header
- Les images d'illustration (Fig1.jpeg, Fig2.png, Fig3.jpeg).

Avatar de l’utilisateur
RyleFury
Maître Suprême Floodeur
Messages : 494
Inscription : 16 janv. 2012, 12:48
Contact :
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par RyleFury »

Je vais déjà répondre à la première question. J'ai écrit un peu vite, il faut en fait effectuer un ET logique pour effectuer le masque.

0x05 & 0x01 = 0x01 (vrai : 1)
0x05 & 0x02 = 0x00 (faux : 0)
0x05 & 0x04 = 0x04 (vrai : 1)

0 = faux, autre valeur = vrai.

Avec p_flags = 0x05, tu as donc Execute et Read, zone contenant de l'ASM et read-only donc.
Avec p_flags = 0x06, tu as Write et read, zone de données.
Avec p_flags = 0x04, tu as Read, zone de données read-only.

Voilà comment il faut faire pour simplifier, désolé pour l'erreur. Je répondrai à la suite à tête reposée plus tard.

Avatar de l’utilisateur
AtelierFlamel
Messages : 5
Inscription : 28 juil. 2020, 13:41
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par AtelierFlamel »

Merci beaucoup pour cette première réponse RyleFury :) !
En effet, j'avais l'impression quand j'avais testé la table de l'opérateur ET que ça se rapprochait plus de ce que tu disais, mais l'opération 0x05 & 0x04 = 0x04 et non 1, j'avais l'impression que je manquais quelque chose - en fait, il ne s'agissait pas d'une table logique à proprement parler, toute valeur différente de 0 correspond donc à vrai - merci pour les clarifications !

Je me suis par ailleurs rendu compte que j'avais fait une erreur lors du parsing de mon dernier Program header (le huitième) dans le ELF_Header.txt que j'ai joint dans l'archive, désolé (il y avait des bits qui étaient mal décalés), je rajoute à ce message la version corrigée de celui-ci :
ELF_Headers_corrected.txt
(2.24 Kio) Téléchargé 11 fois
J'attendrai patiemment tes autres réponses ^^ !


Avatar de l’utilisateur
RyleFury
Maître Suprême Floodeur
Messages : 494
Inscription : 16 janv. 2012, 12:48
Contact :
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par RyleFury »

2) J'ai vérifié par moi-même le fichier ELF et ton analyse est plutôt bonne mais il te manque clairement des bases de romhacking et de programmation pour comprendre un certain nombre de choses...

Pour commencer, je vais t'expliquer les chaînes de caractères que tu devrais normalement connaître, c'est la base pour un hackeur. N'hésite pas à aller voir les différents tutoriels sur la TRAF. Les outils ne font pas de miracle pour un projet à moins qu'ils soient complets et dédiés à un projet.

Une chaîne de caractères, c'est un ensemble consécutif de caractères formant un texte. Un programme trouve cette chaîne de caractères en pointant son premier caractère. Mais, afin de connaître sa taille, il utilise ce qu'on appelle un caractère de fin de chaîne pour savoir où s'arrêter. Ici, t'as la chance d'être en UTF-8, tu n'aurais pas pu rêver mieux, et le caractère de fin de chaîne pour la quasi-totalité des types de chaînes de caractères, c'est 00. Ton exemple où tu as "Nearby" à la suite est donc normal, puisque tu as enlevé le caractère de fin de chaîne. Le programme lit donc la suite. Il faut garder au moins un 00 à la fin de chaque texte.

J'ai vérifié s'il y avait une table de réallocation des adresses sur PS3. Elle existe bel et bien mais est quasi nulle sur PS3, ce qui est une aubaine pour toi. Il y en a deux ici, les tables correspondantes du Program Header sont définies aux adresses 0x190 et 0x1C8, avec leurs tailles respectives de 0x28 et 0x40 octets. On peut donc considérer que ces tables sont inutiles. Ce qui signifie que modifier les pointeurs directement devrait fonctionner. Il faut que tu effectues le test pour en avoir le cœur net. Prends un pointeur de texte et ajoute 1 à ce pointeur. Si dans le jeu tu as bien ton texte affiché avec le premier caractère en moins, c'est que le jeu prend bien les pointeurs directement. Tu me diras ce que ça donne.

Les pointeurs ici sont en 32 bits et non en 64 bits car les développeurs ont choisi ça, ce qui est logique ici car les pointeurs en 64 bits ne serviraient à rien. Cependant, nous sommes confrontés à du ELF64. Etant donné que c'est un standard, tous les pointeurs sans exception feront 64 bits pour les tables spécifiques au ELF64 (ELF Header, Program Header, Session Header).

Aussi, tu dis que les pointeurs sont à la suite. Sache que ce n'est pas une science exacte. Tu pourrais avoir plusieurs pointeurs pour un texte et des pointeurs dans des endroits complètement différents. Les pointeurs sont utilisés par le programme lui-même (la partie ASM). Ils peuvent suivre une structure logique ou non, tout dépend si ça a été programmé avec les pieds ou non. Il faut étudier les textes et trouver les tables de pointeurs en conséquence. Faire un outil qui détecte les textes automatiquement et trouve automatiquement leurs pointeurs est une première étape. Ça permet de définir les zones de pointeurs et d'enlever les faux pointeurs s'il y en a. Tout ça, c'est à étudier. Se mettre sur un jeu de cet acabit sans programmer relève du suicide, ce n'est pas une mince affaire. Les outils disponibles sont rarement suffisants.

J'ai aussi vu que le Session Header existait pour les jeux PS3. Il ne devrait pas être utilisé par le système, mais vu que ça dépend du système lui-même, je n'en mettrais pas ma main à couper. Il faudrait effectuer des tests pour en être sûr. C'est intéressant d'étudier le Session Header car il permet la plupart du temps d'analyser des zones plus précises que le Program Header. Ça pourrait peut-être par exemple montrer les zones de textes exacts ainsi que leurs tables de pointeurs. T'as beaucoup de chance car ce projet a l'air bien structuré et donc très simple. Je te conseille donc de faire une liste de toutes les zones du Session Header.

3) Trouver de la place libre dans un exécutable peut être très simple comme très compliqué selon les différents projets.
Tu as beaucoup de chance, car tu peux effectivement utiliser la zone 0x00FC6BE8 à 0x00FD0000. J'ai fait un (un peu trop gros) schéma pour simplifier :

Sans titre-1.png
Sans titre-1.png (54.23 Kio) Consulté 559 fois
Comme tu peux le voir, la zone est bel et bien inutilisée dans la RAM, c'est dû à l'alignement des zones copiées dans celle-ci. Si tu comprends bien les choses, pour utiliser cette zone, il va falloir modifier "p_filesz" et "p_memsz" de "Phdr 1" par 0xFD0000 afin que ce morceau soit copié dans la RAM. La section finale du fichier ELF est le Section Header, pas un checksum. Relis le ELF Header et tu comprendras. Il existe d'autres façons d'obtenir de l'espace en plus. Il faut soit chercher dans les zones copiées dans la RAM des bouts non utilisés par le jeu ou des zones de débuggage souvent traduites par du texte de débuggage, mais ce n'est pas toujours présent. Au pire des cas, il faut étudier la RAM et voir quelles sont les zones jamais utilisées puis créer sa propre zone dans le "Phdr". En regardant le Schéma, on voit qu'il pourrait y avoir une zone de libre dans la RAM à partir d'adresse 0x17F7828, cependant ce n'est pas sûr du tout. Afin d'en être certain, pas d'autre choix que d'étudier la RAM, ce qui est compliqué sur PS3.

4) L'objectif est de trouver les pointeurs est de tous les modifier. Dès que tu as extrait et listé chaque pointeur correspondant à chaque texte avec un programme "dédié" au projet, il faut ensuite pouvoir réinsérer le tout avec un autre programme "dédié", un outil d'insertion. Les pointeurs ne doivent bouger en aucun cas, ils restent aux même adresses. Par contre, il faut définir plusieurs zones dans lesquelles les textes traduits vont être insérés. Les zones standard (celles où était placé le texte anglais) ainsi que des zones en plus car le français prend plus de place que l'anglais (comme par exemple la zone non utilisée à 0x00FC6BE8). Donc dans tous les cas, tu seras obligé de modifier tous les pointeurs selon les adresses où les textes seront insérés et ce avec un programme "dédié" d'insertion. Il y a BEAUCOUP trop de texte dans un RPG pour espérer faire ça à la main et il n'existe pas de logiciel miracle pour ça.

Avatar de l’utilisateur
AtelierFlamel
Messages : 5
Inscription : 28 juil. 2020, 13:41
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par AtelierFlamel »

Une fois de plus, merci énormément RyleFury d’avoir pris le temps de me répondre et de m’expliquer en détails des trucs tout bêtes que j’ignorais :) !

Je suis désolé du retard de ma réponse par ailleurs, je voulais vraiment tenter de travailler seul dans mon coin pour revenir avec des progrès et des questions plus pertinentes.

Tout d’abord, je te confirme que j’ai effectué le test de rajouter +1 à un pointeur, et j’ai bien eu le même texte avec un caractère en moins qui s’est affiché ! Pour l’exemple, je prends toujours le même pointeur, situé à l’adresse 0x00C9EF50, de valeur 0x00CA8A24 et qui pointe vers le texte « I got Nio’s favorite candy from Ernie. I want to go to the Herb Garden to give it to her. » de mon message précédent. Avec cette modification, mon texte était «  got Nio’s favorite etc. » , donc je pars du principe que je peux modifier tous les pointeurs directement.

Ce n’était évidemment pas le seul test de repointage que j’ai effectué - j’ai par exemple tenté de pointer la ligne de texte qui suivait juste après dans l’EBOOT (texte à l’adresse 0x00C98A9C), du texte qui semblait être du texte de débuggage (texte situé à l’adresse 0x00CBE257) mais également et surtout j’ai essayé de pointer vers du texte situé dans le bloc de programme n°2, donc entre les adresse 0x00FD0000 et 0x014A81DC (je ne me souviens malheureusement pas de l’adresse exacte maintenant, il me semble que c’était 0x012F6420). Toutes ces manipulations ont été parfaitement fonctionnelles, comme en témoigne l’exemple avec les textes de débuggage juste en dessous.
figure_1.jpeg
figure_1.jpeg (232.99 Kio) Consulté 334 fois
Ensuite, j’ai écrit un petit script Python qui m’a permis d’automatiser l’extraction des données concernant les headers de l’EBOOT, à savoir l’ELF Header, le Program Header et le Section Header également cette fois - comme il était relativement gros (0x23 entrées), ce n’était plus très pratique de le faire à la main. Pour faciliter un peu la lecture de celui-ci (puisqu’il y a des blocs de programmes qui ne se suivent pas et sont dans d’autres blocs précédents - par exemple le 6, 7 et 8 ), le script fait aussi un tri automatique des offsets de début et de fin de chaque bloc de programme, section et header et rajoute tout ça à la fin du .txt d’analyse : voici ce fichier
Headers_EBOOT_Original.txt
(14.25 Kio) Téléchargé 7 fois
(il est également présent comme d'habitude dans l'archive .zip dont je donne le lien à la fin du post, comme c'est le cas pour tous les autres fichiers/images/etc. que je présente ici).

Comme tu l’as conseillé, j’ai un peu regardé les différentes sections et je me suis rendu compte que tout le texte du jeu que l’on devait traduire semblait se trouver dans la section 18, à l’intérieur du bloc de programme n°1. Je remets ici son header (il est dans le fichier .txt sus-mentionné également) :

Shdr 18 :
sh_name = 0x0000010A
sh_type = 0x00000001
sh_flags = 0x0000000000000002
sh_addr = 0x0000000000B62E20
sh_offset = 0x0000000000B52E20
sh_size = 0x0000000000398170
sh_link = 0x00000000
sh_info = 0x00000000
sh_addralign = 0x0000000000000010
sh_entsize = 0x0000000000000000

Cela nous permet de réduire le champ d’action pour l’extraction de texte, mais ne nous aide pas outre-mesure, puisque le texte, la majorité des tables de pointeurs associées, les messages débug et les variables systèmes (et il y en a beaucoup) sont dedans. Donc je ne pense pas que ça nous aide pour faire plus de tri que ça (à moins que j’ai raté quelque chose, c’est tout à fait possible).

Après cette analyse, je me suis lancé dans l’extraction proprement dite du texte ainsi que la recherche des pointeurs associées. J’ai donc également écrit un autre script qui utilise une regex (que j’ai mis un peu de temps à peaufiner) pour extraire du le vrai texte du jeu et pas tous les noms de variables, chemins, etc. Je pense réussir à obtenir plus de 99% de texte du jeu (j’oublie peut-être des miettes) et quelques faux-positifs qui semblent justement correspondre aux textes de débuggages en majorité. Ensuite, avec une autre fonction, je calcule l’adresse de mon pointeur et j’exporte tout cela dans un fichier .csv dont la structure va ressembler à ça :

« 11876988,Yes,"[17915076, 17935776]",TRADUCTION_FR
11876996,No,"[17915148, 17935848]",TRADUCTION_FR
11878288,Adjust,[17902836],TRADUCTION_FR
11878304,Ingr.,"[1267426, 17903232]",TRADUCTION_FR
11878320,Tools,[17903376],TRADUCTION_FR
[…]
13208076,\xe2\x98\x85Herb Garden\xe2\x98\x85,[13233988],TRADUCTION_FR
13208100,I got Nio's favorite candy from Ernie. I want to go to the Herb Garden to give it to her.,[13234000],TRADUCTION_FR
13208196,\xe2\x98\x85Nearby Village\xe2\x98\x85,[13234024],TRADUCTION_FR
13208220,Let's go to Nearby Village for now. Maybe someone there knows about the glowing flower.,[13234036],TRADUCTION_FR
[…]
15640544,This does not look like a binary tagfile (magic number mismatch),[17803220],TRADUCTION_FR
15640616,Unrecognised tagfile version.,[17803228],TRADUCTION_FR
15641472,Call hkSerializeDeprecated::initDeprecated() to include loading support for this file,[17803388],TRADUCTION_FR »

Où la structure est la suivante :
ADRESSE TEXTE ORIGINAL [int decimal], TEXTE ORIGINAL [str], LIST ADRESSES POINTEURS [int liste], TRADUCTION [str]

Par défaut, le script met TRADUCTION_FR dans la dernière colonne (ce qui est utile pour choisir ce qu’on veut réinsérer ou non en faisant un petit test), et les adresses sont en décimal parce que c’est plus pratique pour se déplacer dans les fichiers binaires en Python.

Finalement, j’ai également écrit un dernier petit script qui s’occupe de la réinsertion - il s’agit d’une fonction à laquelle tu fournis le fichier .csv précédent avec les chaînes TRADUCTION_FR remplacées par les vraies traductions, l’offset de début où tu commences à insérer le nouveau texte, l’offset de fin d’insertion ainsi que le décalage p_vaddr - p_offset = 0x10000 (= 65536 dec) dans notre cas de figure et il effectue tout le boulot.

Maintenant que j’ai ceci, reste la question de l’endroit où réinsérer le texte - comme tu l’as dit, il s’agit du point le plus délicat, et c’est évidemment celui où ça coince.

En écrémant un peu le .csv que j’ai obtenu et en enlevant les messages de debug, j’arrive à grosso modo ~380000 caractères de texte à traduire. Comme tu l’a expliqué dans le message précédent, la zone entre les deux blocs de programme n°1 et n°2 (donc entre 0x00FC6BE8 et 0x00FD0000) est inoccupée en RAM. Cela correspond à 0x9418 caractères, soit ~37900 caractères environ. Donc en tout, en incluant cette zone de texte, nous n’aurions que 10% de place en plus, or statistiquement le français prend entre 20% et 30% plus de place pour une traduction, donc cela n’est pas suffisant. Par ailleurs, si j’ai bien compris, le problème est que cette place n’est pas toute utilisable d’un seul tenant : si on a un texte trop long pour un endroit, on doit le déplacer en entier vers un autre endroit plus grand, et on ne peut pas « ajouter » à un pointeur unique un autre pointeur qui indique où se trouve la seconde partie d’un même texte - en tout cas, c’est ce que j’ai compris, on ne touche pas aux zones de pointeurs, on ne fait que modifier leur valeur, c’est bien cela ?

Par ailleurs, pour rester dans le thème, j’ai bien modifié les valeurs p_memsz et p_filesz du Phdr 1 (en les mettant à 0x00FCFFFC à la place de 0x00FC6BE8, donc un mot avant le début du bloc de programme n°2 pour laisser une petite marge), et j’ai essayé de pointer vers du texte se situant dans cette zone - cela semble bien marcher quand je mets plusieurs phrases sans remplir le bloc en entier. Pour le moment, je n’ai pas accès à ma PS3 donc j’ai fait les derniers tests avec l’émulateur RPCS3 - je ne sais pas si c’est dû à sa stabilité ou non, mais il a un peu de mal à se lancer lorsque j’écris dans cette zone des blocs de textes relativement grands (parfois messages d’erreur avant de charger la partie), et lorsque je tente de le remplir de texte en entier (environ de 0x00FC6C00 à 0x00FCFFFC), le jeu se lance mais freeze au moment où je tente d’ouvrir le menu avec le texte « I got Nio’s favorite etc. ». J’ai essayé de regarder un peu la mémoire dans l’émulateur les fois où cela s’est lancé, et il semble qu’il n’y ait vraiment que des 0x00 entre 0x00FC6BE8 et 0x00FD0000, donc théoriquement il ne devrait pas y avoir de problèmes.
Du coup, est-ce que la zone entre 0x00FC6BE8 et 0x00FD0000 devrait être vraiment utilisable (ce qui semble être le cas), ou bien il peut y avoir des subtilités supplémentaires ?
Et sinon, est-ce que cela peut-être aussi lié au fait que je n’ai pas touché pour cette modification aux sections? En fait, on peut voir dans le fichier texte joint que le bloc de programme n°1 se termine par les tables d’allocation dynamique (blocs de programme n°7 et 8 ), qui sont identiquement égales à leurs sections (section n°20 et 21). De fait, j’imagine qu’allonger la section n°21 jusqu’en 0x00FDFFFC n’est pas forcément une bonne idée vu qu’elle correspond à la table d’alloc et pas au bloc de programme n°2 en soi, mais je ne suis sûr de rien…
Je vais essayer de faire des tests sur la PS3 dès que je le pourrai, et je rapporterai également les résultats obtenus.

J’ai essayé alors deux choses en particulier pour « rajouter » de la place vide, mais cela a souvent été un échec, ce qui me conduit aux questions principales suivantes :

1) Tu as dit que si l’on trouvait une zone libre en RAM, on pouvait rajouter un Phdr, donc un bloc de programme, dans lequel on pourrait mettre du texte supplémentaire. Or, le programme header se termine à l’octet 0x200, et dès l’octet suivant, on a le contenu (instructions ASM j'imagine) du premier bloc de programme.
Du coup, peut-on vraiment « insérer » un nouvel Phdr à l’octet 0x200 vu que cela décale absolument tout le fichier et tous les offsets sont faussés, donc le programme devient caduc ? Existe-t-il une façon pour tout décaler proprement (cela me paraît être au pire impossible, au mieux un travail titanesque, vu qu’il s’agirait de désassembler et de comprendre absolument TOUT l’EBOOT, dont des instructions systèmes correspondant aux mécaniques de jeu) ?
Ou bien peut-on réutiliser un Phdr qui est complètement vide pour ce faire ? J’ai notamment remarqué que le bloc de programme n°3 avait pour Phdr :

Phdr 3 :
p_type = 0x00000001
p_flags = 0x00000004
p_offset = 0x00000000014A81DC
p_vaddr = 0x0000000000000000
p_paddr = 0x0000000000000000
p_filesz = 0x0000000000000000
p_memsz = 0x0000000000000000
p_align = 0x0000000000010000

Il est donc vide, et en plus son p_flags indique une priorité de lecture uniquement, donc cela semble parfait pour insérer du texte. En utilisant le tableau (très clair et pratique, merci ^^) de ta réponse, j’ai donc mis p_vaddr = p_paddr = 0x0017F7828 (la fin de la mémoire allouée au bloc de programme n°2 en RAM) et une petite taille pour tester (p_filesz = p_memsz = 0x30 de mémoire, pour ne pas empiéter sur les blocs suivants, notamment le section header - j’ai aussi shifté en conséquence les offset de début de la section qui suivait). Malheureusement, l’émulateur n’a même pas voulu booter le jeu - il y avait une erreur où il m’indiquait justement la valeur p_vaddr et p_filesz. Se pourrait-t-il qu’il faille aussi changer la valeur du p_flags (par exemple en 0x00400005 comme le Phdr 1), ou bien il y a quelque chose de plus subtil ?

2) Puisque ma tentative précédente était un échec, j’ai décidé d’attaquer l’affaire différemment : je me suis dit qu’au lieu d’étendre le bloc de programme n°3, j’allais le faire avec le bloc n°2. Plus concrètement, comme la RAM allouée en tout au bloc n°2 va jusqu’à 0x017F7828, j’ai décidé de rajouter suffisamment de 0x00 au bloc n°2 pour dépasser cette valeur (je l’ai dépassée d’environ 1 Mo dans mon cas) - en effet, la zone qui était avant copiée du bloc n°2 allait de l’offset 0x00FD0000 jusqu’à 0x014A81DC tandis que dans la RAM, la mémoire de 0x014B81DC jusqu’à 0x017F7828 était utilisée en écriture. De fait, si l’on plaçait du texte dans l’EBOOT entre l’offset 0x014A81DC et 0x017E7828, il allait être placé en RAM dans la partie initialement réservée au calculs (write) du bloc n°2 - mes tests m’ont montré que lorsque je tentais de pointer vers une ligne de texte placée dans cette région de l’EBOOT, en général je n’avais au mieux rien qui apparaissait à l’écran, au pire des caractères aléatoires.
Par contre, si l’on place du texte au-delà de cette région, il est copié en RAM au-delà de l’adresse 0x017F7828.
Voici un graphique un peu comme le tien qui résume un peu cela (par ailleurs, quel logiciel tu utilises pour le faire ? J’ai l’impression que c’est pas LaTeX ni Excel, donc j’étais curieux).
figure_2.jpeg
figure_2.jpeg (80.77 Kio) Consulté 334 fois
En regardant un peu dans RPCS3 (en attendant de pouvoir le faire sur vraie PS3), j’ai remarqué que jusqu’à environ 0x01820000, il y avait des choses dans la RAM, mais qu’à partir de ce moment elle semblait vide au moins jusqu’à 0x01858000 - j’ai donc tenté d’insérer du texte dans cette zone (c’est à dire, entre 0x01810000 et 0x01848000 dans l’EBOOT avec le décalage de 0x10000). Il se trouve que pour une petite quantité de texte (quelque chose comme 70-80 lignes), le jeu se lançait et tout allait bien. Par contre, quand j’ai inséré un bloc plus gros (quelque chose comme 500-600 lignes), j’ai obtenu l’erreur suivante sur l’émulateur :

« F {PPU[0x1000000] Thread (main_thread) [0x001ef048]} VM: Access violation reading 0x71ec6fa0 (unmapped memory) »

Je ne comprends pas très bien non plus pour quelle raison cela ne marche pas - la mémoire semble libre, cela marche avec un certain nombre de lignes insérées, et soudain cela cesse pour une quantité de texte plus grande. J’ai même rempli toute la zone entre 0x01810000 et 0x018A0000 avec un caractère fixe différent de 0x00 (en l’occurence 0x41 = A) sans l’afficher bien sûr, mais le jeu s’est chargé sans problème et rien ne semblait poser problème. Est-ce que tu penses que cela peut-être dû à l’émulateur (auquel cas je referai des tests en conditions réelles), ou bien c’est vraiment une tactique qu’on ne peut pas adopter pour des raisons plus subtiles ?
Je joins également à ce post le fichier d’analyse du fameux EBOOT « allongé », où j’ai indiqué avec des chevrons > là où j’ai changé des choses dans les headers notamment, et en pièce jointe un lien .zip qui contient l’EBOOT que j’ai allongé et qui est fonctionnel (EBOOT_5Mo_fonctionnel.ELF) ainsi que celui qui freeze, avec plus de texte inséré (EBOOT_5Mo_freeze.ELF).

Pour résumer, après avoir tenté ces deux approches, je n’ai pas d’autres idées pour pouvoir obtenir de la place - aurai-tu s’il te plaît des recommendations par rapport à mes approches précédentes sur des choses que je devrais modifier, quelque chose que j’ai manqué, ou carrément une procédure différente à tenter que j’aurais loupé, concentré sur ces deux-là ?

Voici le lien vers le dossier zippé (mot de passe "AtelierFlamel" (sans les guillemets)), qui contient :
- EBOOT_5Mo_fonctionnel.ELF : l’EBOOT pour lequel j’ai rajouté 5 Mo suivant la prescription comme avant, dans lequel j’ai inséré du texte de jeu factice pour tester le repointage (c’est juste le même texte que celui original avec la casse inversée) et qui est fonctionnel sur RPCS3.
- EBOOT_5Mo_freeze.ELF : Même chose que celui d’au-dessus, mais j’ai inséré plus de texte test et celui-ci freeze pour une raison mystérieuse.
- figure_1.jpeg : Exemple de repointage simple.
- figure_2.jpeg : Graphique avec les zones que j’ai changées.
- Headers_EBOOT_Original.txt : L’analyse totale de tous les Headers de l’ELF original.
- Headers_EBOOT_Modifie.txt : L’analyse totale de tous les Headers de l’ELF allongé de 5 Mo.
- atelier_ayesha_strings.txt : l’ensemble des textes que j’ai extrait du jeu (ainsi que les messages de débuggage et autres messages système).

EDIT : J'ai corrigé quelques offsets mal tapés/calculés (notamment dans la question n°2), maintenant tout devrait être correct.

Avatar de l’utilisateur
RyleFury
Maître Suprême Floodeur
Messages : 494
Inscription : 16 janv. 2012, 12:48
Contact :
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par RyleFury »

Ok, je vais répondre du mieux que je le peux sans tester le jeu de mon côté, ce qui n'est pas simple.

Pour commencer, tout ce que tu as fait est bien pensé. Tu t'es franchement bien débrouillé jusque-là, c'est maintenant qu'on arrive vraiment à la partie compliquée ;)

Je vais essayer en premier lieu de répondre à tes questions dans l'ordre, on va effectuer des tests pour voir ce que ça donne. Je pense que chacun des soucis que tu as posés a une solution pas aussi compliquée que tu le penses.
AtelierFlamel a écrit :
06 sept. 2020, 22:41
Par ailleurs, si j’ai bien compris, le problème est que cette place n’est pas toute utilisable d’un seul tenant : si on a un texte trop long pour un endroit, on doit le déplacer en entier vers un autre endroit plus grand, et on ne peut pas « ajouter » à un pointeur unique un autre pointeur qui indique où se trouve la seconde partie d’un même texte - en tout cas, c’est ce que j’ai compris, on ne touche pas aux zones de pointeurs, on ne fait que modifier leur valeur, c’est bien cela ?
C'est vrai pour la quasi totalité des textes, mais il y a des exceptions. En programmation, on peut aussi concaténer plusieurs textes. Chaque texte aura donc son propre pointeur et le programme va appeler une fonction permettant de concaténer (fusionner) les deux et lui assigner un pointeur. Ici, je ne pense pas que t'en auras, ou très peu, on ne sait jamais. Sinon, tu ne pourras pas faire ça toi-même, car ça demande de toucher à la partie ASM. Il est inutile de chercher compliqué, donc il faut bien replacer le texte en entier en un seul bloc en gardant son/ses pointeur(s) associé(s). Ton script d'insertion en Python a besoin de gérer l'espace libre intelligemment.

Par exemple, effectuer un scan de toutes les tailles des textes à insérer et de mettre l'ordre d'insertion des textes du plus gros vers le plus petit. Sachant que tes espaces sont totalement vides en premier lieu, il est plus simple de caser de gros textes en début d'insertion qu'à la fin. Puis utiliser en priorité les petits espaces et finir par les plus gros. Comme ça la priorité sera de tenter d'insérer les plus gros textes dans les plus petits espaces. Puis si ça ne marche pas, on passe à l'espace suivant pour ce texte. Ce n'est pas un système parfait, mais ça suffit amplement pour bien optimiser la place. Et tu peux aussi très bien définir de petits espaces vides, même de 0x100 octets. Tant que ça permet de caser du texte, n'hésite pas.
AtelierFlamel a écrit :
06 sept. 2020, 22:41
Du coup, est-ce que la zone entre 0x00FC6BE8 et 0x00FD0000 devrait être vraiment utilisable (ce qui semble être le cas), ou bien il peut y avoir des subtilités supplémentaires ?
Je ne crois pas un seul instant que les problèmes proviennent de l'émulateur PS3, il y a forcément quelque chose de plus subtil dans le lot... J'ai trois suppositions.

La première, c'est qu'il faut modifier aussi le Session Header, mais je n'y crois pas trop, car sinon ça chargerait pas du tout les textes. Tu vires carrément le Session Header du fichier ELF (mets que des 00), comme ça tu sauras s'il est réellement utilisé ou non dans le jeu. Ce n'est pas le cas sur PS2 par exemple.

La seconde supposition, c'est que tu places des textes trop gros à lire. Le texte va être forcément recopié dans la RAM à un moment donné et même peut-être plusieurs fois. Ces buffers ont la plupart du temps une taille statique (fixe). Si ces buffers sont trop petits pour caser le texte, ça va quand même copier le texte dedans mais déborder sur d'autres données derrière aussi et les écraser. Résultat, tu peux avoir tout un tas de bugs jusqu'à même un freeze du jeu. C'est un problème récurrent dans les jeux souvent chiant à résoudre.

Mais la 3ème supposition me semble être la bonne. Regarde bien les textes US dans l'ELF. Il sont TOUS alignés sur une adresse de 8 octets. Or, ce n'est pas ce que je vois sur les textes que tu as réinsérés dans "EBOOT_5Mo_freeze.ELF" ou même "EBOOT_5Mo_fonctionnel.ELF". Le processeur, au lieu de lire octet par octet, peut lire 8 octets par 8 octets en même temps, même pour du texte. Beaucoup de processeurs ont besoin d'avoir des données "alignées" en adresse. Si le programme lit 8 octets d'un coup, il faut impérativement que cette donnée soit alignée sur une adresse de 8 octets, signifiant qu'il faut que la valeur du pointeur soit un multiple de 8 octets. Sur la console originale, ne pas respecter cela conclut à un freeze immédiat lors d'une lecture ou écriture. Sur un émulateur, habituellement ça fonctionne sans problème, mais il y a des exceptions, j'en connais quelques-uns qui font des bugs très spéciaux si c'est pas bien aligné... Insère donc tes textes avec des pointeurs ayant TOUJOURS un multiple de 8 octets. Tu me diras ce que ça donne.

1) Ici, tu as eu la bonne réflexion de réutiliser un Phdr qui ne sert à rien comme le Phdr 3. Pour en ajouter un sinon, il faudrait déplacer une partie de l'ASM, ce qui est faisable mais un peu complexe à ton niveau je pense. Il faut désassembler la partie ASM, prendre la première fonction et la déplacer autre part. Cela demande de modifier aussi des valeurs dans la partie ASM. On peut peut-être aussi déplacer le Program Header, mais c'est pas sûr, même en modifiant son pointeur. Ça ne marche pas sur PS2 si je me souviens bien, j'avais testé. Mais vu que Phdr 3 peut idéalement être modifié, on ne va pas se priver.

L'erreur que tu as obtenue doit t'interpeller, pour moi il y a une erreur quelque part dans le Phdr 3. Regarde le p_flags :

Phdr 1 : p_flags = 0x00400005
Phdr 2 : p_flags = 0x00600006
Phdr 3 : p_flags = 0x00000004

Ce qui m'interpelle ici c'est la partie haute du p_flags de Phdr 3. En cherchant un peu, j'ai trouvé ça sur le net pour le ELF64 :

Table 17. Segment Attributes, p_flags

PF_X 0x1 Execute permission
PF_W 0x2 Write permission
PF_R 0x4 Read permission
PF_MASKOS 0x00FF0000 These flag bits are reserved for environment-specific use
PF_MASKPROC 0xFF000000 These flag bits are reserved for processor-specific use

Les trois premiers, on les connait. Ce qui nous intéresse ici, c'est le PF_MASKOS (reserved for environment-specific use). Tu devrais tester de le mettre comme dans le Phdr 2 en premier lieu puis comme dans le premier pour voir si ça passe. Ces valeurs ne sont pas inutiles à mon avis, donc :

Test 1 : Phdr 3 : p_flags = 0x00600004
Test 2 : Phdr 3 : p_flags = 0x00400004
Test 3 : Phdr 3 : p_flags = 0x00600006

2) Ici, ATTENTION ! Dans le fichier ELF, la zone de Phdr 2 prend 0x4D81DC octets, mais en RAM elle prend 0x817828 octets ! Toute la place en RAM sera utilisée, les 0x817828 octets, donc je ne suis pas étonné que ça n'ait pas fonctionné. En fait, seuls 0x4D81DC octets du fichier ELF vont être copiés car ce sont des données initialisées, elles contiennent une valeur de base. Le reste n'a aucune valeur de base, les données ne sont donc pas écrasées lorsque le jeu se lance, ça se fera plus tard par le programme lui-même.

Ton analyse de la RAM avec RPCS3 est une très bonne initiative. Si tu vois qu'en jouant un peu, les valeurs dans certains zones ne changent jamais, c'est qu'elles ne sont jamais utilisées. Le must reste de tester avec un débuggeur (mais il faut en avoir un pour ça) si la moindre donnée est lue ou non dans une zone que tu penses libre. S'il n'y a aucune lecture ou écriture effectuée après avoir joué un peu, t'es quasi certain de pouvoir l'utiliser. Ça pourrait être utilisé à un moment précis dans le jeu, mais là ce serait vraiment pas de bol et c'est rarissime ^^ On peut aussi vérifier ça en étudiant la partie ASM, mais c'est un peu complexe.

On peut donc effectivement utiliser l'espace que tu as vu pour ajouter du texte. Pour ton bloc de texte de 500-600 lignes, même problème qu'au-dessus, tu testeras en alignant les textes sur 8 octets.

Avatar de l’utilisateur
AtelierFlamel
Messages : 5
Inscription : 28 juil. 2020, 13:41
Re: [PS3] Atelier Ayesha - Comment extraire et injecter texte ou désassembler-assembler fichier SELF?

Message non lu par AtelierFlamel »

Tout d'abord, merci beaucoup pour tes explications une fois de plus RyleFury :) ! Honnêtement, si tu n'avais pas pris le temps de m'expliquer tous ces trucs, je n'en serais pas au stade où j'en suis aujourd'hui, j'aurais jamais pu commencer en fait X).

J'ai passé la semaine et demi dernière à essayer divers tests, et voici ce que j'ai pu conclure/déduire, et les nouvelles questions que j'ai. Par ailleurs, comme j'ai eu accès à la PS3, j'ai pu faire de nouveaux tests en conditions réelles, ce qui m'a permis d'éclairer certaines choses qui n'étaient pas claires sur l'émulateur.

Alors, pour commencer, j'ai supprimé le Section Header de l'eboot. Plus concrètement, pour être vraiment sûr, j'ai mis à zéros les variables du ELF Header lui étant dédiées (i.e. e_shoff, e_shsize, e_shnum, e_shstrndx), puis j'ai purement et simplement supprimé le Section header du fichier eboot : le deuxième bloc de programme s'arrêtait à 0x014A81DC (et le 3ème, 4ème et 5ème blocs nuls se trouvaient à cette adresse), et j'ai donc littéralement enlevé tout ce qu'il y avait derrière (des zéros puis le Section Header en 0x014AAE28), réduisant de par-là même la taille du ELF. Conclusion : que ce soit sur l'émulateur ou la PS3, le jeu se lance nickel, sans aucun bug ni problème apparent ! Donc tu avais raison, on peut oublier les sections, et ça nous facilite quand même la tâche de ne pas devoir les modifier également.

Je me suis ensuite penché sur le problème d'alignement sur les 8 octets, comme tu me l'as dit. Puisque pour le premier bloc de programme p_vaddr - p_offset = 0x10000 est congru à 0 modulo 8, il suffit que les adresses des textes soient elles-mêmes congrues à 0 modulo 8. Cependant, en calculant le reste module 8 de toutes les adresses originelles, je me suis rendu compte qu'en fait, toutes n'étaient pas congrues à 0. Je dirais qu'en pourcentage, on en avait 75% congrues à 0 et 25% congrues à 4. Il semblait donc plutôt que les adresses étaient plutôt congrues à 0 modulo 4. Je joins au post le fichier texte_modulo_8.txt (et comme d’habitude, dans l’archive ArchiveAyesha.zip correspondante, fournie en fin de post) contenant une liste sous la forme [Adresse (hexa), Adresse%8 (dec), Texte], peut-être qu’il y a quelque chose d'autre que je n’ai pas compris :
texte_module_8.txt
(545.15 Kio) Téléchargé 0 fois
Par exemple, je vois qu’on obtient comme reste 4 ou 0 mais toujours par gros blocs, je ne sais pas si cela veut dire quelque chose.

Dans tous les cas, puisque 4 divise 8, cela ne pourrait clairement pas poser problème si lors de la réinsertion, les nouvelles adresses du texte sont congrues à 0 module 8. C'est donc ce que j'ai fait, et j'ai voulu insérer le plus gros bloc de texte possible qui respectait ces congruences entre les deux premiers blocs, donc entre les adresses 0x00FC6BE8 et 0x00FD0000 - après changement adéquat des variables p_filesz et p_memsz du premier bloc de programme de 0x00FC6BE8 à 0x00FCFFF0 (j'ai laissé un peu d'espace entre les deux blocs à la fin pour être sûr).

Et là... patatras ! ça ne marche toujours pas, le jeu freeze lorsque je le lance et que j'ouvre le menu avec les quêtes, où apparaît donc la sempiternelle phrase-test "I got Nio's favorite candy from Ernie. I want to go to the Herb Garden to give it to her.", située originellement à l'adresse 0x00C98A24 et avec son pointeur à l'adresse 0x00C9EF50 (c'est fou comment à force on retient par coeur des choses inutiles, par exemple les adresses de textes et de pointeurs sur lesquels on passe trop de temps :lol: )

J’ai alors réduit le texte inséré de moitié, puis encore de moitié, etc. et c’est une fois arrivé à quatre lignes de texte réinsérées seulement et toujours le même freeze à l’ouverture du menu - que ce soit sur émulateur ou PS3 - que je me suis dit qu’il y avait sûrement autre chose. Et après un rapide examen des dites lignes, je me suis rendu compte que le vrai problème était ailleurs : j’avais des « faux-pointeurs », et c’est là que j’aimerais que tu m’éclaires s'il te plaît.

En fait, lorsque je cherche les pointeurs vers le texte, j’ai l’impression que je trouve aussi des séquences qui sont strictement égales à la valeur du pointeur, mais qui correspondent à des instructions ASM. De fait, lorsque j’insère mon texte, elles sont remplacées par un « pointeur » et donc le jeu plante évidemment !

Voici deux exemples de lignes obtenues avec mon script d’extraction où j’ai identifié à coup sûr le phénomène :

B53FA0|Ingr.|['10000', ['1356E2', '1112E80']]|INGR.

C3AD60|For my project, I'm gonna make a base!<CR>I want white and blue clay!|['10000', ['1DF139', '2EAD31', 'C46408']]|FOR MY PROJECT, I’M GONNA MAKE A BASE!<CR>I WANT WHITE AND BLUE CLAY!

J’ai grandement remodelé mon script d’extraction (j’y reviens un peu tard), la structure est cette fois la suivante :

« Adresse_texte (str, hexa)|Ligne_texte (str, txt)|[p_vaddr-p_off (str, hexa), Liste_adresses_pointeurs (str, hexa)]|Ligne_texte_traduit (str,txt) »

Je vais prendre pour exemple la seconde phrase, en 0x00C3AD60. Comme c’est juste pour ce test, j’ai allongé seulement un peu le premier bloc de programme, faisant passer p_filesz = p_memsz = 0x00FC6BE8 à p_filesz = p_memsz = 0x00FC80000 et ensuite j’ai inséré ladite ligne en 0x00FC800000, d’abord en modifiant les trois pointeurs en 0x001DF139, 0x002EAD31 et 0x00C48408, puis ceux en 0x002EAD31 et 0x00C48408, et finalement celui-uniquement en 0x00C48408. Le jeu se lance dans les trois cas de figure, mais dans le premier cas de figure il plante lorsque j’ouvre le menu une fois la partie chargée, alors que dans les autres cas il semble pleinement fonctionnel. Le fichier EBOOT_freeze.ELF qui plante, donc avec les trois pointeurs modifiés, est dans l'archive AchiveAyesha.zip jointe au post.

Ma première question est donc la suivante : y a-t-il un moyen d’identifier de manière précise les « faux-pointeurs », autrement que de tester au cas par cas ? En l’occurence, si j’en ai déjà trouvé deux, je dirais qu’il y en a d’autres, donc le faire à la main serait pénible.
J’ai une petite idée sur comment les identifier, mais je ne suis pas complètement certain qu’elle soit bonne : j’ai l’impression qu’il y a toujours un ordre de grandeur en moins pour le « faux-pointeur », qui se trouve être une instruction ASM du début du premier bloc de programme. Typiquement pour la ligne en 0x00B53FA0 le pointeur se trouvant en 0x001356E2 est faux tandis que celui en 0x01112E80 est vrai (en tout cas, en refaisant un test du même acabit qu’au-dessus, ça plantait quand je modifiais les données en 0x001356E2), pareil pour la deuxième ligne : 0x001DF139 est faux, et je suspecte que 0x002EAD31 l’est aussi et fera planter le jeu à un moment où à un autre.
J’ai dans l’idée de rajouter au script d’extraction une condition pour qu’il calcule le ratio des adresses des pointeurs, et s’il est supérieur à 0x10 qu’il enlève le plus petit pointeur, mais je me dis que ce n’est peut-être pas une science exacte et que je manquerais ainsi d’autres vrais pointeurs…

Par ailleurs, une fois éliminé ces faux-pointeurs, aligner le texte sur 8 octets ne semble en réalité pas essentiel : j’ai testé en alignant sur 4 octets, 2 octets et enfin en laissant simplement un octet d’espace, et tant qu’il n’y pas de « faux-pointeurs » dans le lot, le jeu ne semble vraiment pas planter.


Ensuite, je continue avec ta réponse concernant l’élargissement du ELF, notamment, l’agrandissement du bloc de programme n°3. Tout d’abord, comme j’ai enfin eu accès à ma PS3, j’ai voulu regarder un peu le contenu de la RAM de celle-ci, parce que l’émulateur c'est bien, mais ça ne reste qu’un émulateur (et on va revenir dessus très rapidement d'ailleurs). Là, j’ai pu notamment découvrir qu’il y avait en réalité des choses en 0x01820000 contrairement à ce que me disait RPCS3, donc j’ai abandonné cette zone. Par contre, j’ai trouvé qu’entre 0x01B3C800 et 0x01B70000 il ne semblait vraiment rien y avoir, même après avoir joué une bonne trentaine de minutes - comme la zone fait du coup 0x33800 (= ~210000 dec) caractères, cela paraît être une zone rêvée pour la réinsertion. J’ai donc rajouté le nombre d’octets nécessaires au fichier .ELF et j’ai agrandi le Phdr 3, en testant les différentes modification de flags que tu m’a proposées.

Et c’est là que j’ai découvert une autre subtilité qui m’a fait mettre le doigt sur un bug de l’émulateur (et il faudrait que je le rapporte à l’équipe de développement quand j’aurai le temps).

Cet autre point délicat, c’est que je ne peux pas choisir ce que je veux comme valeur de p_offset et p_vaddr, et ça, je ne l’avais pas saisi au début (j’aurais dû mieux lire la doc, je l’admets, mea culpa :( ) ! Je remets ici le Phdr 3 original :

p_type = 0x00000001
p_flags = 0x00000004
p_offset = 0x00000000014A81DC
p_vaddr = 0x0000000000000000
p_paddr = 0x0000000000000000
p_filesz = 0x0000000000000000
p_memsz = 0x0000000000000000
p_align = 0x0000000000010000

En fait, p_align nous indique que p_vaddr = p_offset modulo p_align, et donc si je mets des valeurs quelconques pour celles-ci, le jeu va évidemment planter dès le démarrage… ou tout du moins théorie.
Car suite à un malheureux hasard, la première fois que j’ai voulu agrandir le Phdr 3 en suivant ta réglementation pour les p_flags, j’ai mis des valeurs sans réfléchir pour p_offset et p_vaddr (de mémoire, c’était respectivement 0x014A9000 et 0x01820000), et le jeu s’est lancé sur émulateur pour les trois valeurs de p_flags que tu m’a proposé, i.e. 0x00600004, 0x00400004 et 0x00600006. Mais évidemment, le jeu crashait violemment sur la PS3. Le problème, c’est qu’une fois que j’ai compris le problème des p_align et que j’ai aligné les données, le jeu refusait de se lancer sur émulateur X) ! Bref, je crois que l’émulateur a un problème au niveau de la gestion de p_align, et veut davantage avoir p_vaddr = 0 modulo p_align (d'où avec 0x01820000 ça marchait…) que p_vaddr = p_offset modulo p_align.

Maintenant, en mettant le bon alignement (pour la PS3), il fallait trouver le bon flags: avec 0x00600004 la PS3 me sortait une erreur système au lancement (ce qui est déja bien mieux qu’un freeze pur et dur), et avec 0x00400004, le jeu s’est enfin lancé ! Après quelques tests où j’ai inséré un gros bloc de texte (justement celui de 500-600 lignes de mon post précédent, en prenant soin d’enlever les « faux-pointeurs » évoqués à la question précédente), tout semblait fonctionnel avec aucun freeze, donc je pense que je vais utiliser cette zone pour la réinsertion. Voici le fichier d’analyse de mon EBOOT modifié et fonctionnel :
eboot_allonge_fonc.txt
(2.82 Kio) Téléchargé 0 fois
Par ailleurs, j’essaierai plus tard d’utiliser aussi le Phdr 4 et Phdr 5 (qui sont aussi vides) pour éventuellement rajouter des zones en plus (parce que j’ai trouvé deux zones vides plus petites mais quand même intéressantes en RAM de la PS3), et je te dirai ce que ça donne.

Pour terminer, j’ai une dernière question, qui tient plus du double-check qu’autre chose : tu as dit que pour être sûr qu’une donnée n’est pas utilisée en RAM, on peut utiliser un débuggeur. Comment devrais-je m’y prendre pour en théorie pour faire ce test ? Je sais qu’il y a des histoires de breakpoint, mais je m’y connais pas vraiment, pourrait-tu m’éclairer stp ?

Pour résumer à peu près tout ce que j’ai appris :
- Le section header ne semble pas essentiel.
- L’alignement sur 8 octets ne semble pas essentiel.
- Il y a des faux-pointeurs dont il faut se débarrasser.
- Il est important de respecter l’alignement des Phdr et également utiliser le bon flag.
- On peut allonger le Phdr 3 en utilisant une zone vide de la RAM de la PS3 et cela ne semble pas faire planter le jeu.


Enfin, je voulais dire qu’en utilisant tout ce que j’ai appris, j’ai décidé de réécrire complètement le script python d’analyse et de réinsertion et de le rendre le plus général possible, pour pouvoir appliquer tout ce que tu m’as expliqué aussi bien pour les ELF de la PS3 que ceux de la PS Vita (et peut-être même à ceux de la PS2, vu que ça a l’air d’avoir une structure similaire). Une fois que ce sera fait, je vais le publier ici pour que tous puissent s’en servir - je pense que ça pourra être utile à ceux qui galèrent un peu comme moi sur des projets semblables.

Pour le moment, j’ai à peu près fini la partie analyse de l’ELF, et il sait faire les choses suivantes :
- Analyse de tous les headers de l’ELF, avec identification automatique du 32/64-bits et si c’est en little ou big endian, et adaptation en conséquence.
- Recherche de sections (ou de bloc de programmes s’il n’y a pas de sections) où le texte du jeu va se trouver, soit en lui fournissant une liste de mots que vous avez vu dans le jeu, soit en le laissant faire tout seul (j’ai rentré une liste de mots très communs en anglais, du type « yes », « no », « have », « look », etc.) ; cela permet en théorie d’être plus rapide pour ne pas chercher de texte dans tout le fichier. Une option existe aussi pour régler la précision (c’est à dire, retourner seulement la section avec le plus de texte qui matche la liste, ou bien la deuxième section qui matche le plus, etc.)
- Extraction de texte à proprement dite, soit automatiquement (j’ai mis une regex suffisamment générale pour sortir je pense la très grand majorité de tous les « vrais » textes (anglais) tout en évitant les faux positifs autres que les textes de déboggage), soit en fournissant soi-même une regex.
- L’extracteur de texte calcule automatiquement les différences d’offset p_vaddr - p_offset pour chaque ligne trouvée, et tente les deux approches que tu m’a présentées : soit chercher l’adresse du pointeur associée en cherchant la valeur addr_text + (p_vaddr - p_offset) (dans le bon encodage), soit s’il ne trouve rien chercher addr_text - p_offset (pour voir si les pointeurs ne sont pas dans la table de réallocation dynamique).
- Enfin, on peut lancer une analyse 100% automatique (il y a un fichier de config dans lequel on met les options qu’on veut), où l'on donne simplement le nom de l’EBOOT à l’algorithme, et il fait tout ce qui est décrit au-dessus d’un coup.
(Je précise quand même, mon script est taillé sur mesure pour l'analyse de jeux en anglais et d'EBOOT encodés en UTF-8. Je pense qu'on peut potentiellement l'étendre à d'autres systèmes d'encodage, quand j'aurai terminé ce projet, j'essaierai de l'adapter pour gérer plus de formats. Après, s'il est en UTF-8, la langue n'est pas trop une barrière, puisqu'on peut lui fournir soi-même la regex pour chercher le texte).

Je vais écrire une petite doc pour l’algo, et je le publie ici quand je peux !
En attendant, je vais aussi me mettre à recoder proprement la partie réinsertion maintenant, avec notamment comme tu m’as recommandé l’insertion du plus gros texte dans la plus petite zone, etc., mais puisque pour le moment tout semble bien marcher (excepté les « faux-pointeurs »), je vais commencer à m’atteler aussi à la partie traduction à proprement parler ^^

Voici le lien vers le dossier zippé (mot de passe "AtelierFlamel" (sans les guillemets)), qui contient :
- texte_module_8.txt : les adresses modulo 8 de toutes les lignes de texte
- EBOOT_freeze.ELF : l'EBOOT qui plante avec une seule ligne de texte réinsérée, mais avec des "faux-pointeurs" modifiés.
- eboot_allonge_fonc.txt : l'analyse de l'EBOOT dans lequel j'ai allongé le fichier, supprimé complètement le Section header et changé les Phdr de façon à aller chercher dans une partie supplémentaire de la RAM de la PS3


Répondre