sundry

home page  »  file  »  sundry  »  Chrome CDM framework has major defects, DRM video can be easily copied

Chrome CDM framework has major defects, DRM video can be easily copied

  1. 1. Install Google Chrome 32bit version (version 32 is easy to debug with tools)
  2. 2. Chrome's built-in CDM is Widevine (acquired a few years ago). The directory is Google Chrome Application 58.0.3029.110 WidevineCdm, and the subdirectory _platform_specific win_x86 has the following two dlls:
  3. Widevinecdm.dll - widevine core module. The exported functions are: InitializeCdmModule_4, InitializeCdmModule, CreateCdmInstance, GetCdmVersion, GetHandleVerifier
  4. Widevinecdmadapter.dll - PPAPI plug-in standard adapter library. Export functions include: PPP_GetInterface, PPP_InitializeModule, PPP_Shutdown Module
  5. 3. Flow of DRM video playing under normal conditions:
  6.     Chrome -> Widevine CDM Adapter(widevinecdmadapter.dll) -> Widevine CDM Module (widevinecdm.dll)  
  7. Widevinecdmadapter.dll calls the exported function CreateCdmInstance of widevinecdm.dll to create a CDM instance   
  8. 4. The CDM framework is the standard of Google Chrome, so the API parameters and interfaces have C++ include File, such as API CreateCdmInstance:
  9.     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);   
  10.   
  11. CDM instances should inherit from class ContentDecryptionModule_8, view class ContentDecryptionModule_8, found a very important function: Decrypt, which is mainly used to transfer encrypted data into and decrypted data out. My God, as long as this function is intercepted, it will be done!!!   
  1. class  CDM_CLASS_API ContentDecryptionModule_8 {       // Decrypts the |encrypted_buffer|.   
  2.     //   
  3.     // Returns kSuccess if decryption succeeded, in which case the callee   
  4.     // should have filled the |decrypted_buffer| and passed the ownership of   
  5.     // |data| in |decrypted_buffer| to the caller.   
  6.     // Returns kNoKey if the CDM did not have the necessary decryption key   
  7.     // to decrypt.   
  8.     // Returns kDecryptError if any other error happened.   
  9.     // If the return value is not kSuccess, |decrypted_buffer| should be ignored   
  10.     // by the caller.   
  11.    virtual Status Decrypt( const  InputBuffer& encrypted_buffer,  
  12.                           DecryptedBlock* decrypted_buffer) = 0;   
  13.  };   

5. After understanding the API parameters and class definitions, it is assumed that if the decrypted data is intercepted when widevinecdmadapter.dll and widevinecdm.dll call each other, the DRM protection mechanism can be bypassed
6. Start writing a DLL called CdmProxy.dll, my own CreateCdmInstance function, which does not explain:

  1. extern  "C"  __declspec(dllexport) void * CDMAPI_DEFINE my_CreateCdmInstance(int cdm_interface_version,  const  char* key_system,  
  2.     uint32_t key_system_size, GetCdmHostFunc get_cdm_host_func, void* user_data)  
  3. {  
  4.     gHostUserData = user_data;   
  5.     wsprintf(wchLog, L "CdmProxy - call CreateCdmInstance(%d, %S, %d, 0x%08X, 0x%08X)" ,  
  6.         cdm_interface_version, key_system, key_system_size, get_cdm_host_func, user_data);   
  7.     OutputDebugStringW(wchLog);   
  8.     void *p = pCreateCdmInstance(cdm_interface_version, key_system, key_system_size, get_cdm_host_func, user_data);   
  9.    
  10.     cdm::ContentDecryptionModule_8 *pCdmModule = (cdm::ContentDecryptionModule_8 *)(p);   
  11.     MyContentDecryptionModuleProxy *pMyCdmModule =  new  MyContentDecryptionModuleProxy(pCdmModule);   
  12.    
  13.      return  pMyCdmModule;   
  14. }  

