dimanche, janvier 22 2012

Du capitalisme

L’Académie française propose une définition simple du capitalisme : le capitalisme est un « régime économique dans lequel les moyens de production sont propriété privée ». Dans la pratique, il est patent que le terme est loin d'être doté d'une acception consensuelle. D'où l'existence de nombreuses significations différentes, dont une se basant sur la mécanique d'accumulation du capital comme facteur de production.[1]

Billets de monopoly - Creative Common by graciepoo on Flickr
Dans l'acceptation du "capitalisme" telle qu'ébauchée en introdution de ce billet "l'accumulation du capital comme facteur de production" est l'un des fondamentaux, et en ce sens je suis un capitaliste de l'informatique. Cette réflexion m'est venu il y a déjà pas mal de temps en lisant une présentation (dont j'ai malheureusement oublié les références :( ) sur la façon de faire efficacement du fuzzing. Dans cette présentation il y avait un slide expliquant qu'écrire un fuzzer évolué n'était pas une bonne façon de faire du fuzzing, mais que la bonne façon de faire du fuzzing c'était d'écrire un fuzzer évolué pendant qu'un fuzzer écrit en 30s tournait. Ainsi, lorsque je me suis finalement mis à jouer un peu sérieusement avec de l'audit de code statique (environ un an après mes premiers tests dans le domaine) j'ai appliqué cette stratégie. Ce sont les premiers résultats de ces recherches que je vais relater dans ce billet.

Les outils

