錦州市廣廈電腦維修|上門維修電腦|上門做系統(tǒng)|0416-3905144熱誠服務,錦州廣廈維修電腦,公司IT外包服務
topFlag1 設(shè)為首頁
topFlag3 收藏本站
 
maojin003 首 頁 公司介紹 服務項目 服務報價 維修流程 IT外包服務 服務器維護 技術(shù)文章 常見故障
錦州市廣廈電腦維修|上門維修電腦|上門做系統(tǒng)|0416-3905144熱誠服務技術(shù)文章
Chrome CDM框架重大缺陷,DRM視頻輕易復制

作者: 佚名  日期:2017-05-19 07:46:52   來源: 本站整理

我的工作領(lǐng)域和視頻相關(guān),保護視頻內(nèi)容非常重要,主流的瀏覽器和移動設(shè)備都支持DRM。近日我偶然發(fā)現(xiàn)Google Chrome瀏覽器的CDM(Content Decryption Module-內(nèi)容解密模塊)框架存在重大的設(shè)計缺陷,通過一些手段就可以輕松繞過DRM保護機制,非常容易的獲取解密后的數(shù)據(jù),從而把視頻重新封裝為未壓縮的MP4等格式文件,還可以做到在觀看的過程中直接進行無加密的視頻直播。

發(fā)現(xiàn)問題后,我花了一點時間,寫了一個測試程序,驗證了我的想法。由于DRM的保護機制非常的重要,如果可以輕松被攻破,將對整個視頻領(lǐng)域是一個威脅,所以我當時就向Google Chromium提交了bug,說明了CDM的框架的重大缺陷,并描述了實現(xiàn)細節(jié)。
這是的issue url(一般人沒有權(quán)限查看,僅內(nèi)部可見):
https://bugs.chromium.org/p/chromium/issues/detail?id=721639

Google有漏洞獎勵規(guī)則,這是規(guī)則描述地址:
https://www.google.com/about/app ... -rewards/index.html
其實我當時的想法是,如果能獲取來自Google的獎勵,不論獎金多少,都將是一個至高無上的榮譽。

我提交后,Chromium團隊很快的做出了回復,他們確認這是一個重大的安全問題,而且影響所有運行Chrome瀏覽器的操作系統(tǒng),包括: Linux, Windows, Chrome, Mac等等. 但另一個員工說這是一個已知的問題,并提供了一個issue號: 658022,但我無限查看漏洞內(nèi)容是否與我提交的一致。之后我向google 團隊的幾個成員發(fā)了郵件,說既然是已知的問題,那也就是不符合獎勵規(guī)則,因此我也就可以公布細節(jié),讓視頻內(nèi)容公司重視這個問題,以便盡早的商討更加安全的解決方案。

說了這么多,只想說明一下事件的背景,下面我就說明實現(xiàn)細節(jié),很多東西可能有些專業(yè),主要講述一個過程。

1.安裝Google Chrome 32bit版本(32版本容易使用工具進行調(diào)試)
2.Chrome內(nèi)置的CDM是Widevine(幾年前收購來的),目錄在Google\Chrome\Application\58.0.3029.110\WidevineCdm,在子目錄_platform_specific\win_x86有下2個dll:
    widevinecdm.dll - widevine核心模塊,導出的函數(shù)有:InitializeCdmModule_4,DeinitializeCdmModule,CreateCdmInstance,GetCdmVersion,GetHandleVerifier
    widevinecdmadapter.dll - PPAPI插件標準適配庫,導出函數(shù)有:PPP_GetInterface,PPP_InitializeModule,PPP_ShutdownModule
3.正常情況下播放DRM視頻的流程:
    Chrome -> Widevine CDM Adapter(widevinecdmadapter.dll) -> Widevine CDM Module (widevinecdm.dll)
    widevinecdmadapter.dll調(diào)用widevinecdm.dll的導出函數(shù)CreateCdmInstance來創(chuàng)建CDM實例.
