Journal Python, encodage, rot13, brainfuck

Posté par  (site web personnel) .
Étiquettes :
45
27
oct.
2010
Bonjour chers amis.

Comme je n'ai pas (plus diraient certains) de blog, et que là je suis tellement
content de ce que j'ai trouvé que j'ai envi d'en informer le monde, et bien
voila, je m'adresse à toi, cher public (et non cher 'Nal).

Vous connaissez tous Python_(langage) comme étant le meilleur langage du
monde. Aussi vous devez savoir que les sources d'un bout de code python peuvent
être écrite dans n'importe quel Codage_des_caractères (genre ascii, UTF-8,
...).

Par exemple en faisant ceci :


# coding: utf8
print "Ceci est du code python encodé en utf8"


Jusqu'à présent je pensais qu'il s'agissait d'un comportement en dur dans
l'interpréteur en fait il n'en est rien, c'est beaucoup plus malin que cela.

Python définit un module codecs pour gérer les
différents codages de caractères, et c'est ce module qui est appelé par
l'interpréteur pour traduire en unicode le code que l'on a écrit. Chaque
encodage différent est représenté dans
/usr/lib/pythonX.Y/encodings par un fichier name.py
contenant les fonctions d'encodage et de décodage adaptées.

Ainsi il est possible d'écrire son code source dans n'importe quel encodage
supporté dans ce répertoire. Or il y en a un que j'utilise depuis longtemps
pour protéger ma vie privé (joke inside), c'est l'encodage [[ROT13]]. Un petit exemple en mode
interactif de python :


>>> u'Hello linuxfr.org'.encode('rot13')
'Uryyb yvahkse.bet'
>>> 'Uryyb yvahkse.bet'.decode('rot13')
u'Hello linuxfr.org'


Et là la question qui m'est venue ce matin c'est "Est il possible d'écrire un
code source en rot13 ?" Essayons :

Le code original:


for i in range(5):
print i, u"test"


La version rot13:


# coding: rot13

sbe v va enatr(5):
cevag v, h"grfg"


L'exécution :


$ python test_rot13.py
0 test
1 test
2 test
3 test
4 test


Petite remarque, si dans le code original l'on remplace u"test"
par "test", et bien cela fonctionne toujours, mais cela affiche
grfg en lieu et place de test. Cela vient d'une particularité de python. Les
chaînes commençant par u représentent une suite de caractère unicode encodés
dans le format du fichier. Ainsi lorsque python converti le code source en
unicode, il prend le contenu de la chaine, le décode avec l'encodage
spécifié, et forme ainsi une chaine unicode.

Dans le cas d'une chaine sans le préfixe u, il s'agit (en python 2), d'une
chaine d'octets purs qui doivent être insérés sans modifications dans le
programme. Ainsi cette chaine est convertie en unicode, donnant le code
"test". Mais lors de l'exécution, le parseur va lire cette chaîne
et se rappeler qu'en vrai il s'agit d'une chaine d'octets, et la reconvertir
dans l'encodage d'origine.

Comme dirait ma sainte grand-mère, Oh my god ! Bref, cette découverte m'a rendue
heureuse. Et soudain mon cerveau malade et pervers s'est demandé jusque où
cette perversion peut elle aller ?

Je regarde un peu comment ce système fonction, et globalement c'est tout simple, python commence le traitement du code source en ASCII, et dès qu'il trouve un #coding: name, il passe toute la suite du fichier à la moulinette de décodage, celle-ci devant normalement lui générer une chaine unicode correspondant à un code source python valide. Mais il n'est écrit nul part que le contenu du fichier doit être un code source python valide ! En fait n'importe quel contenu (voir même pas de contenu) est possible, si la fonction de décodage génère du code valide.

Ainsi je me suis lancé dans l'écriture d'un codec pour le [Brainfuck], langage idolé des foules. Je vous le copie paste en gros (attention, code ugly et hackish a mort, Don't do this at home kids !).

Le fichier /usr/lib/python2.6/encodings/bf.py :


import codecs

indent = 0
started = False

def bf_encode(input, errors='strict',
filename='', mode=0666):
return (str(input), len(input))

def bf_decode(input, errors='strict'):
global indent, started

if not input:
return (u'', 0)
content = input

if not started:
source = u'''
from collections import defaultdict
import sys

data_ptr = 0
memory = defaultdict(int)
'''.split(u'\n')
started = True
else:
source = []

def add(s):
source.append(u'\t' * indent + s)


for char in content:
if char == u'>':
add(u'data_ptr += 1')

if char == u'<':
add(u'data_ptr -= 1')

if char == u'+':
add(u'memory[data_ptr] += 1')

if char == u'-':
add(u'memory[data_ptr] -= 1')

if char == u'.':
#add(u'sys.stdout.write(memory[data_ptr])')
add(u'sys.stdout.write(chr(memory[data_ptr]))')

if char == u',':
add(u'memory[data_ptr] = ord(sys.stdin.read(1))')

if char == u'[':
add(u'while memory[data_ptr]:')
indent += 1
if char == u']':
indent -= 1
return (u'\n'.join(source) + u'\n', len(input))

class Codec(codecs.Codec):
def encode(self, input, errors='strict'):
return bf_encode(input, errors)
def decode(self, input, errors='strict'):
return bf_decode(input, errors)

class StreamWriter(Codec, codecs.StreamWriter):
pass

class StreamReader(Codec, codecs.StreamReader):
pass

def getregentry():
return (bf_encode, bf_decode, StreamReader, StreamWriter)


Le fichier source à exécuter :


# coding: bf
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.


Et son execution :


$ python test.py
Hello World!


Rendons hommage à un autre fou furieux qui à eu la même idée, mais qui s'en est
servi pour remplacer l'indentation significative de python par un système à
base d'accolades, [http://timhatch.com/projects/pybraces/]. Je me suis
largement inspiré de son code pour écrire le mien.


Pour conclure, cela ne sert à rien, c'est marrant, et on doit pouvoir écrire
des préprocesseurs pour python avec ;)