My generation {passes} {filters} manages the class MyContentDecryptionModuleProxy, and the code does not explain:
// class MyContentDecryptionModuleProxy

  1. class  MyContentDecryptionModuleProxy :  public  cdm::ContentDecryptionModule_8  
  2. {  
  3. public :  
  4.     MyContentDecryptionModuleProxy(cdm::ContentDecryptionModule_8 *pCdm)  
  5.     {  
  6.         mCdm = pCdm;   
  7.     }  
  8. private :  
  9.     cdm::ContentDecryptionModule_8 *mCdm;   
  10.    
  11. public :  
  12.      //The most important decryption function, which saves the original data and decrypted data   
  13.     virtual cdm::Status Decrypt( const  cdm::InputBuffer& encrypted_buffer, cdm::DecryptedBlock* decrypted_buffer)  
  14.     {  
  15.         cdm::Status status = cdm::kSuccess;   
  16.         codelive();   
  17.         DebugDecryptBreak(encrypted_buffer.iv, encrypted_buffer.key_id, encrypted_buffer.data);   
  18.         status = mCdm->Decrypt(encrypted_buffer, decrypted_buffer);   
  19.            
  20.         string strIV = data2HexString(( const  char *)encrypted_buffer.iv, encrypted_buffer.iv_size);   
  21.         string strEncData = data2HexString(( const  char *)encrypted_buffer.data, min(encrypted_buffer.data_size, 32));   
  22.         string strDecData = data2HexString(( const  char *)decrypted_buffer->DecryptedBuffer()->Data(),   
  23.             min(decrypted_buffer->DecryptedBuffer()->Size(), 32));   
  24.         wsprintf(wchLog, L "CdmProxy - call Decrypt(IV:%S, encData(%d):%S, decData(%d):%S)" ,   
  25.             strIV.c_str(), encrypted_buffer.data_size, strEncData.c_str(),   
  26.             decrypted_buffer->DecryptedBuffer()->Size(), strDecData.c_str());   
  27.         OutputDebugStringW(wchLog);   
  28.    
  29.          if (mEncFile == NULL)  
  30.         {  
  31.             mEncFile =  fopen ( "d:\\cdm_enc.bin" "wb" );   
  32.         }  
  33.          if (mEncFile != NULL)  
  34.         {  
  35.             fwrite(encrypted_buffer.data, 1, encrypted_buffer.data_size, mEncFile);   
  36.         }  
  37.          if (mDecFile == NULL)  
  38.         {  
  39.             mDecFile =  fopen ( "d:\\cdm_dec.bin" "wb" );   
  40.         }  
  41.          if (mDecFile != NULL)  
  42.         {  
  43.             fwrite(decrypted_buffer->DecryptedBuffer()->Data(), 1, decrypted_buffer->DecryptedBuffer()->Size(), mDecFile);   
  44.         }  
  45.          return  status;   
  46.     }  
  47. };   

7. After the core code is written, we need to solve the problem of DLL loading. We tried several simple ways to rename widevinecdm.dll and widevinecdmadapter.dll to widevinecdm_org.dll and widevinecdmadapter_org.dll, and then wrote a DLL by ourselves, export the same API as widevinecdm.dll or widevinecdmadapter.dll, and then call the original DLL, However, this method is not feasible because of Chrome's security sandbox. All plug-ins are loaded sandbox process space, and sensitive APIs, such as ReadProcessMemory, CeateFile, OutputDebugString, cannot be used Sandbox Escape is a very high bonus for Google, up to $15000
8. Since all processes are created by Chrome, I directly started with chrome.exe, directly patched chrome.exe, and asked it to load my CdmProxy.dll. The result was successful. After all, the sandbox mechanism was not enabled when Chrome started, so it solved the DLL loading problem without spending too much technology

