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

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

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

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

Google有漏洞獎(jiǎng)勵(lì)規(guī)則,這是規(guī)則描述地址:
https://www.google.com/about/app ... -rewards/index.html
其實(shí)我當(dāng)時(shí)的想法是,如果能獲取來(lái)自Google的獎(jiǎng)勵(lì),不論獎(jiǎng)金多少,都將是一個(gè)至高無(wú)上的榮譽(yù)。

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

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

1.安裝Google Chrome 32bit版本(32版本容易使用工具進(jìn)行調(diào)試)
2.Chrome內(nèi)置的CDM是Widevine(幾年前收購(gòu)來(lái)的),目錄在Google\Chrome\Application\58.0.3029.110\WidevineCdm,在子目錄_platform_specific\win_x86有下2個(gè)dll:
    widevinecdm.dll - widevine核心模塊,導(dǎo)出的函數(shù)有:InitializeCdmModule_4,DeinitializeCdmModule,CreateCdmInstance,GetCdmVersion,GetHandleVerifier
    widevinecdmadapter.dll - PPAPI插件標(biāo)準(zhǔn)適配庫(kù),導(dǎo)出函數(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的導(dǎo)出函數(shù)CreateCdmInstance來(lái)創(chuàng)建CDM實(shí)例.
4.CDM框架是Google Chrome的標(biāo)準(zhǔn),所以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實(shí)例要繼承于class ContentDecryptionModule_8,查看class ContentDecryptionModule_8,發(fā)現(xiàn)了一個(gè)非常重要的函數(shù):Decrypt,主要是將加密的數(shù)據(jù)傳入,解密后的數(shù)據(jù)傳出,我的天吶,只要截獲了這個(gè)函數(shù)不就搞定了嗎!!!

[C++] 純文本查看 復(fù)制代碼
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í)截獲到解密后的數(shù)據(jù)就可以繞過(guò)DRM保護(hù)機(jī)制.
6.開(kāi)始寫(xiě)一個(gè)DLL, 名為CdmProxy.dll, 我自己的CreateCdmInstance函數(shù),這部分不解釋了:

[C++] 純文本查看 復(fù)制代碼
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;
}


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

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

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

[C++] 純文本查看 復(fù)制代碼
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.然后進(jìn)行測(cè)試,播放一個(gè)有DRM保護(hù)的DASH視頻:
    
https://shaka-player-demo.appspo ... gleKey/Manifest.mpd
    發(fā)現(xiàn)文件沒(méi)保存下來(lái),LOG也沒(méi)輸出,想必是安全沙盒起作用了。
11.又要逃脫沙盒,經(jīng)過(guò)研究,發(fā)現(xiàn)根本不需要,只要在chrome啟動(dòng)參數(shù)增加 --no-sandbox 即可。我的天吶,為啥要提供這樣一個(gè)后門啊!!!

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

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

這是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這么多年了,很多保護(hù)機(jī)制和算法都做的非常嚴(yán)密,但還是被Zhu一樣的隊(duì)友給出賣了。

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

如果此篇文章不適合公布,請(qǐng)通知我,謝謝。



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

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

    技術(shù)支持:微軟等
    主站蜘蛛池模板: 国产成人无码区免费内射一片色欲| 无码精品蜜桃一区二区三区WW| 午夜无码A级毛片免费视频| 特级小箩利无码毛片| 日韩人妻无码一区二区三区久久99| 亚洲精品无码乱码成人| 亚洲综合无码一区二区痴汉| 亚洲av无码国产精品色在线看不卡| 97无码免费人妻超级碰碰夜夜| 国产亚洲大尺度无码无码专线| 无码国产色欲XXXX视频 | 6080YYY午夜理论片中无码 | 亚洲Av无码乱码在线播放| 麻豆aⅴ精品无码一区二区| 精品无码一级毛片免费视频观看| 亚洲日韩国产AV无码无码精品| 在线播放无码高潮的视频| 亚洲一本到无码av中文字幕| 最新无码A∨在线观看| 国产乱子伦精品免费无码专区| 无码国产精品一区二区免费式影视| 国产成人无码AV一区二区在线观看 | 国产综合无码一区二区辣椒| 国产福利无码一区在线| 亚洲精品天堂无码中文字幕| 无码一区二区三区| 无码少妇一区二区三区浪潮AV| 国产精品无码一区二区在线观一| 无码一区二区三区在线| 亚洲a无码综合a国产av中文| 精品久久久久久中文字幕无码 | 国产精品99久久久精品无码| 久久久久亚洲AV无码专区桃色| 成人免费无码大片A毛片抽搐 | 日韩精品无码一区二区三区不卡 | 亚洲AV无码乱码在线观看| 国产精品无码aⅴ嫩草| 一本色道无码道在线| 国产aⅴ无码专区亚洲av麻豆 | 亚洲AV无码久久久久网站蜜桃 | 人妻系列AV无码专区|