Deezer Defuck
=========
Decrypt ya Deezer
1. DL the stream:
It's a Http - Download - I use Proxomitron LogWindow to 'catch the url on the fly however
http://all-streaming-media.com/recor...g-software.htm Replay Media Catcher 5 or
VSO Downloader
2. The DL MP3 is somehwo crippled.
-> Workaround: Lower player speed to 50% so it didn't sound like Micky mouse.
However that's not really satisfying
Well some how several MP3-Frames are broken/skipped
a close look.
Winhex ( Datensatz-Darstellungen: 32 418 / Columsize: 209)
Pos MP3-Frame
__________________________
1 Encrypted Frame 1 16 31 46
2 Encrypted Frame 2 17 32 47
3 Encrypted Frame 3 18 33 48
4 Encrypted Frame 4 19 34 49
5 Encrypted Frame 5 20 35 50
n*1
1 UnEncryted Frame 6 21 36
2 UnEncryted Frame 7 22 37
3 UnEncryted Frame 8 23 38
4 UnEncryted Frame 9 24 39
5 UnEncryted Frame 10 25 40
6 UnEncryted Frame 11 26(G)41
7 UnEncryted Frame 12 27 42
8 UnEncryted Frame 13 28 43
9 UnEncryted Frame 14 29 44
A UnEncrypted Frame 15 30 45
Okays so much about static look.
II Let's get the real inside
=============================
Chrome ->F12 Develtools/Src Tab
Shift+Ctrg+F (to search all load source) '.swf
Harvest:
http://cdns-files.deezer.com/swf/cor...-v00340583.swf
SoThink SWF-Decompiler
Deed in coreplayer3-v00340583.swf
Okay finally
coreplayer3-v00340583\dzcoreplayer\crypt\Decrypter.as
...
public static const PART_SIZE:uint = 6144;
...
coreplayer3-v00340583\dzcoreplayer\Streamer.as
Code:
private function onLoadTimer(event:TimerEvent) : void
{
var minChunks:uint;
var maxChunks:uint;
var len:uint;
var ba:ByteArray;
var decryptedData:ByteArray;
var e:* = event;
var loaded:* = this._loader.bytesAvailable;
var decrypted:* = this._decryptedData.length;
if (decrypted < this._bytesTotal)
minChunks = Decrypter.PART_SIZE * 2; //0x1800 6144 * 2 -> 12288 0x3000
maxChunks = Decrypter.PART_SIZE * 3; //0x1800 6144 * 3 -> 18432 0x4800
if ( loaded >= minChunks ||
this._bytesTotal - decrypted < minChunks)
{
len = Math.min(loaded, maxChunks); \\ limit loaded to maxChunks
if (len > minChunks) \\ only when within minChunks...maxChunks (0x3000..0x4800)
{
len = len - len % Decrypter.PART_SIZE; // align len to PART_SIZE (0x1800)
}
ba = new ByteArray();
try
{
this._loader.readBytes(ba, 0, len);
}
catch (e:Error)
{
return;
}
decryptedData = this._decrypter.decryptData(ba);
this._decryptedData.writeBytes(decryptedData);
this.writeTagsToStream();
....
Well now let's refine the picture

^- that data block that is not magenta is crypted
and so the target for da BlowFishKey CBC decrypter
(that needs to be written)
In short:
Layout it like this
0x0800 Encrypted Block
0x1000 Unencrypted Block
Code:
coreplayer3-v00340583\dzcoreplayer\crypt\Decrypter.as
private static const INTERVAL_CHUNK:uint = 3;
private static const CHUNK_SIZE:uint = 2048;
public static const PART_SIZE:uint = 6144;
private static const IV:String = "000102030405060708090a0b0c0d0e0f";
public function setKey(param1:ByteArray) : void
{
this._encryptionKey = param1;
return;
}// end function
public function decryptData(param1:ByteArray) : ByteArray
{
var L3_ChunkOff:uint = 0;
var L4_ChunkSize:uint = 0;
this.kill();
//Set Input Data
this._source.writeBytes(param1);
this._source.position = 0;
// Cipher Block Chaining Mode
http://de.wikipedia.org/wiki/Cipher_Block_Chaining_Mode
Code:
this._decrypter = new CBCMode(
new BlowFishKey(this._encryptionKey),
new NullPad() );
// Initialisierungsvektor
this._decrypter.IV = Hex.toArray(IV);
var L2_Chunks:* = Math.ceil(
this._source.length / CHUNK_SIZE);
var L5_OutPut:* = new ByteArray();
var L6_ChunkCur:uint = 0;
while (L6_ChunkCur < L2_Chunks)
{
L3_ChunkOff = L6_ChunkCur * CHUNK_SIZE;
// normally it's 'L4_ChunkSize = CHUNK_SIZE'
// just the tail's different
L4_ChunkSize = Math.min(
CHUNK_SIZE,
this._source.length - L3_ChunkOff
);
L5_OutPut.clear();
L5_OutPut.writeBytes(
this._source, L3_ChunkOff, L4_ChunkSize);
if (L6_ChunkCur % INTERVAL_CHUNK == 0 &&
L5_OutPut.length == CHUNK_SIZE)
{
this._decrypter.decrypt(L5_OutPut);
}
// write output
this._dest.writeBytes(L5_OutPut);
// Inc Count
L6_ChunkCur = L6_ChunkCur + 1;
}
//Ret Decrypted Data
var _loc_7:* = new ByteArray();
new ByteArray().writeBytes(this._dest);
this.kill();
return _loc_7;
}// end function
coreplayer3-v00340583\dzcoreplayer\crypt\Decrypter.as
Code:
package dzcoreplayer.crypt
{
import com.hurlant.crypto.symmetric.*;
^^
Google: "hurlant.crypto.symmetric"
Finally we got it:
http://crypto.hurlant.com/demo/ !!!
Cool there's also a demo so that'll be wonderfull for testing
(before implementing it in well maybe Phython )
So collecting data to decrypt our first DeezerFucked up mp3 frame:
Secret Key
1. Blowfish CBC Padding:NONE
2. IV: 000102030405060708090a0b0c0d0e0f Key: ??
3. Copy first 0x800 bytes from mp3 as hex and past it into Cipher Text:
Hmm only thing that's missing now is the key
Total commander search in decompiled coreplayer3.swf for 'setKey'
onURLBuilt(event:URLBuilderEvent)...
this._decrypter.setKey(event.key);
search: "onURLBuilt"
public class Streamer extends EventDispatcher
{
this._urlBuilder.addEventListener(URLBuilderEvent. URL_BUILT, this.onURLBuilt);
public class URLBuilder extends EventDispatcher
{
public function URLBuilder(param1:uint = 200)
{
this._bd = new BitmapData(500, 8, false);
Strange in 'URLBuilder.as' they build a bitmap to write in data
and read them later
... to be Continued
TestResources
"Aber Bitte mit Sahne"
http://preview-0.deezer.com/stream/2...e4b73801-0.mp3
http://cdn-proxy-2.deezer.com/mobile...ff1fca48738985
http://cdn-proxy-2.deezer.com/mobile...1f ca48738985