Logo Blog perso d'Ozwald

Packons (ou pas) avec miasm et elfesteem

Par Oz le - Sécurité
Hack Outil Reverse Engineering code source miasm python scripts

Comme beaucoup le savent à présent MIASM est un framework d'ingénierie inverse écrit en Python par Fabrice Desclaux. Pour ma part j'avais joué un petit peu avec il y a un an, mais j'étais finalement assez rapidement passé à autre chose devant mon incapacité à porter un "packeur" maison de ''pefile'' jusqu'à ''miasm''. Aujourd'hui, je re-tente la même tâche !

Engine - Creative Common by "cbowns" on Flickr

Il y a un an donc j'avais eu des soucis quand j'avais voulu porter un concept simple de ''packeur'' sur le framework miasm. En effet la base de tout ''packeur'' c'est d'ouvrir un fichier à traiter (dans mon cas un fichier PE), lui apporter des modifications, puis le sauvegarder pour obtenir un clone fonctionnel de notre fichier à traiter (clone dont la signature binaire sera pourtant différente). Le problème c'est que, même en n'apportant aucune modification à mon fichier cible, elfesteem semblait incapable de générer un clone fonctionnel. Par exemple le code suivant, qui est censé n'apporter aucune modification à l'exécutable passé en argument, me retournait systématiquement un clone que Windows refusait de démarrer :

import sys
from elfesteem.pe_init import PE

e = PE( open(sys.argv[1]).read() )

open( sys.argv[1]+'_modified.exe', 'wb').write(str(e))

De mémoire j'avais testé sur "winmine.exe", sur "calc.exe", et sur "notepad.exe" : à chaque fois l'exécutable modifié ne fonctionnait plus :-(

Un an après, re-motivé par la super conf de serpi au SSTIC, je réinstalle smiasm sur ma machine1 et je re-tente. Tristesse : le bilan est le même. Les exécutables que j'ouvre puis que je sauvegarde avec elfesteem ne fonctionnent plus (enfin en tout cas "calc.exe", qui est ma cible de test préférée, donne systématiquement des clones "cassés"). Mais cette année, j'ai décidé d'investiguer un petit peu plus ! Retroussons nous les manches et voyons voir ça de plus près. D'abord assurons nous que "calc.exe" et "calc.exe_modified.exe" sont bien différents (et que nous n'avons donc pas à faire à un simple saute d'humeur de windows) : diff calc.exe calc.exe_modified.exe nous confirme que les fichiers sont différents.

Pour y voir de plus près on va regarder ces deux spécimens en hexa (xxd calc.exe > calc.exe.XXD && xxd calc.exe_modified.exe > calc.modified.XXD && diff *.XXD) :

5,14c5,14
< 0000040: 0e1f ba0e 00b4 09cd 21b8 014c cd21 5468  ........!..L.!Th
< 0000050: 6973 2070 726f 6772 616d 2063 616e 6e6f  is program canno
< 0000060: 7420 6265 2072 756e 2069 6e20 444f 5320  t be run in DOS 
< 0000070: 6d6f 6465 2e0d 0d0a 2400 0000 0000 0000  mode....$.......
< 0000080: 8745 1664 c324 7837 c324 7837 c324 7837  .E.d.$x7.$x7.$x7
< 0000090: 3907 3837 c624 7837 1907 6437 c824 7837  9.87.$x7..d7.$x7
< 00000a0: c324 7837 c224 7837 c324 7937 4424 7837  .$x7.$x7.$y7D$x7
< 00000b0: 3907 6137 ce24 7837 5407 3d37 c224 7837  9.a7.$x7T.=7.$x7
< 00000c0: 1907 6537 df24 7837 3907 4537 c224 7837  ..e7.$x79.E7.$x7
< 00000d0: 5269 6368 c324 7837 0000 0000 0000 0000  Rich.$x7........
---
> 0000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
21c21
< 0000140: 00f0 0100 0004 0000 fcd7 0100 0200 0080  ................
---
> 0000140: 00f0 0100 0004 0000 b16f 0200 0200 0080  .........o......
39,46c39,46
< 0000260: 0ffe 7d3b 3800 0000 0efe 7d3b 4400 0000  ..};8.....};D...
< 0000270: 0efe 7d3b 4f00 0000 0efe 7d3b 5c00 0000  ..};O.....};\\...
< 0000280: 0efe 7d3b 6900 0000 0efe 7d3b 7300 0000  ..};i.....};s...
< 0000290: 0000 0000 0000 0000 5348 454c 4c33 322e  ........SHELL32.
< 00002a0: 646c 6c00 6d73 7663 7274 2e64 6c6c 0041  dll.msvcrt.dll.A
< 00002b0: 4456 4150 4933 322e 646c 6c00 4b45 524e  DVAPI32.dll.KERN
< 00002c0: 454c 3332 2e64 6c6c 0047 4449 3332 2e64  EL32.dll.GDI32.d
< 00002d0: 6c6c 0055 5345 5233 322e 646c 6c00 0000  ll.USER32.dll...
---
> 0000260: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000270: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000280: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 0000290: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00002a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00002b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00002c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
> 00002d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

Voilà donc l'intégralité des changements entre les deux fichiers. Sur 112ko ça fait peu, on devrait pouvoir être capable d'identifier précisément ce qui ne fonctionne plus :)

Le premier bloc de différence se situe entre les octets 0x0000040 et 0x00000d0. La position (en tout début de fichier) et le contenu ("...This program cannot be run in DOS mode...") de ce bloc me font penser qu'il s'agit uniquement du "DOS stub"2. Bref : pas intéressant.