4.CDM框架是Google Chrome的標準,所以API參數(shù)和接口都有C++ include文件,比如API CreateCdmInstance:
    CDM_API void* CreateCdmInstance(int cdm_interface_version, const char* key_system, uint32_t key_system_size, GetCdmHostFunc get_cdm_host_func, void* user_data);

    CDM實例要繼承于class ContentDecryptionModule_8,查看class ContentDecryptionModule_8,發(fā)現(xiàn)了一個非常重要的函數(shù):Decrypt,主要是將加密的數(shù)據(jù)傳入,解密后的數(shù)據(jù)傳出,我的天吶,只要截獲了這個函數(shù)不就搞定了嗎!!!

[C++] 純文本查看 復制代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
class CDM_CLASS_API ContentDecryptionModule_8 {      // Decrypts the |encrypted_buffer|.
   //
   // Returns kSuccess if decryption succeeded, in which case the callee
   // should have filled the |decrypted_buffer| and passed the ownership of
   // |data| in |decrypted_buffer| to the caller.
   // Returns kNoKey if the CDM did not have the necessary decryption key
   // to decrypt.
   // Returns kDecryptError if any other error happened.
   // If the return value is not kSuccess, |decrypted_buffer| should be ignored
   // by the caller.
   virtual Status Decrypt(const InputBuffer& encrypted_buffer,
                          DecryptedBlock* decrypted_buffer) = 0;
 };


5.了解了API參數(shù)和class定義后,就設(shè)想如果在widevinecdmadapter.dll 和 widevinecdm.dll之間在互相調(diào)用時截獲到解密后的數(shù)據(jù)就可以繞過DRM保護機制.
6.開始寫一個DLL, 名為CdmProxy.dll, 我自己的CreateCdmInstance函數(shù),這部分不解釋了:

[C++] 純文本查看 復制代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
extern "C" __declspec(dllexport) void * CDMAPI_DEFINE my_CreateCdmInstance(int cdm_interface_version, const char* key_system,
    uint32_t key_system_size, GetCdmHostFunc get_cdm_host_func, void* user_data)
{
    gHostUserData = user_data;
    wsprintf(wchLog, L"CdmProxy - call CreateCdmInstance(%d, %S, %d, 0x%08X, 0x%08X)",
        cdm_interface_version, key_system, key_system_size, get_cdm_host_func, user_data);
    OutputDebugStringW(wchLog);
    void *p = pCreateCdmInstance(cdm_interface_version, key_system, key_system_size, get_cdm_host_func, user_data);
 
    cdm::ContentDecryptionModule_8 *pCdmModule = (cdm::ContentDecryptionModule_8 *)(p);
    MyContentDecryptionModuleProxy *pMyCdmModule = new MyContentDecryptionModuleProxy(pCdmModule);
 
    return pMyCdmModule;
}


    我的代{過}{濾}理class MyContentDecryptionModuleProxy,代碼不解釋:
    // class MyContentDecryptionModuleProxy

[C++] 純文本查看 復制代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class MyContentDecryptionModuleProxy : public cdm::ContentDecryptionModule_8
{
public:
    MyContentDecryptionModuleProxy(cdm::ContentDecryptionModule_8 *pCdm)
    {
        mCdm = pCdm;
    }
private:
    cdm::ContentDecryptionModule_8 *mCdm;
 
public:
    // 最重要的解密函數(shù),保存原數(shù)據(jù)和解密后的數(shù)據(jù)
    virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer, cdm::DecryptedBlock* decrypted_buffer)
    {
        cdm::Status status = cdm::kSuccess;
        codelive();
        DebugDecryptBreak(encrypted_buffer.iv, encrypted_buffer.key_id, encrypted_buffer.data);
        status = mCdm->Decrypt(encrypted_buffer, decrypted_buffer);
         
        string strIV = data2HexString((const char *)encrypted_buffer.iv, encrypted_buffer.iv_size);
        string strEncData = data2HexString((const char *)encrypted_buffer.data, min(encrypted_buffer.data_size, 32));
        string strDecData = data2HexString((const char *)decrypted_buffer->DecryptedBuffer()->Data(),
            min(decrypted_buffer->DecryptedBuffer()->Size(), 32));
        wsprintf(wchLog, L"CdmProxy - call Decrypt(IV:%S, encData(%d):%S, decData(%d):%S)",
            strIV.c_str(), encrypted_buffer.data_size, strEncData.c_str(),
            decrypted_buffer->DecryptedBuffer()->Size(), strDecData.c_str());
        OutputDebugStringW(wchLog);
 
        if(mEncFile == NULL)
        {
            mEncFile = fopen("d:\\cdm_enc.bin", "wb");
        }
        if(mEncFile != NULL)
        {
            fwrite(encrypted_buffer.data, 1, encrypted_buffer.data_size, mEncFile);
        }
        if(mDecFile == NULL)
        {
            mDecFile = fopen("d:\\cdm_dec.bin", "wb");
        }
        if(mDecFile != NULL)
        {
            fwrite(decrypted_buffer->DecryptedBuffer()->Data(), 1, decrypted_buffer->DecryptedBuffer()->Size(), mDecFile);
        }
        return status;
    }
};


