decryption using js.

This commit is contained in:
aynakeya 2023-12-01 05:45:32 -08:00
parent 2ddcb51aa9
commit 2720364707
6 changed files with 602392 additions and 29 deletions

602282
electron/decrypt.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -18,21 +18,7 @@ export default function initIPC() {
ipcMain.handle('invoke_启动服务', async (event, arg) => {
return startServer({
interceptCallback: phase => async (req, res) => {
if (phase === 'response' && res?._data?.headers?.['content-type'] == 'video/mp4') {
const fixUrl = {}
if(req.fullUrl().includes("video.qq.com")){
fixUrl.fixUrl = req.fullUrl().replace(/\/20302\//g, '/20304/');
fixUrl.hdUrl = fixUrl.fixUrl.replace(/(\?|&)(?!(encfilekey=|token=))[^&]+/g, '');
}
win?.webContents?.send?.('VIDEO_CAPTURE', {
url: req.fullUrl(),
size: res?._data?.headers?.['content-length'] ?? 0,
...fixUrl
});
}
},
win: win,
setProxyErrorCallback: err => {
console.log('开启代理失败', err);
},
@ -49,9 +35,10 @@ export default function initIPC() {
return result?.[0];
});
ipcMain.handle('invoke_下载视频', async (event, { url, savePath }) => {
ipcMain.handle('invoke_下载视频', async (event, { url, decodeKey, savePath }) => {
console.log(url,decodeKey);
return downloadFile(
url,
url,decodeKey,
`${savePath}/${Date.now()}.mp4`,
throttle(value => win?.webContents?.send?.('e_进度变化', value), 1000),
).catch(err => {

View File

@ -11,8 +11,66 @@ if (process.platform === 'win32') {
process.env.OPENSSL_CONF = CONFIG.OPEN_SSL_CNF_PATH;
}
const injection_script =`
(function () {
if (window.wvds !== undefined) {
return
}
let receiver_url = "https://aaaa.com"
function send_response_if_is_video(response) {
if (response == undefined) {
return;
}
if (response["err_msg"] != "H5ExtTransfer:ok") {
return;
}
let value = JSON.parse(response["jsapi_resp"]["resp_json"]);
if (value["object"] == undefined || value["object"]["object_desc"] == undefined || value["object"]["object_desc"]["media"].length == 0) {
return
}
let media = value["object"]["object_desc"]["media"][0]
let video_data = {
"decode_key": media["decode_key"],
"url": media["url"]+media["url_token"],
"size": media["file_size"],
"description": value["object"]["object_desc"]["description"].trim(),
"uploader": value["object"]["nickname"]
}
fetch(receiver_url, {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(video_data),
}).then((resp) => {
console.log(\`video data for \${video_data["description"]} sent\`);
});
}
function wrapper(name,origin) {
console.log(\`injecting \${name}\`);
return function() {
let cmdName = arguments[0];
if (arguments.length == 3) {
let original_callback = arguments[2];
arguments[2] = async function () {
if (arguments.length == 1) {
send_response_if_is_video(arguments[0]);
}
return await original_callback.apply(this, arguments);
}
}
let result = origin.apply(this,arguments);
return result;
}
}
console.log(\`------- Invoke WechatVideoDownloader Service ---------\`);
window.WeixinJSBridge.invoke = wrapper("WeixinJSBridge.invoke",window.WeixinJSBridge.invoke);
window.wvds = true;
})()
`;
export async function startServer({
interceptCallback = f => f => f,
win,
setProxyErrorCallback = f => f,
}) {
const port = await getPort();
@ -40,15 +98,27 @@ export async function startServer({
proxy.intercept(
{
phase: 'request',
hostname: 'aaaa.com',
as: 'json'
},
(req, res) => {
res.string = "ok";
res.statusCode = 200;
win?.webContents?.send?.('VIDEO_CAPTURE', req.json)
},
interceptCallback('request'),
);
proxy.intercept(
{
phase: 'response',
hostname: 'res.wx.qq.com',
as: "string"
},
async (req, res) => {
if (req.url.includes("polyfills.publish")) {
res.string = res.string + "\n" + injection_script;
}
},
interceptCallback('response'),
);
});
}

View File

@ -2,6 +2,8 @@ import { get } from 'axios';
import { app, dialog, shell } from 'electron';
import semver from 'semver';
import fs from 'fs';
import {getDecryptionArray} from './decrypt';
import {Transform } from 'stream';
// packageUrl 需要包含 { "version": "1.0.0" } 结构
function checkUpdate(
@ -28,7 +30,27 @@ function checkUpdate(
.catch(err => {});
}
function downloadFile(url, fullFileName, progressCallback) {
function xorTransform(decryptionArray) {
let processedBytes = 0;
return new Transform({
transform(chunk, encoding, callback) {
if (processedBytes < decryptionArray.length) {
let remaining = Math.min(decryptionArray.length - processedBytes, chunk.length);
for (let i = 0; i < remaining; i++) {
chunk[i] = chunk[i] ^ decryptionArray[processedBytes + i];
}
processedBytes += remaining;
}
this.push(chunk);
callback();
}
});
}
function downloadFile(url,decodeKey, fullFileName, progressCallback) {
const xorStream = xorTransform(getDecryptionArray(decodeKey));
return get(url, {
responseType: 'stream',
headers: {
@ -47,7 +69,7 @@ function downloadFile(url, fullFileName, progressCallback) {
data.on('error', err => reject(err));
data.pipe(
data.pipe(xorStream).pipe(
fs.createWriteStream(fullFileName).on('finish', () => {
resolve({
fullFileName,

View File

@ -61,7 +61,7 @@ function App() {
dataIndex: 'action',
key: 'action',
width: '210px',
render: (_, { url, hdUrl, fixUrl, fullFileName, }) => (
render: (_, { url, decodeKey, hdUrl, fixUrl, fullFileName, }) => (
<div>
{fullFileName ? (
<Button
@ -80,7 +80,7 @@ function App() {
icon={<DownloadOutlined />}
type="primary"
onClick={() => {
send({ type: 'e_下载', url: (hdUrl || url) });
send({ type: 'e_下载', url: (hdUrl || url), decodeKey: decodeKey });
}}
size="small"
>

View File

@ -161,8 +161,8 @@ export default createMachine(
.finally(() => send('e_重新检测'));
},
invoke_启动服务: (context, event) => send => {
const fnDealVideoCapture = (eName, { url, size, ...other }) => {
send({ type: 'e_视频捕获', url, size, ...other });
const fnDealVideoCapture = (eName, { url, size, description, decode_key, ...other }) => {
send({ type: 'e_视频捕获', url, size, description, decodeKey: decode_key, ...other });
};
ipcRenderer
@ -190,11 +190,12 @@ export default createMachine(
.catch(() => send('e_取消'));
},
invoke_下载视频:
({ currentUrl, savePath }) =>
({ currentUrl, savePath, decodeKey }) =>
send => {
ipcRenderer
.invoke('invoke_下载视频', {
url: currentUrl,
decodeKey: decodeKey,
savePath,
})
.then(({ fullFileName }) => {
@ -217,7 +218,7 @@ export default createMachine(
},
},
actions: {
action_视频捕获: actions.assign(({ captureList }, { size, url, ...other }) => {
action_视频捕获: actions.assign(({ captureList }, { url, size, description, decode_key, ...other }) => {
captureList.push({ size, url, prettySize: prettyBytes(+size), ...other });
return {
@ -229,9 +230,10 @@ export default createMachine(
captureList: [],
};
}),
action_设置当前地址: actions.assign((_, { url }) => {
action_设置当前地址: actions.assign((_, { url,decodeKey }) => {
return {
currentUrl: url,
decodeKey: decodeKey,
};
}),
action_存储下载位置: actions.assign((_, { data }) => {