Le second bloc de différence est beaucoup plus petit puisqu'il n'impacte que la ligne de notre dump commençant à l'octet 0x0000140. Sur toute cette ligne on a 3 octets de différence. Si jamais ces octets participent à une indirection quelconque on a peut-être trouvé notre coupable :) ! Après un petit tour dans "Lord PE" je réalise que, malheureusement, ce n'est probablement pas là que se situe le coupable. En effet la valeur du checksum de "calc.exe" est "0001D7FC" et celle de "calc.exe_modified.exe" est de "00026FB1". Cette suite d'octets correspond donc probablement au checksum du fichier contenu dans l'en-tête NT_HEADER. Comme le DOS-stub a été supprimé il est normal que la valeur de checksum soit différente; de plus il me semble qu'un checksum erroné n'empèche pas le lancement de programme simples (pour les drivers c'est une autre histoire, mais pour un soft user-land je ne crois pas que Windows vérifie). Pour avoir la conscience tranquille je modifie quand même la valeur du checksum dans "calc.exe.XXD" puis je reconstruit un binaire (xxd -r calc.exe.XXD > calc.exe.badchecksum.exe) et je peux confirmer que cette copie de calc.exe dont j'ai uniquement modifié le checksum pour en mettre un invalide fonctionne :)

Nous arrivons ainsi au troisième bloc, qui est devenu notre suspect numéro 1. En plus, les quelques chaines de caractères qui ont été remplacées par des octets nuls ont bien la tête d'informations provenant d'une table d'importation ("msvcrt.dll" , "KERNEL3.dll", etc.) ce qui pourrait expliquer le non-fonctionnement du programme si ces informations ont été altérées. Malheureusemnt "Lord PE" me donne des tables d'importations identiques entre les deux fichiers "calc.exe" et "calc.exe_modified.exe", et je ne trouve pas d'outil sympa pour disséquer simplement un PE :-( ...Qu'à celà ne tienne : on va en pondre un maison, à la porc bien entendu ! L'idée est simple : visualiser les structures du fichier PE simplement afin d'identifier à quelle structure appartient la zone d'octets modifiés.

Pour réaliser cet outil, quick&dirty rappelons-le, je vais faire appel à une poignée de fidèles compagnons :

  • VIM/gedit : parce que ce sont d'excellents éditeurs de code :)
  • C : parce que pour les manipulation "bas niveau" on n'a pas encore fait mieux que le bon vieux C.
  • Python : parce que pour les prototypages rapide c'est le must.
  • Python Imaging Library (PIL) : parce qu'on veut "visualiser les structures du fichier PE" et que c'est la meilleure librairie de manipulation d'image en python que je connaisse.

Enfin, en guise d'aide mémoire, on utilisera aussi les excellents tutos d'Iczelions sur les entrailles du format PE et quelques URL de références de chez Krosoft. Au final j'ai obtenu en un aprem de jeu :

* un prog en C qui analyse un fichier PE et dump ses caractéristiques sur la sortie standard (en prime j'ai rajouté un .h contenant les définitions des structures Windows, bien que j'aurai pu utiliser winnt.h qui doit se trouver quelque part sur ma machine étant donné que j'ai une gentoo avec de quoi compiler pour des centaines de systèmes exotiques, mais j'avais la flemme de le chercher :-D)
* un prog en python qui lance le prog en C sur un exécutable passé en argument, parse la sortie obtenue, et génère deux <s>jolies</s> images permettant de visualiser quelle partie du fichier appartient à quelle structure.

Toutes les sources sont en pièce-jointe de ce billet, donc n'hésitez pas à jouez vous aussi (mais n'oubliez pas : j'ai codé ça comme un goret donc si ça ne marche pas chez vous "c'est normal", et si vous le lancez sur des PE de plusieurs Mo il est probable que votre RAM soit intégralement vampirisée, vous êtes prévenus)(NDLR : utilisez plutôt la v4 qui génère directement un svg et qui est donc infiniment plus performante).

La première image est une représentation à l'échelle des différentes structures du fichier PE sur disque (pas tel qu'il sera mappé en mémoire donc). La seconde image n'est qu'une représentation "compressée" de l'image précédente où j'ai inséré des séries de "petits points" pour remplacer visuellement les énormes applats de couleurs unis représentant des zones de fichier où "rien de spécial ne se passe" et qui rendent le premier type d'image carrément gigantesque (1200x14688 pour représenter "calc.exe" dans mon environnement de test ;) ). Voilà comment on s'en sert :

$ gcc -o a.out oz_pedissector.c
$ python oz_pedissector3.py calc.exe
INFO:root:PE zone discovered [ZONE : IMAGE_DOS_HEADER   En-tete DOS 0   64]
INFO:root:PE zone discovered [ZONE : IMAGE_NT_HEADERS   En-tete NT  240 488]
INFO:root:PE zone discovered [ZONE : SECTION_HEADERS[0] En tete de section ".text"  488 528]
INFO:root:PE zone discovered [ZONE : SECTION[0] Contenu de section ".text"  1024    76800]
(...)
INFO:root:Compression second pass 98%
INFO:root:Compression second pass 99%
INFO:root:Compression second pass finished.

