Télécharger les musiques de Deezer sur son disque dur
Un article de LaPageDuJour.
Vous connaissez tous Deezer, le site qui permet d'écouter de la musique gratuitement. Bien que ce ne soit pas le souhait des créateurs de Deezer (et de ceux qui leur confient leur musique), il est tout à fait possible de télécharger la musique présente sur ce site.
Dysnomia
Un développeur nommé Solozerk a développé un programme appelé dysnomia qui permet de montrer que le système de Deezer n'est pas fiable. Vous pouvez rechercher une musique et là télécharger directement sur votre ordinateur.
Pour résumer la stratégie de sécurité de Deezer, voici un petit commentaire de l'auteur :
// On a désormais deux clefs différentes, contrairement aux versions précédentes. // Par ailleurs, deux RC4 sont désormais appliqués sur chacun des deux champs // (clef de download + identifiants de session) au lieu d'un seul, ce qui est tout // aussi inutile que précédemment. // La clef de cryptage des clefs de streaming, dans la version 7b. const unsigned int deezer_streaming_key[24]={246, 73, 184, 214, 95, 23, 185, 121, 202, 15, 39, 120, 193, 190, 236, 170, 61, 82, 73, 102, 90, 123, 129, 8}; // La clef de cryptage des identifiants de session, dans la version 7b. const unsigned int deezer_sessions_key[24]={9, 55, 125, 125, 15, 147, 226, 60, 114, 144, 166, 8, 83, 184, 57, 234, 171, 13, 71, 134, 167, 42, 119, 142};
Voici ce qui permet de générer la bonne adresse :
decrypted_key=deezer_decrypt(key, TYPE_STREAMING); strcpy(url, "http://proxy-"); url[13]=id[strlen(id)-2]; url[14]='\0'; strcat(url, ".deezer.com/cacheDiffusion.php?ID="); strcat(url, id); strcat(url, "&KEY="); strcat(url, decrypted_key); strcat(url, "&SESSION_ID="); strcat(url, decrypted_sessid);
Tout ça serait bien plus beau en C# .Net, mais bon, c'est quand même intéressant... Voici la fonction de décryptage :
char* deezer_decrypt(const char* str, int type) { int size=strlen(str); if((size%2)!=0) { cerr<<"Erreur : id de session ou clef incorrect(e)."<<endl; exit(42); } char nb[3]; int vnb; const unsigned int* key; char* decrypted=new char[size+1]; if(type==TYPE_SESSION) key=deezer_sessions_key; else key=deezer_streaming_key; memset(decrypted, 0, size+1); for(int i=0; i<size; i+=2) { strncpy(nb, str+i, 2); nb[2]='\0'; sscanf(nb, "%x", &vnb); sprintf(nb, "%02x", (vnb^key[i/2])); strcat(decrypted, nb); } return(decrypted); }
On a donc un hashage avec deux clès 24 * 8 = 192 bits pour les identifiants de sessions et pour les demandes de streaming. La clès pourrait faire 2048 bits, ça ne changerait rien. L'auteur peut de toute façon la déterminer en fouillant l'application flash.
Comme l'auteur du programme a donné une interview exclusive sur MindFree, je me permet de recopier ses propos :
Deezer, pour dialoguer entre l’applet flash du site (exécutée en local chez le visiteur) et les scripts php du site en lui même, utilise un framework : amfphp (http://amfphp.org/). Ce framework permet à flash de serialiser les données à transmettre à php sous une forme particuliere (se réferer au code de amfphp pour plus de détails), et à php de les désérialiser pour les obtenir sous une forme lisible. Il permet également l’opération inverse, pour passer des données de PHP à l’applet flash en réponse à des requetes provenant de l’applet flash.
Le script php deezer s’occupant de gérer les requetes (recherche, demande de lecture de musiques, etc…) envoyées par l’applet flash, et d’y répondre, est nommé gateway.php (http://www.deezer.com/flashservices/gateway.php).. Ce script s’occupe donc de recevoir les demandes de l’applet flash et d’y répondre, les deux (les requetes comme les réponses) étant transmises sous forme sérialisée amfphp.
Pour effectuer une recherche sur le site deezer, l’applet flash (aprés avoir permis à l’utilisateur de taper sa recherche dans la textbox en flash) procède comme ceci :
- Envoi d’une requete demandant un identifiant de session deezer (en réalité, cette requete est envoyée au chargement de l’applet flash et le résultat est utilisé partout aprés, mais en demander un nouveau avant chaque opération ne pose pas de probleme, et les opérations en question ne peuvent pas être exécutées sans identifiant de session).
- L’identifiant de session deezer est récupéré en résultat de la requete. Il est reçu sous forme cryptée, avec une clef A.
- Une requete de recherche est envoyée, comportant le motif de recherche tapé par le visiteur (exemple : “tryad”). Ce motif de recherche est crypté avec une clef B avant son envoi.
- Les résultats de la recherche sont récupérés en résultat de la requete. Ils sont en clair et chacun des résultats comporte de nombreux champs (artiste, titre, album, PUID unique de la chanson, note des visiteurs, etc…).
- L’applet flash n’a plus qu’à afficher les différents résultats de la recherche.
Toutes ces requetes/ces résultats de requetes sont, là encore, envoyés/reçus à gateway.php, sous forme sérialisée amfphp. Or, l’applet flash n’est qu’une application comme une autre, exécutée sur l’ordinateur du visiteur. Ce qu’elle fait (requetes, déserialisation et analyse des réponses, etc…), n’importe quelle autre application peut le faire. C’est ce que fait dysnomia : il imite completement le comportement de l’applet flash deezer pour avoir accés aux même fonctionnalités (recherche, lecture de musique…) que l’applet.
Le champs PUID renvoyé avec les résultats de recherche est un identifiant unique pour chaque chanson. Il s’agit en réalité de l’identifiant musicbrainz, une base de données libre regroupant des informations sur des musiques. Par exemple, le PUID de la musique “The final rewind”, de l’artiste Tryad, est : 0d9236ca-3027-0a87-3f9a-4e8c11b6aa4a.
On peut consulter les informations relatives à ce PUID ici, par exemple : http://musicbrainz.org/show/puid/?puid=0d9236ca-3027-0a87-3f9a-4e8c11b6aa4a
Voyons maintenant comment fonctionne la lecture de musique. L’applet flash, une fois la recherche effectuée, a une liste de musiques résultante de la recherche, avec pour chacune un PUID. Pour pouvoir lire une musique en particulier, elle doit exécuter une requete demandant le mp3 en spécifiant ce PUID unique. Elle recevra en réponse le mp3, qu’elle pourra jouer. Dysnomia, là encore, se contente d’imiter son comportement, mais au lieu de jouer le mp3 reçu, il l’enregistre sur le disque. Cela permet de télécharger des musiques depuis deezer. En réalité, la demande de réception du mp3 est un peu plus compliquée qu’une simple requete précisant le PUID, pour des raisons de protection du contenu.
Voila comment ça se passe :
- Envoi d’une requete demandant un identifiant de session deezer (même remarque que plus haut : l’identifiant de session est en fait récupéré juste aprés le chargement de l’applet, meme si dysnomia en redemande un à chaque fois - ce qui n’a aucun impact sur le fonctionnement).
- Reception de cet identifiant, crypté avec la clef A.
- Envoi d’une requete demandant une clef de téléchargement de mp3. Une telle clef donne l’autorisation de “télécharger” (normalement afin de lire la musique dans l’applet, mais dans le cas de dysnomia de la sauver sur le disque) une musique en particulier, une fois et une seule. Cette requete de demande de clef doit comporter l’identifiant de session deezer récupéré, *aprés* l’avoir decrypté, ainsi que le PUID de la musique que l’on souhaite télécharger (et qui est disponible dans les résultats de recherche). Si l’identifiant de session ou le PUID spécifié dans la requete n’existent pas (PUID ne correspondant à aucune musique ou identifiant de session jamais attribué en réponse à une requete), alors une erreur se produit et rien n’est renvoyé.
- Reception de la clef de téléchargement, cryptée avec la clef A.
- Envoi d’une requete demandant le mp3. Cette requete doit comporter l’identifiant de session deeezer decrypté, la clef de téléchargement decryptée, et le PUID de la musique désirée. Contrairement aux autres requetes, celle ci est effectuée sur une page différente, cacheDiffusion.php, et sur un serveur différent. L’url de la requete est plus précisément :
http://proxy-<x>.deezer.com/cacheDiffusion.php ?ID=<PUID de la chanson> &KEY=<clef de téléchargement decryptée> &SESSION_ID=<identifiant de session deezer decrypté>où <x> est le dernier caractere du PUID de la chanson désiré. Exemple pour le PUID évoqué plus haut (tryad - the final rewind) : proxy-a.deezer.com.. Si l’identifiant de session spécifié n’existe pas (parce que jamais attribué ou mal decrypté), si la clef n’existe pas (mêmes raisons possibles), ou si le PUID spécifié ne correspond pas à celui pour lequel la clef de téléchargement a été demandée, ou encore si la clef a déjà été utilisée pour télécharger la musique, alors un message d’erreur apparait, indiquant que “votre ip a été enregistrée”, blablabla. Le message d’erreur en question peut par exemple être visualisé simplement en ne passant aucun parametre, ici par exemple :
http://proxy-a.deezer.com/cacheDiffusion.php
On note également que contrairement aux autres requetes, celle ci passe les parametres par GET dans la requete et non par sérialisation amfphp. A noter également que n’importe quel proxy est en fait utilisable pour télécharger la chanson, pas forcément celui dont le caractere correspond au dernier caractere du PUID de la musique (même si à l’époque où j’avais testé ça, des erreurs php apparaissaient en plus de la musique indiquant un espace disque insuffisant pour copier un fichier, probablement le mp3 dans le cache du proxy utilisé, qui n’est pas prévu à la base pour relayer cette musique).
- Réception du mp3 et lecture (dans le cas de l’applet) ou enregistrement sur le disque (dans le cas de dysnomia).
http://proxy-<x>.deezer.com/cache/<y>/<PUID>.rbsoù <x> est le dernier caractere du PUID, <y> l’avant dernier, et <PUID> le puid de la musique désirée. Exemple avec une autre musique de tryad, “Beat into submission”, dont le PUID est
4b75899d-6c0f-dcf8-ccf8-889a31042c0f : http://proxy-0.deezer.com/cache/f/4b75899d-6c0f-dcf8-ccf8-889a31042c0f.rbs
Cependant, ce mode de téléchargement n’est pas utilisé par l’applet flash deezer (et il est *théoriquement* détectable dans les logs http du proxy concerné). Il est proposé dans dysnomia en tant que proof of concept (et permet de téléchargement des musiques tout aussi bien que le téléchargement “classique”, sans besoin de demander aucune clef).
Voila donc comment fonctionne dysnomia, en imitant le comportement de l’applet flash deezer (à l’exception de ce dernier mode de téléchargement où il procede différemment).
A noter que j’ai parlé d’une clef A et d’une clef B, utilisées respectivement pour decrypter/crypter les identifiants de session et clef de téléchargement et les motifs de recherche. En réalité, l’applet flash utilise un cryptage RC4 mais étant donné la maniere dont fonctionne l’algorithme RC4, il revient exactement au meme dans ce cas précis de crypter/decrypter avec un simple XOR à l’aide de ces deux clef différentes. C’est ce que fait dysnomia, au lieu d’utiliser RC4. A noter qu’utiliser du cryptage pour “protéger” deezer n’a en réalité strictement aucune utilité, puisque qu’on dispose du texte en clair (simplement en sniffant les requetes envoyées par l’applet flash pour voir quelle est la version decryptée de la clef de téléchargement/de l’identifiant de session - et qu’on a la version en clair du motif de recherche pour la bonne raison que c’est le visiteur qui le tape) ET de la version cryptée (en sniffant les réponses envoyées depuis deezer à l’applet pour la clef/l’identifiant de session, et en sniffant la requete de recherche envoyée par l’applet pour le motif de recherche). Dans ces conditions, aucune véritable cryptanalyse n’est nécessaire pour obtenir des clefs permettant de crypter les données à l’identique.
Dysnomia utilise curl pour effectuer les requetes http (http://curl.haxx.se/) et wxWidgets pour l’interface (http://wxwidgets.org/). Il n’y a pas de véritable sérialisation/déserialisation amfphp implémentée dans dysnomia. A la place, le logiciel se contente de serialiser/déserialiser au format amfphp les données dans le cas particulier de deezer, en connaissant la structure des résultats de requete/des requetes à envoyer.
Voila voila :-)
Pour plus de détails (notamment les valeurs exactes des clefs A et B, ou encore le format exact de serialisation), il suffit de se reporter au code du module “deezer” (deezer.cpp et deezer.hpp) de Dysnomia. A noter qu’à deux reprises depuis la création de dysnomia, effectivement, le fonctionnement de deezer a changé (la premiere fois pour rajouter le cryptage des identifiants de session/clefs de téléchargement, la deuxieme pour modifier légerement les structures de données et les clefs de cryptage). Dans les deux cas, dysnomia a été patché pour s’adapter, et il continuera de l’être à chaque modification deezer :-) </quote>
Ethereal
Cette technique permet de télécharger les MP3 quelque soit les changement de stratégie de sécurité effectués par Deezer. Cette méthode nécessite de bonnes connaissances informatiques. Je ne décris ici que les grandes étapes
- Allez sur Deezer, cherchez votre musique (qui n'est pas protégée par les droits d'auteurs bien sur)
- Ouvrez [Ethereal http://www.ethereal.com/download.html], lancez la capture
- Ouvrez la musique que vous voulez écouter
- Dans Ethereal, une adresse devrait arriver en grand nombre. Une fois que le téléchargement de la musique s'est terminé, faites un clique droit sur un de ces paquets et choisissez "Follow TCP stream".
- Une fenêtre s'ouvre alors, cliquez sur le menu déroulant, choisissez là où il y a le plus de "bytes" (chez moi les indications de sens de communication sont fausses).
- Cliquez sur Raw, faites "Save As" et sauvez dans un fichier "fichier.mp3"
- Prenez un éditeur Hexadecimal (genre PsPad)
- Ouvrez le fichier en HexaDecimal (Clique droit / PsPad Hex pour PsPad)
- Sélectionnez toute la partie textuelle jusqu'aux quatre petits points qui correspondent au code Hexa :
0D 0A 0D 0A, c'est à dire le "\r\n\r\n" du protocole HTTP. Puis coupez ça. - Sauvez le fichier
- Ouvrez le fichier avec Winamp, remplissez les infos correctes et voila, votre MP3 téléchargé est prêt