Skip navigation.

devloop :: blog

Blog sur la sécurité informatique, la programmation, Linux et le Web

Posts tagged with "flash"

It's fun to play with the D.M.C.A.

, , ,

Ca fait quelques mois que mes outils pour lire directement sa musique sur le site Imeem ne fonctionnent plus. On tombe à la place sur une espèce de message de répondeur où quatre voix féminines nous informent qu'il faut passer par le site Internet.

J'avais étudié quelques captures réseau pour comprendre ce qu'il cloche sans jamais voir d'où ça pouvait venir, j'ai essayé différentes clés d'API sans meilleur résultat.
En fin de compte il s'avère que Imeem a quelque peu modifié sa fonction non documentée mediaGetStreamInfo. Auparavant un paramêtre "referrer" était passé et pouvait contenir plusieurs valeurs possibles, celle habituelle étant la chaine de caractères "web".

Ce paramêtre a été modifié : à l'ancienne valeur est maintenant accolé un pipe "|" puis une longue chaine de caractères (118 caractères) formatée (à priori) selon l'algorithme utilisé pour les urls des flux.

D'après le code décompilé, Imeem a rajouté une fonction baptisée "authenticateRequest" qui génère un hash MD5 qui sera envoyé au serveur comme variable de referrer.

Les codes Flash décompilés étant généralement à la limite du compréhensible p: j'ai fait le lien grâce au fait que cette fonction utilise une constante nommée "REFERRER_ENCRYPTION_KEY" de type string et de valeur "faad2ae444233f97367cc35da6de80b51767f182".

Dans le code il reste tout de même deux variables dont l'identité reste mystérieuse... l'une semble liée au temps, l'autre est probablement un caractère de délimitation (voire rien du tout).
On remarque si on joue plusieurs titres à la suite, ce referrer "magique" ne change pas même après 10 minutes passées. On trouve dans le code des références à l'heure courante, ce qui laisse supposer qu'un indicatif d'heure (arondi) est utilisé.
Enfin une autre variable constante fait référence au temps : "REFERRER_TIMESTAMP_THRESHOLD" de valeur 30000 (secondes ?)

Pour le moment je n'ai fait que quelques tests rapides. Il faut tout de même que ces deux variables soient assez longues pour que la forme encodée atteigne 118 octets... L'hypothèse qu'un "vrai" referrer (http://bidule...) soit utilisé n'est donc pas exclue :smile:

Une spécification non-officielle du protocole RTMPE d'Adobe sur Internet

, , , ...

J'ai déjà parlé du protocole RTMPE, protocole de streaming propriétaire de chez Adobe, dans un billet sur M6 Replay ainsi qu'une façon de passer outre les sécurités du protocole en utilisant un logiciel spécifique.

J'avais aussi connaissance de "rtmpdump", petit logiciel en ligne de commande qui permet de télécharger les flux RTMP sur différents sites. Pendant un moment je suivais son développement de près puis m'en était petit à petit éloigné en me disant que si un jour le RTMPE, la version chiffrée du RTMP, était implémentée, j'en entendrais forcément parler d'une manière quelconque.

Et c'est arrivé :D
Un lisant cet article de Slashdot, on apprend qu'une spécification non-officielle a été créée en se basant sur le code source de rtmpdump.
La réaction d'Adobe a consisté à utiliser le DMCA (loi sur le droit d'auteur) pour forcer SourceForge à retirer de ses pages le projet rtmpdump qui y était hébergé.

L'implémentation de RTMPE dans rtmpdump n'est pourtant pas si récente. Si on regarde dans le ChangeLog du logiciel, on voit que le code a été rendu publique le 27 avril de cette année par la version 1.5.
On comprend l'agacement d'Adobe qui tenait secret les spécifications de RTMPE et vend ses solutions (serveurs de streaming) à un certain prix faisant tout pour que des solutions libres comme le serveur Red5 ou le player Gnash ne parlent que le RTMP qui est "non protégé".

Toutefois je n'ai pas encore jeté un oeil sérieux au support du RTMPE dans rtmpdump ainsi qu'à la spécification (qui commencent tous deux à se répliquer sur Internet dans un mouvement d'anti-censure avec en plus SF qui est montré du doigt) pour déterminer s'il est possible de télécharger facilement les vidéos sur des sites de streaming basés sur les technos Flash ou si cela requiert des connaissances particulières...

Bref un buzz à suivre de très près et qui montre une fois de plus que les DRMs sont voués à l'échec.

Plantages sous Linux

, , , ...

J'ai beau aimer Linux penguin et dire régulièrement du mal de Windows (la gestion des fenêtres est tout simplement exaspérante face à un bon KDE), j'admets tout de même volontier que Linux est aussi sujet à des plantages.
MAIS quand ça survient sous Linux, on se dit qu'il y a une bonne raison et que surtout il y a une façon de trouver quelle en est la cause et d'agir pour que ça ne se reproduise plus. C'est tout l'avantage d'un système open-source face à un système propriétaire où l'on peut juste... subir et espérer que les devs découvrent puis décident finalement de corriger le problème :frown:

Je ne sais plus exactement depuis combien de temps j'ai ce souci mais ça fait un bail... en fait probablement depuis que j'ai Shirley :eyes:
Le symptôme est toujours le même : tout d'un coup le disque dur se met à gratter et les commandes ne répondent plus. Seul le curseur de la souris est d'accord pour réagir (par intermittence). Si on est suffisamment rapide (c'est rare), on peut parvenir à fermer les applications et éviter le pire.
Le pire (qui est tout de même loin d'être l'enfer mais très énervant) c'est que le système reste inutilisable et gratte pendant un bon moment. Ca peut durer jusqu'à une demi-heure dans mon cas.
Ce comportement est typique d'un programme quelconque qui explose son utilisation mémoire et, une fois la RAM complètement utilisée, s'attaque à la SWAP (d'où l'utilisation intensive du disque dur)

Si on est patient, tout se termine "bien". Le kernel s'est chargé de tuer toutes applications graphiques, le WM et Xorg inclus (bref la crémerie avec la crémière et ses yaourts) et on est bon pour se relogger.
Quelques minutes plus tôt on s'acharnait à faire des Ctrl+Alt+F1 dans l'espoir de pouvoir régler nous même le problème.

Il est très difficile de déterminer quel programme est à l'origine du plantage... surtout quand on a toujours les même programmes ouverts p:
Longtemps j'ai soupçonné le "operapluginwrap" qui est le programme qui gère les plugins sous Opera. Son utilisation du CPU grimpe en flèche dès que l'on tombe sur du Flash (d'où l'utilité d'un FlashBlocker pour limiter les dégats).

Au même titre, j'ai soupçonné Vuze (client BitTorrent développé en Java), ce qui m'a poussé à passer à Deluge récemment.
Mais si on suit la logique, le souci pourrait aussi bien être lié à Firefox (souvent ouvert aussi) ou des daemons (mysqld, apache, tor...)

Malgré le passage à Deluge, le souci est réapparu. Je suis donc allé fouiller dans les logs :sherlock:
La dernière fois j'avais vu des appels à un programme nommé "kwin_killer_helper", sorte de soupape de sécurité de KDE qui tente de fermer des programmes quand ça part en sucettes... seulement son action a l'air peut efficace est surtout l'application n'est pas configurable :frown:

Cette fois j'ai remarqué des références à "oom-killer". Il agit de la même façon que kwin_killer_helper mais au niveau du noyau.
Le problème c'est qu'il semble reporter seulement le programme qui fait déborder le vase or ce n'est pas forcément celui qui s'est chargé de le remplir à 99%... toutefois en faisant des statistiques, on peut en déduire les programmes qui sont plus gourmants que les autres.

Ainsi un grep sur /var/log/messages me donne 9 "java invoked oom-killer", 1 pour opera, 1 pour policykit-kde et le dernier en date pour klipper :eek:
Le bon côté c'est qu'en abandonnant Vuze le problème apparait moins souvent mais il reste toujours présent :frown:

Si je regarde dans les logs plus anciens (archivés par logrotate), on retrouve java mais sont aussi mis en cause hald-addon-stor et mysqld Homer: Doh!

Si on effectue des recherches sur Google, on retrouve tous un tas de programmes variés qui ont "invoqué" oom-killer dont certains sont réputés stables.
Le souci semble donc venir d'ailleurs.

Pour certains, le souci est lié à la taille de la SWAP... il faudrait qu'elle soit au moins de la taille de la RAM. Je ne dirais pas que je n'y crois pas une seconde... mais presque (va pour deux secondes d'incrédulité :rolleyes: )
Pour d'autres (et sans doute plus fiables) et ici, ce serait lié à l'utilisation d'un système 32bits avec une machine 64bits (ce qui est mon cas).
Je vais probablement suivre la seconde possibilité mais je ne compte pas réinstaller mon système pour le moment (j'attends la version 11.2 d'openSUSE) p:

Pour le moment j'ai vu ici que chaque processus a dans /proc/ des fichiers oom_adj et oom_score dont il serait peut-être bon de connaître l'utilité :confused:
Et enfin un comique a enregistré le domaine oomkill.net sur lequel ont peut lire le boût de kernel correspondant à ce tueur de gaspilleurs de mémoire (oom = "Out Of Memory") :ninja:

FlashBlock pour Opera

, ,

Mieux vaut tard que jamais, j'ai découvert FlashBlock pour Opera. Je l'utilise depuis une semaine et j'en suis très content.
Il est composé d'un UserJS et d'un feuillet de style. Une fois les fichiers copiés dans leurs dossiers Opera, il suffit d'aller dans Afficher > Style et de sélectionner "Flash Blocker" pour l'activer.
L'utilisation est exactement la même que celle du plugin Firefox et en plus c'est compatible avec les dernières build béta d'Opera 10 :smile:

FlashBlock pour Opera

Jouons avec l'API de IMEEM.com

, , , ...

Introduction
Il y a quelques temps, je m'étonnais que mon script permettant de lire ma musique depuis Anywhere.FM ne fonctionnait plus, pensant qu'il sagissait de changements dans l'API du site.

En réalité la cause s'est avérée être le rachat de Anywhere.FM par le site "musico-social" Imeem.com.
Lors de ma dernière connexion sur l'ancien site, on m'a demandé obligé d'effectuer la transition des musiques vers Imeem.com. Une barre de status en Flash (bien évidemment) semblait affirmer que la migration se déroulait parfaitement... seulement leur outil m'a perdu les 9/10ème de ma collection :furious:
Déjà que les anciens scripts n'ont plus d'utilité, je l'ai senti passer (il m'avait bien fallu un mois pour tout uploader) :frown:

Décu mais pas désespéré pour autant, j'ai profité du fait qu'une vrai API soit disponible pour Imeem.com (contrairement à Anywhere.FM que j'avais du étudier moi-même sous toutes les coutures) pour coder un petit programme d'upload et peut-être à venir un nouveau player :smile:

L'API
Les bons points de l'API d'Imeem, c'est que l'on est pas obligé de manipuler cet affreux AMF propriétaire :smile: On peut communiquer au format XML ou Json pour lequel j'ai opté.

Communiquer avec les serveurs s'avère beaucoup plus simple, en revanche différentes protections sont présentes, comme la nécessité de disposer d'une clé d'API fournie par le site et de la communiquer à chaque appel de fonction. Anywhere.FM n'était aucunement documenté mais aucune protection n'était vraiment présente.
Les méthodes proposées par l'API donnent un goût de déjà vu : on se connecte (usersLogin), on récupère son ID (usersGetCurrentUser), on demande les IDs de nos listes de lecture (playlistsGetByUser), on en extrait les IDs des musiques (mediaGetByPlaylist) et on obtient au final une adresse où lire le titre (mediaGetInfo).

Le problème c'est que ces adresses sont destinées à être lues depuis un navigateur car il ne s'agit pas de lien direct vers un flux. Sans compter que ces urls permettent de lire le titre seul et qu'à côté de ça le Flash permettant de jouer toute une playlist ne fonctionne pas (les joies de Flash...)
Enfin bref, le site est à chier et plus vite je pourrais communiquer directement avec les serveurs mieux ce sera :D

La lecture des flux
Seulement la lecture des flux risquent de poser problème.
D'abord ce n'est plus de mp3 mais du flv (beurk). Ensuite ça passe par une méthode non documentée (ce serait trop beau) nommée "mediaGetStreamInfo" qui prend comme argument :
  • key : l'ID du média concerné
  • forceSample : un booléen (mettre à false)
  • isEmbed : booléen (false)
  • isFeatured : booléen (false)
  • methodVersion : nombre (à fixer à 2)
  • referrer : chaine, à fixer à "web"
  • supportsHD : booléen (probablement selon vos goûts)

Cette merveilleuse fonction recrache trois paramêtres :
  • h : le nom du serveur où se trouve le flv (par exemple "srv0202-01.sjc3.imeem.com")
  • p : un path qui semble fixe à chaque média (ex: "/g/m/5352700b33c7347ae3342390187a7fa4.flv") mais inexploitable directement
  • ep : une chaine de cracatère très longue qui fait penser à du base64

Ces trois paramêtres sont traitées dans notre navigateur par l'animation Flash du site (audio_player3.swf) qui nous recrache une URL valide vers un flv.
Il faudrait donc parvenir à extraire l'Action Script du Flash pour trouver l'algorithme permettant de récupérer une adresse de flux valide.
Si vous en avez le courage, toute aide sera la bienvenue ;-)

L'upload
Pour ce qui est de l'upload, le problème est réglé. L'API permet deux possibilitées, les temps passent par la page http://upload.imeem.com/apiupload.aspx :
  • Upload depuis une URL : on envoit des données en spécifiant où une des machines de Imeem doit aller chercher le fichier mp3
  • Upload par HTTP Post : un upload "classique" où il fait bien prendre soin au formatage des données.

Dans les deux cas, la page répond (si la requête est bien formatée) par un XML qui donne le code d'erreur correspondant (0 en cas de succès).
Certains auront déjà pensé aux possibilités offertent par la première méthode (transformer le serveur d'Imeem en outil de scan de ports etc p: ) mais ce n'est pas le sujet de l'article.

J'ai implémenté les deux méthodes d'upload, la méthode traditionnelle me semble un peu plus rapide. J'ai développé un outil en ligne de commande qui prend un répertoire en argument et upload tous les mp3s trouvés dans l'arborescence :smile:
Dans cette archive vous trouverez ma petite implémentation de l'API d'Imeem en Python, une librairie pour l'upload de fichier en HTTP (trouvée sur VoidSpace, légérement retouchée), l'uploader en ligne de commande ainsi qu'une capture au format pcap qui montre comment on arrive à la lecture d'un flux FLV en partant d'une adresse récupérée par mediaGetInfo (reste à savoir comment l'adresse du flux est calculée côté client).

Anywhere.FM Python CLI Player

, , , ...

C'est après pas mal de recherches et de tests de code que je suis finalement parvenu à développer en Python un lecteur de ma musique présente sur le site Anywhere.FM :smile:

Le dernier bug rencontré était, comme expliqué dans un précédent, le décodage de certaines chaines de caractères qui ne respectaient pas le format UTF-8. Le responsable était finalement le site lui-même ou plutôt les utilisateurs qui envoit des données au serveur à l'aide d'un navigateur utilisant un autre encodage.

Ainsi dans mon cas, le décodage de mon profil échouait car dans les données retournées se trouvait entre autres le nom mal encodé Ted Gärdestad correspondant à l'un des artistes les plus écoutés par une personne dans ma liste d'amis (la fonctione RPC get_complete_profiles du site renvoie beaucoup d'infos superflues :-| )

Pour résoudre ce souci il fallait intervenir sur la méthode readString de la classe Decoder de PyAMF (module amf3).
Comme je ne voulais pas et d'ailleurs ne pouvais pas modifier le code de PyAMF (installé sous forme de "egg" sur mon système), j'ai plutôt imaginé une technique de "Python API Hooking".
En fait il s'agit simplement de la redéfinition puis d'un écrasement d'une méthode d'une classe Python... mais avouez que dit comme ça, ça en jette beaucoup moins p:

La vérification ajoutée à la méthode readString consistait à lever une exception si le passage en unicode échouait et dans ce cas, à remplacer les mauvais caractères par des underscore. Ce qui donnait :
from pyamf.amf3 import Decoder
from string import maketrans

in_string="".join([chr(i) for i in range(126,256)])
out_string="_"*130
trans=maketrans(in_string,out_string)

def my_readString(object, use_references=True):
  def readLength():
      x = object.readUnsignedInteger()

      return (x >> 1, x & 1 == 0)

  length, is_reference = readLength()

  if use_references and is_reference:
      return object.context.getString(length)

  buf = object.stream.read(length)

  try:
    result = unicode(buf, "utf8")
  except UnicodeDecodeError:
    buf=buf.translate(trans)
    result = unicode(buf, "utf8")

  if len(result) != 0 and use_references:
      object.context.addString(result)

  return result

sav_readString=Decoder.readString
Decoder.readString=my_readString

L'opération de récupération et de correction de la liste de lecture "Entire Library" pouvant prendre un certain temps si la liste de lecture est importante, j'ai préféré diviser le programme en deux : le premier code récupère la liste de lecture et l'enregistre sous forme d'une liste Python dans un fichier ; le second est le lecteur qui charge la liste de lecture et va lire de façon aléatoire et unique un titre de la bibliothèque :smile:
Au final on gagne largement en charge réseau et en temps d'accès par rapport à l'utilisation du site Internet et on n'a pas à subir les utilisations mémoire et processeur gourmandes du plugin Flash p:

Vous avez donc get_library.py qui donne un résultat de ce style :
Connection on the server...
Connexion successfull!
user_id = 5418
Hooking the PyAMF Decoder
Getting the Entire Library playlist...
Library dumped!

qui génère un fichier library.py utilisé par le lecteur player.py qui joue les flux musicaux et donne un affichage dans ce genre :
Connecting on the server...
Connexion successfull
user_id = 5418
White America - Eminem ( The Eminem Show )
Troisième tour - Rapaces ( 2000 )
Les Poubelles du Coeur - Parabellum ( 1984-2004 )
Question de fun - Les Shériff ( La Saga Des Sheriff (disc 2) )
Alcohol - Dropkick Murphys ( Live On St. Patrick's Day )
Marilyn Moore - Sonic Youth ( Evol )
Come Back, Baby - Ramones ( The Chrysalis Years (disc 1) )
The Watcher - Dr. Dre ( 2001 )
...

La partie lecteur nécessite la présence de mplayer sur le système. Seul regret : on ne peut pas trop jouer sur l'interface de mplayer pour avoir quelque chose de moins verbeux et agréable... une simple barre de défilement de la lecture en cours (à l'instar de wget) m'aurait bien plu... mais c'est une autre histoire :smile:

Enregistrer les vidéos de M6Replay Béta sous Windows c'est possible

, , , ...

J'étais désespéremment à la recherche d'une spécification qui aurait pû m'en apprendre sur le protocole RTMPE utilisé par la Béta de M6Replay, en particulier sur la couche de cryptage, quand je suis finalement tombé sur un blog parlant d'un logiciel qui avait réussi à casser cette protection.

Ca m'a finalement amené à cette discussion sur le forum d'Adobe, puis au fameux logiciel ainsi qu'à un article sur le sujet sur Slashdot.

Après essai de Replay Media Catcher, il s'avère que effectivement ça fonctionne, entre autres sur M6Replay Béta.
Bien sûr, aucune information n'est disponible sur le fonctionnement du logiciel mais il doit utiliser à priori une attaque de type MITM, se faisant passer auprès du plugin Flash comme le serveur de streaming avant de renvoyer le flux réencodé par ses soins vers le véritable serveur. Bref il doit agir comme un proxy.
Le logiciel ne se sert pas de modules Internet Explorer, ni de la librairie pcap. Il n'y a donc pas, à la différence des logiciels concurrents, de sniffing. Le protocole RTMPE n'est donc pas "cassé", ce sont juste les développeurs de Replay Media Catcher qui ont trouvé puis implémenté la bonne astuce :smile:

Face à tout ça, Adobe a rajouté une clarification dans un document sur son Flash Media Server (pdf).

Et moi je n'ai trouvé aucune information intéressante sur le RTMPE... la magie des logiciels propriétaires :frown:


Ci-dessus, le fameux logiciel en action.

Dissection de Anywhere.FM

, , , ...

Il y a presque un an maintenant j'avais testé Anywhere.FM, un site permettant d'uploader sa musique en ligne pour pouvoir ensuite la réécouter depuis n'importe quel poste connecté à Internet et disposant du plugin Flash d'Adobe.
C'est très pratique et agréable... quand cela fonctionne. Et malheureusement sous Linux, ça ne fonctionne pas souvent. Premièrement la fonction d'upload ne fonctionne pas sous Linux avec Flash9 (je n'ai pas testé avec la version béta 10), la faute à Adobe qui semble moins pressé quand il s'agit de systèmes libres. Deuxièmement la lecture des titres (et c'est quand même l'utilité principale du site) semble fonctionner uniquement quand ça lui chante... et de toute évidence depuis des mois le site a décidé qu'il ne voulait pas lire les musiques sous Linux. Ainsi l'animation Flash reste bloquée sur "Loading..." mais ne charge rien du tout.

Bref dans mon raz-le-bol général j'ai décidé d'analyser le fonctionnement du site et de voir s'il est possible de développer un petit quelque chose qui me permettrait de pouvoir lire ma musique en ligne directement sur les serveurs sans avoir à passer par l'interface pompeuse existante p:
A l'heure actuelle il n'y a pas encore de code, seulement mon analyse qui dévoile la grosse partie du fonctionnement du site.

Identification
Si vous rendez sur une des urls du site, vous serez redirigés automatiquement vers http://www.anywhere.fm/player/. Cette page est chargée dynamiquement et en fonction de la page que vous avez demandé en premier, différentes variables seront passées à l'animation Flash chargée dans le navigateur (par exemple pour indiquer qu'il faut aller chercher la librairie de tel utilisateur).
Durant ce premier contact avec le site, un identifiant de session PHP (variable PHPSESSID) vous sera attribué et vous n'en changerez normalement pas même si vous fermez/ouvrez une session sur le site.
Cette variable de session définie comme cookie est spécifique à l'hôte www du domaine anywhere.fm (d'autres hôtes entrent en jeu comme on le verra plus tard).

Tracking
La page chargée fait appel à différents scripts JavaScript dont un qui a sans doute un rôle de tracking (analyse pour étude commerciale etc) mais à l'heure de ces lignes je ne le jurerais pas.
Le script en question se situe à l'adresse http://edge.quantserve.com/quant.js et il déclare une variable nommée "dc" qui est générée aléatoirement par le serveur.
La fonction principale nommée quantserve() récupère différentes informations sur la configuration de l'internaute et construit une url vers un "pixel traqueur" qui est ensuite chargée par le client pour envoyer les données au serveur pixel.quantserve.com.

La partie qui nous intéresse dans ce script (mais qui est peut-être optionnelle) est la modification des cookies.
La variable dc qui était définie se retrouve dans les cookies sous le nom "__qca".
Une autre variable, générée aléatoirement en JavaScript (code : Math.round(Math.random()*2147483647) ) est aussi ajoutée au cookie et se nomme "__qcb".

Contrairement à PHPSESSID qui est propre à www, __qca et __qcb sont propres à l'ensemble du domaine.

Crossdomain
L'animation Flash étant chargée, elle a besoin de vérifier qu'elle a bien la permission de communiquer avec certains serveurs. Pour cela elle va demander pour chacun un fichier crossdomain.xml qui définie les règles d'accès.
Parmis les serveurs on trouve music, upload-XX (XX étant un nombre multiple de 5) et upload-full.

Authentification
L'animation ayant déterminée qu'elle pouvait dialoguer avec music.anywhere.fm, elle effectue une requête GET sur la page /account/get_current_user?_unique_request_id_=XXXXXXXXXXXXXX.
L'argument _unique_request_id_ a pour valeur un nombre probablement aléatoire mais toujours très grand. Cet "identifiant unique de requête" est incrémenté à chaque communication avec le serveur music.

Le serveur music donne ses réponses sous le format XML. En l'absence de la saisie de vos identifiants, les données renvoyées sont celles de l'utilisateur par défaut (user_id = 89, login = demo, fullname = Lux)

La page en profite pour définir trois nouvelles variables de cookie :
  • auth_token et auth_session_id ont pour restriction l'hôte music
  • _HotPot_session_id est défini sur tout le domaine anywhere.fm

Ces trois variables sont typiques des variables de session : 32 caractères sous l'alphabet hexadécimal.
Malgré leur nom différents, auth_session_id et _HotPot_session_id ont la même valeur.

L'opération suivante est un POST à l'adresse /account/create_login_tracker?_unique_request_id_=XXXXXXXXXXXXXX.
Les variables vues précédemment et concernant l'hôte music ou tout le domaine sont bien sûr renvoyés à nouveau au serveur.
Les données passées dans le corps de la requêtes sont session_id (correspondant à auth_session_id de tout à l'heure) et user_id (correspondant à celui renvoyé par la requête précédente soit 89 ici).
Cela renvoit une information très courte de la forme : <tracker_id>XXXXXXXXXX</tracker_id>

Introduction à AMF
Débutent alors les communications AMF avec le serveur www. Action Message Format est un format de données créé à l'origine par Macromedia puis conservé par Adobe.
Il est utilisé principalement par Flash pour des communications RPC.
L'avantage par rapport à un formatage XML sont son format binaire (on n'est pas resteint à des caractères imprimables) et son gain de place. Il utilise notemment un système de références qui permet de ne pas avoir à redéfinir des données déjà envoyées (pour le moment je n'ai pas étudié en détails ce sujet).
On pourrait comparer ce format au bencodage de BitTorrent mais en réalité il est bien plus complexe, voire carrément prise de tête (rien que le codage d'entiers sur 29 bits donne un aperçu)
De plus deux versions existent : AMF0 et AMF3. Il peut possible d'intégrer des données AMF3 dans du AMF0 par le biais d'un type particulier (0x11)
Les requêtes HTTP transportant des données AMF ont l'entête "Content-Type: application/x-amf"

Ce format n'étant pas le sujet de l'article, je n'en dirais pas plus pour le moment.

Connexion
Passons pour l'instant sur les échanges RPC qui ont été faits, et rentrons nos identifiants de connexion sur le site.
Cela génère une requête POST sur /account/login?_unique_request_id_=XXXXXXXXXXXXXX sur le serveur music. L'identifiant de requête a (ne l'oublions pas) été incrémenté à plusieurs reprises et cela ne changera pas.
Les données envoyées sont :
  • dans le header Cookie : __qca, __qcb, auth_token, auth_session_id et _HotPot_session_id
  • dans le body : "login" et "password"

auth_session_id et _HotPot_session_id ont toujours la même valeur.
La réponse du serveur nous informe d'une nouvelle valeur pour auth_token (qui définie donc notre session utilisateur sur le serveur music)
Sur le serveur www, aucun changement n'est à prendre en compte, les données du cookies n'ont pas changées.

Les deux requêtes effectuées avant connexion sont répétées sauf que get_current_user renverra notre user_id et create_login_tracker renverra une nouvelle valeur.

RPC et Flex
Les échanges RPC sont effectués uniquement avec le serveur www et à destination de la page
/amfphp/gateway.php?_unique_request_id_=XXXXXXXXXXXXXX
Les requêtes RPC sont toujours groupées par deux et ont le même _unique_request_id_. Anywhere.FM utilise les librairies Flex dans ses échanges.
On trouve ainsi deux types de messages :
  • flex.messaging.messages.CommandMessage : permet de faire un "ping" du serveur. C'est toujours la première requête du groupe
  • flex.messaging.messages.RemotingMessage : permet d'appeler une fonction RPC sur le serveur. C'est toujours la seconde requête

Ces fonctions Flex se basent toujours sur les mêmes arguments.
Concernant les fonctions RPC, l'argument source sera toujours fixé à "BlazingFast.DBQueries". L'argument operation contient le nom de la fonction à appeler sur le serveur et body est un tableau contenant les arguments à y passer.

Fonctions
Le premier argument de chacune des fonctions existante correspont au auth_session_id, permettant ainsi au serveur de vérifier les permissions d'accès aux données.

La première fonction qui nous intéresse est get_complete_profiles. Elle prend deux argument : le premier est (bien entendu) l'identifiant de session et le second est un tableau contenant une liste d'identifiants utilisateur.
Le résultat retourné est un tableau contenant des informations diverses sur les utilisateurs correspondant, notemment les liste de titres dont ils disposent.
Trois données importantes permettent de définir une playlist : playlist_type, playlist_id et playlist_name.
Le type d'une playlist aura la valeur 200 s'il s'agit de la collection entière de l'utilisateur et 300 s'il s'agit d'une librairie personnalisée.

La fonction get_songs_for_user_playlists pourrait se définir de cette façon :
get_songs_for_user_playlists(session_id, user_id, bool, [playlist_id], [playlist_type], ['0','0'...])
Le rôle du booléen en troisième argument m'échappe pour le moment ainsi que le tableau de chaines de caractères définie à '0'.
Cette fonction retourne un tableau qui peut être imposant définissant chaque titre d'une liste de lecture. En dehors des données "classiques" (name, artist, album), on trouve les données "master_id" et "song_ressource_id" qui permettent de récupérer une adresse valide pour un titre donné.

C'est la fonction get_song_url qui nous donne le sésame. On pourrait la déclarer de cette façon :
get_song_url(session_id, user_id, master_id, song_ressource_id)
Elle retourne une chaine de caractères correspondant à une url HTTP vers le fichier MP3 :smile:
Cette url dispose d'une clé d'accès ainsi qu'un temps de validation. Par conséquent il ne doit pas être possible d'utiliser deux fois la même url.

Conlusion
Il reste de nombreux points à éclaircir qui doivent pouvoir être déterminés par expérimentation comme :
  • _unique_request_id_ est-il généré aléatoirement au début ?
  • Les variables définies par quantserve() ont-elle une utilité autre que le tracking ?
  • Quelle utilité a la valeur tracker_id ?
  • A quoi sert le booléen et le tableau de chaines passé à get_songs_for_user_playlists ?

Netographie
Open Source Flash documentation
PyAMF (les codes sources m'ont bien aidé)
Charles Web Debugging Proxy : Un proxy qui comprend l'AMF :smile: Malheureusement en shareware :frown:

Ping Flex
Pour terminer un petit exemple de code utilisant l'API PyAMF qui réalisé un ping en Flex (merci aux développeurs de l'API pour leur aide ;-) ) :
import pyamf
from pyamf.remoting import client
from pyamf.flex import messaging
from pyamf import remoting

gw = client.RemotingService('http://127.0.0.1/',pyamf.AMF0,pyamf.ClientTypes.Flash9)
message = messaging.CommandMessage(body={},
    timestamp=0,
    destination='',
    clientId=None,
    headers={'DSId': u'nil'},
    timeToLive=0,
    messageId='7478D81C-65B4-EA4B-B52E-4689507A7241',
    operation=5,correlationId='',
    messageRefType='flex.messaging.messages.CommandMessage')
gw.addRequest('null', message)
gw.execute()

M6Replay Béta et Linux

, , , ...

Vous êtes peut-être au courant de l'existence d'une version béta de M6Replay qui permet aux utilisateurs de Linux et Mac de profiter des vidéos du site de M6.

Je m'étais déjà penché il y a quelques temps sur la version initiale (pour le moment toujours la version officielle) histoire de voir si il était possible de bricoler une "zapette" comme celle pour les émissions en clair de Canal+.
Après avoir pas mal fouillé, et trouvé assez facilement les urls des flux, il s'avérait que le protocole utilisait était du "RTSP-over-HTTP" (ou du moins quelque chose dans ces eaux là :D ), qu'on pouvait normalement le lire en bidouillant les options de VLC ou en passant un simple "mms://" au lieu du protocole dit officiel.
On final on avait bien de l'image et du son mais pas vraiment ce qu'on désirait. J'ai quand même cru discerner durant un dixième de seconde une paire d'yeux au milieu d'un artistique nuage jaune fluo et de taches oranges... à moins que ce soit l'effet du hazard p:
En fait il semblerait qu'un codec propiétaire (DRM-powered) soit utilisé en plus, rendant impossible la lecture sur les systèmes libres :frown:

Avec la version béta les vidéos sont bien lisibles sous Linux, mais M6 s'est visiblement cresé la tête pour trouver une solution tout aussi imbuvable.
Ca fonctionne donc avec le lecteur Flash (qui rappelons le est propriétaire)... mais pas autre chose.

On a bien un flux XML dans lequel on retrouve des urls du style
mp4:production/live/DIFF_28072008/H264_UN_DINER_PRESQ__UN_DINER_PRESQ__280720080615__LIVE.mp4
et en observant le traffic réseau on sait que le flux vidéo est lu depuis le serveur fcds98.lon.llnw.net (le nom de machine n'est probablement pas fixe) mais à part ça difficile de retrouver un lien direct pour y pointer son lecteur multimédia préféré.

Après recherches le flux vidéo utiliserait le protocole RTMP (Real Time Messaging Protocol), un protocole (bien évidemment) propriétaire développé par Macromédia (avant rachat par Adobe).
Il existe différents outils closed-source pour capturer les vidéos des flux RTMP, la majorité se basant sur le même principe que cette source C++ à savoir utiliser la librairie pcap et surveiller tout ce qui communique avec le port TCP 1935.

Le protocole RTMP a aussi été analysé et est utilisé par le projet "Open Source Flash" par le biais du serveur Red5.
Comble du bonheur, les versions béta de VLC (>= 0.9.*) supportent le RTMP :smile:

Petit problème : M6Replay utilise à priori le RTMPE ("RTMPE is a version of RTMP which is an 128-bit encrypted protocol"), ce qui fait que ces outils se montrent inefficaces.

Bref pour le moment seulement le lecteur Flash peut lire ces vidéos sous Linux. Il reste à trouver les adresses exactes des flux et attendre que le RTMPE soit supporté par un logiciel libre pour pouvoir en profiter "pleinement".

J'ai testé Anywhere.FM

, , , ...

Quoique "testé" est peut-être un faible mot en comparaison de la quantité de données que j'ai pu envoyer sur ce nouveau service en ligne.

Anywhere.FM est, comme son nom peut le laisser deviner, un site web communautaire dédié à la musique.
Il vous propose de mettre en ligne votre discothèque afin que vous puissiez l'écouter de n'importe quel poste relié à Internet, le tout sans limite d'espace ni de taille de fichiers à l'upload.

La première réaction que l'on a est de chercher la petite bête et l'on s'attend à voir quelque part un nom de serveur dans le genre "all-your-music-is-belong-to-us.riaa.com".
Mais première surprise, on peut créer un compte le plus facilement du monde, seulement en choisissant un login et un mot de passe, sans être obligé de donner une adresse email ^_^
Après une petite analyse on découvre que la musique est uploadée sur des serveurs d'Amazon. On est sans doute plus proche d'une étude commerciale sur les gouts musicaux qu'un honeypot de la RIAA p:

Le site est fait en Flash et est très simple d'utilisation mais un peu limité côté fonctionnalités. Plusieurs interfaces bien flashies sont proposées. Les boutons que l'on utilise le plus souvent sont "Upload" et la flèche de lecture.

Une fois que la musique est uploadée, il n'y a plus grand chose à faire : impossible de supprimer un titre ou de le retagger, cela oblige à avoir proprement taggé sa musique avant l'upload.
Le site est peu portable, la fonction principale (upload) ne fonctionne pas avec Firefox ou Opera. Si vous avez le trio perdant Flash/Linux/Opera, l'appli est carrèment impraticable. Cela oblige à utiliser un IE4Linux pour les amateurs d'OS libres :frown:
Ca pompe beaucoup de ressources mais ça fonctionne.
Difficile d'expliquer pourquoi l'upload ne se lance pas sur FF et Opera. J'ai d'abord pensé à l'utilisation d'un protocole non supporté (ça aurrait pû être intéressant de jouer avec du WebDAV) mais les fichiers sont en réalité envoyés par un bête POST.

Difficile aussi de dire à qui revient la faute (Opera et firefox, Macromedia ou Anywhere.FM ?) Je n'exclus pas non plus l'hypothèse que seul IE fonctionne à cause d'un bug que les autres n'ont pas (dans le genre non respect d'une "cross-domain policy" à tout hazard).

Le service est toujours en version Béta, c'est écrit blanc sur rouge sur le site. C'est déjà arrivé que le service soit indisponible ou que la playlist ne se mette pas à jour etc. J'ai aussi eu des erreurs plus ou moins abstraintes (erreur 429 et "no more files to upload"), mais à chaque fois le site est revenu assez vite.

Pour s'éviter des procès pour piratage, le site de permet pas le téléchargement des titres uploadés. Il propose à tous les visiteurs la lecture de morceaux gratuits en streaming.
Des titres non gratuits peuvent aussi être écoutés mais dans ce cas le visiteur ne peut pas les sélectionner lui-même.
La musique des membres peut en effet être écoutée en ligne par les visiteurs (par exemple ma musique est ici) mais la playlist ne s'affiche pas et les titres sont sélectionnés aléatoirement.
Si vous n'êtes pas membre, la lecture est bloquée au boût de 5 morceaux.

La relation avec last.fm est évidente : contrat avec Amazon, système communautaire qui calcule le taux de compatibilité musicale avec les autres membres, le nombre de titres joués et sans doute plus à venir.

Pour conclure, Anywhere.FM s'avère être quasi indispensable pour apporter votre musique avec vous (pour peut que vous ayez l'accès à Internet) ou simplement si vous avez peur de perdre votre musique dans un crash disque.
Malheureusement on regrettera l'utilisation de Flash qui pose problème avec certains navigateurs (et pas les moindres pourtant).