Nous voilà à présent avec deux fichiers ".png", l'un étant la représentation à l'échelle, l'autre étant la représentation compressée. Voyons voir ce que donne l'image "compressée" générée automatiquement à partir du "calc.exe" de mon environnement de test (je vous invite à l'avoir sous les yeux en taille normale lorsque vous lirez le paragraphe suivant) :

Oz PE dissector (Compressed Example file) - Creative Common CC-BY-SA by Ozwald

En la regardant de haut en bas on retrouve bien :

  • L'en-tête MS-DOS commençant à l'adresse 0x0 et allant jusqu'à 0x40
  • Une zone noire (représentation graphique de "on ne sait pas ce que c'est") juste en dessous. C'est le DOS Stub.
  • l'en-tête NT commençant à 0xF0 et allant jusqu'à 0x1E8 (on se rappelle à ce moment là que le second bloc de différence était situé aux alentours de l'adresse 0x140 et que nous avions supposé qu'il s'agissait du checksum contenu dans l'en-tête NT, nous avons à présent confirmation que cette adresse est bien dans l'en-tête NT ce qui conforte notre hypothèse s'il en était besoin)
  • Les trois en-têtes de sections qui suivent l'en-tête NT
  • Une seconde zone noir juste en dessous...on en reparlera.
  • La première section (".text") qui va de 0x400 à 0x12C0. On notera (sur la droite de l'image) que cette section contient les zones mémoires suivantes appartenant au DATA_DIRECTORY : Import Address Table, Debug, Import Symbols.
  • La seconde section (".data") qui ne contient rien de spécial.
  • La troisième section (".rsrc") qui contient, on s'en doutai, la zone spéciale "Resources" du DATA_DIRECTORY.

Bref : tout va bien :) Tout va tellement bien d'ailleurs que je tombe d'accord avec elfesteem : il ne devrait rien y avoir d'important entre l'adresse 0x260 et 0x4003...Le mystère reste donc entier4.

Edit du lundi 9 Juillet 2012, 14h53 : je remplace les 3 fichiers sources joints au billet par une archive unique contenant le tout, ça évitera les 403 imposés par mon hébergeurs sur les fichiers d'extension ".py" -_-

Edit du mardi 10 Juillet 2012, 17h30 : après vérification sur d'autres versions de "calc.exe" (provenant de diverses versions de WinXP) on retrouve toujours ce bloc non nul entre la fin des en-tête de section et le début de la première section... Etrange... Par contre je viens seulement maintenant de vérifier un exécutable qui serait plus typiquement ce que j'aurai besoin de packer5, à savoir gsecdump, et il ne contient bien que des octets nuls entre la fin de ses en-tête de section et le début de sa première section. Donc à priori il devrait pouvoir être ouvert/enregistré par elfesteem sans être "cassé". Je teste ce soir :) ! Accessoirement je rajoute en PJ la version 4 du script dégueu programme permettant la visualisation des structures de fichiers PE. Il est maintenant infiniment moins gourmand en mémoire (forcément: il génère du svg directement compressé au lieu de générer un bitmap "taille réelle" puis de le compresser...); je l'ai testé sans douleur sur un binaire de 13Mo (en lui demandant cependant de n'afficher que les structures de plus de 40 octets).

Edit du mardi 10 Juillet 2012, 20h30 : A force d'avoir l'évidence sous les yeux (à savoir que je ne vois aucune raison pour lesquelles elfesteem ne pourrait pas légitimement écrire des bytes nuls sur ces zones du fichier) j'ai re-testé l'ouverture/sauvegarde simple mais sur plusieurs binaires ne provenant pas de Microsoft cette fois (à savoir : gsecdump-v2b5.exe, ftpserv.exe, notepad++.exe, 7zFM.exe, python.exe) et ils ont tous fonctionné à merveille après leur passage dans elfesteem. La conclusion du premier chapitre de ce mystère s'écrit donc d'elle même : elfesteem fonctionne très bien, c'est le compilateur utilisé par microsoft qui produit des résultats "exotiques" (et je suis un gros c*uillon de ne pas m'en être rendu compte il y a un an -_- ...).

Edit du mercredi 11 Juillet 2012 : Serpi a trouvé la solution (lisez les commentaires du billet pour les détails). Le problème venait de bound imports..


Gorgonite le 2012/07/09 10:53

Allez, je tente ma chance :

open(sys.argv[1],'rb')
au lieu de
open(sys.argv[1])

nb: je ne peux pas tester au boulot ^^

Ozwald le 2012/07/09 12:02

La vache tu es rapide à répondre :-D !
Alors l'idée était intéressante mais je viens de tester et, malheureusement, j'obtiens le même résultats avec et sans le 'rb' :

$ md5sum  calc.exe\*
829e4805b0e12b383ee09abdc9e2dc3c  calc.exe
bab1aecd69fa7e93d03da67b8601faf4  calc.exe_modified.exe
bab1aecd69fa7e93d03da67b8601faf4  calc.exe_gorgonite.exe

Décidément plus j'y réfléchi plus je me dit que ça doit avoir à faire avec la table d'importation. Vivement que j'ai un peu de temps pour me re-pencher sur le souci :) !

Baboon le 2012/07/09 13:48

File les binaires ! (les 2 calcs)
J'ai jamais réussit à installer miasm chez moi et le SDK de win veux même plus s'installer non plus...)
(vais regarder ton précédent post tient)

serpilliere le 2012/07/10 15:38

Yop!
C'est peut être normal. Sous windows 7:
* tu copies calc.exe de c:\\windows\\system32
* tu la colles sur le bureau
* tu lances celle sur le bureau
* *surprise* => segfault.

il a un problème pour retrouver ses petits dans les ressource de LANG FR s'il n'est pas lancé depuis c:\\windows\\system32

Après si tu es sous XP, ca devrait fonctionner :/

Ozwald le 2012/07/10 20:51

Merci à tous pour vos réactions !

@baboon> Tu as les deux binaires dans tes mails (si l'adresse que j'ai utilisé est la bonne).

@serpi> Je testais sous WinXP :-/ En fait (je répète un peu mon dernier edit, mais s'pas grave) je ne vois toujours aucune raison pour lesquelles les binaires générés ne marchent pas. Du coup j'ai testé avec pas mal de PE non-Microsoft et ils fonctionnent tous à merveille. On peut donc plus ou moins dire que mon problème est résolu :) Mais niveau curiosité intellectuelle le mystère reste quand même entier : toutes les versions de "calc.exe" ou de "winmine.exe" sur lesquels j'ai pu mettre la main (WinXP, et 7), avaient cette caractéristique étrange d'embarquer, entre la fin des headers de section et le début de la première section, une partie binaire non-nulle qui semble essentielle au fonctionnement de l'appli (et qui, entre autre, répète la liste ASCII des DLL utilisées dans la table d'importation). Bref on peut renommer le mystère de "pourquoi elfesteem ne parvient pas à ouvrir/sauvegarder mes binaires de tests ?" en "pourquoi le compilateur/linkeur de microsoft fait des choses étranges ?" ^_^

serpilliere le 2012/07/11 08:53