Je me suis bien marré, j'espère que vous aussi ;)
  • # Rhhaa, de balises

    Posté par  (site web personnel) . Évalué à 3.

    Gre, j'ai pas vérifié l'indentation.
    • [^] # Re: Rhhaa, de balises

      Posté par  (site web personnel) . Évalué à 4.

      et en même temps si tu n'avais pas coupé tes phrases pour aller à la ligne, ça aurait été plus agréable à lire sur un écran faisant plus de 800px de large...
      • [^] # Re: Rhhaa, de balises

        Posté par  (site web personnel) . Évalué à 2.

        Question de point de vu ;)

        En fait c'est mon éditeur qui coupe les phrases et après pour linuxfr j'oublie de remettre le bon formatage.

        En fait c'est tordu, parce que si l'on coche la case "ajouter des retours a la ligne", cela prend en compte mes retours a la ligne inutiles, et si l'on ne la coche pas, cela font le bronx.

        Bref toutes mes excuses pour cela.

        Sinon, simple remarque, mais sur un viewport de plus de 800 px de large, si tu laisse le texte couler normalement, cela ne fait pas des lignes de 400 caractères ? Personnellement mon cerveau décroche sur les lignes de plus de 80 caracteres (je crois que des études prouvent que la longueur moyenne de confort c'est 40em)
        • [^] # Re: Rhhaa, de balises

          Posté par  (site web personnel) . Évalué à 3.

          le problème n'est pas vraiment là
          C'est surtout que les navigateurs sont capables, en fonction de la css, de remplir correctement le texte et même de le justifier. Aussi si ma css me présente des lignes de 400 mots c'est que ma css est moisie. Mais si je la configure bien et que je la laisse faire, ben quelque soit mon écran l'affichage sera correct.
          Si par contre je prend un texte en dur à 80 (qui en outre a un aspect dégeu sur le bord droit) sur un écran plus petit, ça devient franchement crade.
      • [^] # Re: Rhhaa, de balises

        Posté par  (Mastodon) . Évalué à 2.

        C'est tout le contraire justement.
  • # Fonctionnalité qui tue

    Posté par  . Évalué à 4.

    Effectivement, cette fonctionnalité est tout-à-fait essentielle.

    Étant donné que Python est un langage facile et accessible, on peut supposer que la moitié des développeurs Python ne sait pas ce qu'est un encodage.

    Par corollaire, on démontre que cette "fonctionnalité" est presque complètement inutile pour tous les autres langages. Un bon préprocesseur suffit largement.

    Hein? Ouai, bon, avec toutes les civilisations qu'on a eu sur Terre, je vais bien trouver un calendrier sur lequel on est vendredi!!

    -----------------> [ ]
  • # nan mais vraiment

    Posté par  . Évalué à 10.

    je suis jaloux, je n'ai pas autant de temps à perdre, et comme dit l'autre c'est encore plus beau lorsque c'est inutile ^^

    Bon réflexion personnelles mise à part, c'est intéressant comme système; j'avais vu que des fichier perl pouvaient être encode en whitespace, le gars qui ouvrait un fichier ne vois que des espaces ou tabulation; donc rien.

    C'est tout ce genre de trucs qui me font aimer l'info.

    Il ne faut pas décorner les boeufs avant d'avoir semé le vent

  • # Faux espoir…

    Posté par  . Évalué à 10.

    « Bref, cette découverte m'a rendue heureuse. »

    Tiens ?!

    *scroll*

    « Guillaume »

    *soupir*

    --
    Les fautes de grammaire c’est le mal…
    • [^] # Re: Faux espoir…

      Posté par  (site web personnel) . Évalué à -2.

      Attend, là tu bouleverses toutes mes convictions.

      Le COD c'est découverte, donc c'est avant, donc il faut accorder, or c'est féminin ?

      Donc avec ce raisonnement (que j'ai eu en écrivant cet article, et j'étais tout fier de ne pas avoir, pour une fois, oublié d'accorder avec le COD), et bien l'accord est juste non ?

      Bon, ok, après réflexion, le COD c'est pas "découverte", mais "m", toutes mes confuses !

      N'empêche...
      • [^] # Re: Faux espoir…

        Posté par  (site web personnel) . Évalué à 4.

        Tu as parfaitement le droit d'assumer ta part de féminité...

        Il n'empêche que dans tous les cas, "heureuse" est un adjectif qui s'accorde avec le nom... rien à voir avec le "participe passé".

        On dira donc "la découverte qu'il a faite" alors qu'on dira "il a fait une découverte".

        Axel
        • [^] # Re: Faux espoir…

          Posté par  . Évalué à 2.

          Il y a aussi le verbe rendre qui est au participe passé, et accordé à tort. :)
      • [^] # Re: Faux espoir…

        Posté par  . Évalué à 3.

        Même réaction que nicolas...

        PS : Je ne suis pas sûr qu'il soit de bon ton d'utiliser des acronymes qui ne soient pas dans le champ lexical du domaine traité par le site. J'ai mis trop longtemps à comprendre que COD voulait dire Complément d'Objet Direct et non pas autre chose (comme "CODe". Je me suis dis qu'ayant pour "habitude" de mettre un 'e' de trop, tu n'étais pas lojn d'enlever aussi :) )
    • [^] # Re: Faux espoir…

      Posté par  (site web personnel) . Évalué à 1.

      Ça me rappelle une lecture "récente", que je recommande à tout le monde :
      http://www.megaconnard.com/le-billet-reproduction-du-jour-co(...)
  • # chiffrage ?

    Posté par  . Évalué à 4.

    et on peut chiffrer le code avec appel à une clef sur le réseau ?

    Si oui, python est le premier logiciel de script pour faire du code proprio...
    • [^] # Re: chiffrage ?

      Posté par  . Évalué à 4.

      Oui facilement, dans la mesure où peut mettre ce que tu veux dans super_decrypteur_de_code_super_proprio.py qui décodera tes fichiers munis de l'entête
      # -*- coding: super_decrypteur_de_code_super_proprio -*-

      Mais ton troll est trop gros pour que je te réponde sérieusement en rappelant que la faculté de lire un code n'est en rien lié à sa notre proprio/libre.
    • [^] # Re: chiffrage ?

      Posté par  (site web personnel) . Évalué à 4.

      Super bonne idée tient !

      Oui, cela doit être techniquement possible.

      Cependant, j'avoue que je ne voie aucun moyen de ne pas exposer le code python déchiffré après (il est forcement en mémoire à un moment donné).

      D'ailleurs j'en viens à me demander comment ils "sécurisent" ce type d'outil, les proprios ;)

      Sinon, il faudra penser à désactiver la génération du bytecode .pyc, parce que celui-ci est en "clair".

      Qu'entends tu par "de script"
      • [^] # Re: chiffrage ?

        Posté par  . Évalué à 2.

        D'ailleurs j'en viens à me demander comment ils "sécurisent" ce type d'outil, les proprios ;)

        Pour Javascript, il existe des obfuscateurs de code (à ne pas confondre avec les compresseurs).

        « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

        • [^] # Re: chiffrage ?

          Posté par  . Évalué à 10.

          Et Perl est naturellement obscur.

          Depending on the time of day, the French go either way.

          • [^] # Re: chiffrage ?

            Posté par  . Évalué à 3.

            Tandis que Python est obscurément naturel.

            Article Quarante-Deux : Toute personne dépassant un kilomètre de haut doit quitter le Tribunal. -- Le Roi de Cœur

    • [^] # Re: chiffrage ?

      Posté par  . Évalué à 3.

      Sauf que ton algo de déchiffrage doit être en clair et que, si tu récupère la clef et que tu as l'algo, tu récupère très facilement du code lisible. De plus, le décodage du code risque de plomber tes perfs si ton chiffrage est un peu compliqué.

      « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

    • [^] # Re: chiffrage ?

      Posté par  . Évalué à 3.

      Perl le faisait ya 10 ans
      • [^] # Re: chiffrage ?

        Posté par  . Évalué à 10.

        Etre aussi lisible que du Rot13?

        Depending on the time of day, the French go either way.

      • [^] # Re: chiffrage ?

        Posté par  . Évalué à 2.

        cf les modules ACME::*

        Par ex Acme::EyeDrops, Acme::ǝmɔA, Acme::Palindrome, Acme::Greek,... (sur cpan.org)
  • # Le pouvoir du libre c'est aussi de rendre les gens heureux !

    Posté par  . Évalué à 3.

    Journal très sympa qui démontre une fois encore le pouvoir d'une communauté libre !

    Car si python avait été propriétaire et fermé, jamais l'entreprise en charge du développement n'aurait proposé un codec rot13!

    Et jamais une centaine de personnes n'auraient pu sortir de la lecture de ce journal avec le sourire aux lèvres, amusés et heureux de cette découverte.

    Merci pour ce journal sympa comme tout.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.