谷歌 Widevine DRM 破解

标签: 无

精彩评论
  1. 关于IAT修改导入表的函数地址来实现拦截和EAT拦截应该怎么做呢?如何定位Decrypt和DecryptAndDecodeFrame的地址呢,我尝试用IDA直接打开,没有找到它们在哪儿,是否是通过OD单步调试还是怎么操作?

    期待你的回复!

    1. 地址动态获取:

      BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew, HMODULE hmodCaller)
      {
          HMODULE hMod;
          LPCSTR szLibName;
          PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
          PIMAGE_THUNK_DATA pThunk;
          DWORD dwOldProtect, dwRVA;
          PBYTE pAddr;
      
          // hMod, pAddr = ImageBase of calc.exe
          //             = VA to MZ signature (IMAGE_DOS_HEADER)
          //hMod = GetModuleHandle(NULL);
          hMod = hmodCaller;
          pAddr = (PBYTE)hMod;
      
          // pAddr = VA to PE signature (IMAGE_NT_HEADERS)
          pAddr += *((DWORD*)&pAddr[0x3C]);
      
          // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table
          dwRVA = *((DWORD*)&pAddr[0x80]);
      
          // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table
          pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod + dwRVA);
      
          for (; pImportDesc->Name; pImportDesc++)
          {
              // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name
              szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
              if (!_stricmp(szLibName, szDllName))
              {
                  // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk
                  //        = VA to IAT(Import Address Table)
                  pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
                      pImportDesc->FirstThunk);
      
                  // pThunk->u1.Function = VA to API
                  for (; pThunk->u1.Function; pThunk++)
                  {
                      if (pThunk->u1.Function == (DWORD)pfnOrg)
                      {
                          // 更改内存属性为 E/R/W
                          VirtualProtect((LPVOID)&pThunk->u1.Function,
                              4,
                              PAGE_EXECUTE_READWRITE,
                              &dwOldProtect);
      
                          // 修改 IAT (钩取)
                          pThunk->u1.Function = (DWORD)pfnNew;
      
                          // 恢复内存属性
                          VirtualProtect((LPVOID)&pThunk->u1.Function,
                              4,
                              dwOldProtect,
                              &dwOldProtect);
      
                          return TRUE;
                      }
                  }
              }
          }
      
          return FALSE;
      }
      
       hWideVineCdm = LoadLibraryW(L"C:\\Users\\XWL\\AppData\\Local\\Google\\Chrome\\Application\\62.0.3192.0\\WidevineCdm\\_platform_specific\\win_x86\\widevinecdm.dll"icon_wink.gif;
      
              if (hWideVineCdm == NULL)
              {
                  WriteLog("load widevinecdm.dll failed", 0);
              }
              else
              {
                  WriteLog("load widevinecdm.dll success", 0);
              }
              
      
              PROC pfnOrig = GetProcAddress(hWideVineCdm, "CreateCdmInstance"icon_wink.gif;
      
      
      
              pCreateCdmInstance = (CreateCdmInstanceFunc)pfnOrig;
      
              //hWideVineCdmAdapter = LoadLibraryW(L"C:\\Program Files\\Google\\Chrome\\Application\\62.0.3192.0\\WidevineCdm\\_platform_specific\\win_x86\\widevinecdmadapter.dll"icon_wink.gif;
              hWideVineCdmAdapter = GetModuleHandle(L"widevinecdmadapter"icon_wink.gif;
      
              if (hWideVineCdmAdapter != NULL)
              {
                  BOOL ok = hook_iat("widevinecdm.dll", pfnOrig, (PROC)my_CreateCdmInstance, hWideVineCdmAdapter);
                  if (!ok)
                  {
                      WriteLog("hook failed:", 0);
                  }
                  else
                  {
                      WriteLog("hook success:", 0);
                  }
                 
              }
              else
              {
                  DWORD err = GetLastError();
                  char buf[20] = { '\0' };
                  _ultoa(err, buf, 10);
                  WriteLog("load widevinecdmadapter.dll failed:", 0);
                  WriteLog(buf, 0);
              }

      以上代码测试在win732位上通过,win10无效。

      1. karll karll

        [secret]大神能透露下是在哪个版本的chromium上做的吗[/secret]

  2. xiaoxian xiaoxian

    大佬请问有小白能用的方法吗? 有偿也行呀!

    1. 没有,实践测试只做到简单的拦截视频流写出.yuv文件播放,若想做成工具也比较麻烦,需要添加拦截开关(即何时开始拦截,何时结束拦截),以及多个网页视频同时拦截,.yuv重新编码回H264,实现比较复杂。

  3. k1ty k1ty

    在头文件前面还有InitializeVideoDecoder这个虚函数的定义,博主有尝试过hook让它返回kInitializationError之后能否用Decrypt方法来解密视频吗?

    1. 试过直接解密报错。

  4. 小明 小明

    很有帮助

  5. doit doit

    [secret]你好,可以付费定制工具吗,方便的话,可以加您QQ吗[/secret]

    1. 没有工具,无法全自动,目前已实现yuv420p编码回h264,按播放列表自动dump视频文件保存到本地,音频暂未处理(音频处理比较麻烦,比如某酷视频开头部分音频未加密是不会走Decrypt方法的,需要dump Decrypt解密后的音频然后合并自己手动下载未加密的部分,最终再与h264视频合并),测试与录屏差不多,使用ffmpeg编码yuvh264比较耗时,可能我电脑的原因编码比较慢,无法使用视频快进加快编码速度,理论上电脑配置足够好的话可以使用快进功能加快视频编码,这样一来相比录屏速度就会加快,编码参数可控,视频大小与编码参数设置有关。

发表评论:

icon_mrgreen.gificon_neutral.gificon_twisted.gificon_arrow.gificon_eek.gificon_smile.gificon_confused.gificon_cool.gificon_evil.gificon_biggrin.gificon_idea.gificon_redface.gificon_razz.gificon_rolleyes.gificon_wink.gificon_cry.gificon_surprised.gificon_lol.gificon_mad.gificon_sad.gificon_exclaim.gificon_question.gif