Ok. c'est les bound imports.
En fait de base, elfesteem ne parse pas (encore) les bound import, du coup quand il rebuild, il les met à 0, par contre, il laisse l'entrée dans l'import directory.
résultat: quand tu lances, certaines fonctions ne sont pas fixée dans l'iat.

petit fix rapide: il faut transformer ton code en :

import sys
from elfesteem.pe_init import PE, pe


fname = sys.argv[1]
e = PE(open(fname).read())
e.NThdr.optentries[pe.DIRECTORY_ENTRY_BOUND_IMPORT].rva = 0
e.NThdr.optentries[pe.DIRECTORY_ENTRY_BOUND_IMPORT].size = 0
open(fname+".mod", "w").write(str(e))

bon ok: je vais fixer le tripou soit pour qu'il les parse, soit qu'il supprime l'entrée dans le header.
note: je n'ai pas remarqué au premier coup d'oeil, parce que *tout* mes scripts d'unpacking ou autre ont ce fix... oui, j'avoue c'est *painfull*.

du coup corolaire: Avec miasm, on apprend plein de truc dans la bonne humeur... Désolé pour le temps perdu!

serpilliere le 2012/07/11 09:09

Au passage, pour se marrer, la fameuse ligne de code est dans la doc (les slides du repository) de miasm: slide 9/105. (comme quoi je ne suis pas totalement un escroc :p)

Concernant le packer, j'en ai également un basé sur miasm développé pour les pentest, et j'avoue il y a encore 2/3 "blagues" du même style, du coup tu me donnes presque envie de le releaser (ce qui diminuera peut être son efficacité). Mais l'exercice reste intéressant.
Après j'avais une amie juriste qui m'avait glissé à l'oreille que la publication de code de ce genre reste illégale. Dieu ait pitié de l'âme des juristes.

Ozwald le 2012/07/11 11:56

Bound Import, nice catch !! Je ne maitrise pas du tout cette structure là des PE...je vais y jeter un oeil du coup (comme quoi tu as raison : c'est un exercice intéressant et instructif).

Pour la petite histoire : wine, lui, réussi à lancer les versions modifiées de "calc.exe" dont les bound imports sont "pourris" :-)

"Désolé pour le temps perdu!" <= c'est une blague ^_^ ? Tu ponds un framework super, tu le mets à dispo en open source, et tu t'excuse qu'il ne soit pas parfait ? C'est plutôt à moi de te payer une bière si on se croise un jour pour te remercier de l'opportunité d'en apprendre plus sur les bound imports :-p

Pour ce qui est de releaser un packeur il y a en effet un problème légal (une sombre histoire de "motif légitime" en rapport avec l'article 323-3-1 du code pénal si j'ai tout suivi...http://www.legifrance.gouv.fr/affic...).En tout cas c'est pour ça que je n'ai pas non plus releasé le mien :-/

  1. L'installation n'est d'ailleurs toujours pas plus facile, je vous recommande la lecture de mon billet de l'époque, ça peut éventuellement aider
  2. Le DOS stub est,en résumé, un programme MSDOS censé être lancé à la place du programme PE lorsqu'il est exécuté sous DOS. Le but de ce mini programme DOS est principalement d'avertir l'utilisateur que le programme ne peut pas fonctionner sous DOS.C'est une zone totalement facultative de nos jours.
  3. Ce genre de zone "poubelle" pouvant apparaitre naturellement dans le fichier suite à des jeux de padding puisque les structures PE imposent des tailles multiples de certaines constantes renseignées dans les en-tête
  4. En attendant un autre après-midi de libre (où je pourrait me re-pencher sur le problème), ou que quelqu'un poste un commentaire expliquant ce bien étrange phénomène
  5. Pour rappel : je suis pentesteur pro, si je pack des binaires c'est pour mon taff et pour sensibiliser mes clients au fait qu'un AV ce n'est pas l'outil miracle qui les protègera de tout et n'importe quoi; ce n'est pas pour faire du pr0n, monter un botnet, ou pire : boire dans votre YOP.

Brèves du jour

Par Oz le - Geek
I.A. Outil Réseau python scripts

Un mini billet pour deux petites brèves : une astuce python (déjà bien connue, mais plus on réplique ce genre d'info moins on a de mal à la retrouver quand on l'a oublié), et un pointeur sur un jouet sympa.

Tools - Creative Common by mtneer_man on Flickr

Tout d'abord l'astuce python : comment changer son user-agent quand on utilise la librairie urllib. C'est un problème ultra-classique puisque pas mal de sites font du filtrage sur ce user-agent et refusent de répondre quand ils repèrent un script python1. En fouillant sur le net on trouve pleins de méthodes plus ou moins bidons (certaines se contentent carrément de rajouter un second header "user-agent", ce qui n'est évidemment pas ce que l'on cherche à faire), et après en avoir testé au moins une demi-douzaine voici celle que j'utilise à présent (et qui marche :D) :

import urllib
# ============ USER AGENT MAGIC ===========
class UAOpener(urllib.FancyURLopener):
        version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11'
urllib._urlopener = UAOpener()
# =========================================

Et voilà :) Comme pour le passage via TOR c'est tout ce qu'il y a à faire. Accessoirement j'ai réalisé récemment que le dernier paramètre de "setdefaultproxy" que j'utilise pour passer par TOR souffre un bug ennuyeux : il est ignoré :-D Donc que vous mettiez à "True" ou à "False" les résolutions DNS ne passeront JAMAIS par votre proxy socks (et donc par TOR)...ennuyeux n'est-ce-pas ? En tout cas maintenant vous êtes prévenus :)

Le petit jouet maintenant : ça n'a rien de nouveau non plus, mais c'est tout de même un jouet rigolo. Il s'agit du WOrdnet Libre du Français2. Pour ceux qui connaissent Wordnet : c'est Wordnet pour le Français ;) Pour ceux qui ignorent ce qu'est Wordnet c'est une base de donnée concernant le sens des mots et leurs relations les uns avec les autres. Un usage immédiat de Wordnet c'est de trouver des synonymes (dans la langue française avec WOLF, dans la langue anglaise avec le Wordnet original), un autre usage consiste à mesurer la proximité de sens entre deux mots par exemple. Les possibilités sont vraiment nombreuses, notamment en data-mining. Bref vous pouvez le télécharger ici au format XML, et ces trois mini remarques pourraient vous êtes utiles si vous voulez travailler avec :

  • Le document ne contient pas de balise racine, ajoutez donc en une si vous voulez que xmllint --format ou xml.dom.minidom.Parse ne vous insulte pas. Par exemple un petit <wolf> en début de document et un petit </wolf> en fin de document feront parfaitement l'affaire.
  • Les & ont tendance à faire crier le module python xml.dom.minidom. Pour ma part je les ai donc purement et simplement supprimés grâce à un petit sed.
  • Le XML pèse 38Mo sur disque, une fois parsé par xml.dom.minidom mon interpréteur Python prend plus de 1Go ;-) Veillez donc à avoir de la RAM disponible.

Gorgonite le 2011/10/22 13:45

MiniDOM... tu cherches aussi les ennuis pour traiter une telle quantité de données :p

  1. Par exemple wikipedia.
  2. WOLF

metasm, en mieux (ou pas)

Par Oz le - Sécurité
Gentoo Outil Reverse Engineering miasm python

Oz2020Migration : True

Il n'y a que deux choses qui manquent à Python, à mon humble avis : metasploit, et metasm. Or il semblerait qu'il y a quelques jours/semaines, l'un de ces deux manques ait été comblé par miasm, l'équivalent de metasm en python ! J'étais donc obligé de faire au moins un petit billet là dessus :)

Engine - Creative Common by "cbowns" on Flickr

