Update inject.js
logic flow, anchored functions, etc.
This commit is contained in:
parent
046d64bbc5
commit
8a2fc2dfed
182
inject.js
182
inject.js
@ -4,7 +4,9 @@ let originalChallenge = null
|
|||||||
let serviceCertFound = false;
|
let serviceCertFound = false;
|
||||||
let drmType = "NONE";
|
let drmType = "NONE";
|
||||||
let psshFound = false;
|
let psshFound = false;
|
||||||
let pssh = null;
|
let foundWidevinePssh = null;
|
||||||
|
let foundPlayreadyPssh = null;
|
||||||
|
let drmDecided = null;
|
||||||
let drmOverride = "DISABLED";
|
let drmOverride = "DISABLED";
|
||||||
let interceptType = "DISABLED";
|
let interceptType = "DISABLED";
|
||||||
let remoteCDM = null;
|
let remoteCDM = null;
|
||||||
@ -298,14 +300,17 @@ class remoteWidevineCDM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
const hexStrToU8 = hexString =>
|
function hexStrToU8(hexString) {
|
||||||
Uint8Array.from(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
return Uint8Array.from(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
||||||
|
}
|
||||||
|
|
||||||
const u8ToHexStr = bytes =>
|
function u8ToHexStr(bytes) {
|
||||||
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
|
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
|
||||||
|
}
|
||||||
|
|
||||||
const b64ToHexStr = b64 =>
|
function b64ToHexStr(b64) {
|
||||||
[...atob(b64)].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join``;
|
return [...atob(b64)].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join``;
|
||||||
|
}
|
||||||
|
|
||||||
function jsonContainsValue(obj, prefix = "CAES") {
|
function jsonContainsValue(obj, prefix = "CAES") {
|
||||||
if (typeof obj === "string") return obj.startsWith(prefix);
|
if (typeof obj === "string") return obj.startsWith(prefix);
|
||||||
@ -338,14 +343,14 @@ function jsonReplaceValue(obj, target, newValue) {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isJson = (str) => {
|
function isJson(str) {
|
||||||
try {
|
try {
|
||||||
JSON.parse(str);
|
JSON.parse(str);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function getWidevinePssh(buffer) {
|
function getWidevinePssh(buffer) {
|
||||||
const hex = u8ToHexStr(new Uint8Array(buffer));
|
const hex = u8ToHexStr(new Uint8Array(buffer));
|
||||||
@ -373,11 +378,11 @@ function getPlayReadyPssh(buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getClearkey(response) {
|
function getClearkey(response) {
|
||||||
let obj = JSON.parse((new TextDecoder("utf-8")).decode(response));
|
let obj = JSON.parse((new TextDecoder("utf-8")).decode(response));
|
||||||
return obj["keys"].map(o => ({
|
return obj["keys"].map(o => ({
|
||||||
key_id: b64ToHexStr(o["kid"].replace(/-/g, '+').replace(/_/g, '/')),
|
key_id: b64ToHexStr(o["kid"].replace(/-/g, '+').replace(/_/g, '/')),
|
||||||
key: b64ToHexStr(o["k"].replace(/-/g, '+').replace(/_/g, '/')),
|
key: b64ToHexStr(o["k"].replace(/-/g, '+').replace(/_/g, '/')),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function base64ToUint8Array(base64) {
|
function base64ToUint8Array(base64) {
|
||||||
@ -391,14 +396,14 @@ function base64ToUint8Array(base64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function arrayBufferToBase64(uint8array) {
|
function arrayBufferToBase64(uint8array) {
|
||||||
let binary = '';
|
let binary = '';
|
||||||
const len = uint8array.length;
|
const len = uint8array.length;
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
binary += String.fromCharCode(uint8array[i]);
|
binary += String.fromCharCode(uint8array[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.btoa(binary);
|
return window.btoa(binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Challenge generator interceptor
|
// Challenge generator interceptor
|
||||||
@ -408,23 +413,16 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) {
|
|||||||
generateRequestCalled = true;
|
generateRequestCalled = true;
|
||||||
const session = this;
|
const session = this;
|
||||||
let playReadyPssh = getPlayReadyPssh(initData);
|
let playReadyPssh = getPlayReadyPssh(initData);
|
||||||
if (playReadyPssh && drmOverride !== "WIDEVINE") {
|
if (playReadyPssh) {
|
||||||
// PlayReady Code
|
|
||||||
drmType = "PlayReady";
|
|
||||||
window.postMessage({ type: "__DRM_TYPE__", data: "PlayReady" }, "*");
|
|
||||||
console.log("[DRM Detected] PlayReady");
|
console.log("[DRM Detected] PlayReady");
|
||||||
pssh = playReadyPssh;
|
foundPlayreadyPssh = playReadyPssh;
|
||||||
window.postMessage({ type: "__PSSH_DATA__", data: playReadyPssh }, "*");
|
|
||||||
console.log("[PlayReady PSSH found] " + playReadyPssh)
|
console.log("[PlayReady PSSH found] " + playReadyPssh)
|
||||||
}
|
}
|
||||||
let wideVinePssh = getWidevinePssh(initData)
|
let wideVinePssh = getWidevinePssh(initData)
|
||||||
if (wideVinePssh && drmOverride !== "PLAYREADY") {
|
if (wideVinePssh) {
|
||||||
// Widevine code
|
// Widevine code
|
||||||
drmType = "Widevine";
|
|
||||||
window.postMessage({ type: "__DRM_TYPE__", data: "Widevine" }, "*");
|
|
||||||
console.log("[DRM Detected] Widevine");
|
console.log("[DRM Detected] Widevine");
|
||||||
pssh = wideVinePssh;
|
foundWidevinePssh = wideVinePssh;
|
||||||
window.postMessage({ type: "__PSSH_DATA__", data: wideVinePssh }, "*");
|
|
||||||
console.log("[Widevine PSSH found] " + wideVinePssh)
|
console.log("[Widevine PSSH found] " + wideVinePssh)
|
||||||
}
|
}
|
||||||
// Challenge message interceptor
|
// Challenge message interceptor
|
||||||
@ -434,7 +432,7 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) {
|
|||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
const uint8Array = new Uint8Array(event.message);
|
const uint8Array = new Uint8Array(event.message);
|
||||||
const base64challenge = arrayBufferToBase64(uint8Array);
|
const base64challenge = arrayBufferToBase64(uint8Array);
|
||||||
if (base64challenge === "CAQ=" && interceptType !== "DISABLED" && drmOverride !== "PLAYREADY") {
|
if (base64challenge === "CAQ=" && interceptType !== "DISABLED") {
|
||||||
const {
|
const {
|
||||||
device_type, system_id, security_level, host, secret, device_name
|
device_type, system_id, security_level, host, secret, device_name
|
||||||
} = widevineDeviceInfo;
|
} = widevineDeviceInfo;
|
||||||
@ -448,23 +446,35 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) {
|
|||||||
if (!originalChallenge) {
|
if (!originalChallenge) {
|
||||||
originalChallenge = base64challenge;
|
originalChallenge = base64challenge;
|
||||||
}
|
}
|
||||||
if (!remoteCDM && drmType === "Widevine" && drmOverride !== "PLAYREADY") {
|
if (originalChallenge.startsWith("CAES")) {
|
||||||
const {
|
window.postMessage({ type: "__DRM_TYPE__", data: "Widevine" }, "*");
|
||||||
device_type, system_id, security_level, host, secret, device_name
|
window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*");
|
||||||
} = widevineDeviceInfo;
|
if (interceptType === "EME" && !remoteCDM) {
|
||||||
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name);
|
const {
|
||||||
remoteCDM.openSession();
|
device_type, system_id, security_level, host, secret, device_name
|
||||||
}
|
} = widevineDeviceInfo;
|
||||||
if (!remoteCDM && drmType === "PlayReady" && drmOverride !== "WIDEVINE") {
|
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name);
|
||||||
const {
|
remoteCDM.openSession();
|
||||||
security_level, host, secret, device_name
|
remoteCDM.getChallenge(foundWidevinePssh);
|
||||||
} = playreadyDeviceInfo;
|
}}
|
||||||
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name)
|
if (!originalChallenge.startsWith("CAES")) {
|
||||||
remoteCDM.openSession();
|
const buffer = event.message;
|
||||||
}
|
const decoder = new TextDecoder('utf-16');
|
||||||
if (remoteCDM && interceptType === "EME") {
|
const decodedText = decoder.decode(buffer);
|
||||||
remoteCDM.getChallenge(pssh);
|
const match = decodedText.match(/<Challenge encoding="base64encoded">([^<]+)<\/Challenge>/);
|
||||||
}
|
if (match) {
|
||||||
|
window.postMessage({ type: "__DRM_TYPE__", data: "PlayReady" }, "*");
|
||||||
|
window.postMessage({ type: "__PSSH_DATA__", data: foundPlayreadyPssh }, "*");
|
||||||
|
originalChallenge = match[1];
|
||||||
|
if (interceptType === "EME" && !remoteCDM) {
|
||||||
|
const {
|
||||||
|
security_level, host, secret, device_name
|
||||||
|
} = playreadyDeviceInfo;
|
||||||
|
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name)
|
||||||
|
remoteCDM.openSession();
|
||||||
|
remoteCDM.getChallenge(foundPlayreadyPssh);
|
||||||
|
}
|
||||||
|
}}
|
||||||
if (interceptType === "EME" && remoteCDM) {
|
if (interceptType === "EME" && remoteCDM) {
|
||||||
const uint8challenge = base64ToUint8Array(remoteCDM.challenge);
|
const uint8challenge = base64ToUint8Array(remoteCDM.challenge);
|
||||||
const challengeBuffer = uint8challenge.buffer;
|
const challengeBuffer = uint8challenge.buffer;
|
||||||
@ -494,11 +504,15 @@ const originalUpdate = MediaKeySession.prototype.update;
|
|||||||
MediaKeySession.prototype.update = function(response) {
|
MediaKeySession.prototype.update = function(response) {
|
||||||
const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response);
|
const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response);
|
||||||
const base64Response = window.btoa(String.fromCharCode(...uint8));
|
const base64Response = window.btoa(String.fromCharCode(...uint8));
|
||||||
console.log(base64Response);
|
if (base64Response.startsWith("CAUS") && foundWidevinePssh && remoteCDM) {
|
||||||
if (base64Response.startsWith("CAUS") && pssh && remoteCDM) {
|
|
||||||
remoteCDM.setServiceCertificate(base64Response);
|
remoteCDM.setServiceCertificate(base64Response);
|
||||||
|
if (interceptType === "EME") {
|
||||||
|
remoteCDM.getChallenge(foundWidevinePssh);
|
||||||
|
}
|
||||||
|
window.postMessage({ type: "__DRM_TYPE__", data: "Widevine" }, "*");
|
||||||
|
window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*");
|
||||||
}
|
}
|
||||||
if (!base64Response.startsWith("CAUS") && pssh) {
|
if (!base64Response.startsWith("CAUS") && (foundWidevinePssh || foundPlayreadyPssh)) {
|
||||||
if (licenseResponseCounter === 1 || foundChallengeInBody) {
|
if (licenseResponseCounter === 1 || foundChallengeInBody) {
|
||||||
remoteCDM.parseLicense(base64Response);
|
remoteCDM.parseLicense(base64Response);
|
||||||
remoteCDM.getKeys();
|
remoteCDM.getKeys();
|
||||||
@ -508,7 +522,7 @@ MediaKeySession.prototype.update = function(response) {
|
|||||||
licenseResponseCounter++;
|
licenseResponseCounter++;
|
||||||
}
|
}
|
||||||
const updatePromise = originalUpdate.call(this, response);
|
const updatePromise = originalUpdate.call(this, response);
|
||||||
if (!pssh && (drmOverride !== "WIDEVINE" && drmOverride !== "PLAYREADY")) {
|
if (!foundPlayreadyPssh && !foundWidevinePssh) {
|
||||||
updatePromise
|
updatePromise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
let clearKeys = getClearkey(response);
|
let clearKeys = getClearkey(response);
|
||||||
@ -573,23 +587,76 @@ MediaKeySession.prototype.update = function(response) {
|
|||||||
XMLHttpRequest.prototype.send = function(body) {
|
XMLHttpRequest.prototype.send = function(body) {
|
||||||
if (this._method && this._method.toUpperCase() === 'POST') {
|
if (this._method && this._method.toUpperCase() === 'POST') {
|
||||||
if (body) {
|
if (body) {
|
||||||
|
|
||||||
if (body instanceof ArrayBuffer || body instanceof Uint8Array) {
|
if (body instanceof ArrayBuffer || body instanceof Uint8Array) {
|
||||||
const buffer = body instanceof Uint8Array ? body : new Uint8Array(body);
|
const buffer = body instanceof Uint8Array ? body : new Uint8Array(body);
|
||||||
const base64Body = window.btoa(String.fromCharCode(...buffer));
|
const base64Body = window.btoa(String.fromCharCode(...buffer));
|
||||||
if (base64Body.startsWith("CAES") && base64Body !== remoteCDM.challenge && interceptType === "EME") {
|
if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64Body !== remoteCDM.challenge) && interceptType === "EME") {
|
||||||
foundChallengeInBody = true;
|
foundChallengeInBody = true;
|
||||||
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
|
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
|
||||||
// Block the request
|
// Block the request
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (base64Body.startsWith("CAES") && interceptType == "LICENSE") {
|
if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && interceptType == "LICENSE") {
|
||||||
foundChallengeInBody = true;
|
foundChallengeInBody = true;
|
||||||
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
|
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
|
||||||
remoteCDM.getChallenge(pssh)
|
if (!remoteCDM) {
|
||||||
const injectedBody = base64ToUint8Array(remoteCDM.challenge);
|
if (base64EncodedBody.startsWith("CAES")) {
|
||||||
|
const {
|
||||||
|
device_type, system_id, security_level, host, secret, device_name
|
||||||
|
} = widevineDeviceInfo;
|
||||||
|
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name);
|
||||||
|
remoteCDM.openSession();
|
||||||
|
remoteCDM.getChallenge(foundWidevinePssh);
|
||||||
|
}
|
||||||
|
if (base64EncodedBody.startsWith("PD94")) {
|
||||||
|
const {
|
||||||
|
security_level, host, secret, device_name
|
||||||
|
} = playreadyDeviceInfo;
|
||||||
|
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name);
|
||||||
|
remoteCDM.openSession();
|
||||||
|
remoteCDM.getChallenge(foundPlayreadyPssh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const injectedBody = atob(remoteCDM.challenge);
|
||||||
return originalSend.call(this, injectedBody);
|
return originalSend.call(this, injectedBody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof body === 'string') {
|
||||||
|
const base64EncodedBody = btoa(body);
|
||||||
|
if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64EncodedBody !== remoteCDM.challenge) && interceptType === "EME") {
|
||||||
|
foundChallengeInBody = true;
|
||||||
|
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
|
||||||
|
// Block the request
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && interceptType == "LICENSE") {
|
||||||
|
foundChallengeInBody = true;
|
||||||
|
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
|
||||||
|
if (!remoteCDM) {
|
||||||
|
if (base64EncodedBody.startsWith("CAES")) {
|
||||||
|
const {
|
||||||
|
device_type, system_id, security_level, host, secret, device_name
|
||||||
|
} = widevineDeviceInfo;
|
||||||
|
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name);
|
||||||
|
remoteCDM.openSession();
|
||||||
|
remoteCDM.getChallenge(foundWidevinePssh);
|
||||||
|
}
|
||||||
|
if (base64EncodedBody.startsWith("PD94")) {
|
||||||
|
const {
|
||||||
|
security_level, host, secret, device_name
|
||||||
|
} = playreadyDeviceInfo;
|
||||||
|
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name);
|
||||||
|
remoteCDM.openSession();
|
||||||
|
remoteCDM.getChallenge(foundPlayreadyPssh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const injectedBody = atob(remoteCDM.challenge);
|
||||||
|
return originalSend.call(this, injectedBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isJson(body)) {
|
if (isJson(body)) {
|
||||||
if (jsonContainsValue(body) && !jsonContainsValue(body, remoteCDM.challenge)) {
|
if (jsonContainsValue(body) && !jsonContainsValue(body, remoteCDM.challenge)) {
|
||||||
foundChallengeInBody = true;
|
foundChallengeInBody = true;
|
||||||
@ -597,6 +664,7 @@ MediaKeySession.prototype.update = function(response) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return originalSend.apply(this, arguments);
|
return originalSend.apply(this, arguments);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user