7.核心代碼寫完了,就要解決DLL加載的問題,嘗試了幾種簡單的方式,分別把widevinecdm.dll和widevinecdmadapter.dll改名為widevinecdm_org.dll和widevinecdmadapter_org.dll,然后自己寫一個DLL,導出和widevinecdm.dll或者widevinecdmadapter.dll相同的API,然后再調(diào)用原來DLL的方式,但這種方式發(fā)現(xiàn)不可行,原因在于Chrome的安全沙盒,所有的插件都是加載的沙盒進程空間,敏感的API都無法使用,比如:ReadProcessMemory,CeateFile,OutputDebugString等等. 逃脫沙盒(Sandbox Escape)是Google獎金數(shù)額非常高的,最高可達$15,000.
8.既然進程都是Chrome創(chuàng)建的,所以就直接對chrome.exe下手,直接patch chrome.exe,讓其加載我的CdmProxy.dll,結(jié)果成功了,畢竟chrome啟動時還未啟用沙盒機制,所以沒花費太多技術(shù)就解決了DLL加載問題.

9.改變API截獲方式,對widevinecdmadapter.dll進行動態(tài)的補丁,將調(diào)用API CreateCdmInstance的地址改為我自己的API my_CreateCdmInstance,然后在DLL加載時進行以下處理:

[C++] 純文本查看 復制代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch(ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        {
            hWideVineCdm = LoadLibraryW(L"{PATH}\\widevinecdm.dll");
 
            pInitializeCdmModule_4 = (InitializeCdmModule_4Func)GetProcAddress(hWideVineCdm, "InitializeCdmModule_4");
            pDeinitializeCdmModule = (DeinitializeCdmModuleFunc)GetProcAddress(hWideVineCdm, "DeinitializeCdmModule");
            pCreateCdmInstance = (CreateCdmInstanceFunc)GetProcAddress(hWideVineCdm, "CreateCdmInstance");
            pGetCdmVersion = (GetCdmVersionFunc)GetProcAddress(hWideVineCdm, "GetCdmVersion");
 
            hWideVineCdmAdapter = LoadLibraryW(L"{PATH}\\widevinecdmadapter.dll");
            if(hWideVineCdmAdapter != NULL)
            {
                DWORD dwSrcAddr = (DWORD)hWideVineCdmAdapter + 0x0000446D;
                const BYTE chVerify[] = { 0xFF, 0x15 };
                BOOL isOK = patch_DsCallFunction(dwSrcAddr, (DWORD)my_CreateCdmInstance, chVerify, sizeof(chVerify));
                wsprintf(wchLog, L"CdmProxy - patch CreateCdmInstance, Address:0x%08X-0x%08X, %s.",
                    dwSrcAddr, (uint32_t)my_CreateCdmInstance,
                    isOK ? L"OK" : L"FAILED");
                OutputDebugStringW(wchLog);
            }
        }
        break ;
    }
}