Comment installer miasm ? La page officielle du projet est relativement claire, mais ne s'applique pas immédiatement (en particulier quand on est sous gentoo). Voilà donc la démarche que j'ai suivi, ce n'est peut être pas la meilleure mais "chez moi ça marche"(c). D'abord voyons les dépendances :

  • Grandalf (https://github.com/bdcht/grandalf) in order to render graphical disassembler.
  • Modified libtcc (http://bellard.org/tcc/) to Jit code for emulation mode. see below
  • python-ply for parsing
  • numpy
  • python-virtualenv
  • python-dev
  • python-qt4

Allons y par étape en adaptant pour gentoo :

Grandalf viendra tout seul quand on clonera le méta-répertoire, on ne s'en occupe donc pas.

Concernant la libtcc on va suivre les instructions tout simplement : git clone git://repo.or.cz/tinycc.git puis on édite le Makefile conformément à ce qu'indique le site de miasm, puis make...échec texi2html: Command not found...Pas de problème emerge -v texi2html puis on re-tente le make : victoire ! Il ne reste plus qu'à make install.

Pour "python-ply" il faut en fait installer "dev-python/ply", sauf qu'il est masqué par le keyword x86. On démasque donc et on installe joyeusement : echo "dev-python/ply ~x86" >> /etc/portage/package.keywords && emerge -v ply

Pour "numpy" rien de plus simple emerge -v numpy.

Idem pour "python-virtualenv" qui s'installe sans poser de problème avec un simple emerge -v virtualenv

Pour "python-dev" c'est encore plus simple : rien à installer puisque nous sommes sous Gentoo :)

Enfin "python-qt4" est lui aussi assez simple à obtenir puisqu'il suffit d'un emerge PyQt41

Maintenant on clone le répertoire hg clone https://code.google.com/p/smiasm/ smiasm...fail. Après un emerge -v hg-git ça ne se passe pas mieux...Après quelques recherches la version de mercurial stable sous gentoo est la 1.7, or le support des subrepository GIT n'apparait dans mercurial qu'à partir de 1.8 -_- ....bon...echo "=dev-vcs/mercurial-1.8.2 ~x86" >> /etc/portage/package.keywords && emerge -v =dev-vcs/mercurial-1.8.2...damned ça foire encore. Après un poil de trifouillage il semblerait que ça soit le clonage de grandalf qui foire...probablement à cause du .hgsub du répertoire smiasm qui pointe (à tort?) vers [git]https://github.com/bdcht/grandalf ...Bon on clone grandalf à la main git clone git://github.com/bdcht/grandalf.git, et également miasm parceque "chezmoiçaapasmarché"(c) hg clone https://code.google.com/p/miasm/ miasm -v --traceback ainsi que le README.txt qui est à la racine du repository smiasm et, plus important : pyMakefile et Makefile. Pfiou, tout ça ! Maintenant on revient sur le chemin tracé par le site officiel : on tape donc make && make install dans le répertoire de smiasm..victoire :) !!!

On teste si tout marche bien cd miasm/example/ && python disas_and_graph.py /bin/ls ...FAIL NameError: global name 'route_with_nurbs' is not defined

Bon, on re-trifouille et je vous fait grace des détails. Au final la méthode que j'ai employé (et qui marche sur gentoo et ubuntu 10.4 une fois les dépendances de chaque distro réglées) c'est :

  1. D'abord on se débarasse du problème de TCC en suivant à la lettre les instruction du site officiel smiasm : clone git://repo.or.cz/tinycc.git, ensuite on rajoute "-fPIC" dans les CFLAGS du fichier Makefile (CFLAGS+=-g -Wall -fPIC) puis on compile/installe : ./configure && make && make install
  2. Maintenant on attaque smiasm à proprement parler : On clone une première fois smiams : hg clone http://code.google.com/p/smiasm/, ça se termine avec un message d'erreur mais "pas grave". On télécharge ensuite à la main les fichiers Makefile et pyMakefile depuis l'accès HTTP au repository (http://code.google.com/p/smiasm/source/browse/)
  3. On clone à la main grandalf parce que le .hgsub de smiasm utilise une notation exotique qui n'est comprise ni par le mercurial gentoo par défaut ni par le mercurial ubuntu 10.4 par défaut : cd smiasm && rm -Rf grandalf && git clone git://github.com/bdcht/grandalf.git grandalf
  4. On fait un rollback de version de grandalf puisque les versions commitées depuis juillet dernier ne semblent plus compatibles avec miasm (disparition de la fonction route_with_nurbs) : git checkout a851b359185132645d9dce3d11037f3263604358
  5. Il ne reste plus qu'à compiler et installer smiasm :) ! cd .. && make && make install

A présent on peut tester le tout cd miasm/example/ && python disas_and_graph.py /bin/ls. Victoire :

miasm - test script

Il ne nous reste plus qu'à dévorer la documentation pointée par Sid2 et à nous les joies du bas niveau avec encore plus de facilité ! A bientôt donc pour de nouveaux billets passionants (en tout cas j'espère qu'ils réussiront au moins à me passioner lors de leur rédaction lol) !


ipv le 2011/08/30 15:25

Pour l'install auto sur debian/ubuntu :
http://www.ring0.me/scripts/miasm_i...
;)

Ozwald le 2011/08/30 22:43

J'avais déjà vu le script mais je ne dois pas avoir de bol parce, "chezmoiçamarchaitpas"(c) malheureusement :-(

Ceci dit, encouragé par l'intéret porté à mon mini retour d'expérience ^^, je viens de refaire quelques bidouilles et finalement le clonage du git grandalf fonctionne sur la gentoo à condition de rajouter le flag "gnutls" à mon package curl (j'étais encore en "ssl" simple, oui j'ai honte :-/)

Ce qui m'inquiète un poil c'est que le clonage de grandalf me descend une version où "route_with_nurbs" n'existe pas dans "grandalf/routing.py" donc je me dis que, probablement, le problème de version de grandalf se pose toujours et que le script "example/disas_and_graph.py" ne marchera toujours pas (ce qui ne semble pas adressé par le script. Je suis le seul à avoir ce problème de "route_with_nurbs" ?)

...Bref assez de digression Gentoo, revenons aux debian-like : je viens de revérifier sur internet (n'ayant pas accès à mon ubuntu en ce moment pour vérifier sur pièce) et la version de git qui vient avec lucid (10.04, la dernière LTS) c'est la 1.4.3-1, donc on a probablement trouvé d'où venait mon problème de fetch puisqu'il faut un git plus récent que 1.5.6-4 :-/ En résumé : pour ubuntu il faut donc au moins être en maverick (10.10) pour que le clonage git (et donc le script) fonctionne puisque Maverick propose la version 1.6.3-1.

Seule question qui reste en suspens : quid de la version de grandalf clonée et de la disparition de la fonction "route_with_nurbs" dans grandalf/routing.py qui semble pourtant être utilisée par miasm ?

serpi le 2011/08/31 18:30

Yop

oué le capitaine igloo qui maintient Grandalf avait une merde sur un patch, c'est pour ca que le meta repo SMIASM avait un pointeur grandalf sur l'avant dernier patch, qui du coup, n'est pas appliqué quand on clone a la main. Tout devrait rentrer dans l'ordre (ainsi que le .git) sous peu. (ou pas, bien évidement)

ps:chémoicamarche
pps: fais gaffe, tu disasm un binaire /bin/ls ELF64 en utilisant le moteur de disasm 32 bit, d'ou un code étrange.
ppps: je réponds a la future question: non le moteur 64 bit de disasm est pas fini

The Rasta Gods are pleased for your sacrifice.

  1. Ce qui peut prendre pas mal de temps si jamais vous devez recompiler qt-core :( ...
  2. Merci d'ailleurs, sans ce blog post je ne l'aurai jamais trouvé lol

Petite visite du guérisseur

Par Oz le - Geek
Hack Réseau android python

Monsieur Soleil peut tout faire. Retour de l'être aimé, prédiction des résultats du loto, choix infaillible de la couleur de la cravatte, conjuration des problèmes d'impuissance, etc... Bon plus sérieusement je ne vais pas parler de ce type de guérisseur là, rassurez-vous. Je vais vous parler de Jason, figure de la mythologie grecque connu sous le surnom "le guérisseur"...bon non plus, ça c'était juste pour le jeu de mot (bien pourri). En fait je vais vous parler de JSON et, plus précisément, des JSON-RPC que j'ai eu la surprise de découvrir accessibles sur mon serveur...

Marabout - Based on a Creative Common photo published by "Ludovic Hirlimann" on Flickr

Pour poser le cadre donc : j'ai un serveur multimédia (allumé H241, comme tout bon serveur qui se respecte) et l'autre jour alors que je faisais le petit nmap routinier de mon réseau local apparait une nouveauté sur ce serveur :

PORT     STATE SERVICE     VERSION
9090/tcp open  zeus-admin?

Autant vous dire que, sur le moment, j'ai un peu fait la gueule :-D Après enquête rapide il s'avère que c'est en fait le XBMC qui tourne sur mon serveur qui expose là l'une de ses nouvelles interfaces de commande... Dans les anciennes versions, XBMC exposait uniquement (pour autant que je sache) une interface de commande HTTP accessible après authentification et qui permettait ainsi de controller les lectures multimédia via le réseau (super pratique pour transformer votre téléphone android en télécommande wifi). Dans les dernières version l'interface HTTP est deprecated, et c'est l'interface JSON-RPC qui devrait la remplacer.

Pour ceux qui l'ignorent (comme moi il y a quelques semaines), le JSON-RPC c'est une méthode de RPC qui utilise la syntaxe JSON (on ne s'en douterai pas hein ?). Et pour la conclure l'explication : le JSON c'est une méthode de formatage de données dont l'esprit est de se rapprocher de la syntaxe....javascript. Je ne plaisante pas, JSON c'est même l'acronyme de "JavaScript Object Notation" si vous voulez tout savoir. Du coup une requête JSON-RPC ça ressemble à ça :

{"jsonrpc": "2.0", "method": "subtract", "params": {42, 23}, "id": 1}

Bon...c'est pas très joli mais ça reste compréhensible :) Du coup je me suis dit "voyons ce que l'on peut faire avec ça", mais avant d'exposer les résultats je dois faire un disclaimer : "j'ai peut-être été trop permissif dans ma configuration d'xbmc (même si j'en doute), donc ne blamons pas trop les réglages qu'on peut/doit faire pour utiliser une télécommande, c'est peut être ma faute". Maintenant répondons à "que peut on faire avec ça ?" :

Première chose : survoler la liste des procédures accessibles. La première qui a une tête sympa c'est "JSONRPC.Ping". Aussitôt identifiée aussitôt testée :

echo '{"jsonrpc": "2.0", "method": "JSONRPC.Ping", "id":"5"}' | nc serveurmultimedia 9090

Et réponse immédiate :

{
   "id" : "5",
   "jsonrpc" : "2.0",
   "result" : "pong"
}

Cool ça marche :-D !!!...Pas cool : j'ai pas eu besoin de m'identifier :-/

La paranoïa aidant la seconde procédure que je teste c'est "Files.GetDirectory"...forcément.

echo '{"jsonrpc": "2.0", "method": "Files.GetDirectory", "id":"peur", "params":{"directory":"/"}}' | nc serveurmultimedia 9090

Ah bah....ça marche :

{
   "id" : "peur",
   "jsonrpc" : "2.0",
   "result" : {
      "directories" : [
         {
            "fanart" : "special://masterprofile/Thumbnails/Video/Fanart/cfb24076.tbn",
            "file" : "/var/",
            "label" : "var"
         },
         {
            "fanart" : "special://masterprofile/Thumbnails/Video/Fanart/b1fe6366.tbn",
            "file" : "/usr/",
            "label" : "usr"
         },
         {
            "fanart" : "special://masterprofile/Thumbnails/Video/Fanart/dafd2084.tbn",
            "file" : "/tmp/",
...ETC...

Et vous savez le mieux ? Y'a une procédure Files.Download :-D !...Après l'avoir testé dans tout les sens il semblerait qu'en fait cette procédure ne soit, pour l'instant, pas implémentée (ouf !) et que de toute façon elle ne permette de télécharger que des ressources générées par XBMC (vignettes, etc.). M'enfin déjà le simple Files.GetDirectory c'est pas très joli2...Du coup une seule chose à dire : faites attentions aux permissions que vous mettez sur votre XBMC si vous voulez utiliser une télécommande réseau, il se peut que vous ouvriez des portes à n'importe qui ayant accès au port TCP.

Ceci étant dit, on va considérer que ça cloture la partie "sécu" de ce billet, et on va voir si on peut jouer avec ça sans vilaines pensées, par exemple essayons de faire les requêtes en python :

>>> import json, socket, random
>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100)), 'method':'JSONRPC.Ping'}
>>> s =  socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.connect(("serveurmultimedia",9090))
>>> s.send(json.dumps(req))
>>> reponse = s.recv(1024)
>>> print reponse
{
   "id" : "82",
   "jsonrpc" : "2.0",
   "result" : "pong"
}

Intéressant...Du coup si jamais un script python sur android qui est en train d'exécuter time.sleep() ne consomme pas de CPU (donc de batterie) je pourrai m'écrire un script, utilisable sur mon téléphone, qui me réveille à l'heure de mon choix en jouant le média de mon choix sur mon serveur !!! Intéressant non ?

Pour que ça soit intéressant j'ai envie de lire un mp3 aléatoire. La logique de XBMC étant ce qu'elle est3 je dois d'abord récupérer les "sources" de music :

>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "Files.GetSources"
>>> req['params'] = {'media':'Music'}
>>> s.send(json.dumps(req))
>>> reponse = s.recv(10*1024)
>>> shares = json.loads(reponse)['result']['shares']
>>> for s in shares:
>>>    print s['file']
/mnt/raid0/musiquelegalementachetee/
...

Une fois les sources récupérées (et surtout les répertoires associées, dans l'attribu bien nommé "file"...) on peut utiliser Files.GetDirectory dont on parlait plus haut afin de récupérer l'ensemble des morceaux directement à la racine de notre source (j'ai la flemme de coder le récursif pour l'instant) :

>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "Files.GetDirectory"
>>> req['params'] = {'directory':shares[0]['file']}
>>> s.send(json.dumps(req))
>>> reponse = s.recv(100*1024)
>>> musics = json.loads(reponse)
>>> random.shuffle(musics['result']['files'])
>>> musique = musics['result']['files'][0]["file"]
>>> print musique
/mnt/raid0/musiquelegalementachetee/rickroll.mp3

Maintenant j'aurai adoré utilisé AudioPlaylist.Insert pour insérer la chanson à l'endroit de mon choix dans la playlist courante puis en lancer la lecture, sauf que quand je fais appel à AudioPlaylist.Insert j'ai une réponse Method not found....On va donc supposer que ce n'est pas encore implémenté :-( Résultat il ne me reste plus qu'à me rabattre sur AudioPlaylist.Add puis prier le guérisseur pour que lors de l'appel à AudioPlaylist.Play ce soit le bon morceau qui soit joué...Je l'ai donc fait :

>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "AudioPlaylist.Add"
>>> req['params'] = {"item": {"file":musique}}
>>> s.send(json.dumps(req))
>>> reponse = s.recv(1024)
>>> print reponse
{
   "id" : "2",
   "jsonrpc" : "2.0",
   "result" : "OK"
}
>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "AudioPlaylist.Play"
>>> s.send(json.dumps(req))

Et là ça ne joue pas le bon morceau :-S ... Vérification de la playlist : mon morceau n'a même pas été ajouté en fait... Heureusement la communauté des dev de XBMC est plutôt très active et j'ai rapidement trouvé le problème :

The current playlist implementation/representation in json rpc has many (design) flaws and there are lots of unsupported things and it will hopefully be completely rewritten for the next release (if there's enough time). Until then it might be the safest approach not to use the Playlist methods (except SkipPrevious, SkipNext, GetItems, Clear, Shuffle and UnShuffle which should work as expected).

We're sorry for the inconvenience and hope to provide a much better solution in the next version

Donc voilà : actuellement il est impossible de faire ce que je voulais :-( ..Snif. La suite peut-être à un prochain numéro :) !

  1. Que les amis de la planète se rassurent : c'est une machine à base d'atom qui consomme moins en H24 qu'une seule heure de jeu sur les dernières cartes graphiques à la mode
  2. Même si, comme moi, vous faites tourner votre XBMC avec des droits minimalistes
  3. et comme je n'utilise pas les fonctionnalitées "library" d'XBMC

Android c'est bon, mangez en !

Par Oz le - Geek
android python

Avec l'été qui revient le soleil se fait de plus en plus présent et c'est donc le moment de ressortir...son android ! Bah oui, avec le beau temps on se retrouve plus souvent à glander en plein air et pour geeker il n'y a alors rien de plus pratique/portable qu'un android :)

Droid Has Landed

J'avais déjà expliqué dans un vieux billet comment installer un environnement d'émulation Android1, et bien cette fois je vais parler de SL4A. SL4A c'est une application qui vous permet d'utiliser des langages de scripts populaires (LUA, ruby, python, etc.) sur votre android. Pour en profiter il suffit d'installer SL4A (soit depuis le market soit en téléchargeant l'APK directement depuis leur site. Pour l'installer sur emulateur ca fera donc adb install ~/droid/sl4a_r4.apk), de le lancer, de choisir "ajouter" dans le menu de l'application, puis de choisir l'interpretteur du langage de votre choix (pour ma part j'ai choisi Python ;) ).

Une fois SL4A installé et les interpretteurs de votre choix téléchargés et installés depuis SL4A vous avez accès à toute une API qui vous permet d'interragir, depuis vos scripts, avec le terminal Android. Vous pouvez par exemple accéder à la liste des contacts enregistrés, lire les capteurs divers et variés, utiliser l'apareil photo, envoyer/lire des sms, etc. Par contre, coder sur son téléphone ça devient vite fatiguant, donc un tout petit peu de préparation est utile pour coder à son aise.

Tout d'abord nous décidons de coder sur émulateur, ça évitera de mettre trop la grouille sur notre téléphone. Ensuite il faut savoir que les scripts que SL4A est capable de lancer sont stockés par défaut dans /mnt/sdcard/sl4a/scripts. Enfin il faut savoir un petit peu se servir de l'outil adb qui est fourni avec le SDK Android (mais vraiment juste "un peu" je vous rassure).

Pour coder à notre aise donc, nous allons commencer par lancer un AVD2 (je vous conseille de lui accorder au moins 512Mo de mémoire vive, sinon ça rame vraiment trop !). Ensuite nous choisissons un répertoire sur notre machine hote qui servira de répertoire de développements3. Enfin, on va y coder notre premier petit script python pour android (avec VIM bien entendu) :

import android
droid = android.Android()
droid.makeToast("Bonjour le monde")

Pour info on aurait pu se contenter de print "Bonjour le monde" mais dans ce cas nous n'aurions pas eu d'interfaçage avec l'UI android et donc nous n'aurions pas pu afficher "Bonjour le monde" dans une petite info-bulle stylée en bas de l'écran. Bref, une fois le script écrit il faut le pousser sur l'AVD adb push bonjour.py /mnt/sdcard/sl4a/scripts/bonjour.py, il apparait alors dans le répertoire "scripts" de SL4A et en cliquant dessus ça fait bien apparaitre une mini info-bulle contenant "Bonjour le monde" :)

Script PYTHON sous emulateur Android.

Maintenant on peut se pencher sur une amélioration de tout ça. Par exemple optimiser le push. Pour cette étape j'ai bêtement fait un Makefile :

all:
    ls *.py | xargs -I 'X' ../tools/adb push X /mnt/sdcard/sl4a/scripts/X
    ls *.py | xargs -I 'X' ../tools/adb shell rm /mnt/sdcard/sl4a/X.log

Grâce à cette "astuce" il me suffit de faire un "make" avant chaque test pour que l'ensemble de mes scripts soient poussés sur l'AVD, et ça ne me change pas trop des habitudes prises en C/CUDA4.

Une fois qu'on sait afficher du texte dans une info-bulle on peut se pencher sur toutes les autres fonctions de l'API et laisser courir notre imagination. Pour les besoins de la cause je conclue d'ailleurs ce billet par quelques exemples de scripts inutiles mais drôles (et donc indispensables) qui m'ont servi de tests :

IRC-like :

import android
droid = android.Android()

victime = droid.pickPhone()
droid.smsSend(victime.result , "I slap you with a large trout")

IRC-like++:

import android
droid = android.Android()

armes = ['Truite', 'Chaussette', 'Andouillette']
droid.dialogCreateAlert("Choix des armes")
droid.dialogSetItems(armes)
droid.dialogShow()
response = droid.dialogGetResponse().result['item']
try:
  response = armes[int(response)]
except:
  response = armes[0]

victime = droid.pickPhone()
message =  "Je viens de te baffer avec une "+response
droid.smsSend(victime.result , message)
droid.makeToast("ENVOYE : "+message)

Gorgonite le 2011/06/06 12:24

C'est assez sympa de pouvoir développer des applications sur Android dans le langage de son choix, mais quid de la pérennité et l'éventuel frein à leur diffusion... tout le monde ne voudra pas forcément installer des machines virtuelles supplémentaires.

Enfin ça l'avantage d'être rapide pour réaliser des PoC sur cette plate-forme :)

  1. si tant est que quelqu'un aurait eu besoin d'explications pour ça...
  2. AVD = instance d'android virtuel
  3. Répertoire dans lequel vous pourrez faire un petit bzr init && bzr add * && bzr commit si le coeur vous en dit.
  4. Pour en savoir plus sur ce que j'ai fais récemment en C/CUDA (et pourquoi j'ai délaissé ce blog pendant des mois) je vous invite à aller suivre le SSTIC puisque j'ai l'immense honneur d'y donner une conférence mercredi prochain :) !

« Page 2 / 3 »