Bonjour,
je travaille sur un projet ou je dois modifier un fichier lors de sa propre exécution (je change les opcodes pendant l'exécution du binaire).
Mon problème et que je charge des opcodes non-valides et que l'interpreteur me renvoie l'exception :
illegal instruction
J'essaie de contourner cette limitation, j'ai pensé à mettre une exception (exit), et de la modifier ensuite en une instruction ne faisant rien.
J'ai remarqué que le processeur charge en cache des groupes d'instruction, ce qui fait que je dois laisser un certaines nombres d'opcodes entre mes modifications.
Ce qui me surprend et que mon programme fonctionne avec GDB, car celui-ci interprète ligne par ligne et n'effectue que des opcodes valides, alors que l'interpréteur lui ne voit pas que les opcodes seront ensuite modifié. Mais lorsque j'essaie de le contourner en modifiant des instructions alors la il arrive à me suivre...
J'aurai besoin d'information sur l'interpréteur, principalement savoir comment il vérifie qu'un programme est valide, utilise-t-il le processeur ou un check logicielle ?
Merci d'avance pour votre aide.
Manticore
OS : Linux armadeus 2.6.29.6
support matériel : APF27
# je penses qu'il nous manque plein d'infos
Posté par NeoX . Évalué à 2.
quel langage ?
quelle techno (compilée, interpretée) ?
en effet, tu parles de l'execution du binaire on peut donc en deduire que c'est un langage compilé
mais tu cherches à savoir comment fonctionne l'interpreteur, ce qui est en contradiction avec la ligne precedente.
bref, dis nous plus precisement ce que tu fais et avec quel langage, on aura peut-etre des pistes pour t'aiguiller.
# pourquoi faire comme ça ?
Posté par kuroineko . Évalué à 2.
la methode est etrange de mon point de vu... mais bon ... y'a longtemps que j'ai pas codé...
J'aurai bien vu un chargement en memoire le binaire réel généré par le binaire executable et le lancer dans un processus fils tout connement ?? (genre donnée encapsulée dans le binaire executable)...
un peu comme quand on codait des 4kdemos avec vidéos sur atari...on intégrait les vidéos et images dans le binaire executé sur le secteur de boot de la disquette....
ca évite tous les problèmes d'interpretation et ton binaire reste un encrypté avec cette structure là....
# Interpréteur ?
Posté par Obsidian . Évalué à 4.
Tu parles de quel interpréteur ?
Tu modifies tes opcodes de quelle façon ? Si c'est en modifiant le fichier proprement dit, alors bien que celui-ci soit mappé en mémoire par le système, cette opération n'est qu'une vue de l'esprit et code réellement exécuté par le micro-processeur ne sera modifié qu'à partir du moment où le système aura pu reprendre la main au moins une fois et en profiter pour faire la mise à jour. Et encore, seulement s'il décide de la faire et s'il n'estime pas que les deux images sont désormais distinctes entre elles.
Ça dépend aussi de ce que tu utilises pour modifier ton fichier. Si c'est un fwrite() ou un fprintf() en C standard, alors tout ce que tu vas écrire va aller dans un tampon propre à ton processus, et l'appel système qui va effectivement modifier le fichier n'aura lieu que plus tard.
Tout cela dépend donc de beaucoup de points différents, surtout quand on tient compte du fait que le système est en principe conçu pour que ce genre de manip' soit interdite. Donc, il ne faut pas s'attendre à ce que les choses soient faciles.
# complément d'informations
Posté par manticore . Évalué à 1.
Désolé je suis conscient que j'étais un peu vague, j'étais trop plongé dans mon projet quand j'ai écris ce post.
Alors dans l'ordre :
> en effet, tu parles de l’exécution du binaire on peut donc en déduire que c'est un langage compilé
Oui je parle d'un fichier compilé (elf), donc en langage machine (opcode). Pour moi l’interpréteur est le "logiciel" qui lance l'exécutable (commande ./). Si je fais une erreur de sémantique, peux-tu me donner les termes correctes ?
Donc en gros je récupère un fichier binaire, compilé dans un langage compilé (c, c++...), je le modifie avec un script python pour y injecter du code, en modifiant les sections, segments pour garder la structure du fichier elf. Cette partie fonctionne très bien.
Oui, c'est vers cela que je vais me diriger, mais j'avais envie de comprendre dans les détails pourquoi ma première idée ne fonctionne pas.
Je modifie directement les opcodes chargé en ram.
Je modifie directement le fichier avant l'exécution.
En résumé :
1. Je reçois un fichier binaire compilé
2. Je le patch (chiffrement + modification du point d'entrée, ajout de la méthode de déchiffrement)
3. Lorsque j'exécute le fichier, il est chargé en mémoire et ma routine de déchiffrement prend le relais pour aller modifier les opcodes chargés en mémoire. (Cela fonctionne aussi)
Justement j'essaie de me documenter sur les limitations que le système met en place ;).
Merci de votre aide
[^] # Re: complément d'informations
Posté par Obsidian . Évalué à 3. Dernière modification le 15 février 2012 à 14:39.
Avant toute chose : chaque commentaire est muni d'un lien « répondre » après le texte. Il faut l'utiliser quand tu réponds à une personne, pour que les différents commentaires soient bien organisés en arbre.
Pour le reste :
Un « interpréteur » est un système qui lit des instructions, les unes à la suite des autres, et qui les « interprète », c'est-à-dire qui va exécuter, généralement au moment où il les lit, une action correspondante. En ce sens, le CPU interprète du code en langage machine, mais on n'utilise jamais le terme dans ce sens-là, justement pour éviter des confusions.
Un interpréteur est donc, en pratique, un logiciel qui est déjà en cours d'exécution et donc la fonction est de lire et d'exécuter, pendant sa propre exécution, un programme transmis par une source donnée (généralement un fichier, mais ça peut être aussi l'entrée standard, ou autre). Ça va donc concerner des langages comme le Shell, les vieux BASIC, le Perl, le PHP, etc.
C'est de la même façon qu'en musique, un interprète est un musicien qui va exécuter un morceau, qu'il lit sur une partition.
Le système chargé de reconnaître la nature d'un fichier et, s'il est exécutable, soit de l'exécuter directement, soit de démarrer l'interpréteur approprié n'a pas, à ma connaissance, de nom académique, mais tout le monde parle de « lanceur », tout simplement.
Oui mais comment ? En principe, un segment de code est protégé en écriture. Tu devrais obtenir systématiquement une segfault si tu n'as pas obtenu explicitement les droits d'accès dessus, et ça ne se fait pas de façon simple.
Que tu patches le fichier exécutable avant lancement et que tu y ajoutes une routine, OK. Par contre, à l'exécution, la routine ne devrait pas pouvoir modifier directement le code sans avoir obtenu les privilèges nécessaires. Tu es sûr que c'est bien le code que tu modifies alors, et pas un segment de données quelconque ?
[^] # Re: complément d'informations
Posté par manticore . Évalué à 0.
Désolé, j'avais pas compris, j'ai plutôt l'habitude de voir des forums en liste.
Il suffit de changer les droits du segment, cela se fait assez facilement.
merci je parlerai de lanceur désormais, (laucher en anglais ?). J'ai utilisé ce terme à défaut d'avoir trouvé le terme adéquat.
En tout cas, lorsque j’exécute mon code au débugger (ida ou gdb), mes instructions changes et j'arrive à dévier le flux de mon programme de la façon suivante :
entry_point -> routine_dechiffrement -> exception(exit)
en
entry_point -> routine_dechiffrement -> branch_old_entry_point
[^] # Re: complément d'informations
Posté par -=[ silmaril ]=- (site web personnel) . Évalué à 2.
As-tu regardé les projets similaires à ton besoin (si j'ai bien tout compris) d'outil de compression ELF, exemple "UPX" (http://upx.sourceforge.net/).
Ce sont des outils qui compressent le binaire initial, y ajoutent une routine de decompression et regenerent un ELF après.
A l'execution c'est la routine de decompression qui prend la main initialement, elle charge et decompresse le code binaire initial puis "branch" dessus.
[^] # Re: complément d'informations
Posté par manticore . Évalué à 1.
Oui, il s'agit de packers, suite à mes échecs répétés, j'ai décidé d'en porter un pour arm:
packer elf par nibbles
Mais je suis toujours intéresser à comprendre pourquoi ma première idée ne fonctionne pas. Je vais cependant la laisser un peu de côté et je l'attaquerai à nouveau lorsque j'aurai pris un peu de recul.
Merci
[^] # Re: complément d'informations
Posté par manticore . Évalué à 1.
J'ai enfin trouvé d'où venait mon problème : l'architecture de Harvard utilisé par Arm (contrairement à Intel qui utilise du Von Neumann).
Dans notre cas on s'intéresse surtout à la gestion du cache, avec l'architecture de Harvard, nous en avons deux distinct, l'un pour les datas l'autre pour les instructions.
Ainsi en faisant du code auto-modifié, certaines données sont considéré comme des datas et ne sont pas mise à jour. Il est possible de forcer cette mise à jour grâce à un appel de la fonction (gcc) suivante :
Code : Tout sélectionner
void __clear_cache(char* beg, char* end);
# c'est sensé fonctionner ?
Posté par steph1978 . Évalué à 3.
Pour aller dans le sens de ce que dit Obsidian, les instructions à exécuter se baladent un peut partout: disk, ram, cache, pipe du cpu.
Si tu les modifient à un endroit (ram ?) qu'est ce qui les modifie ailleurs ?
Ce que tu cherche à faire me fait penser à ce que l'on peut faire avec du bytecode java et qui est utilisé par exemple en programmation par aspects. Mais dans ce cas, tu as toute une artillerie pour y parvenir: jvm, bytecode, classloader, security manager, etc.
Bref, c'est prévu dans la théorie de Turing, mais dans les faits, pas persuadé que cela fonctionne avec la machinerie ELF.
# oui
Posté par manticore . Évalué à 1.
J'imagine que c'est faisable, le principale soucis est de déchiffrer avant que le processeur charge en mémoire les instructions, comme je travail sur de l'embarqué avec des processeurs ayant des caches de quelques ko, ça me semble largement faisable.
Hormis le cache, je ne vois pas ou les instructions peuvent se cacher, en tout cas, pas sur le disque (il peut y avoir un sys.read(), mais c'est l'appel que je chiffre pas les datas).
Oui cela doit fonctionner (en tout cas, j'ai fais plusieurs tests fonctionnels, ou je change dynamiquement une levé d'exception en branch).
[^] # Re: oui
Posté par totof2000 . Évalué à 3.
http://www.laurentbloch.org/Livre-Systeme/livre006.html#toc32
http://jve.linuxwall.info/ressources/taf/linux_ps-et-scheduler.pdf
http://sebastien-viardot.imag.fr/Enseignements/SEPC/Documents/TD_shell.pdf
http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html
http://cosy.univ-reims.fr/~kabdurusul/enseignement/INFO0401/TP2/TP2-joshua.pdf
http://pwet.fr/man/linux/appels_systemes/fork
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.