9. Change the API interception method, dynamically patch widevinecdmadapter.dll, change the address of calling API CreateCdmInstance to my own API my_CreateCdmInstance, and then perform the following processing when loading the DLL:

  1. BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)  
  2. {  
  3.      switch (ul_reason_for_call)  
  4.     {  
  5.      case  DLL_PROCESS_ATTACH:  
  6.         {  
  7.             hWideVineCdm = LoadLibraryW(L "{PATH}\\widevinecdm.dll" );   
  8.    
  9.             pInitializeCdmModule_4 = (InitializeCdmModule_4Func)GetProcAddress(hWideVineCdm,  "InitializeCdmModule_4" );   
  10.             pDeinitializeCdmModule = (DeinitializeCdmModuleFunc)GetProcAddress(hWideVineCdm,  "DeinitializeCdmModule" );   
  11.             pCreateCdmInstance = (CreateCdmInstanceFunc)GetProcAddress(hWideVineCdm,  "CreateCdmInstance" );   
  12.             pGetCdmVersion = (GetCdmVersionFunc)GetProcAddress(hWideVineCdm,  "GetCdmVersion" );   
  13.    
  14.             hWideVineCdmAdapter = LoadLibraryW(L "{PATH}\\widevinecdmadapter.dll" );   
  15.              if (hWideVineCdmAdapter != NULL)  
  16.             {  
  17.                 DWORD dwSrcAddr = (DWORD)hWideVineCdmAdapter + 0x0000446D;   
  18.                  const  BYTE chVerify[] = { 0xFF, 0x15 };   
  19.                 BOOL isOK = patch_DsCallFunction(dwSrcAddr, (DWORD)my_CreateCdmInstance, chVerify, sizeof(chVerify));   
  20.                 wsprintf(wchLog, L "CdmProxy - patch CreateCdmInstance, Address:0x%08X-0x%08X, %s." ,  
  21.                     dwSrcAddr, (uint32_t)my_CreateCdmInstance,  
  22.                     isOK ?  L "OK"  : L "FAILED" );   
  23.                 OutputDebugStringW(wchLog);   
  24.             }  
  25.         }  
  26.          break  ;   
  27.     }  
  28. }  

10. Then test and play a DASH video with DRM protection:
https://shaka-player-demo.appspo ... gleKey/Manifest.mpd
It is found that the file has not been saved and LOG has not been output. It must be that the safety sandbox works.
11. It is also necessary to escape the sandbox. After research, it is found that it is not necessary at all. Just add no sandbox to the chrome startup parameter. My God, why should we provide such a back door!!!

12. Play the encrypted video again, and the file is saved successfully. LOG is also output successfully. After verification, the decrypted data is consistent with the previously unencrypted data.

13. Google Chrome's CDM has been cracked in this way. It is very simple to bypass the Widevine DRM algorithm. This should be a serious problem in the framework design of Chrome CDM. It is estimated that it is not easy to change it.

