詳情資料可以參考微軟提供的 Specification: Advanced Systems Format (ASF) Specification
大致上很簡單,一個 ASF 檔案主要有三個部分: Header Object, Data Object, Index Object.
而每個部分又各由不同的 Object 所組成.
要分辨是否為 DRM,主要是在 Header Object 裡面找尋是否有 Content Encryption Object.
那麼要怎麼找尋呢?? OK, 每個 Object 的最前面有唯一的 GUID,你可以依據 GUID 來找到.
而這些,你都可以在 Specification 的後面幾張找到 ( 或者你可以搜尋 ASF GUIDs ).
至此,寫起來並不太困難.程式碼如下:
typedef unsigned long UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;
typedef int BOOL;
enum BOOLEAN
{
FALSE = 0,
TRUE
};
typedef struct GUID_s
{
UINT32 id1;
UINT16 id2;
UINT16 id3;
UINT16 id4;
UINT8 id5[6];
}GUID;
//Content Encryption Object
GUID ASF_Content_Encryption_Object_guid =
{
0x2211b3fb,
0xbd23,
0x11d2,
0xb7b4,
{
0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e
}
};
/*
typedef struct ContentEncryptionObject_s
{
GUID objectID;
UINT32 objectSize; // objectSize should be QWORD, 8 bytes, but CCS has not this type.
UINT32 objectSize1;
UINT32 secretDataLength;
BYTE* secretData; // according secretDataLength
UINT32 protectionTypeLength;
char* protectionType;
UINT32 keyIDLength;
char* keyID;
UINT32 licenseURLLength;
char* licenseURL;
}ContentEncryptionObject_t;
*/
//extern CF_FILE* filePtrR;
typedef FILE CF_FILE;
CF_FILE* filePtrR;
long
FILEr_ftell()
{
return ftell( filePtrR );
}
int
FILEr_fseek( long filepos, int whence)
{
return fseek( filePtrR, filepos, whence );
}
long
FILEr_fread( UINT8* ptr, long length )
{
return fread( ptr, 1, length, filePtrR );
}
char* SDRAM_HOLE = NULL;
BOOL
isASFDRM( )
{
long filePos;
char* spaceForParsing = (char*)( SDRAM_HOLE + 0x800000 ); // use the space after 8 MB, prevent conflict
int GUIDLen = sizeof( GUID );
int iter, len = 0;
BOOL bFound = FALSE, bResult = FALSE;
UINT32 headerObjectSize = 0;
UINT32 secretDataLength=0;
UINT32 protectionTypeLength=0;
char* protectionType=NULL;
long readLen=0;
char* encryptionPos = NULL;
// save file position
filePos = FILEr_ftell();
// seek to the start position
FILEr_fseek( 16, 0); //SEEK_SET
FILEr_fread( (UINT8*)&headerObjectSize, 4 ); // we should read 8 here, but ....
// read all header object
// then search the specified GUID, use pattern compare
FILEr_fseek( 0, 0 ); // SEEK_SET
readLen = FILEr_fread( spaceForParsing, headerObjectSize );
len = headerObjectSize - GUIDLen;
// searching.
for( iter = 0; iter < len; iter++ )
{
if( memcmp( (char*)&ASF_Content_Encryption_Object_guid, (spaceForParsing + iter ), GUIDLen ) == 0 )
{
bFound = TRUE;
break;
}
}
if( bFound == FALSE )
{
// this is not a DRM file.
bResult = FALSE;
goto OnFinally;
}
// if we got it, start to process it.
// ( encryptionPos ) ==> objectID
encryptionPos = spaceForParsing + iter;
// ( encryptionPos + GUIDLen ) ==> objectSize
// ( encryptionPos + GUIDLen + 8 ) ==> secretDataLength
secretDataLength = (UINT32) *( encryptionPos + GUIDLen + 8 );
// ( spaceForParsing + iter + GUIDLen + 8 + 4 ) ==> secretData
// ( spaceForParsing + iter + GUIDLen + 12 + secretDataLength ) ==> protectionTypeLength;
protectionTypeLength = (UINT32) *(encryptionPos + GUIDLen + 12 + secretDataLength );
protectionType = (char*) (encryptionPos + GUIDLen + 12 + secretDataLength + 4 );
if( strncmp( protectionType, "DRM", 3 ) == 0 )
bResult = TRUE;
OnFinally:
// restore file position
FILEr_fseek( filePos, 0); //SEEK_SET
// return result.
return bResult;
}
int main(int argc, char* argv[])
{
SDRAM_HOLE = (char*) malloc( 0x800000 * 2 );
filePtrR = fopen( "00 Cannot Play.wma", "rb" );
if( isASFDRM() )
printf("is a drm file.\n");
else
printf("not a drm file.\n");
fclose( filePtrR );
return 0;
}
ok, 程式裡面你會發現一些奇怪的地方,是的,因為我需要在 embedded 環境下運作,所以為了符合環境,所以做了一些調整,不過你還是可以在 Visual C++ 上來運行這段代碼.
看不順眼的,就自己調整吧...
這份 specification 其實斷斷續續看一陣子了,都沒怎麼專心看,不過昨天看的時候,突然開竅了,於是就把這麼一段代碼搞定了.
真神奇啊....
沒有留言:
張貼留言