photo of devloop

devloop :: blog

sécurité informatique, programmation, Linux et plus encore

Subscribe to RSS feed

Posts tagged with "flash"

Corriger le problème d'affichage des animations Flash sous Linux / Nvidia

, , , ...

Un des problèmes que je rencontre régulièrement avec le plugin Flash concerne les vidéos présentes sur YouTube.

Leur lecture dans le navigateur laisse comme un résidu à l'affichage sous X et quand une fenêtre repasse devant alors que la vidéo est en cours de lecture, la zone délimitée par Flash reste visible comme si elle était devant toutes les autres fenêtres mais avec un effet de transparence indésirable.
C'est en particulier énervant avec Konsole car les caractères blancs et le fond noir rajoutent presque un effet de flou ou de distorsion...

Je n'avais pas de solution pour ce problème si ce n'est d'utiliser autant que possible la technologie HTML5 proposée par YouTube soit en activant un cookie spécifique soit en ayant recours à une extension de navigateur comme WebM+ pour Opera.

Mais sur My-Guides.net (déniché il me semble via le RSS de TuxMachines) il semble bien qu'une solution réellement efficace corrige ce problème :

My-Guides : How to fix the leaking overlay problem of Flash and Nvidia

L'opération décrite consiste à définir la variable d'environnement VDPAU_NVIDIA_NO_OVERLAY à la valeur 1.
Chacun trouvera un emplacement dans son système où définir cette variable. Comme le bug ne concerne que le plugin Flash, on peut par exemple rajouter la ligne dans le script chargé de lancer le navigateur (/usr/bin/opera et /usr/bin/firefox sont tous les deux des scripts bash) :
export VDPAU_NVIDIA_NO_OVERLAY=1


En ce qui me concerne j'ai cherché un emplacement pour que la variable soit définie pour toutes les applications sous Xorg et j'ai finalement rajouté cette ligne dans le fichier /etc/X11/xinit/xinitrc (présent au moins sous openSUSE 12.1).

En espérant que ça en aide d'autres smile

HTML5 pour Google Play (Music)

, , ,

J'ai découvert que Google Play permettait d'écouter sa musique via le HTML5 au lieu de cette immonde technologie Flash (un jour il faudra condamner Adobe pour cyber-crime contre l'humanité).

Du coup c'est un vrai plaisir d'écouter sa musique en ligne sans voir les temps de réponses de son système augmenter exponentiellement et certains processus tourner autour des 100% smile



Espérons que d'autres services feront le pas vers HTML5, surtout que le support de Flash pour Linux a encore été remis en question...

L'astuce pour activer le HTML5 sous Google Play : Rendez-vous sur l'icône de roue en haut à droite de l'écran puis dans Music labs activez HTML5 Audio smile

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é bigsmile
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 sad

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 bigeyes
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 sad

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 sad

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 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) sad

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 bigsmile

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 sad


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 sad

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()