10.然后進行測試,播放一個有DRM保護的DASH視頻:
    
https://shaka-player-demo.appspo ... gleKey/Manifest.mpd
    發(fā)現(xiàn)文件沒保存下來,LOG也沒輸出,想必是安全沙盒起作用了。
11.又要逃脫沙盒,經(jīng)過研究,發(fā)現(xiàn)根本不需要,只要在chrome啟動參數(shù)增加 --no-sandbox 即可。我的天吶,為啥要提供這樣一個后門啊!!!

12.再次播放加密的視頻,文件順利保存下來,LOG也輸出成功,經(jīng)驗證,解密后的數(shù)據(jù)與之前未加密的數(shù)據(jù)是一致的。

13.Google Chrome的CDM就這樣被破解了,非常的簡單的就繞過了Widevine DRM的算法,這應該是Chrome CDM的框架設(shè)計的嚴重問題,估計要改變也不是非常容易的。

這是LOG數(shù)據(jù):

( "CdmProxy - call CreateCdmInstance(8, com.widevine.alpha, 18, 0x70F86310, 0x00D75A88)" )           0.0001399
( "CdmProxy - call CreateCdmInstance(8, com.widevine.alpha, 18, 0x70F86310, 0x00D757C8)" )           0.0000937
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F00000000000000000, encData(348):FFF158402B9FFC2FF05300F2BF83E9A0, decData(0):)" )           0.0001350
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F00000000000000000, encData(348):FFF158402B9FFC2FF05300F2BF83E9A0, decData(348):FFF158402B9FFC00D03403E95B8639BD)" )         0.0001335
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F00000000000000000, encData(348):FFF158402B9FFC2FF05300F2BF83E9A0, decData(348):FFF158402B9FFC00D03403E95B8639BD)" )         0.0001032
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F10000000000000000, encData(348):FFF158402B9FFC487380B8930FFFAB41, decData(348):FFF158402B9FFC00F43420C24620902C)" )         0.0001392
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F20000000000000000, encData(349):FFF158402BBFFC1175E15FE4B6154B30, decData(349):FFF158402BBFFC00FA342D90762A3188)" )         0.0001032
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F30000000000000000, encData(348):FFF158402B9FFCC45D5715E87235E5CF, decData(348):FFF158402B9FFC00F8342CEC825A2D85)" )         0.0000994
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F40000000000000000, encData(348):FFF158402B9FFC6749FBAF64926471DE, decData(348):FFF158402B9FFC00F83421884529290A)" )         0.0000880
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F50000000000000000, encData(349):FFF158402BBFFCF8132EFC31C186DDE1, decData(349):FFF158402BBFFC00F2342D9049124988)" )         0.0001088
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F60000000000000000, encData(348):FFF158402B9FFC82EDA0BD4AB7158938, decData(348):FFF158402B9FFC00EE342D7475223D85)" )         0.0001035
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F70000000000000000, encData(348):FFF158402B9FFC4B2C585CC10F74036E, decData(348):FFF158402B9FFC00F4342D74662A2088)" )         0.0001555
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F80000000000000000, encData(349):FFF158402BBFFCCF33665AC4E219EC92, decData(349):FFF158402BBFFC00FA342E30547B0604)" )         0.0001494
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F90000000000000000, encData(348):FFF158402B9FFC2C9A7362594261CE23, decData(348):FFF158402B9FFC00F4342E305429150A)" )         0.0004035
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FA0000000000000000, encData(348):FFF158402B9FFC1905A086AE3CEF0AEC, decData(348):FFF158402B9FFC00EE342E3456391906)" )         0.0005913
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FB0000000000000000, encData(349):FFF158402BBFFC8D0EB865013262FB6E, decData(349):FFF158402BBFFC00F6342E34563A2186)" )         0.0001479
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FC0000000000000000, encData(348):FFF158402B9FFC484211C612F22283FB, decData(348):FFF158402B9FFC0102342E74482A2E02)" )         0.0002507
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FD0000000000000000, encData(348):FFF158402B9FFC283122B1DDE740DAC2, decData(348):FFF158402B9FFC0104342EB464190D08)" )         0.0003011
( "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FE0000000000000000, encData(349):FFF158402BBFFCAC8759D48FF1A258A3, decData(349):FFF158402BBFFC010E342ED04A1A2982)" )         0.0003095