Adhérant totalement à la philosophie du "je travaille sur un bon outil pendant qu'un outil pourri que j'ai écrit en 10mn est déjà en train de tourner" j'ai donc commencé à analyser du code source PHP avec une dizaine de lignes de python qui se contentaient de :

  • Télécharger un projet PHP sur sourceforge/drupal/wordpress
  • Décompresser l'archive du projet
  • Faire l'équivalent d'un "grep" sur l'ensemble des fichiers PHP contenus dans l'archive
  • Effacer le repertoire temporaire dans lequel j'avais téléchargé et décompressé l'archive (ça a l'air con mais vu la simplicité du projet une fonctionnalité, même aussi triviale, compte).

Voilà quelques exemples des expressions régulières que ce mini script cherche:

  • (XSS) .*echo .*\$_GET.*
  • (XSS) .*echo .*\$_POST.*
  • (XSS) .*echo .*\$_REQUEST.*
  • (SQLi) .*SELECT .* FROM .* WHERE .* \$_GET.*
  • (SQLi) .*SELECT .* FROM .* WHERE .* \$_POST.*
  • (SQLi) .*SELECT .* FROM .* WHERE .* \$_REQUEST.*
  • ([LR]FI) .*require\(\$_GET.*
  • ...

Bref, cette première version était vraiment très rustique et pourrait être re-codé 100% en bash à coup de wget, unzip, et grep.

Pendant que cette première version tournait je me suis penché sur l'utilisation de PHC. L'idée est de réaliser ce dont je parlais dans mon vieux billet, à savoir d'utiliser PHC pour convertir le code source PHP en une représentation plus simple, et de réaliser une analyse par propagation de teinte sur cette représentation simplifiée. PHC propose 3 représentations intermédiaires, la plus simple d'entre elle étant la "MIR" c'est celle-ci que j'ai choisi (au format texte brute plutôt qu'XML) : phc --dump=mir mon_fichier.php.

Une fois mes fichiers PHP convertit en représentations "MIR" je parse le texte résultant pour en extraire des blocs de codes, chacun portant une étiquette utilisée par la représentation MIR pour d'éventuels GOTO, puis je débute ma simulation de propagation de teinte par la première ligne du premier bloc. A chaque assignation de variable rencontrée :

  • j'enregistre son nom dans un dictionnaire
  • je lui associe une valeur de teinte (si la variable se voit attribuée une constante la teinte est nulle, si elle se voit attribuée une variable de type $_GET[...] elle obtient une valeur de 1, si elle se voit attribuée la concaténation de deux autres variables sa teinte est la somme des teintes des variables concaténées, etc.)
  • je lui attribue une représentation (si son assignation est une constante je la reprend comme représentation, si son assignation est une variable sensible type "$_GET[...]" j'utilise ça, si on lui assigne la concaténation de variables je lui attribue la concaténation des représentation des variables concaténées, etc.)

Lorsqu'une fonction est appelée je regarde si son nom apparait dans l'une des listes de "fonctions sensibles" que j'ai hardcodé[2] et si tel est le cas je vérifie la teinte de la variable utilisée en argument. Si la teinte n'est pas nulle je lève une alerte en spécifiant la valeur de teinte utilisée, la représentation de la variable incriminée, et la famille de la fonction sensible (XSS, SQLi, [RL]Fi, PHPi).

Ce deuxième outil, bien qu'encore extrèmement rustique (246 lignes de python (199 sans les commentaires)), est sensiblement plus efficace que mon grossier "grep-like", comme nous allons voir tout de suite.

Les résultats

Ecrire un outil d'analyse de code c'est bien, mais encore faut-il avoir du code à analyser (et taper aléatoirement dans sourceforge c'est amusant deux secondes mais ça lasse vite) ! C'est donc en me demandant ce que j'allais bien pouvoir analyser que je me suis souvenu de cette liste de "bounty programs", et en particulier du dernier programme listé : celui de White Fir Design. Cette entreprise américaine, que je vous invite à découvrir, propose plusieurs bounty programs sur des logiciels open source dont un sur Wordpress et ses plugins téléchargés à plus d'un million d'exemplaires. C'est donc sur cette cible que j'ai testé mes deux outils d'analyse de code.

Pendant que j'écrivais mon outil de propagation de teinte basé sur PHC le premier script (grep-like) a relevé un nombre important d'alertes. C'est là que l'un des gros défaut de cette approche se fait sentir : il y a énormément de faux positifs. Par exemple la ligne suivante, bien que n'étant absolument pas vulnérable à quoi que ce soit, remonte à chaque itération de mon script comme un XSS potentiel :

echo (isset($_GET['session']) ) ? '?session=1' : '';

Malgré ces faux positifs j'ai tout de même réussi à confirmer quelques vulnérabilités de type XSS dans des pages d'admins de plugins téléchargés à plus d'un million d'exemplaire, et j'ai donc eu la double joie de toucher un petit bounty[3] tout en ayant le sentiment d'avoir rendu internet un peu plus sûr (tout ça avec "grep"...).

Une fois mon script d'analyse par propagation de teinte terminé je l'ai relancé sur le même périmètre et, après quelques réglages, j'ai eu le plaisir de voir qu'il parvenait à identifier l'ensemble des XSS que mon grep-like avait trouvé et que j'avais confirmé. Non seulement il obtient donc d'aussi bon résultats mais, en plus, le nombre de faux positif est nettement plus faible (la majorité de ceux qui restent sont dus à la non-prise en charge des fonctions de "sanitize-check" type "preg_match"...il faudra que je rajoute le support de ces vérifications à l'occasion). Enfin, cerise sur le gateau, la version par propagation de teinte a réussi à lever un lièvre que le "grep-like" n'aurai pas pu avoir (parce que plusieurs lignes de code étaient impliquées) : une jolie time-based-blind-SQLi.

En guise de conclusion sauvage qu'est-ce-qu'il y a à retirer de tout ça ?

  • que l'analyse statique de code, même dans ses versions les plus rustiques (grep) peut encore être utile de nos jours.
  • que l'approche consistant à faire tourner un outil pourri pendant que l'on travaille à la fabrication d'outils plus évolué est une bonne approche (en tout cas moi je l'aime bien, elle me donne l'impression que mon temps CPU est utile et, en remontant des résultats de temps en temps, elle me garde motivée sur le codage des outils performants et prépare les cas de tests sur lesquels on pourra tester les outils performants :) ).
  • que l'équipe de White Fir Design est impressionante (ces gars donnent de l'argent pour aider à sécuriser des logiciels dont ils ne retirent qu'indirectement profit, moi je trouve ça fort !)
  • que les développeurs de plugins Wordpress sont généralement très sympa (j'ai eu à chaque fois des retours très cools de leur part)
  • <troll>que le code de Drupal est plus sécurisé que celui de Wordpress</troll> (ou que mes outils lui sont moins adaptés et/ou que j'ai eu moins de chance avec Drupal qu'avec Wordpress).

Notes

[1] Toute l'intro est largement pompée de : http://fr.wikipedia.org/wiki/Capitalisme

[2] print,->get_var,->get_results, ->query, mysql_query, require, require_once, include, eval

[3] Bounty immédiatement dépensé sur eBay en composants électroniques divers et variés pour mon arduino

samedi, octobre 22 2011

Brèves du jour

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 python[1]. 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çais[2]. 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.

Notes

[1] Par exemple wikipedia.

[2] WOLF

mardi, août 30 2011

metasm, en mieux (ou pas)

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 PyQt4[1]

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 Sid[2] 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) !

Notes

[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

lundi, août 22 2011

Pleine puissance monsieur Sulu !

Comme déjà évoqué précédemment je me suis mis à l'électronique amateur depuis quelques temps, et j'en arrive aujourd'hui au point où j'ai assez de matière pour écrire un nouveau petit billet. Dans ce billet je vais donc tenter d'expliquer clairement les problèmes qui se sont posés lorsque j'ai tenté de controller des moteurs avec mon micro-controlleur, et surtout la solution que j'ai trouvé ! Vous allez voir c'est amusant :)

DC Engine - Creative Common by "Zach Hoeken" on Flickr

La problématique

Tout est parti de l'envie de controller des moteurs à partir de mon arduino. Pour être plus précis je voulais controller des moteurs assez puissants pour pouvoir mouvoir un robot autonome embarquant donc l'arduino, des batteries, et tout un tas d'électronique et de mécanique utile ou inutile.

Concernant les moteurs le choix s'est immédiatement porté sur des moteurs à courant continu. J'aurai pu viser des servo-moteurs à rotation continue ou des moteurs pas à pas qui sont beaucoup plus simples à commander, mais ces solutions sont beaucoup plus honéreuses et ont donc vite été oubliées étant donné que le but est de jouer à moindre cout. Un moteur à courant continu c'est tout bête : on applique une tension entre les deux bornes du moteur et celui-ci se met à tourner à une vitesse proportionnelle à la tension appliquée[1]. Petite subtilité : si vous fournissez une tension variable périodiquement (sinusoïdale, carrée, triangle, ou n'importe quoi d'autre) de fréquence "suffisante" le moteur ne prendra en compte que la moyenne de votre signal et tournera donc de façon régulière comme si vous lui aviez appliquée une tension continue équivalent à la moyenne de votre signal périodique[2].

Pour controller la vitesse de rotation d'un moteur à courant continu on a donc une solution ultra classique qui consiste à lui appliquer une tension périodique en forme de créneau de valeur maximale fixe. En faisant varier la proportion de temps où le créneau est haut par rapport à la proportion de temps où le créneau est bas on fait très simplement varier la moyenne de la tension appliquée, et donc la vitesse de rotation du moteur. Ce système est connu sous le nom "modulation de largeur d'impulsion" l'équivalent anglais c'est "Pulse Width Modulation" qui lui donne son patronyme le plus connu (même pour les francophone) : PWM ! Ca tombe bien : pleins de micro-controlleurs sont capables de sortir des PWM, et en particulier l'arduino sait faire ça très bien et très simplement grace à la commande analogWrite(PIN, valeur). Si on résume voilà la tête de deux signaux exemples, l'un avec une PWM qui a un voltage moyen de 2,5V (50% du temps à 5V, 50% du temps à 0V) et l'autre qui a un voltage moyen de 1V (20% de 5V, 80% du temps à 0V)[3] :

PWM signals

Si on résume : je n'ai qu'à brancher une borne de mon moteur à la masse et l'autre borne à une broche de mon arduino sur laquelle j'utilise la commande analogWrite. Ainsi mon arduino va générer un signal PWM sur cette broche, signal que le moteur va comprendre comme un ordre de tourner à une vitesse correspondant à la moyenne de ma PWM, donc juste en jouant sur le second paramètre de analogWrite(PIN, vitesse) entre 0 et 255 je fait varier la vitesse de rotation de mon moteur de 0% à 100% de ses possibilités. Ca a l'air trop simple pour être vrai, mais en fait ça marche :) ! Par contre ce système a 2 gros problèmes, que nous allons voir les uns après les autres.

Sans puissance, la maitrise n'est rien

Lorsque je vous ai dit qu'un moteur à courant continu c'était simple et qu'il suffisait d'appliquer une tension à ses bornes pour qu'il tourne j'ai bien pris soin de préciser que ça ne marchait que si on lui fournissait une intensité de courant suffisante. C'est là que la premier problème se pose : un micro-controlleur (comme ceux qui sont au coeur des arduino) ce n'est pas prévu pour fournir une forte intensité de courant, et généralement ça n'en fournit pas assez pour faire tourner un moteur. A titre informatif l'arduino UNO peut sortir jusqu'à 40mA par PIN, ce qui est pile le courant consommé par un de mes mini-moteurs quand ils n'a aucune charge à entrainer (c'est donc, grossièrement, un dixième du courant maximal que ce même moteur est susceptible de demander s'il est chargé à son maximum). Sachant que mes mini-moteurs sont environ 3 fois plus petits qu'une pile AA j'espère que vous réalisez maintenant à quel point 40mA est une intensité liliputienne dans notre contexte :-) Le premier problème qui se pose à nous est donc : comment vais-je augmenter l'intensité du courant envoyé à mes moteurs :( ?

La réponse la plus triviale qui peut venir c'est : utiliser un transistor :) Pour ceux qui ne savent pas du tout comment marche un transistor sachez que, en très résumé, c'est un composant qui posssède trois pattes appelée "Base", "Collecteur", et "Emetteur".[4] Dans le mode de fonctionnement qui nous intéresse voilà comment se comporte le transistor :

  • Quand on envoie un courant, même de faible intensité, sur la base du transistor celui-ci "ouvre une vanne" entre son collecteur et son emeteur ce qui permet de faire passer de l'une de ces pattes à l'autre environ 100 fois le courant que l'on a envoyé sur la base.
  • Quand on ne fournit pas de courant sur la base du transistor alors celui-ci se comporte comme un interrupteur ouvert entre le collecteur et l'emetteur.

Une fois ce mécanisme bien compris il devient naturel d'envisager l'utilisation d'un transistor pour controller nos moteurs : on branche le signal PWM du micro-controlleur sur l'entrée "Base" du transistor et on se sert du courant 100 fois plus important circulant entre le collecteur et l'emetteur pour alimenter le moteur. Aussitôt dit aussitôt fait :

Amplification de puissance simple à base de transistor
Le transistor que j'utilise est un 2N1711. Ce transistor a pour principale qualité de correspondre à mes besoins puisqu'en lui fournissant environ 5mA sur sa base il permet de faire circuler jusqu'à 500mA entre son collecteur et son émetteur. La seconde grande qualité de ce transistor c'est que je l'ai trouvé à pas trop cher dans une boutique d'électronique à Nation ^_^ ! Quoiqu'il en soit le montage fonctionne, et le moteur tourne ! ... Mais fournir une intensité suffisante n'est pas le seul problème que l'on doit traiter.

Quand je m'avance toi tu recules, comment veux-tu que je ...?

Jusqu'à présent le branchement de notre moteur est plutôt simple : on branche une borne à la masse, et on envoie la commande PWM (amplifiée ou non en puissance) sur l'autre borne. Ce cablage "fixe" est simple à comprendre mais il ne permet de faire tourner le moteur que dans un seul sens :-/ En effet pour le faire tourner dans l'autre sens il faudrait pouvoir inverser le cablage en live, ce qui n'est pas vraiment réaliste. Sachant qu'à terme on aimerait bien faire rouler un robot, et donc disposer d'une marche arrière, il faut se résigner à abandonner ce branchement (et nos transistors) afin de se pencher sur un montage un poil plus complexe.

La solution à ce petit problème a, heureusement, été donnée il y a longtemps : le pont en H ! Pour résumer ce qu'est le pont en H : c'est un montage qui permet d'inverser le sens de branchement d'un dipole à la volée, sans toucher aux branchements physiques. Bonne nouvelle : ce montage étant ultra-classique on trouve des dizaines et des dizaines de circuits intégrés implémentant des pont en H ! Seconde bonne nouvelle : ils font quasiment tous l'amplification d'intensité par la même occasion. Pour ma part j'ai acheté un L293D (parce que, comme pour les 2N7111, je les ai trouvé pas cher et qu'ils répondent à mes besoins) mais d'autres références existent comme par exemple les SN754410 ou les L298[5].

Une fois notre pont en H à disposition le montage est simple. Tout d'abord les branchements essentiels pour que le circuit intégré puisse fonctionner :

  • Une PIN à la masse (sans commentaire).
  • Une PIN au voltage de référence correspondant au niveau "haut" logique (dans notre cas 5V car les signaux PWM générées par l'arduino sont entre 0 et 5V)
  • Une PIN à l'alimentation de puissance des moteurs (dans mon cas une batterie de 7.2V, mais on peut remplacer par n'importe quoi fournissant de la puissance avec un voltage compris entre 5V et 36V).

Les branchements de base effectués il suffit de relier "Input 1" et "Input 2" à deux bornes de l'arduino capables de générer des PWM, et "Output 1/Output 2" aux bornes du moteur. Pour faire tourner le moteur dans un sens il suffit par exemple d'envoyer une PWM sur "Input 1" et un signal nul sur l'autre; puis pour inverser le sens de rotation du moteur (sans toucher au cablage :D !) il suffit d'envoyer "0V" sur "Input 1" et une PWM sur "Input 2" ! Simple, non ^_^ ?

What else ?

Une fois que l'on est capables de faire tourner nos moteurs dans les 2 sens à une vitesse variable arbitraire on a déjà fait un grand pas vers la réalisation d'un robot mobile autonome, mais les pistes d'améliorations sont plus que nombreuses et feront peut-être l'objet d'articles ultérieurs. Pour vous donner quelques idées voici deux améliorations que j'ai déjà apporté à mon robot :) :

  • Dans l'article on utilise 2 signaux PWM pour controller un seul moteur mais ça serait mieux d'utiliser un unique signal PWM et un bit de sens en tout ou rien (l'arduino, comme tout les micro-controlleurs, n'étant capable de générer des PWM que sur un nombre réduit de ses sorties il est toujours bon d'économiser ces sorties là). Pour information ce petit tour de passe-passe peut être réalisé en utilisant, par exemple, un circuit intégré de la famille des 74XX pour faire de l'algèbre de bool sur nos signaux. En particulier le 7404 peut s'avérer utile (et suffisant :)) pour résoudre notre problème. Je vous laisse trouver la solution exacte ;-)
  • Il n'y a pas d'isolation sérieuse entre nos moteurs et notre arduino. Donc si nos moteurs se mettent à générer de l'électricité pour une raison ou pour une autre (oui, ça peut arriver) toute la puissance électrique générée risque de remonter jusqu'à notre arduino et peut potentiellement le griller :-/ Pour contrer ce risque on peut utiliser des "optocoupleurs"[6]. Ces petits circuits intégrés ont pour unique fonction d'apporter justement une isolation entre commande et puissance. En très résumé vous leur donnez à manger un signal logique en entrée et il vous le recopie sur sa sortie en vous garantissant qu'aucun reflux de puissance ne pourra remonter sur le signal logique :) Pour les plus intéressés d'entre vous sachez que la magie s'opère généralement grace à une LED et un photo-transistor, et voilà quelques références si vous en cherchez : 4N35, PC845, etc.

Notes

[1] pourvu que l'intensité du courant fournie soit suffisante

[2] La "magie" derrière tout ça c'est qu'un moteur à courant continu, de par sa construction même, peut être modélisé comme un circuit RLC qui s'avère être un filtre fréquentiel passe-bas :)

[3] La première PWM s'obtient avec analogWrite(PIN,125) et la seconde avec analogWrite(PIN,51)

[4] Je commence par parler des bipolaire car ils sont simples :)

[5] Les L298 proposent d'ailleurs nettement plus de puissance que les L293D, intéressez-vous à ceux là si vous voulez avoir des moteurs capables de bouger rapidement un robot digne de ce nom.

[6] un autre mot clef de la même famille c'est photocoupleurs

mercredi, juillet 6 2011

Petite visite du guérisseur

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é H24[1], 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 joli[2]...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 est[3] 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 :) !

Notes

[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

- page 2 de 8 -