Hi,
alglib1.cpp is a plugin for RainbowCrack 1.6.1
(http://project-rainbowcrack.com/), and it enables offline attacks against
Audible AAX files.
Vesselin
From 95a40310e0eecadc5b2c653609163ec8d2682d94 Mon Sep 17 00:00:00 2001
From: Vesselin Bontchev <vesselin.bontc...@yandex.com>
Date: Sat, 11 Jul 2015 18:02:47 +0000
Subject: [PATCH] Add support for Audible AAX (and AAX+) files
---
libavformat/isom.h | 3 +
libavformat/mov.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 166 insertions(+)
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 5d48989..6a054e8 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -198,6 +198,9 @@ typedef struct MOVContext {
MOVFragmentIndex** fragment_index_data;
unsigned fragment_index_count;
int atom_depth;
+ unsigned int aax_mode; ///< 'aax' file has been detected
+ unsigned char file_key[20];
+ unsigned char file_iv[20];
} MOVContext;
int ff_mp4_read_descr_len(AVIOContext *pb);
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 6d59863..62495a8 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -26,6 +26,7 @@
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
+#include <ctype.h>
#include "libavutil/attributes.h"
#include "libavutil/channel_layout.h"
@@ -37,6 +38,8 @@
#include "libavutil/dict.h"
#include "libavutil/display.h"
#include "libavutil/opt.h"
+#include "libavutil/aes.h"
+#include "libavutil/hash.h"
#include "libavutil/timecode.h"
#include "libavcodec/ac3tab.h"
#include "avformat.h"
@@ -807,6 +810,126 @@ static int mov_read_mdat(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0; /* now go for moov */
}
+
+static int hexchar2int(char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ return -1;
+}
+
+static void hex_encode(unsigned char *s, int len, unsigned char *o)
+{
+ char itoa16_private[16] = "0123456789abcdef";
+ int i;
+ for (i = 0; i < len; ++i) {
+ o[0] = itoa16_private[s[i] >> 4];
+ o[1] = itoa16_private[s[i] & 15];
+ o += 2;
+ }
+}
+
+#define DRM_BLOB_SIZE 56
+
+static int aax_parser(MOVContext *c, AVIOContext *pb)
+{
+ unsigned char activation_bytes[4];
+
+ // extracted from libAAX_SDK.so and AAXSDKWin.dll files!
+ unsigned char fixed_key[] = { 0x77, 0x21, 0x4d, 0x4b, 0x19, 0x6a, 0x87, 0xcd,
+ 0x52, 0x00, 0x45, 0xfd, 0x20, 0xa5, 0x1d, 0x67 };
+ unsigned char intermediate_key[20] = {0};
+ unsigned char intermediate_iv[20] = {0};
+ unsigned char input[64] = {0};
+ unsigned char output[64] = {0};
+ unsigned char file_checksum[20] = {0};
+ unsigned char file_checksum_encoded[41] = {0};
+ unsigned char file_key_encoded[41] = {0};
+ unsigned char file_iv_encoded[41] = {0};
+ unsigned char calculated_checksum[20];
+ struct AVHashContext *ctx;
+ struct AVAES *aes_decrypt;
+ int a, b, i;
+ const char *magic = "drm";
+ char *s;
+
+ av_hash_alloc(&ctx, "SHA160");
+ aes_decrypt = av_aes_alloc();
+ if (!aes_decrypt) {
+ return AVERROR(ENOMEM);
+ }
+
+ /* drm blob processing */
+ avio_seek(pb, 0x246, 0);
+ avio_read(pb, input, 3);
+ if (strncmp(input, magic, 3)) {
+ av_log(c->fc, AV_LOG_FATAL, "[aax] drm blob is missing from this file!\n");
+ exit(-1);
+ }
+ avio_seek(pb, 0x251, 0);
+ avio_read(pb, input, DRM_BLOB_SIZE);
+ avio_seek(pb, 0x28d, 0);
+ avio_read(pb, file_checksum, 20);
+ hex_encode(file_checksum, 20, file_checksum_encoded);
+ av_log(c->fc, AV_LOG_DEBUG, "[aax] file checksum == %s\n", file_checksum_encoded);
+
+ /* extract activation data */
+ s = getenv("activation_bytes");
+ if (!s || strlen(s) < 8) {
+ av_log(c->fc, AV_LOG_FATAL, "[aax] export activation_bytes=<value> is missing!\n");
+ exit(-1); // XXX exit more gracefully
+ }
+ av_log(c->fc, AV_LOG_DEBUG, "[aax] activation_bytes == %s!\n", s);
+ for (i = 0; i < 4 && isxdigit(*s); i++) {
+ a = hexchar2int(*s++);
+ b = hexchar2int(*s++);
+ activation_bytes[i] = (a << 4) | b;
+ }
+
+ /* AAX (and AAX+) key derivation */
+ av_hash_init(ctx);
+ av_hash_update(ctx, fixed_key, 16);
+ av_hash_update(ctx, activation_bytes, 4);
+ av_hash_final(ctx, intermediate_key);
+ av_hash_init(ctx);
+ av_hash_update(ctx, fixed_key, 16);
+ av_hash_update(ctx, intermediate_key, 20);
+ av_hash_update(ctx, activation_bytes, 4);
+ av_hash_final(ctx, intermediate_iv);
+ av_hash_init(ctx);
+ av_hash_update(ctx, intermediate_key, 16);
+ av_hash_update(ctx, intermediate_iv, 16);
+ av_hash_final(ctx, calculated_checksum);
+ if (memcmp(calculated_checksum, file_checksum, 20)) {
+ av_log(c->fc, AV_LOG_ERROR, "[aax] mismatch in checksums, terminating!\n");
+ exit(-1);
+ }
+ av_aes_init(aes_decrypt, intermediate_key, 128, 1);
+ av_aes_crypt(aes_decrypt, output, input, DRM_BLOB_SIZE >> 4, intermediate_iv, 1);
+ for (i = 0; i < 4; i++) {
+ if (activation_bytes[i] != output[3 - i]) {
+ av_log(c->fc, AV_LOG_ERROR, "[aax] error in drm blob decryption, terminating!\n");
+ exit(-1);
+ }
+ }
+ memcpy(c->file_key, output + 8, 16);
+ memcpy(input, output + 26, 16);
+ av_hash_init(ctx);
+ av_hash_update(ctx, input, 16);
+ av_hash_update(ctx, c->file_key, 16);
+ av_hash_update(ctx, fixed_key, 16);
+ av_hash_final(ctx, c->file_iv);
+ hex_encode(c->file_key, 16, file_key_encoded);
+ av_log(c->fc, AV_LOG_DEBUG, "[aax] file key == %s\n", file_key_encoded);
+ hex_encode(c->file_iv, 16, file_iv_encoded);
+ av_log(c->fc, AV_LOG_DEBUG, "[aax] file iv == %s\n", file_iv_encoded);
+
+ av_free(aes_decrypt);
+ av_free(ctx);
+
+ return 0;
+}
+
/* read major brand, minor version and compatible brands and store them as metadata */
static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
@@ -814,6 +937,7 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int comp_brand_size;
char* comp_brands_str;
uint8_t type[5] = {0};
+ int64_t current_pos = avio_tell(pb);
int ret = ffio_read_size(pb, type, 4);
if (ret < 0)
return ret;
@@ -822,6 +946,13 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
c->isom = 1;
av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type);
av_dict_set(&c->fc->metadata, "major_brand", type, 0);
+ /* Recognize Audible AAX files */
+ if (!strncmp((char*)&type, "aax", 3)) {
+ av_log(c->fc, AV_LOG_DEBUG, "[aax] aax file detected!\n");
+ c->aax_mode = 1;
+ aax_parser(c, pb);
+ avio_seek(pb, current_pos, 0);
+ }
minor_ver = avio_rb32(pb); /* minor version */
av_dict_set_int(&c->fc->metadata, "minor_version", minor_ver, 0);
@@ -4336,6 +4467,35 @@ static int should_retry(AVIOContext *pb, int error_code) {
return 1;
}
+/* Audible AAX (and AAX+) bytestream decryption
+ *
+ * export activation_bytes=CAFED00D # only 4 bytes ;)
+ *
+ * ffmpeg -i test.aax -vn -c:a copy -v debug output.mp4
+ */
+static int aax_filter(uint8_t *input, int size, MOVContext *c)
+{
+ int blocks = 0;
+ unsigned char key[16];
+ unsigned char iv[16];
+ struct AVAES *aes_decrypt;
+
+ aes_decrypt = av_aes_alloc();
+ if (!aes_decrypt) {
+ return AVERROR(ENOMEM);
+ }
+
+ memcpy(key, c->file_key, 16);
+ memcpy(iv, c->file_iv, 16);
+ blocks = size >> 4;
+ av_aes_init(aes_decrypt, key, 128, 1);
+ av_aes_crypt(aes_decrypt, input, input, blocks, iv, 1);
+
+ av_free(aes_decrypt);
+
+ return 0;
+}
+
static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVContext *mov = s->priv_data;
@@ -4430,6 +4590,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
pkt->pos = sample->pos;
+ if (mov->aax_mode)
+ aax_filter(pkt->data, pkt->size, mov);
+
return 0;
}
--
1.7.10.4
/*
* http://www.tobtu.com/rtcalc.php#params
*
* keyspace is 256^4 (length is always 4)
* 99.999999% (Total success rate)
*
* ./rtgen audible byte 4 4 0 10000 10008356 0
* ./rtgen audible byte 4 4 1 10000 10008356 0
* ./rtgen audible byte 4 4 2 10000 10008356 0
* ./rtgen audible byte 4 4 3 10000 10008356 0
* ./rtgen audible byte 4 4 4 10000 10008356 0
* ./rtgen audible byte 4 4 5 10000 10008356 0
* ./rtgen audible byte 4 4 6 10000 10008356 0
* ./rtgen audible byte 4 4 7 10000 10008356 0
* ./rtgen audible byte 4 4 8 10000 10008356 0
* ./rtgen audible byte 4 4 9 10000 10008356 0
*
* ./rtsort *.rt
* ./rt2rtc *.rt 21 24 -m 18 -p
* ./rt2rtc *.rt 25 25 -m 512 -p
*/
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <stdio.h>
#ifdef _WIN32
#pragma comment(lib, "libeay32.lib")
#endif
#define MIN_HASH_LEN 8
#define MAX_HASH_LEN 32
void
#ifdef _WIN32
__stdcall
#endif
Audible(
unsigned char *pData, // [in] plaintext to be hashed
unsigned int uLen, // [in] length of the plaintext
unsigned char Hash[MAX_HASH_LEN]) // [out] the result hash, size of the buffer is MAX_HASH_LEN bytes
{
unsigned char fixed_key[] = { 0x77, 0x21, 0x4d, 0x4b, 0x19, 0x6a, 0x87,
0xcd, 0x52, 0x00, 0x45, 0xfd, 0x20, 0xa5, 0x1d, 0x67 };
unsigned char intermediate_key[20] = {0};
unsigned char intermediate_iv[20] = {0};
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, fixed_key, 16);
SHA1_Update(&ctx, pData, uLen);
SHA1_Final(intermediate_key, &ctx);
SHA1_Init(&ctx);
SHA1_Update(&ctx, fixed_key, 16);
SHA1_Update(&ctx, intermediate_key, 20);
SHA1_Update(&ctx, pData, uLen);
SHA1_Final(intermediate_iv, &ctx);
SHA1_Init(&ctx);
SHA1_Update(&ctx, intermediate_key, 16);
SHA1_Update(&ctx, intermediate_iv, 16);
SHA1_Final(Hash, &ctx);
}
struct HashAlgorithmEntry
{
const char *szName; // name of the hash algorithm
void *pHashAlgorithm; // function pointer to the hash algorithm's implementation
unsigned int uHashLen; // output length of the hash algorithm, MIN_HASH_LEN <= uHashLen <= MAX_HASH_LEN
// input plaintext length range supported by the hash algorithm's implementation
unsigned int uPlaintextLenMin;
unsigned int uPlaintextLenMax;
};
struct HashAlgorithmEntry HashAlgorithms[] = { // this symbol will be exported
{"audible", (void *)Audible, 20, 4, 4}, // always 4 bytes
{0, 0, 0, 0, 0}, // terminated by an entry of all zeroes
};
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel