Petite recherche sur le Web

Une petite recherche rapide sur google me donne ce lien : http://www.net-security.org/vuln.php?id=2464 . Le programme est donc encodé en XOR, opération qui consiste à remplacer tous les 0 par des 1 et inversement en fonction d'une clef. Par exemple :

Pour la suite suivante :

  • 01010101

Avec la clef suivante :

  • 00001111

Le résultat sera (0 xor 0 = 0; O xor 1 = 1; 1 xor 1 = 0) :

  • 01011010

Pour retrouver l'original, il suffit d'appliquer la clef d'origine :

  • 00001111

Ce méchanisme est facilement réversible et il reste juste à trouver la clef qui va bien.

Vous aurez remarque que si la clef est 00000000, le contenu ne change pas. Mais, si le contenu est 00000000, on pourra trouver la clef non ?

Premiers tests

Pour vérifier mon hypothèse, j'ai installé Perl et Perl2exe. J'ai crée un fichier perl pour voir et j'ai lancé la création d'un .exe. J'ai obtenu sans problème le fichier qui effectuait l'action correcpondant à mon script.

Après plusieurs manipulations, je me suis rend compte que la syntaxe du fichier de script n'est pas vérifiée lors de la création du script.

Grâce à /dev/null, j'ai crée un fichier contenant des 0 (pas le caractère 0, uniquement des bits à 0). Ensuite, en observant le contenu du fichier créé, j'ai pu observer la chaîne de caractères suivante : For more information visit www.indigostar.com (on peut la trouver sur le web donc je me permet de la poster ici).

ça avance un peu ...

Pour récapituler, j'ai la clef, j'ai la partie chiffrée, il ne reste plus qu'une chose à faire : bien positionner le texte chiffré par rapport à la clef.

Pour cette opération, j'ai utilisé un petit script python (La classe PEcrypt est de "Simon Peverett - January 2004"):

class PEcrypt:
    def __init__(self, aKey):
        self.key = aKey
        self.crc = 0
        for x in self.key:
            intX = ord(x)
            self.crc = self.crc ^ intX

    def Crypt(self, aString):
        kIdx = 0
        cryptStr = ""   # empty 'crypted string to be returned
        for x in range(len(aString)):
            cryptStr = cryptStr + \
                       chr( ord(aString[x]) ^ ord(self.key[kIdx]))
            kIdx = (kIdx + 1) % len(self.key)
        return cryptStr

if __name__ == "__main__":
    out = file("sortie.test", "wb")
    code = file("clef.txt", "rb").read()
    pe = PEcrypt(code)
    offset = 20583
    f = file("analyse.exe", "rb")
    f.seek(offset)
    lines = f.read()
    result = pe.Crypt(lines)
    out.write(result)

Le plus difficile à trouver est le "offset = 20583", J'ai repéré dans le fichier l'emplacement du fichier source puis j'ai ajusté l'emplacement en testant avec une boucle for (en faisant varier l'offset). L'offset en question devra être réajusté pour chaque cas.

Enfin le code source

Ensuite, dans la partie récupérée il faut couper la fin qui ne contient rien en rapport avec le code source.

Après avoir retrouvé le code source en question, je suis tombé sur le programme exe2perl ( D'autres l'avaient fait avant moi !) mais c'est limité à 256 caractères.

Pour les intéressés, je peux vous envoyer le code source (c'est du GPL...), je ne le publie par sur mon site par politesse car il n'est pas de moi.

Conclusion

En guise de conclusion, je dirai simplement qu'il est difficile de cacher le code source d'un langage interprêté, que ce soit perl, python ou un autre. Afin de ralentir la relecture du code il existe des outils qui rendent le code "inpénétrable" temporairement (le temps de l'analyse). Ce sujet a été débattu plusieurs fois sur fr.comp.lang.python sans aboutir à une solution miracle.

Peut être que l'avenir des langages de script n'est pas dans le logiciel propriétaires mais dans les logiciels libres (qui en usent déjà largement).

EDIT : Petite discussion avec Maxime sur l'origine du .exe.