锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务,锦州广厦维修电脑,公司IT外包服务
topFlag1 设为首页
topFlag3 收藏本站
 
maojin003 首 页 公司介绍 服务项目 服务报价 维修流程 IT外包服务 服务器维护 技术文章 常见故障
锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务技术文章
Chrome CDM框架重大缺陷,DRM视频轻易复制

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

我的工作领域和视频相关,保护视频内容非常重要,主流的浏览器和移动设备都支持DRM。近日我偶然发现Google Chrome浏览器的CDM(Content Decryption Module-内容解密模块)框架存在重大的设计缺陷,通过一些手段就可以轻松绕过DRM保护机制,非常容易的获取解密后的数据,从而把视频重新封装为未压缩的MP4等格式文件,还可以做到在观看的过程中直接进行无加密的视频直播。

发现问题后,我花了一点时间,写了一个测试程序,验证了我的想法。由于DRM的保护机制非常的重要,如果可以轻松被攻破,将对整个视频领域是一个威胁,所以我当时就向Google Chromium提交了bug,说明了CDM的框架的重大缺陷,并描述了实现细节。
这是的issue url(一般人没有权限查看,仅内部可见):
https://bugs.chromium.org/p/chromium/issues/detail?id=721639

Google有漏洞奖励规则,这是规则描述地址:
https://www.google.com/about/app ... -rewards/index.html
其实我当时的想法是,如果能获取来自Google的奖励,不论奖金多少,都将是一个至高无上的荣誉。

我提交后,Chromium团队很快的做出了回复,他们确认这是一个重大的安全问题,而且影响所有运行Chrome浏览器的操作系统,包括: Linux, Windows, Chrome, Mac等等. 但另一个员工说这是一个已知的问题,并提供了一个issue号: 658022,但我无限查看漏洞内容是否与我提交的一致。之后我向google 团队的几个成员发了邮件,说既然是已知的问题,那也就是不符合奖励规则,因此我也就可以公布细节,让视频内容公司重视这个问题,以便尽早的商讨更加安全的解决方案。

说了这么多,只想说明一下事件的背景,下面我就说明实现细节,很多东西可能有些专业,主要讲述一个过程。

1.安装Google Chrome 32bit版本(32版本容易使用工具进行调试)
2.Chrome内置的CDM是Widevine(几年前收购来的),目录在Google\Chrome\Application\58.0.3029.110\WidevineCdm,在子目录_platform_specific\win_x86有下2个dll:
    widevinecdm.dll - widevine核心模块,导出的函数有:InitializeCdmModule_4,DeinitializeCdmModule,CreateCdmInstance,GetCdmVersion,GetHandleVerifier
    widevinecdmadapter.dll - PPAPI插件标准适配库,导出函数有:PPP_GetInterface,PPP_InitializeModule,PPP_ShutdownModule
3.正常情况下播放DRM视频的流程:
    Chrome -> Widevine CDM Adapter(widevinecdmadapter.dll) -> Widevine CDM Module (widevinecdm.dll)
    widevinecdmadapter.dll调用widevinecdm.dll的导出函数CreateCdmInstance来创建CDM实例.
4.CDM框架是Google Chrome的标准,所以API参数和接口都有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,发现了一个非常重要的函数:Decrypt,主要是将加密的数据传入,解密后的数据传出,我的天呐,只要截获了这个函数不就搞定了吗!!!

[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参数和class定义后,就设想如果在widevinecdmadapter.dll 和 widevinecdm.dll之间在互相调用时截获到解密后的数据就可以绕过DRM保护机制.
6.开始写一个DLL, 名为CdmProxy.dll, 我自己的CreateCdmInstance函数,这部分不解释了:

[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:
    // 最重要的解密函数,保存原数据和解密后的数据
    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,然后再调用原来DLL的方式,但这种方式发现不可行,原因在于Chrome的安全沙盒,所有的插件都是加载的沙盒进程空间,敏感的API都无法使用,比如:ReadProcessMemory,CeateFile,OutputDebugString等等. 逃脱沙盒(Sandbox Escape)是Google奖金数额非常高的,最高可达$15,000.
8.既然进程都是Chrome创建的,所以就直接对chrome.exe下手,直接patch chrome.exe,让其加载我的CdmProxy.dll,结果成功了,毕竟chrome启动时还未启用沙盒机制,所以没花费太多技术就解决了DLL加载问题.

9.改变API截获方式,对widevinecdmadapter.dll进行动态的补丁,将调用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
    发现文件没保存下来,LOG也没输出,想必是安全沙盒起作用了。
11.又要逃脱沙盒,经过研究,发现根本不需要,只要在chrome启动参数增加 --no-sandbox 即可。我的天呐,为啥要提供这样一个后门啊!!!

12.再次播放加密的视频,文件顺利保存下来,LOG也输出成功,经验证,解密后的数据与之前未加密的数据是一致的。

13.Google Chrome的CDM就这样被破解了,非常的简单的就绕过了Widevine DRM的算法,这应该是Chrome CDM的框架设计的严重问题,估计要改变也不是非常容易的。

这是LOG数据:

( "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非常的安全,有时候真的是不堪一击,某个环节出现漏洞,同样面临极大的安全问题。

如果此篇文章不适合公布,请通知我,谢谢。



热门文章
  • 机械革命S1 PRO-02 开机不显示 黑...
  • 联想ThinkPad NM-C641上电掉电点不...
  • 三星一体激光打印机SCX-4521F维修...
  • 通过串口命令查看EMMC擦写次数和判...
  • IIS 8 开启 GZIP压缩来减少网络请求...
  • 索尼kd-49x7500e背光一半暗且闪烁 ...
  • 楼宇对讲门禁读卡异常维修,读卡芯...
  • 新款海信电视机始终停留在开机界面...
  • 常见打印机清零步骤
  • 安装驱动时提示不包含数字签名的解...
  • 共享打印机需要密码的解决方法
  • 图解Windows 7系统快速共享打印机的...
  • 锦州广厦电脑上门维修

    报修电话:13840665804  QQ:174984393 (联系人:毛先生)   
    E-Mail:174984393@qq.com
    维修中心地址:锦州广厦电脑城
    ICP备案/许可证号:辽ICP备2023002984号-1
    上门服务区域: 辽宁锦州市区
    主要业务: 修电脑,电脑修理,电脑维护,上门维修电脑,黑屏蓝屏死机故障排除,无线上网设置,IT服务外包,局域网组建,ADSL共享上网,路由器设置,数据恢复,密码破解,光盘刻录制作等服务

    技术支持:微软等