This is LOG data:

  1. "CdmProxy - call CreateCdmInstance(8, com.widevine.alpha, 18, 0x70F86310, 0x00D75A88)"  )            zero point zero zero zero one three nine nine   
  2. "CdmProxy - call CreateCdmInstance(8, com.widevine.alpha, 18, 0x70F86310, 0x00D757C8)"  )            zero point zero zero zero zero nine three seven   
  3. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F00000000000000000, encData(348):FFF158402B9FFC2FF05300F2BF83E9A0, decData(0):)"  )            zero point zero zero zero one three five zero   
  4. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F00000000000000000, encData(348):FFF158402B9FFC2FF05300F2BF83E9A0, decData(348):FFF158402B9FFC00D03403E95B8639BD)"  )          zero point zero zero zero one three three five   
  5. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F00000000000000000, encData(348):FFF158402B9FFC2FF05300F2BF83E9A0, decData(348):FFF158402B9FFC00D03403E95B8639BD)"  )          zero point zero zero zero one zero three two   
  6. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F10000000000000000, encData(348):FFF158402B9FFC487380B8930FFFAB41, decData(348):FFF158402B9FFC00F43420C24620902C)"  )          zero point zero zero zero one three nine two   
  7. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F20000000000000000, encData(349):FFF158402BBFFC1175E15FE4B6154B30, decData(349):FFF158402BBFFC00FA342D90762A3188)"  )          zero point zero zero zero one zero three two   
  8. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F30000000000000000, encData(348):FFF158402B9FFCC45D5715E87235E5CF, decData(348):FFF158402B9FFC00F8342CEC825A2D85)"  )          zero point zero zero zero zero nine nine four   
  9. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F40000000000000000, encData(348):FFF158402B9FFC6749FBAF64926471DE, decData(348):FFF158402B9FFC00F83421884529290A)"  )          zero point zero zero zero zero eight eight zero   
  10. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F50000000000000000, encData(349):FFF158402BBFFCF8132EFC31C186DDE1, decData(349):FFF158402BBFFC00F2342D9049124988)"  )          zero point zero zero zero one zero eight eight   
  11. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F60000000000000000, encData(348):FFF158402B9FFC82EDA0BD4AB7158938, decData(348):FFF158402B9FFC00EE342D7475223D85)"  )          zero point zero zero zero one zero three five   
  12. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F70000000000000000, encData(348):FFF158402B9FFC4B2C585CC10F74036E, decData(348):FFF158402B9FFC00F4342D74662A2088)"  )          zero point zero zero zero one five five five   
  13. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F80000000000000000, encData(349):FFF158402BBFFCCF33665AC4E219EC92, decData(349):FFF158402BBFFC00FA342E30547B0604)"  )          zero point zero zero zero one four nine four   
  14. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818F90000000000000000, encData(348):FFF158402B9FFC2C9A7362594261CE23, decData(348):FFF158402B9FFC00F4342E305429150A)"  )          zero point zero zero zero four zero three five   
  15. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FA0000000000000000, encData(348):FFF158402B9FFC1905A086AE3CEF0AEC, decData(348):FFF158402B9FFC00EE342E3456391906)"  )          zero point zero zero zero five nine one three   
  16. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FB0000000000000000, encData(349):FFF158402BBFFC8D0EB865013262FB6E, decData(349):FFF158402BBFFC00F6342E34563A2186)"  )          zero point zero zero zero one four seven nine   
  17. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FC0000000000000000, encData(348):FFF158402B9FFC484211C612F22283FB, decData(348):FFF158402B9FFC0102342E74482A2E02)"  )          zero point zero zero zero two five zero seven   
  18. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FD0000000000000000, encData(348):FFF158402B9FFC283122B1DDE740DAC2, decData(348):FFF158402B9FFC0104342EB464190D08)"  )          zero point zero zero zero three zero one one   
  19. "CdmProxy - call Decrypt(IV:6CD1F4FBCE5818FE0000000000000000, encData(349):FFF158402BBFFCAC8759D48FF1A258A3, decData(349):FFF158402BBFFC010E342ED04A1A2982)"  )          zero point zero zero zero three zero nine five   

DRM has done a lot of strict protection mechanisms and algorithms for many years, but it was betrayed by Zhu's teammates.

The purpose of making this research public is to make the majority of video companies not think DRM is very secure. Sometimes DRM is really vulnerable. If there is a loophole in a certain link, they will also face great security problems.

After I submitted it, the Chromium team replied quickly. They confirmed that this is a major security issue and affects all operating systems running Chrome, including Linux, Windows, Chrome, Mac, and so on However, another employee said that this was a known problem and provided an issue number: 658022. However, I checked indefinitely whether the vulnerability content was consistent with my submission. Later, I sent an email to several members of the Google team, saying that since it was a known problem, it was not in line with the reward rules, so I could also publish details to let the video content company pay attention to this problem, so as to discuss a more secure solution as soon as possible. If this article is not suitable for publication, please let me know. Thank you.

fabulous ( three )

This article is written by Ji Changxin Author, article address: https://blog.isoyu.com/archives/chrome-cdmkuangjiazhongdaquexiandrmshipinqingyifuzhi.html
use Knowledge Sharing Attribution 4.0 International License Agreement. Unless the reprint/source is indicated, they are all original or translated by this website. Please sign your name before reprinting. Last editing time: August 1, 2019 at 02:38 PM

Popular articles

Comments:

1 comment, 0 visitors, 0 bloggers

Post reply

[Required]

I am a human?

Please wait three seconds after submission to avoid unsubmission and repetition