157 lines
4.8 KiB
JavaScript
157 lines
4.8 KiB
JavaScript
|
const CDM_VERSION = ''
|
||
|
|
||
|
// The TextEncoder/Decoder API isn't supported so it has to be polyfilled.
|
||
|
// Taken from https://gist.github.com/Yaffle/5458286#file-textencodertextdecoder-js
|
||
|
function TextEncoder() {
|
||
|
}
|
||
|
|
||
|
TextEncoder.prototype.encode = function (string) {
|
||
|
var octets = [];
|
||
|
var length = string.length;
|
||
|
var i = 0;
|
||
|
while (i < length) {
|
||
|
var codePoint = string.codePointAt(i);
|
||
|
var c = 0;
|
||
|
var bits = 0;
|
||
|
if (codePoint <= 0x0000007F) {
|
||
|
c = 0;
|
||
|
bits = 0x00;
|
||
|
} else if (codePoint <= 0x000007FF) {
|
||
|
c = 6;
|
||
|
bits = 0xC0;
|
||
|
} else if (codePoint <= 0x0000FFFF) {
|
||
|
c = 12;
|
||
|
bits = 0xE0;
|
||
|
} else if (codePoint <= 0x001FFFFF) {
|
||
|
c = 18;
|
||
|
bits = 0xF0;
|
||
|
}
|
||
|
octets.push(bits | (codePoint >> c));
|
||
|
c -= 6;
|
||
|
while (c >= 0) {
|
||
|
octets.push(0x80 | ((codePoint >> c) & 0x3F));
|
||
|
c -= 6;
|
||
|
}
|
||
|
i += codePoint >= 0x10000 ? 2 : 1;
|
||
|
}
|
||
|
return octets;
|
||
|
}
|
||
|
|
||
|
function getPrivateKey(address) {
|
||
|
Interceptor.attach(ptr(address), {
|
||
|
onEnter: function (args) {
|
||
|
if (!args[6].isNull()) {
|
||
|
const size = args[6].toInt32();
|
||
|
if (size >= 1000 && size <= 2000 && !args[5].isNull()) {
|
||
|
const buf = args[5].readByteArray(size);
|
||
|
const bytes = new Uint8Array(buf);
|
||
|
// The first two bytes of the DER encoding are 0x30 and 0x82 (MII).
|
||
|
if (bytes[0] === 0x30 && bytes[1] === 0x82) {
|
||
|
try {
|
||
|
const binaryString = a2bs(bytes)
|
||
|
const keyLength = getKeyLength(binaryString);
|
||
|
const key = bytes.slice(0, keyLength);
|
||
|
send('private_key', key);
|
||
|
} catch (error) {
|
||
|
console.log(error)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// nop privacy mode.
|
||
|
// PrivacyMode encrypts the payload with the public key returned by the license server which we don't want.
|
||
|
function disablePrivacyMode(address) {
|
||
|
Interceptor.attach(address, {
|
||
|
onLeave: function (retval) {
|
||
|
retval.replace(ptr(0));
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function prepareKeyRequest(address) {
|
||
|
Interceptor.attach(ptr(address), {
|
||
|
onEnter: function (args) {
|
||
|
switch (CDM_VERSION) {
|
||
|
case '14.0.0':
|
||
|
case '15.0.0':
|
||
|
case '16.0.0':
|
||
|
this.ret = args[4];
|
||
|
break;
|
||
|
case '16.1.0':
|
||
|
this.ret = args[5];
|
||
|
break;
|
||
|
default:
|
||
|
this.ret = args[4];
|
||
|
break;
|
||
|
}
|
||
|
},
|
||
|
onLeave: function () {
|
||
|
if (this.ret) {
|
||
|
const size = Memory.readU32(ptr(this.ret).add(Process.pointerSize))
|
||
|
const arr = Memory.readByteArray(this.ret.add(Process.pointerSize * 2).readPointer(), size)
|
||
|
send('device_info', arr);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function hookLibFunctions(lib) {
|
||
|
const name = lib['name'];
|
||
|
const baseAddr = lib['base'];
|
||
|
const message = 'Hooking ' + name + ' at ' + baseAddr;
|
||
|
|
||
|
send('message_info', new TextEncoder().encode(message))
|
||
|
|
||
|
Module.enumerateExportsSync(name).forEach(function (module) {
|
||
|
try {
|
||
|
let hookedModule;
|
||
|
if (module.name.includes('UsePrivacyMode')) {
|
||
|
disablePrivacyMode(module.address);
|
||
|
hookedModule = module.name
|
||
|
} else if (module.name.includes('PrepareKeyRequest')) {
|
||
|
prepareKeyRequest(module.address);
|
||
|
hookedModule = module.name
|
||
|
} else if (module.name.match(/^[a-z]+$/)) {
|
||
|
getPrivateKey(module.address);
|
||
|
hookedModule = module.name
|
||
|
}
|
||
|
|
||
|
if (hookedModule) {
|
||
|
const message = 'Hooked ' + hookedModule + ' at ' + module.address;
|
||
|
send('message_info', new TextEncoder().encode(message));
|
||
|
}
|
||
|
} catch (e) {
|
||
|
console.log("Error: " + e + " at F: " + module.name);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getModuleByName(lib) {
|
||
|
return Process.getModuleByName(lib);
|
||
|
}
|
||
|
|
||
|
function a2bs(bytes) {
|
||
|
let b = '';
|
||
|
for (let i = 0; i < bytes.byteLength; i++)
|
||
|
b += String.fromCharCode(bytes[i]);
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
function getKeyLength(key) {
|
||
|
let pos = 1 // Skip the tag
|
||
|
let buf = key.charCodeAt(pos++);
|
||
|
let len = buf & 0x7F; // Short tag length
|
||
|
|
||
|
buf = 0;
|
||
|
for (let i = 0; i < len; ++i)
|
||
|
buf = (buf * 256) + key.charCodeAt(pos++);
|
||
|
return pos + Math.abs(buf);
|
||
|
}
|
||
|
|
||
|
rpc.exports.hooklibfunctions = hookLibFunctions;
|
||
|
rpc.exports.getmodulebyname = getModuleByName;
|