DRM這么多年了,很多保護機制和算法都做的非常嚴密,但還是被Zhu一樣的隊友給出賣了。

公開這個研究,是為了讓廣大的視頻公司不要以為DRM非常的安全,有時候真的是不堪一擊,某個環(huán)節(jié)出現(xiàn)漏洞,同樣面臨極大的安全問題。

如果此篇文章不適合公布,請通知我,謝謝。



熱門文章
  • 機械革命S1 PRO-02 開機不顯示 黑...
  • 聯(lián)想ThinkPad NM-C641上電掉電點不...
  • 三星一體激光打印機SCX-4521F維修...
  • 通過串口命令查看EMMC擦寫次數(shù)和判...
  • IIS 8 開啟 GZIP壓縮來減少網(wǎng)絡(luò)請求...
  • 索尼kd-49x7500e背光一半暗且閃爍 ...
  • 樓宇對講門禁讀卡異常維修,讀卡芯...
  • 新款海信電視機始終停留在開機界面...
  • 常見打印機清零步驟
  • 安裝驅(qū)動時提示不包含數(shù)字簽名的解...
  • 共享打印機需要密碼的解決方法
  • 圖解Windows 7系統(tǒng)快速共享打印機的...
  • 錦州廣廈電腦上門維修

    報修電話:13840665804  QQ:174984393 (聯(lián)系人:毛先生)   
    E-Mail:174984393@qq.com
    維修中心地址:錦州廣廈電腦城
    ICP備案/許可證號:遼ICP備2023002984號-1
    上門服務區(qū)域: 遼寧錦州市區(qū)
    主要業(yè)務: 修電腦,電腦修理,電腦維護,上門維修電腦,黑屏藍屏死機故障排除,無線上網(wǎng)設(shè)置,IT服務外包,局域網(wǎng)組建,ADSL共享上網(wǎng),路由器設(shè)置,數(shù)據(jù)恢復,密碼破解,光盤刻錄制作等服務

    技術(shù)支持:微軟等
    主站蜘蛛池模板: 人妻无码中文字幕免费视频蜜桃| 中文字幕无码免费久久9一区9| 色窝窝无码一区二区三区色欲| 亚洲av无码无在线观看红杏| 亚洲看片无码在线视频| 免费无码看av的网站| 午夜福利无码不卡在线观看| 久久精品日韩av无码| 人妻丰满熟妇无码区免费| 亚洲?V无码成人精品区日韩| 亚洲精品无码久久久久久久| 国产乱人伦Av在线无码| 人妻少妇乱子伦无码专区| 中文字幕人成无码免费视频| 国产三级无码内射在线看| 国产怡春院无码一区二区| 蜜桃无码AV一区二区| 国产在线拍偷自揄拍无码| 亚洲国产av无码精品| 性色AV一区二区三区无码| 亚洲av无码专区在线观看亚| 无码专区天天躁天天躁在线| 亚洲韩国精品无码一区二区三区| 精品无码国产AV一区二区三区| 亚洲综合久久精品无码色欲| 人妻丰满熟妇av无码区不卡| 少妇人妻无码专区视频 | 亚洲av无码成人黄网站在线观看| 国内精品久久人妻无码不卡| 亚洲精品无码av片| MM1313亚洲精品无码久久| 亚洲AV无码一区二区三区在线| 无码免费一区二区三区免费播放| 中文字幕精品无码一区二区三区 | 中文字幕精品无码亚洲字| 国产强被迫伦姧在线观看无码| (无码视频)在线观看| 无码任你躁久久久久久老妇| 亚洲av无码乱码在线观看野外 | 国产精品亚韩精品无码a在线| 中文字幕乱妇无码AV在线|