first commit

This commit is contained in:
luochaolun 2024-01-03 16:16:00 +08:00
commit faed055d57
59 changed files with 23348 additions and 0 deletions

32
.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build-web
/build-electron
/build
/dist
/packs
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
package-lock.json
.parcel-cache
.history
test.mjs

0
.prettierignore Normal file
View File

15
.prettierrc Normal file
View File

@ -0,0 +1,15 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"proseWrap": "never",
"arrowParens": "avoid",
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
}
]
}

66
README.md Normal file
View File

@ -0,0 +1,66 @@
# 微信视频号下载器
> 🔥🔥🔥 V2.x版本支持加密视频的下载可到 Release 中下载更新。
<img src="https://user-images.githubusercontent.com/11046969/169296046-513b5e3a-a688-4342-9759-eb131ef7e42f.png" width="100" />
- 支持实时捕获视频号的视频地址
- 捕获后,可进行预览和下载
- 支持 Win/Mac
![image](https://github.com/lecepin/WeChatVideoDownloader/assets/11046969/b5fa47fd-5ca2-4b4e-a036-432562ca0ec6)
> 内部采用代理拦截请求识别,所以本软件需要安装证书及自动开启代理(当然这些都是自动执行的,无需手动操作)。关闭此软件时会自动清除代理信息,不影响使用。
### 下载
请到 Release 中进行下载https://github.com/lecepin/WeChatVideoDownloader/releases
---
### 效果
1. 运行本软件
2. 打开视频号的视频进行播放,如下图视频:
![image](https://user-images.githubusercontent.com/11046969/169697581-6851f4d1-376b-42c5-825b-8e8101261027.png)
3. 本软件会自动捕获到该视频,可以点击 “预览” 按钮来确认是否拦截正确
![image](https://user-images.githubusercontent.com/11046969/169732758-90685ad1-6092-4c27-bfc5-ff13588f4927.png)
4. 点击 “下载” 按钮进行下载
![image](https://user-images.githubusercontent.com/11046969/169732854-371c5962-b9a1-47da-90a4-d50473e08509.png)
![image](https://user-images.githubusercontent.com/11046969/169698058-5d9f213e-91c7-4f77-872e-db3d983d1a97.png)
---
### 使用
1. 首次打开需要进行初始化,此过程会进行证书安装:
![image](https://user-images.githubusercontent.com/11046969/169732890-9d7af116-d9f3-47cc-a2d7-091b78930c94.png)
2. 点击 “是”,安装后,就可以正常使用了:
![image](https://user-images.githubusercontent.com/11046969/169732926-5c8cfce4-4856-48e2-a268-22e1e5278c2d.png)
#### Mac 系统处理
由于新 Mac OS 不支持非交互式执行 sudo 命令,所以本软件初始化时会自动将命令复制到剪切板,你只需要打开 “终端”,粘贴一下就可以了,然后回车执行,效果如下图所示:
![image](https://user-images.githubusercontent.com/11046969/169732943-4815fa79-dda4-4bfd-904c-70d8e625d8f6.png)

17
config-overrides.js Normal file
View File

@ -0,0 +1,17 @@
const { override, addLessLoader, adjustStyleLoaders } = require('customize-cra');
module.exports = override(
addLessLoader({
lessOptions: {
javascriptEnabled: true,
},
}),
adjustStyleLoaders(({ use: [, , postcss] }) => {
const postcssOptions = postcss.options;
postcss.options = { postcssOptions };
}),
function (config) {
config.target = 'electron-renderer';
return config;
},
);

49
electron/cert.js Normal file
View File

@ -0,0 +1,49 @@
import CONFIG from './const';
import mkdirp from 'mkdirp';
import fs from 'fs';
import path from 'path';
import { clipboard, dialog } from 'electron';
import spawn from 'cross-spawn';
export function checkCertInstalled() {
return fs.existsSync(CONFIG.INSTALL_CERT_FLAG);
}
export async function installCert(checkInstalled = true) {
if (checkInstalled && checkCertInstalled()) {
return;
}
mkdirp.sync(path.dirname(CONFIG.INSTALL_CERT_FLAG));
if (process.platform === 'darwin') {
return new Promise((resolve, reject) => {
clipboard.writeText(
`echo "输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${CONFIG.CERT_PUBLIC_PATH}" && touch ${CONFIG.INSTALL_CERT_FLAG} && echo "安装完成"`,
);
dialog.showMessageBoxSync({
type: 'info',
message: `命令已复制到剪贴板,粘贴命令到终端并运行以安装并信任证书`,
});
reject();
});
} else {
return new Promise((resolve, reject) => {
const result = spawn.sync(CONFIG.WIN_CERT_INSTALL_HELPER, [
'-c',
'-add',
CONFIG.CERT_PUBLIC_PATH,
'-s',
'root',
]);
if (result.stdout.toString().indexOf('Succeeded') > -1) {
fs.writeFileSync(CONFIG.INSTALL_CERT_FLAG, '');
resolve();
} else {
reject();
}
});
}
}

37
electron/const.js Normal file
View File

@ -0,0 +1,37 @@
import path from 'path';
import isDev from 'electron-is-dev';
import url from 'url';
import os from 'os';
import { app } from 'electron';
const APP_PATH = app.getAppPath();
// 对于一些 shell 去执行的文件asar 目录下无法使用。配合 extraResources
const EXECUTABLE_PATH = path.join(
APP_PATH.indexOf('app.asar') > -1
? APP_PATH.substring(0, APP_PATH.indexOf('app.asar'))
: APP_PATH,
'public',
);
const HOME_PATH = path.join(os.homedir(), '.wechat-video-downloader');
export default {
APP_START_URL: isDev
? 'http://localhost:3000'
: url.format({
pathname: path.join(APP_PATH, './build/index.html'),
protocol: 'file:',
slashes: true,
}),
IS_DEV: isDev,
EXECUTABLE_PATH,
HOME_PATH,
CERT_PRIVATE_PATH: path.join(EXECUTABLE_PATH, './keys/private.pem'),
CERT_PUBLIC_PATH: path.join(EXECUTABLE_PATH, './keys/public.pem'),
INSTALL_CERT_FLAG: path.join(HOME_PATH, './installed.lock'),
WIN_CERT_INSTALL_HELPER: path.join(EXECUTABLE_PATH, './w_c.exe'),
APP_CN_NAME: '微信视频号下载器',
APP_EN_NAME: 'WeChat Video Downloader',
REGEDIT_VBS_PATH: path.join(EXECUTABLE_PATH, './regedit-vbs'),
OPEN_SSL_BIN_PATH: path.join(EXECUTABLE_PATH, './openssl/openssl.exe'),
OPEN_SSL_CNF_PATH: path.join(EXECUTABLE_PATH, './openssl/openssl.cnf'),
};

4745
electron/decrypt.js Normal file

File diff suppressed because one or more lines are too long

49
electron/index.js Normal file
View File

@ -0,0 +1,49 @@
import { app, BrowserWindow, Menu } from 'electron';
import CONFIG from './const';
import { checkUpdate } from './utils';
import initIPC, { setWin } from './ipc';
app.commandLine.appendSwitch('--no-proxy-server');
process.on('uncaughtException', () => {});
process.on('unhandledRejection', () => {});
function createWindow() {
Menu.setApplicationMenu(null);
checkUpdate(
'https://cdn.jsdelivr.net/gh/lecepin/WeChatVideoDownloader/package.json',
'https://github.com/lecepin/WeChatVideoDownloader/releases',
);
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
// resizable: false,
// maximizable: false,
webPreferences: {
webSecurity: false,
nodeIntegration: true,
contextIsolation: false,
},
});
setWin(mainWindow);
mainWindow.loadURL(CONFIG.APP_START_URL);
CONFIG.IS_DEV && mainWindow.webContents.openDevTools();
}
app.whenReady().then(() => {
initIPC();
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

81
electron/ipc.js Normal file
View File

@ -0,0 +1,81 @@
import { ipcMain, dialog, clipboard } from 'electron';
import log from 'electron-log';
import { throttle } from 'lodash';
import { startServer } from './proxyServer';
import { installCert, checkCertInstalled } from './cert';
import { downloadFile, decodeFile } from './utils';
let win;
export default function initIPC() {
ipcMain.handle('invoke_初始化信息', async (event, arg) => {
return checkCertInstalled();
});
ipcMain.handle('invoke_开始初始化', (event, arg) => {
return installCert(false);
});
ipcMain.handle('invoke_启动服务', async (event, arg) => {
return startServer({
win: win,
setProxyErrorCallback: err => {
console.log('开启代理失败', err);
},
});
});
ipcMain.handle('invoke_选择下载位置', async (event, arg) => {
const result = dialog.showOpenDialogSync({ title: '保存', properties: ['openDirectory'] });
if (!result?.[0]) {
throw '取消';
}
return result?.[0];
});
ipcMain.handle('invoke_下载视频', async (event, { url, decodeKey, savePath, description }) => {
let fileName = description?.replaceAll?.(/\\|\/|:|\*|\?|"|<|>|\|/g, '') || Date.now();
//console.log('description:', description);
//console.log('fileName:', fileName);
//console.log('url:', url);
//console.log('decodeKey:', decodeKey);
return downloadFile(
url,
decodeKey,
`${savePath}/${fileName}.mp4`,
throttle(value => win?.webContents?.send?.('e_进度变化', value), 1000),
).catch(err => {
console;
});
});
ipcMain.handle('invoke_复制', async (event, { url, decodeKey, description }) => {
//const strInfo = `复制\nurl:${url}\ndecodeKey:${decodeKey}\ndescription${description}\n`;
//console.log(strInfo);
clipboard.writeText(url);
});
ipcMain.handle('invoke_选择文件位置', async (event, arg) => {
const result = dialog.showOpenDialogSync();
if (!result?.[0]) {
throw '取消';
}
return result?.[0];
});
ipcMain.handle('invoke_解密视频', async (event, { url, decodeKey, jieMiFilePath, description }) => {
return decodeFile(decodeKey, jieMiFilePath).catch(err => {
console;
});
});
}
export function setWin(w) {
win = w;
}

216
electron/proxyServer.js Normal file
View File

@ -0,0 +1,216 @@
import fs from 'fs';
import hoxy from 'hoxy';
import getPort from 'get-port';
import log from 'electron-log';
import { app } from 'electron';
import CONFIG from './const';
import { setProxy, closeProxy } from './setProxy';
if (process.platform === 'win32') {
process.env.OPENSSL_BIN = CONFIG.OPEN_SSL_BIN_PATH;
process.env.OPENSSL_CONF = CONFIG.OPEN_SSL_CNF_PATH;
}
const WVDS_DEBUG = process.env.WVDS_DEBUG !== undefined;
const injection_html = `
<script type="text/javascript" src="//res.wx.qq.com/t/wx_fed/finder/web/web-finder/res/js/wvds.inject.js"></script>
`;
// setTimeout to allow working in macOS
// in windows: H5ExtTransfer:ok
// in macOS: finderH5ExtTransfer:ok
const injection_script = `
setTimeout(() => {
if (window.wvds !== undefined) return;
${
WVDS_DEBUG
? `
document.body.style.border = "2px solid #0000FF";
let ele_app = document.getElementById("app");
let ele_btn1 = document.createElement("a");
let ele_btn2 = document.createElement("a");
let ele_debug = document.createElement("textarea");
`
: ''
}
function debug_wvds(msg) {
${WVDS_DEBUG ? `ele_debug.value += "\\n" + msg;` : ''}
}
${
WVDS_DEBUG
? `
ele_btn1.style = "position:absolute;top:3px;left:20px;width:80px;height:30px;cursor:pointer;";
ele_btn1.text = "Source";
ele_btn1.onclick = () => {
var source = "<html>";
source += document.getElementsByTagName('html')[0].innerHTML;
source += "</html>";
debug_wvds(source);
};
ele_app.appendChild(ele_btn1);
ele_btn2.style = "position:absolute;top:3px;left:120px;width:80px;height:30px;cursor:pointer;";
ele_btn2.text = "Test";
ele_btn2.onclick = () => {
debug_wvds("Hello WeChatVideo Downloader!");
};
ele_app.appendChild(ele_btn2);
ele_debug.setAttribute("rows", "60");
ele_debug.setAttribute("cols", "60");
ele_debug.style = "position:absolute;top:600px;left:20px;width:600px;height:300px;border:2px solid #FF00FF;";
ele_debug.value = "Debug:\\n";
ele_app.appendChild(ele_debug);
`
: ''
}
let receiver_url = "https://aaaa.com";
function send_response_if_is_video(response) {
if (response == undefined) return;
// debug_wvds(JSON.stringify(response));
debug_wvds("send 1: " + response["err_msg"]);
if (!response["err_msg"].includes("H5ExtTransfer:ok")) return;
let value = JSON.parse(response["jsapi_resp"]["resp_json"]);
// debug_wvds("send 2: " + JSON.stringify(value));
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];
// debug_wvds("send 3: " + JSON.stringify(media));
let description = value["object"]["object_desc"]["description"].trim();
debug_wvds("send x decode key: " + media["decode_key"] + " for " + description);
let video_data = {
"decode_key": media["decode_key"],
"url": media["url"]+media["url_token"],
"size": media["file_size"],
"description": description,
"uploader": value["object"]["nickname"]
};
fetch(receiver_url, {
method: "POST",
mode: "no-cors",
body: JSON.stringify(video_data),
}).then((resp) => {
debug_wvds(\`video data for \${video_data["description"]} sent!\`);
});
}
function wrapper(name,origin) {
return function() {
let cmdName = arguments[0];
if (arguments.length == 3) {
let original_callback = arguments[2];
arguments[2] = async function () {
if (arguments.length == 1) {
debug_wvds("wrapper 3: " + JSON.stringify(arguments[0]));
send_response_if_is_video(arguments[0]);
}
return await original_callback.apply(this, arguments);
}
debug_wvds("wrapper 1: " + cmdName + ", " + typeof(arguments[1]) + ", " + typeof(arguments[2]));
} else {
debug_wvds("wrapper 2: " + cmdName + ", " + arguments.length + ", " + arguments[1] + ", " + typeof(arguments[2]));
}
let result = origin.apply(this,arguments);
return result;
}
}
window.WeixinJSBridge.invoke = wrapper("WeixinJSBridge.invoke", window.WeixinJSBridge.invoke);
window.wvds = true;
debug_wvds("Invoke WechatVideoDownloader Service!");
}, 1000);`;
export async function startServer({ win, setProxyErrorCallback = f => f }) {
const port = await getPort();
return new Promise(async (resolve, reject) => {
const proxy = hoxy
.createServer({
certAuthority: {
key: fs.readFileSync(CONFIG.CERT_PRIVATE_PATH),
cert: fs.readFileSync(CONFIG.CERT_PUBLIC_PATH),
},
})
.listen(port, () => {
setProxy('127.0.0.1', port)
.then(() => resolve())
.catch(() => {
setProxyErrorCallback(data);
reject('设置代理失败');
});
})
.on('error', err => {
log.log('proxy err', err);
});
proxy.intercept(
{
phase: 'request',
hostname: 'aaaa.com',
as: 'json',
},
(req, res) => {
console.log('request(aaaa.com):', req.json);
res.string = 'ok';
res.statusCode = 200;
win?.webContents?.send?.('VIDEO_CAPTURE', req.json);
},
);
proxy.intercept(
{
phase: 'response',
hostname: 'channels.weixin.qq.com',
as: 'string',
},
async (req, res) => {
if (req.url.includes('/web/pages/feed')) {
res.string = res.string.replace('</body>', injection_html + '\n</body>');
res.statusCode = 200;
console.log('inject[channels.weixin.qq.com]:', req.url, res.string.length);
}
},
);
proxy.intercept(
{
phase: 'response',
hostname: 'res.wx.qq.com',
as: 'string',
},
async (req, res) => {
if (req.url.includes('wvds.inject.js')) {
console.log('inject[res.wx.qq.com]:', req.url, res.string.length);
res.string = injection_script;
res.statusCode = 200;
}
},
);
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;
}
},
);
});
}
app.on('before-quit', async e => {
e.preventDefault();
try {
await closeProxy();
console.log('close proxy success');
} catch (error) {}
app.exit();
});

109
electron/setProxy.js Normal file
View File

@ -0,0 +1,109 @@
import { exec } from 'child_process';
import regedit from 'regedit';
import CONFIG from './const';
regedit.setExternalVBSLocation(CONFIG.REGEDIT_VBS_PATH);
export async function setProxy(host, port) {
if (process.platform === 'darwin') {
const networks = await getMacAvailableNetworks();
if (networks.length === 0) {
throw 'no network';
}
return Promise.all(
networks.map(network => {
return new Promise((resolve, reject) => {
exec(`networksetup -setsecurewebproxy "${network}" ${host} ${port}`, error => {
if (error) {
reject(null);
} else {
resolve(network);
}
});
});
}),
);
} else {
const valuesToPut = {
'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings': {
ProxyServer: {
value: `${host}:${port}`,
type: 'REG_SZ',
},
ProxyEnable: {
value: 1,
type: 'REG_DWORD',
},
},
};
return regedit.promisified.putValue(valuesToPut);
}
}
export async function closeProxy() {
if (process.platform === 'darwin') {
const networks = await getMacAvailableNetworks();
if (networks.length === 0) {
throw 'no network';
}
return Promise.all(
networks.map(network => {
return new Promise((resolve, reject) => {
exec(`networksetup -setsecurewebproxystate "${network}" off`, error => {
if (error) {
reject(null);
} else {
resolve(network);
}
});
});
}),
);
} else {
const valuesToPut = {
'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings': {
ProxyEnable: {
value: 0,
type: 'REG_DWORD',
},
},
};
return regedit.promisified.putValue(valuesToPut);
}
}
function getMacAvailableNetworks() {
return new Promise((resolve, reject) => {
exec('networksetup -listallnetworkservices', (error, stdout) => {
if (error) {
reject(error);
} else {
Promise.all(
stdout
.toString()
.split('\n')
.map(network => {
return new Promise(resolve => {
exec(
`networksetup getinfo "${network}" | grep "^IP address:\\s\\d"`,
(error, stdout) => {
if (error) {
resolve(null);
} else {
resolve(stdout ? network : null);
}
},
);
});
}),
).then(networks => {
resolve(networks.filter(Boolean));
});
}
});
});
}

105
electron/utils.js Normal file
View File

@ -0,0 +1,105 @@
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';
import path from 'path';
// packageUrl 需要包含 { "version": "1.0.0" } 结构
function checkUpdate(
// 可以使用加速地址 https://cdn.jsdelivr.net/gh/lecepin/electron-react-tpl/package.json
packageUrl = 'https://raw.githubusercontent.com/lecepin/electron-react-tpl/master/package.json',
downloadUrl = 'https://github.com/lecepin/electron-react-tpl/releases',
) {
get(packageUrl)
.then(({ data }) => {
if (semver.gt(data?.version, app.getVersion())) {
const result = dialog.showMessageBoxSync({
message: '发现新版本,是否更新?',
type: 'question',
cancelId: 1,
defaultId: 0,
buttons: ['进入新版本下载页面', '取消'],
});
if (result === 0 && downloadUrl) {
shell.openExternal(downloadUrl);
}
}
})
.catch(err => {});
}
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: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
},
}).then(({ data, headers }) => {
let currentLen = 0;
const totalLen = headers['content-length'];
return new Promise((resolve, reject) => {
data.on('data', ({ length }) => {
currentLen += length;
progressCallback?.(currentLen / totalLen);
});
data.on('error', err => reject(err));
data.pipe(xorStream).pipe(
fs.createWriteStream(fullFileName).on('finish', () => {
resolve({
fullFileName,
totalLen,
});
}),
);
});
});
}
function decodeFile(decodeKey, jieMiFilePath) {
const xorStream = xorTransform(getDecryptionArray(decodeKey));
const dirName = path.dirname(jieMiFilePath);
const fileName = "jiemi_" + path.basename(jieMiFilePath);
const fullFileName = path.join(dirName, fileName);
const data = fs.createReadStream(jieMiFilePath);
const totalLen = 0;
return new Promise((resolve, reject) => {
data.on('error', err => reject(err));
data.pipe(xorStream).pipe(
fs.createWriteStream(fullFileName).on('finish', () => {
resolve({
fullFileName,
totalLen,
});
}),
);
});
}
export { checkUpdate, downloadFile, decodeFile };

131
package.json Normal file
View File

@ -0,0 +1,131 @@
{
"name": "wechat-video-downloader",
"version": "2.1.0",
"description": "",
"main": "./build-electron/index.js",
"homepage": "./",
"scripts": {
"postinstall": "husky install",
"start": "concurrently \"cross-env BROWSER=none npm run start-web\" \"wait-on http://localhost:3000 && npm run start-electron\" ",
"start-debug": "concurrently \"cross-env BROWSER=none npm run start-web\" \"wait-on http://localhost:3000 && npm run debug-electron\" ",
"start-web": "react-app-rewired start",
"start-electron": "webpack --config webpack.electron.js && electron .",
"debug-electron": "webpack --config webpack.electron.js && electron --inspect --unhandled-rejections=strict --trace-deprecation .",
"build-web": "react-app-rewired build",
"build-electron": "webpack --config webpack.electron.js",
"build-all": "rm -rf ./build && rm -rf ./build-electron && npm run build-electron && npm run build-web",
"pack": "npm run build-all && electron-builder",
"mypack": "npm run build-electron && npm run build-web && electron-builder",
"gen-icon": "electron-icon-builder --input=./public/icon/icon.png --output=./public/icon",
"pretty": "prettier -c --write \"(src/**/*|electron/**/*)\""
},
"targets": {
"electron": {
"source": "electron/index.js",
"context": "electron-main",
"distDir": "build-electron"
}
},
"husky": {
"hooks": {
"pre-commit": "prettier -c --write \"(src/**/*|electron/**/*)\" && git add -A ."
}
},
"devDependencies": {
"concurrently": "^7.1.0",
"cross-env": "^7.0.3",
"customize-cra": "^1.0.0",
"electron": "^26.1.0",
"electron-builder": "^23.0.3",
"electron-icon-builder": "^2.0.1",
"husky": "^8.0.1",
"less": "^4.1.2",
"less-loader": "^11.0.0",
"prettier": "^2.6.2",
"react-app-rewired": "^2.2.1",
"react-scripts": "5.0.1",
"wait-on": "^6.0.1",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@xstate/react": "^3.0.0",
"antd": "^4.20.5",
"axios": "^0.27.2",
"cross-spawn": "^7.0.3",
"electron-is-dev": "^2.0.0",
"electron-log": "^4.4.7",
"get-port": "^6.1.2",
"hoxy": "^3.3.1",
"lodash": "^4.17.21",
"mkdirp": "^1.0.4",
"pretty-bytes": "^6.0.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"regedit": "5.0.0",
"semver": "^7.3.7",
"xstate": "^4.32.1"
},
"author": "lecepin",
"license": "ISC",
"repository": {
"type": "git",
"url": "git+https://github.com/lecepin/WeChatVideoDownloader.git"
},
"build": {
"extends": null,
"productName": "WeChatVideoDownloader",
"appId": "com.lecepin.WeChatVideoDownloader",
"directories": {
"output": "packs"
},
"npmRebuild": false,
"files": [
"build/**/*",
"build-electron/**/*",
"public/**/*"
],
"mac": {
"icon": "public/icon/icons/mac/icon.icns",
"target": {
"target": "default",
"arch": [
"universal"
]
}
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"ia32"
]
}
],
"icon": "public/icon/icons/win/icon.ico"
},
"nsis": {
"oneClick": false,
"perMachine": false,
"allowElevation": true,
"allowToChangeInstallationDirectory": true
},
"extraResources": [
"public"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

15141
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

BIN
public/icon/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

12
public/index.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>微信视频号下载器</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

27
public/keys/private.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsmAqn3hYd/YZcrfgqM1Q6xgHI50EBckbOkfCqTWS1yVFZjLF
bMehWb9xGFZJD21A5sxl4xelIWblhety+YTVa/mn2CEJh3je069oeULfXdzhhHyf
/ci0IloJhvX+2RJ+176uTKKcWhuOtNVs5VeFoHDoUcISnTqkaVyWeeLfafgrOW7w
N8ip128nuBx19ylIygb/DELmjKRRCSpx2vOw2JErTM8L5r0f4eWdqiwBOwu0NHWy
Svh9YG8B31UPga4I8FbFhybOP9cQNQPafOSfjwuZoi5CAtyJbwT7KyII9iMD74bZ
1mTx2xokmQ2TeiCSKSF8Mx9/8Gq+95mzvvIbRwIDAQABAoIBAHNt++caj9WBclJk
X4Oc6eJYuDX5o+LCk1YRngy12IJVYiWScWPFg8p6MouXOsw63Sb92mksofWNirYw
+UQzC5FGC7G3H12FgFzoQ+lEtxscluuPYlFukfMw5L1rbzG14FNo145MJHXDI4Qu
ILwA+T4sEorl1fndOwvbmJzjjcQaeRNz7/R9e6QTOlZ2+IEMKnHSBXXGJbDj6mPN
+f1/ec6nVENdxazgRCi0xfinyft4Ipst93Eb+wGcpk+J43aF+0leWQCdl6Y9U1Lz
zpv5H5XOQdwpX+dpuioRp73zwPwIialq+hTUN28Bn9U1jW2tjxUl/vgIpjy1s94a
UipRwSECgYEA57vYB+wGnxQxY9IPpr9H/y3HciIwCnuOEsWBzjYe8sIqBif2tEpO
OgDZZMQY7+JJrDQbDRs442TuRjKhJ5hiW+MyoiFWaYkBBoNVM8RBTkIjHfrh+uB2
XT15FbEyyxo3n9QY610ZJFRnW4Uf5V0osjOqqUgQRrVXvamk6NQH6FkCgYEAxQ3v
jFYPL3EkZe1br6X0RM42ykGv5Di5Q6NnjpSPcyn9a2obA1cZuCd5S1lhrkuZGsdI
iFapeL+7vpts9gu9/ii9y+CgEKplOMmm0ZrChBKAcXMZvdDKV3y5SmTMZPas4X5i
hqNqatx9/J93sMYWc0CuoosDEJYKtSz8GE+1rJ8CgYAmp5rdl21zU7b5Y6zgr7+e
vVArpbBFz15fmzqP309CR0kjRb9NS6fI3SNmP5+5RBHt+7MXeJcAt3FXnFJtfGnL
0hY8HTuA1y2onHe17uLF3xpkgdj4NEEKRJrSF4DViEYHDyYo/JqZCMtE5OvxIp0L
PLsXCcJNSSqdpJKxk8zN4QKBgEuoxSAh7uStUWddUkXHt1kvwDO6MtmyuddxhxJk
kguKxMWYUNTgfXyKk3TN1caBOkDg4UWP2LQHEgPmU1jJO2K5q9362hpsAj9ilY2H
GUZygCSPKAQMhZQ/zDj3KM9fMxPFXfkKB5MOI8V6SQ9zjy0jWaoJK90TbvsPUZ/Y
Aw5LAoGADifZwCHPiXhTfJjOom2uBgXmL03yTXcCw4EDIX3ZR0sP6ACPQq4T4jxZ
UJLXLjOb2pzCq0c5+k0cG6ahYINq4tGOo+vQ9fDvhKg0nlf1FrzxSd7S12o+un2q
+U+dBllYIDlRMgMhXu9CxFDjUsCwPRmsBvmVZiH4XSs6QVnfn90=
-----END RSA PRIVATE KEY-----

27
public/keys/private.pem Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsmAqn3hYd/YZcrfgqM1Q6xgHI50EBckbOkfCqTWS1yVFZjLF
bMehWb9xGFZJD21A5sxl4xelIWblhety+YTVa/mn2CEJh3je069oeULfXdzhhHyf
/ci0IloJhvX+2RJ+176uTKKcWhuOtNVs5VeFoHDoUcISnTqkaVyWeeLfafgrOW7w
N8ip128nuBx19ylIygb/DELmjKRRCSpx2vOw2JErTM8L5r0f4eWdqiwBOwu0NHWy
Svh9YG8B31UPga4I8FbFhybOP9cQNQPafOSfjwuZoi5CAtyJbwT7KyII9iMD74bZ
1mTx2xokmQ2TeiCSKSF8Mx9/8Gq+95mzvvIbRwIDAQABAoIBAHNt++caj9WBclJk
X4Oc6eJYuDX5o+LCk1YRngy12IJVYiWScWPFg8p6MouXOsw63Sb92mksofWNirYw
+UQzC5FGC7G3H12FgFzoQ+lEtxscluuPYlFukfMw5L1rbzG14FNo145MJHXDI4Qu
ILwA+T4sEorl1fndOwvbmJzjjcQaeRNz7/R9e6QTOlZ2+IEMKnHSBXXGJbDj6mPN
+f1/ec6nVENdxazgRCi0xfinyft4Ipst93Eb+wGcpk+J43aF+0leWQCdl6Y9U1Lz
zpv5H5XOQdwpX+dpuioRp73zwPwIialq+hTUN28Bn9U1jW2tjxUl/vgIpjy1s94a
UipRwSECgYEA57vYB+wGnxQxY9IPpr9H/y3HciIwCnuOEsWBzjYe8sIqBif2tEpO
OgDZZMQY7+JJrDQbDRs442TuRjKhJ5hiW+MyoiFWaYkBBoNVM8RBTkIjHfrh+uB2
XT15FbEyyxo3n9QY610ZJFRnW4Uf5V0osjOqqUgQRrVXvamk6NQH6FkCgYEAxQ3v
jFYPL3EkZe1br6X0RM42ykGv5Di5Q6NnjpSPcyn9a2obA1cZuCd5S1lhrkuZGsdI
iFapeL+7vpts9gu9/ii9y+CgEKplOMmm0ZrChBKAcXMZvdDKV3y5SmTMZPas4X5i
hqNqatx9/J93sMYWc0CuoosDEJYKtSz8GE+1rJ8CgYAmp5rdl21zU7b5Y6zgr7+e
vVArpbBFz15fmzqP309CR0kjRb9NS6fI3SNmP5+5RBHt+7MXeJcAt3FXnFJtfGnL
0hY8HTuA1y2onHe17uLF3xpkgdj4NEEKRJrSF4DViEYHDyYo/JqZCMtE5OvxIp0L
PLsXCcJNSSqdpJKxk8zN4QKBgEuoxSAh7uStUWddUkXHt1kvwDO6MtmyuddxhxJk
kguKxMWYUNTgfXyKk3TN1caBOkDg4UWP2LQHEgPmU1jJO2K5q9362hpsAj9ilY2H
GUZygCSPKAQMhZQ/zDj3KM9fMxPFXfkKB5MOI8V6SQ9zjy0jWaoJK90TbvsPUZ/Y
Aw5LAoGADifZwCHPiXhTfJjOom2uBgXmL03yTXcCw4EDIX3ZR0sP6ACPQq4T4jxZ
UJLXLjOb2pzCq0c5+k0cG6ahYINq4tGOo+vQ9fDvhKg0nlf1FrzxSd7S12o+un2q
+U+dBllYIDlRMgMhXu9CxFDjUsCwPRmsBvmVZiH4XSs6QVnfn90=
-----END RSA PRIVATE KEY-----

17
public/keys/public.crt Normal file
View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICuDCCAaACCQC7PQmrxgWOlTANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJs
ZWNlcGluLTIwMjItMDUtMTkwIBcNMjIwNTE5MTI1NjA0WhgPMzAyMTA5MTkxMjU2
MDRaMB0xGzAZBgNVBAMMEmxlY2VwaW4tMjAyMi0wNS0xOTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALJgKp94WHf2GXK34KjNUOsYByOdBAXJGzpHwqk1
ktclRWYyxWzHoVm/cRhWSQ9tQObMZeMXpSFm5YXrcvmE1Wv5p9ghCYd43tOvaHlC
313c4YR8n/3ItCJaCYb1/tkSfte+rkyinFobjrTVbOVXhaBw6FHCEp06pGlclnni
32n4Kzlu8DfIqddvJ7gcdfcpSMoG/wxC5oykUQkqcdrzsNiRK0zPC+a9H+Hlnaos
ATsLtDR1skr4fWBvAd9VD4GuCPBWxYcmzj/XEDUD2nzkn48LmaIuQgLciW8E+ysi
CPYjA++G2dZk8dsaJJkNk3ogkikhfDMff/BqvveZs77yG0cCAwEAATANBgkqhkiG
9w0BAQsFAAOCAQEADymHk+wLJAdv3p+4hHo57VLaBtwVYXc5oRUbUzgMYTTtPWIs
xuILEqXftMspt6PzdEt0V1WeCWNyypsAbur/CKpAOoVjBDPIo09TiYnYIn9xt5wQ
AmR5kVEZheuazcvzW3C9NAY1T6QDmxNvFCiCXRbtklOg2HqFDZX+pkj8CylQ9TDk
rroUg17b/FD1ds1uyPXzucEWfxqkOaujvsCnzrbFs9luB5VfM+QzLU+l9QRN9Tmj
z7CpGuP6vKvhXJLUjXkZ0q5JyL5wEAe6Ttbu+c/8HhPFKQsW6q/lQSDo0v0LGDrd
ikjWXhSrVjd8+qTTVgia/UNqv/wi+bkWnVdRzQ==
-----END CERTIFICATE-----

17
public/keys/public.pem Normal file
View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICuDCCAaACCQC7PQmrxgWOlTANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJs
ZWNlcGluLTIwMjItMDUtMTkwIBcNMjIwNTE5MTI1NjA0WhgPMzAyMTA5MTkxMjU2
MDRaMB0xGzAZBgNVBAMMEmxlY2VwaW4tMjAyMi0wNS0xOTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALJgKp94WHf2GXK34KjNUOsYByOdBAXJGzpHwqk1
ktclRWYyxWzHoVm/cRhWSQ9tQObMZeMXpSFm5YXrcvmE1Wv5p9ghCYd43tOvaHlC
313c4YR8n/3ItCJaCYb1/tkSfte+rkyinFobjrTVbOVXhaBw6FHCEp06pGlclnni
32n4Kzlu8DfIqddvJ7gcdfcpSMoG/wxC5oykUQkqcdrzsNiRK0zPC+a9H+Hlnaos
ATsLtDR1skr4fWBvAd9VD4GuCPBWxYcmzj/XEDUD2nzkn48LmaIuQgLciW8E+ysi
CPYjA++G2dZk8dsaJJkNk3ogkikhfDMff/BqvveZs77yG0cCAwEAATANBgkqhkiG
9w0BAQsFAAOCAQEADymHk+wLJAdv3p+4hHo57VLaBtwVYXc5oRUbUzgMYTTtPWIs
xuILEqXftMspt6PzdEt0V1WeCWNyypsAbur/CKpAOoVjBDPIo09TiYnYIn9xt5wQ
AmR5kVEZheuazcvzW3C9NAY1T6QDmxNvFCiCXRbtklOg2HqFDZX+pkj8CylQ9TDk
rroUg17b/FD1ds1uyPXzucEWfxqkOaujvsCnzrbFs9luB5VfM+QzLU+l9QRN9Tmj
z7CpGuP6vKvhXJLUjXkZ0q5JyL5wEAe6Ttbu+c/8HhPFKQsW6q/lQSDo0v0LGDrd
ikjWXhSrVjd8+qTTVgia/UNqv/wi+bkWnVdRzQ==
-----END CERTIFICATE-----

BIN
public/openssl/HashInfo.txt Normal file

Binary file not shown.

View File

@ -0,0 +1,126 @@
LICENSE ISSUES
==============
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
the OpenSSL License and the original SSLeay license apply to the toolkit.
See below for the actual license texts. Actually both licenses are BSD-style
Open Source licenses. In case of any license issues related to OpenSSL
please contact openssl-core@openssl.org.
OpenSSL License
---------------
/* ====================================================================
* Copyright (c) 1998-2016 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
Original SSLeay License
-----------------------
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* "This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)"
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/

59
public/openssl/ReadMe.txt Normal file
View File

@ -0,0 +1,59 @@
=============================================================================
OpenSSL v1.0.2q Precompiled Binaries for Win32
-----------------------------------------------------------------------------
*** Release Information ***
Release Date: Nov 22, 2018
Author: Frederik A. Winkelsdorf (opendec.wordpress.com)
for the Indy Project (www.indyproject.org)
Requirements: Indy 10.5.5+ (SVN Version or Delphi 2009 and newer)
Dependencies: The libraries have no noteworthy dependencies
Installation: Copy both DLL files into your application directory
Supported OS: Windows 2000 up to Windows 10
-----------------------------------------------------------------------------
*** Legal Disclaimer ***
THIS SOFTWARE IS PROVIDED BY ITS AUTHOR AND THE INDY PROJECT "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
OpenSSL license terms are provided in the file "OpenSSL License.txt".
PLEASE CHECK IF YOU NEED TO COMPLY WITH EXPORT RESTRICTIONS FOR CRYPTOGRAPHIC
SOFTWARE AND/OR PATENTS.
-----------------------------------------------------------------------------
*** Build Information Win32 ***
Built with: Microsoft Visual C++ 2008 Express Edition
The Netwide Assembler (NASM) v2.11.08 Win32
Strawberry Perl v5.22.0.1 Win32 Portable
Windows PowerShell
FinalBuilder 7
Commands: perl configure VC-WIN32
ms\do_nasm
adjusted ms\ntdll.mak (replaced "/MD" with "/MT")
adjusted ms\version32.rc (Indy Information inserted)
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak test
editbin.exe /rebase:base=0x11000000 libeay32.dll
editbin.exe /rebase:base=0x12000000 ssleay32.dll
=============================================================================

BIN
public/openssl/libeay32.dll Normal file

Binary file not shown.

350
public/openssl/openssl.cnf Normal file
View File

@ -0,0 +1,350 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# Note that you can include other files from the main configuration
# file using the .include directive.
#.include filename
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
# Policies used by the TSA examples.
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several certs with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem# The private key
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = default # use public key default MD
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 2048
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
# utf8only: only UTF8Strings (PKIX recommendation after 2004).
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
string_mask = utf8only
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = AU
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Internet Widgits Pty Ltd
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
#organizationalUnitName_default =
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This is required for TSA certificates.
# extendedKeyUsage = critical,timeStamping
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
####################################################################
[ tsa ]
default_tsa = tsa_config1 # the default TSA section
[ tsa_config1 ]
# These are used by the TSA reply generation only.
dir = ./demoCA # TSA root directory
serial = $dir/tsaserial # The current serial number (mandatory)
crypto_device = builtin # OpenSSL engine to use for signing
signer_cert = $dir/tsacert.pem # The TSA signing certificate
# (optional)
certs = $dir/cacert.pem # Certificate chain to include in reply
# (optional)
signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
signer_digest = sha256 # Signing digest to use. (Optional)
default_policy = tsa_policy1 # Policy if request did not specify it
# (optional)
other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory)
accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
clock_precision_digits = 0 # number of digits after dot. (optional)
ordering = yes # Is ordering defined for timestamps?
# (optional, default: no)
tsa_name = yes # Must the TSA name be included in the reply?
# (optional, default: no)
ess_cert_id_chain = no # Must the ESS cert id chain be included?
# (optional, default: no)
ess_cert_id_alg = sha1 # algorithm to compute certificate
# identifier (optional, default: sha1)

BIN
public/openssl/openssl.exe Normal file

Binary file not shown.

BIN
public/openssl/ssleay32.dll Normal file

Binary file not shown.

View File

@ -0,0 +1,75 @@
' Notes: wanted to implement this using a class but:
' 1. No matter what I did I could not assign the result of GetObject to a private member
' 2. It looks as if all methods were treated as subs from the outside world which is not good since
' some of these need to return a value
'
Set private_oReg = GetObject("winmgmts:\root\default:StdRegProv")
Function SetStringValue(constHive, strSubKey, strValueName, strValue)
SetStringValue = private_oReg.SetStringValue(constHive, strSubKey, strValueName, strValue)
End Function
Sub GetStringValue(constHive, strKey, strValueName, strValue)
private_oReg.GetStringValue constHive, strKey, strValueName, strValue
End Sub
Function SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
SetExpandedStringValue = private_oReg.SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
End Function
Sub GetExpandedStringValue(constHive, strKey, strValueName, strValue)
private_oReg.GetExpandedStringValue constHive, strKey, strValueName, strValue
End Sub
Function SetMultiStringValue(constHive, strSubKey, strValueName, arrValue)
SetMultiStringValue = private_oReg.SetMultiStringValue(constHive, strSubKey, strValueName, arrValue)
End Function
Sub GetMultiStringValue(constHive, strKey, strValueName, arrStrValue)
private_oReg.GetMultiStringValue constHive, strKey, strValueName, arrStrValue
End Sub
Function SetDWORDValue(constHive, strSubKey, strValueName, arrValue)
SetDWORDValue = private_oReg.SetDWORDValue(constHive, strSubKey, strValueName, arrValue)
End Function
Sub GetDWORDValue(constHive, strKey, strValueName, intDWordValue)
private_oReg.GetDWORDValue constHive, strKey, strValueName, intDWordValue
End Sub
Function SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue)
SetQWORDValue = private_oReg.SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue)
End Function
Sub GetQWORDValue(constHive, strKey, strValueName, intQWordValue)
private_oReg.GetQWORDValue constHive, strKey, strValueName, intQWordValue
End Sub
Function SetBinaryValue(constHive, strSubKey, strValueName, arrValue)
SetBinaryValue = private_oReg.SetBinaryValue(constHive, strSubKey, strValueName, arrValue)
End Function
Sub GetBinaryValue(constHive, strKey, strValueName, arrBinaryValue)
private_oReg.GetBinaryValue constHive, strKey, strValueName, arrBinaryValue
End Sub
Function EnumKey(constHive, strSubKey, arrKeyNames)
EnumKey = private_oReg.EnumKey(constHive, strSubKey, arrKeyNames)
End Function
Function EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes)
EnumValues = private_oReg.EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes)
End Function
Function CreateKey(constHive, strSubKey)
CreateKey = private_oReg.CreateKey(constHive, strSubKey)
End Function
Function DeleteKey(constHive, strSubKey)
DeleteKey = private_oReg.DeleteKey(constHive, strSubKey)
End Function
Function DeleteValue(constHive, strSubKey, strValue)
DeleteValue = private_oReg.DeleteValue(constHive, strSubKey, strValue)
End Function

View File

@ -0,0 +1,358 @@
' Notes: wanted to implement this using a class but:
' 1. No matter what I did I could not assign the result of GetObject to a private member
' 2. It looks as if all methods were treated as subs from the outside world which is not good since
' some of these need to return a value
' should be removed when migration is complete
Set private_oReg = GetObject("winmgmts:\root\default:StdRegProv")
Set private_oCtx = CreateObject("WbemScripting.SWbemNamedValueSet")
private_oCtx.Add "__ProviderArchitecture", CInt(OSArchitecture)
Set private_oLocator = CreateObject("Wbemscripting.SWbemLocator")
Set private_oServices = private_oLocator.ConnectServer(".", "root\default","","",,,,private_oCtx)
Set private_oRegSpecific = private_oServices.Get("StdRegProv")
Function CheckAccess(hDefKey,sSubKeyName,uRequired, bGranted )
Set Inparams = private_oRegSpecific.Methods_("CheckAccess").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.uRequired = uRequired
set Outparams = private_oRegSpecific.ExecMethod_("CheckAccess", Inparams,,private_oCtx)
bGranted = Outparams.bGranted
CheckAccess = 0
End Function
Function CreateKey(hDefKey,sSubKeyName)
Set Inparams = private_oRegSpecific.Methods_("CreateKey").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
set Outparams = private_oRegSpecific.ExecMethod_("CreateKey", Inparams,,private_oCtx)
CreateKey = 0
End Function
Function DeleteKey(hDefKey,sSubKeyName)
Set Inparams = private_oRegSpecific.Methods_("DeleteKey").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
set Outparams = private_oRegSpecific.ExecMethod_("DeleteKey", Inparams,,private_oCtx)
DeleteKey = 0
End Function
Function DeleteValue(hDefKey,sSubKeyName,sValueName)
Set Inparams = private_oRegSpecific.Methods_("DeleteValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
set Outparams = private_oRegSpecific.ExecMethod_("DeleteValue", Inparams,,private_oCtx)
DeleteValue = 0
End Function
Function EnumKey(hDefKey,sSubKeyName, sNames )
Set Inparams = private_oRegSpecific.Methods_("EnumKey").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
set Outparams = private_oRegSpecific.ExecMethod_("EnumKey", Inparams,,private_oCtx)
sNames = Outparams.sNames
EnumKey = 0
End Function
Function EnumValues(hDefKey,sSubKeyName, sNames,Types )
Set Inparams = private_oRegSpecific.Methods_("EnumValues").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
set Outparams = private_oRegSpecific.ExecMethod_("EnumValues", Inparams,,private_oCtx)
sNames = Outparams.sNames
Types = Outparams.Types
EnumValues = 0
End Function
Function GetBinaryValue(hDefKey,sSubKeyName,sValueName, uValue )
Set Inparams = private_oRegSpecific.Methods_("GetBinaryValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
set Outparams = private_oRegSpecific.ExecMethod_("GetBinaryValue", Inparams,,private_oCtx)
uValue = Outparams.uValue
GetBinaryValue = 0
End Function
Function GetDWORDValue(hDefKey,sSubKeyName,sValueName, uValue )
Set Inparams = private_oRegSpecific.Methods_("GetDWORDValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
set Outparams = private_oRegSpecific.ExecMethod_("GetDWORDValue", Inparams,,private_oCtx)
uValue = Outparams.uValue
GetDWORDValue = 0
End Function
Function GetExpandedStringValue(hDefKey,sSubKeyName,sValueName, sValue )
Set Inparams = private_oRegSpecific.Methods_("GetExpandedStringValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
set Outparams = private_oRegSpecific.ExecMethod_("GetExpandedStringValue", Inparams,,private_oCtx)
sValue = Outparams.sValue
GetExpandedStringValue = 0
End Function
Function GetMultiStringValue(hDefKey,sSubKeyName,sValueName, sValue )
Set Inparams = private_oRegSpecific.Methods_("GetMultiStringValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
set Outparams = private_oRegSpecific.ExecMethod_("GetMultiStringValue", Inparams,,private_oCtx)
sValue = Outparams.sValue
GetMultiStringValue = 0
End Function
Function GetQWORDValue(hDefKey,sSubKeyName,sValueName, uValue )
Set Inparams = private_oRegSpecific.Methods_("GetQWORDValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
set Outparams = private_oRegSpecific.ExecMethod_("GetQWORDValue", Inparams,,private_oCtx)
uValue = Outparams.uValue
GetQWORDValue = 0
End Function
Function GetSecurityDescriptor(hDefKey,sSubKeyName, Descriptor )
Set Inparams = private_oRegSpecific.Methods_("GetSecurityDescriptor").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
set Outparams = private_oRegSpecific.ExecMethod_("GetSecurityDescriptor", Inparams,,private_oCtx)
Descriptor = Outparams.Descriptor
GetSecurityDescriptor = 0
End Function
Function GetStringValue(hDefKey,sSubKeyName,sValueName, sValue )
Set Inparams = private_oRegSpecific.Methods_("GetStringValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
set Outparams = private_oRegSpecific.ExecMethod_("GetStringValue", Inparams,,private_oCtx)
sValue = Outparams.sValue
GetStringValue = 0
End Function
Function SetBinaryValue(hDefKey,sSubKeyName,sValueName,uValue)
Set Inparams = private_oRegSpecific.Methods_("SetBinaryValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
Inparams.uValue = uValue
set Outparams = private_oRegSpecific.ExecMethod_("SetBinaryValue", Inparams,,private_oCtx)
SetBinaryValue = 0
End Function
Function SetDWORDValue(hDefKey,sSubKeyName,sValueName,uValue)
Set Inparams = private_oRegSpecific.Methods_("SetDWORDValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
Inparams.uValue = uValue
set Outparams = private_oRegSpecific.ExecMethod_("SetDWORDValue", Inparams,,private_oCtx)
SetDWORDValue = 0
End Function
Function SetExpandedStringValue(hDefKey,sSubKeyName,sValueName,sValue)
Set Inparams = private_oRegSpecific.Methods_("SetExpandedStringValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
Inparams.sValue = sValue
set Outparams = private_oRegSpecific.ExecMethod_("SetExpandedStringValue", Inparams,,private_oCtx)
SetExpandedStringValue = 0
End Function
Function SetMultiStringValue(hDefKey,sSubKeyName,sValueName,sValue)
Set Inparams = private_oRegSpecific.Methods_("SetMultiStringValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
Inparams.sValue = sValue
set Outparams = private_oRegSpecific.ExecMethod_("SetMultiStringValue", Inparams,,private_oCtx)
SetMultiStringValue = 0
End Function
Function SetQWORDValue(hDefKey,sSubKeyName,sValueName,uValue)
Set Inparams = private_oRegSpecific.Methods_("SetQWORDValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
Inparams.uValue = uValue
set Outparams = private_oRegSpecific.ExecMethod_("SetQWORDValue", Inparams,,private_oCtx)
SetQWORDValue = 0
End Function
Function SetSecurityDescriptor(hDefKey,sSubKeyName,Descriptor)
Set Inparams = private_oRegSpecific.Methods_("SetSecurityDescriptor").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.Descriptor = Descriptor
set Outparams = private_oRegSpecific.ExecMethod_("SetSecurityDescriptor", Inparams,,private_oCtx)
SetSecurityDescriptor = 0
End Function
Function SetStringValue(hDefKey,sSubKeyName,sValueName,sValue)
Set Inparams = private_oRegSpecific.Methods_("SetStringValue").Inparameters
Inparams.hDefKey = hDefKey
Inparams.sSubKeyName = sSubKeyName
Inparams.sValueName = sValueName
Inparams.sValue = sValue
set Outparams = private_oRegSpecific.ExecMethod_("SetStringValue", Inparams,,private_oCtx)
SetStringValue = 0
End Function

View File

@ -0,0 +1,7 @@
<job id="JsonSafeStreamTest">
<script language="VBScript" src="util.vbs" />
<script language="VBScript">
str = """" & vbcrlf & "测试\"
Write("{ ""a"": """ & JsonSafe(str) & """}" & vbcrlf)
</script>
</job>

View File

@ -0,0 +1,32 @@
<job id="createKeyStream">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
CheckZeroArgs("usage: cscript regCreateKey.wsf architecture")
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
Do While Not stdin.AtEndOfLine
strLine = stdin.ReadLine()
strLine = unescape(trim(strLine))
If IsNull(strLine) or strLine = "" Then
WScript.Quit 25127
End If
ParseHiveAndSubKey strLine, constHive, strSubKey
if IsNull(constHive) Then
WriteLineErr "unsupported hive " & strLine
WScript.Quit 25122
End If
Result = CreateKey(constHive, strSubKey)
If Not Result = 0 Then
WScript.Quit Result
End If
Loop
</script>
</job>

View File

@ -0,0 +1,29 @@
<job id="deleteKey">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
CheckZeroArgs("usage: cscript regDeleteKey.wsf architecture")
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
Do While Not stdin.AtEndOfLine
strLine = stdin.ReadLine()
strLine = unescape(trim(strLine))
ParseHiveAndSubKey strLine, constHive, strSubKey
if IsNull(constHive) Then
WriteLineErr "unsupported hive " & strLine
WScript.Quit 25122
End If
Result = DeleteKey(constHive, strSubKey)
If Not Result = 0 Then
WScript.Quit Result
End If
Loop
</script>
</job>

View File

@ -0,0 +1,29 @@
<job id="deleteValue">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
CheckZeroArgs("usage: cscript regDeleteValue.wsf architecture")
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
Do While Not stdin.AtEndOfLine
strLine = stdin.ReadLine()
strLine = unescape(trim(strLine))
ParseHiveAndSubKeyAndValue strLine, constHive, strSubKey, strValue
if IsNull(constHive) Then
WriteLineErr "unsupported hive " & strLine
WScript.Quit 25122
End If
Result = DeleteValue(constHive, strSubKey, strValue)
If Not Result = 0 Then
WScript.Quit Result
End If
Loop
</script>
</job>

View File

@ -0,0 +1,49 @@
'
' Lists the sub keys and values of a given registry key
'
' cscript regList.wsg HKLM\Software
'
' Will Yield:
'
' {
' "hklm\software": {
' "keys": [ .. array of sub keys .. ],
' "values": {
' "moo": {
' "type": "REG_SZ",
' "value": "bar"
' }
' }
' }
' }
<job id="list">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
CheckZeroArgs("usage: cscript regList.wsf architecture regpath1 [regpath2] ... [regpathN]")
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
Write "{"
On Error Resume Next
For v = 1 To args.Count - 1
If (v > 1) Then
Write ","
End If
Dim key: key = trim(args(v))
Write """" & JsonSafe(key) & """: "
ParseHiveAndSubKey key, constHive, strSubKey
If IsNull(constHive) Then
WriteLineErr "unsupported hive " & args(v)
WScript.Quit 25122
End If
ListChildrenAsJson constHive, strSubKey
Next
Write "}"
</script>
</job>

View File

@ -0,0 +1,46 @@
'
' Lists the sub keys and values of a given registry key, this script is slightly different
' than regList because it reads stdin for the keys to list
'
' echo HKLM\Software | cscript regListStream.wsf A
'
' Will Yield:
'
' {
' "hklm\software": {
' "keys": [ .. array of sub keys .. ],
' "values": {
' "moo": {
' "type": "REG_SZ",
' "value": "bar"
' }
' }
' }
' }
<job id="listStream">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
CheckZeroArgs("usage: echo KEY | cscript regListStream.wsf architecture")
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
Do While Not stdin.AtEndOfLine
strLine = stdin.ReadLine()
strLine = unescape(trim(strLine))
ParseHiveAndSubKey strLine, constHive, strSubKey
if IsNull(constHive) Then
WriteLineErr "unsupported hive " & strLine
WScript.Quit 25122
End If
Write "{ ""key"" : """ & JsonSafe(strLine) & """, ""data"": "
ListChildrenAsJson constHive, strSubKey
Write "}" & vbcrlf
Loop
</script>
</job>

View File

@ -0,0 +1,56 @@
<job id="putValue">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
usage = "usage: cscript regPutValue.wsf architecture" & vbNewLine _
& "types: REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_MULTI_SZ, REG_QWORD"
CheckZeroArgs(usage)
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
ReadCount = 0
Dim lineArgs(3)
Do While Not stdin.AtEndOfLine
strLine = stdin.ReadLine()
strLine = unescape(trim(strLine))
If IsNull(strLine) or strLine = "" Then
WScript.Quit 25127
End If
lineArgs(ReadCount) = strLine
ReadCount = ReadCount + 1
If ReadCount = 4 Then
ParseHiveAndSubKey lineArgs(0), constHive, strSubKey
if IsNull(constHive) Then
WriteLineErr "unsupported hive " & lineArgs(0)
WScript.Quit 25122
End If
strValueName = lineArgs(1)
strValue = lineArgs(2)
strType = lineArgs(3)
Result = PutValue(constHive, strSubKey, strValueName, strValue, strType)
If Not Result = 0 Then
WriteLineErr "error while putting value: " & result
WScript.Quit Result
End If
ReadCount = 0
Erase lineArgs
End If
Loop
If ReadCount <> 0 Then
WScript.Quit 25123
End If
</script>
</job>

View File

@ -0,0 +1,358 @@
' TODO: consider incorporating a json writer of some sort instead of adhoc solution like the following
' e.g: http://demon.tw/my-work/vbs-json.html
const HKEY_CLASSES_ROOT = &H80000000
const HKEY_CURRENT_USER = &H80000001
const HKEY_LOCAL_MACHINE = &H80000002
const HKEY_USERS = &H80000003
const HKEY_CURRENT_CONFIG = &H80000005
Sub LoadRegistryImplementationByOSArchitecture()
If IsNull(OSArchitecture) Then
WriteLineErr "missing OSArchitecture global. did not call util.DetermineOSArchitecture? or Forgot to load util.vbs?"
WScript.Quit 25125
End If
If OSArchitecture = "A" Then
Include "ArchitectureAgnosticRegistry.vbs"
Else
Include "ArchitectureSpecificRegistry.vbs"
End If
End Sub
Function PutValue(constHive, strSubKey, strValueName, strValue, strType)
Select Case UCase(strType)
Case "REG_SZ"
PutValue = SetStringValue(constHive, strSubKey, strValueName, strValue)
Case "REG_EXPAND_SZ"
PutValue = SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
Case "REG_BINARY"
PutValue = SetBinaryValue(constHive, strSubKey, strValueName, ToBinaryValue(strValue))
Case "REG_NONE"
PutValue = SetBinaryValue(constHive, strSubKey, strValueName, ToBinaryValue(strValue))
' TODO: need to check that indeed int is the right type here
Case "REG_DWORD"
PutValue = SetDWORDValue(constHive, strSubKey, strValueName, CDbl(strValue))
Case "REG_MULTI_SZ"
PutValue = SetMultiStringValue(constHive, strSubKey, strValueName, Split(strValue, ","))
Case "REG_QWORD"
PutValue = SetQWORDValue(constHive, strSubKey, strValueName, strValue)
Case "REG_DEFAULT"
PutValue = SetStringValue(constHive, strSubKey, "", strValue)
Case Else
PutValue = SetStringValue(constHive, strSubKey, strValueName, strValue)
End Select
End Function
' render the child of a sub path strSubKey in hive constHive
' as json.
Sub ListChildrenAsJson(constHive, strSubKey)
' start outputting json to stdout
Write "{"
Dim e1: e1 = EnumKey (constHive, strSubKey, arrKeyNames)
If e1 <> 0 Then
Write """exists"": false,"
Dim arrValueNames: arrValueNames = null
Else
Write """exists"": true,"
Dim e2: e2 = EnumValues (constHive, strSubKey, arrValueNames, arrValueTypes)
If e2 <> 0 Then
WScript.Quit e2
End If
End If
Write """keys"": ["
If Not IsNull(arrKeyNames) Then
For x = 0 To UBound(arrKeyNames)
If (x > 0) Then
Write ","
End If
Write """" & JsonSafe(arrKeyNames(x)) & """"
Next
End If
Write "],"
' TODO: some duplicity of code between the two paths of this condition, this needs to be address at some point
Write """values"":{"
If Not IsNull(arrValueNames) Then
For y = 0 To UBound(arrValueNames)
If y > 0 Then
Write ","
End If
strValueName = arrValueNames(y)
intValueType = arrValueTypes(y)
' assign the value to varValue
GetValueByType constHive, strSubKey, strValueName, intValueType, varValue
WriteValue strValueName, intValueType, varValue
Next
Else
' fix for keys with only default values in them
' see http://stackoverflow.com/questions/8840343/how-to-read-the-default-value-from-registry-in-vbscript
GetStringValue constHive, strSubKey, "", strDefaultValue
If IsNull(strDefaultValue) = false and strDefaultValue <> "" Then
' write the default value with REG_SZ
WriteValue "", 1, strDefaultValue
End If
End If
Write "}}"
End Sub
Sub WriteValue (strValueName, intValueType, varValue)
Write """"
Write JsonSafe(strValueName)
Write """:{"
Write """type"": """
Write RenderType(intValueType)
Write ""","
Write """value"":"
Write RenderValueByType(intValueType, varValue)
Write "}"
End Sub
' give a raw HKLM\something\somewhere
' output the hive constant and the subkey, in this case:
' HKEY_LOCAL_MACHINE will be assigned to outConstHive
' and something\somewhere will be assigned to outStrSubKey
Sub ParseHiveAndSubKey(strRawKey, outConstHive, outStrSubKey)
' split into two parts to deduce the hive and the sub key
arrSplitted = Split(strRawKey, "\", 2, 1)
If UBound(arrSplitted) > 0 Then
strHive = arrSplitted(0)
outStrSubKey = arrSplitted(1)
Else
strHive = strRawKey
outStrSubKey = ""
End If
outConstHive = StringToHiveConst(UCase(strHive))
End Sub
Function ArrayRemoveAt(arr, pos)
Dim i
If IsArray(arr) Then
If pos >= 0 And pos <= UBound(arr) Then
For i = pos To UBound(arr) - 1
arr(i) = arr(i + 1)
Next
ReDim Preserve arr(UBound(arr) - 1)
End If
End If
End Function
Sub ParseHiveAndSubKeyAndValue(strRawKey, outConstHive, outStrSubKey, outStrValue)
' split into two parts to deduce the hive and the sub key
arrSplitted = Split(strRawKey, "\", -1, 1)
If UBound(arrSplitted) > 0 Then
strHive = arrSplitted(0)
outStrValue = arrSplitted(UBound(arrSplitted))
test = ArrayRemoveAt(arrSplitted, UBound(arrSplitted))
test = ArrayRemoveAt(arrSplitted, 0)
outStrSubKey = Join(arrSplitted, "\")
Else
strHive = strRawKey
outStrSubKey = ""
End If
outConstHive = StringToHiveConst(UCase(strHive))
End Sub
Function StringToHiveConst(strHive)
Select Case strHive
Case "HKCR"
StringToHiveConst = HKEY_CLASSES_ROOT
Case "HKCU"
StringToHiveConst = HKEY_CURRENT_USER
Case "HKLM"
StringToHiveConst = HKEY_LOCAL_MACHINE
Case "HKU"
StringToHiveConst = HKEY_USERS
Case "HKCC"
StringToHiveConst = HKEY_CURRENT_CONFIG
Case Else
StringToHiveConst = Null
End Select
End Function
' TODO: this entire "by type" should be transformed into OOP style
' where each type will have a class with render(), getValue() etc...
' convert a value type number into a string label
Function RenderType(intType)
RenderType = "REG_UNKNOWN"
Select Case intType
Case 0
RenderType = "REG_NONE"
Case 1
RenderType = "REG_SZ"
Case 2
RenderType = "REG_EXPAND_SZ"
Case 3
RenderType = "REG_BINARY"
Case 4
RenderType = "REG_DWORD"
Case 7
RenderType = "REG_MULTI_SZ"
Case 11
RenderType = "REG_QWORD"
Case Else
' TODO: should report / throw an error here
WriteErr("invalid Registry Value Type " & intType)
End Select
End Function
' render by value type:
' string will return as a string with double quotes, e.g "value"
' multi string values which return as an array ot strings "["1", "2"]" (double quotes included ofc)
' numeric values like DWORD and QWORD just return as the number e.g. 1
' byte arrays such as reg_binary return as an array of ints, e.g [1,2,3]
Function RenderValueByType(intType, varValue)
Select Case intType
' REG_NONE
Case 0
RenderValueByType = "0"
' REG_SZ
Case 1
RenderValueByType = """" & JsonSafe(varValue) & """"
' REG_EXPAND_SZ
Case 2
RenderValueByType = """" & JsonSafe(varValue) & """"
' REG_BINARY
Case 3
RenderValueByType = RenderByteArray(varValue)
' REG_DWORD
Case 4
RenderValueByType= varValue
' REG_MULYI_SZ'
Case 7
RenderValueByType = RenderStringArray(varValue)
' REG_QWORD
Case 11
RenderValueByType = varValue
Case Else
' TODO: should report / throw an error here
WriteErr("invalid Registry Value Type " & intType)
End Select
End Function
' get the value of a registry based on its value type and assign it to out parameter outVarValue
Sub GetValueByType(constHive, strKey, strValueName, intType, outVarValue)
Select Case intType
' REG_NONE
Case 0
GetStringValue constHive, strKey, strValueName, "0"
Exit Sub
' REG_SZ
Case 1
GetStringValue constHive, strKey, strValueName, outVarValue
Exit Sub
' REG_EXPAND_SZ
Case 2
GetExpandedStringValue constHive, strKey, strValueName, outVarValue
Exit Sub
' REG_BINARY
Case 3
GetBinaryValue constHive, strKey, strValueName, outVarValue
Exit Sub
' REG_DWORD
Case 4
GetDWORDValue constHive, strKey, strValueName, outVarValue
' #21 - VBS does not support UInt32. This is the workaround
If outVarValue < 0 Then outVarValue = 4294967296 + outVarValue
Exit Sub
' REG_MULYI_SZ'
Case 7
GetMultiStringValue constHive, strKey, strValueName, outVarValue
Exit Sub
' REG_QWORD
Case 11
GetQWORDValue constHive, strKey, strValueName, outVarValue
Exit Sub
Case Else
' TODO: should report / throw an error here
WriteErr("invalid Registry Value Type " & intType)
End Select
End Sub
' render a byte array as a json array of numbers
Function RenderByteArray(arr)
RenderByteArray = "[]"
If Not IsNull(arr) Then
RenderByteArray = "[" & Join(arr, ",") & "]"
End If
End Function
' render a string array as json string array
Function RenderStringArray(arr)
Result = "["
If Not IsNull(arr) Then
For t = 0 To UBound(arr)
If (t > 0) Then
Result = Result & ","
End If
Result = Result & """" & JsonSafe(arr(t)) & """"
Next
End If
Result = Result & "]"
RenderStringArray = Result
End Function
Function ToBinaryValue(strValue)
arrValue = Split(strValue, ",")
If IsNull(arrValue) Then
ToBinaryValue = Array()
Exit Function
End If
For i = 0 To UBound(arrValue)
arrValue(i) = CInt(arrValue(i))
Next
ToBinaryValue = arrValue
End Function

162
public/regedit-vbs/util.vbs Normal file
View File

@ -0,0 +1,162 @@
Set stdout = WScript.StdOut
Set stderr = WScript.StdErr
Set stdin = WScript.StdIn
Set args = WScript.Arguments
Set fs = CreateObject("scripting.filesystemobject")
Dim OSArchitecture
Sub WriteErr(message)
stderr.Write message
End Sub
Sub WriteLineErr(message)
stderr.WriteLine message
End Sub
Sub Write(message)
stdout.Write message
End Sub
Sub WriteLine(message)
stdout.WriteLine message
End Sub
Function IndexOf(varNeedle, arrHaystack)
IndexOf = -1
If Not IsArray(arrHaystack) Then
Exit Function
End If
For xyz = 0 To UBound(arrHaystack)
If arrHaystack(xyz) = varNeedle Then
IndexOf = xyz
Exit Function
End If
Next
End Function
Sub CheckZeroArgs(message)
' bail if args are missing
If args.Count = 0 Then
WriteLineErr message
WScript.Quit 25121
End If
End Sub
Dim ALLOWED_OS_ARCHITECTURE_VALUES: ALLOWED_OS_ARCHITECTURE_VALUES = Array("S", "A", "32", "64")
'
' determine the architecture of the operating system, that will be used. there are 4 possibilities:
' A - means agnostic
' S - means that we want to use a specific architecture, but auto detect Item
' 32 - explicitly use 32 bit architecture
' 64 - explicitly use 64 bit architecture
'
Sub DetermineOSArchitecture()
strArchitecture = args(0)
If IsNull(strArchitecture) Then
WriteLineErr "missing architecture argument"
WScript.Quit 25124
End If
strArchitecture = UCase(strArchitecture)
If IndexOf(strArchitecture, ALLOWED_OS_ARCHITECTURE_VALUES) = -1 Then
WriteLineErr "invalid architecture argument"
WScript.Quit 25124
End If
If (strArchitecture = "S") Then
OSArchitecture = GetOSArchitecture()
If OSArchitecture = -1 Then
WriteLineErr "invalid os architecture detected " & OSArchitecture
WScript.Quit 25126
End If
Else
OSArchitecture = strArchitecture
End If
End Sub
Sub Include(sPath)
' TODO this is fragile, but should work for "modules" nested relatively to script root
include_ScriptPath = Left(WScript.ScriptFullName, InStr(WScript.ScriptFullName, WScript.ScriptName) - 2)
sPath = include_ScriptPath & "\" & sPath
include_code = fs.OpenTextFile(sPath).ReadAll
ExecuteGlobal include_code
End Sub
Function GetOSArchitecture()
Dim ObjWMI, ColSettings, ObjProcessor
Dim StrComputer, ObjNetwork
Set ObjWMI = GetObject("winmgmts:\Root\CIMV2")
Set ColSettings = ObjWMI.ExecQuery ("SELECT DataWidth, AddressWidth, Architecture FROM Win32_Processor")
' TODO: I make two assumptions here:
' 1. Eveyone will have CPU0 device
' 2. There is only one cpu defined in the wmi database (and if not, then they are all of the same architecture)
Set ObjProcessor = ColSettings.Item("Win32_Processor.DeviceID=""CPU0""")
If ObjProcessor.Architecture = 0 AND ObjProcessor.AddressWidth = 32 Then
GetOSArchitecture = 32
ElseIf (ObjProcessor.Architecture = 6 OR ObjProcessor.Architecture = 9) AND ObjProcessor.DataWidth = 64 AND ObjProcessor.AddressWidth = 32 Then
GetOSArchitecture = 32
ElseIf (ObjProcessor.Architecture = 6 OR ObjProcessor.Architecture = 9) AND ObjProcessor.DataWidth = 64 AND ObjProcessor.AddressWidth = 64 Then
GetOSArchitecture = 64
Else
GetOSArchitecture = -1
End If
End Function
Function JsonSafe(inStrText)
If inStrText = "" Then
JsonSafe = ""
Exit Function
End If
Dim outStrText: outStrText = inStrText
outStrText = Replace(outStrText, "\", "\\")
outStrText = Replace(outStrText, vbcrlf, "\\r\\n")
outStrText = Replace(outStrText, vblf, "\\n")
outStrText = Replace(outStrText, vbcr, "\\r")
outStrText = Replace(outStrText, """", "\""")
outStrText = JsonU(outStrText)
JsonSafe = outStrText
End Function
'TODO: need to change this function's name to something more appropriate
Function JsonU(astr)
If isNull(astr) Then
JsonU = ""
Exit Function
End If
Dim c
Dim utftext: utftext = ""
For n = 1 To Len(astr)
c = CLng(AscW(Mid(astr, n, 1)))
If c < 0 Then
c = &H10000 + c
End If
If c < &H80 Then
utftext = utftext & Mid(astr, n, 1)
ElseIf c < &H100 Then
utftext = utftext & "\u00" & Hex(c)
ElseIf c < &H1000 Then
utftext = utftext & "\u0" & Hex(c)
Else
utftext = utftext & "\u" & Hex(c)
End If
Next
JsonU = utftext
End Function

View File

@ -0,0 +1,52 @@
'
' Lists the values of a given registry path, this script takes its input from stdin
'
' cscript regListStream.wsf A "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData"
'
' Will Yield:
'
' {
' "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData": "value here"
' }
<job id="regRead">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
CheckZeroArgs("usage: cscript wsRegRead.wsf architecture path1...pathN")
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
set ws = createobject("Wscript.shell")
Write "["
On Error Resume Next
For v = 1 To args.Count - 1
If (v > 1) Then
Write ","
End If
Dim key: key = trim(args(v))
' not really needed except for validation
ParseHiveAndSubKeyAndValue key, constHive, strSubKey, strValue
if IsNull(constHive) Then
WriteLineErr "unsupported hive " & args(v)
WScript.Quit 25122
End If
Write "{ ""path"" : """ & JsonSafe(key) & """, "
Dim result: result = ws.RegRead(args(v))
Dim exists: exists = "true"
If Err.Number <> 0 Then
exists = "false"
End if
Write """exists"": " & exists & ", "
Write """value"": """ & JsonSafe(result) & """}" & vbcrlf
Next
Write "]"
</script>
</job>

View File

@ -0,0 +1,47 @@
'
' Lists the values of a given registry path, this script takes its input from stdin
'
' echo HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData | cscript regListStream.wsf A
'
' Will Yield:
'
' {
' "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData": "value here"
' }
<job id="regRead">
<script language="VBScript" src="util.vbs" />
<script language="VBScript" src="regUtil.vbs" />
<script language="VBScript">
CheckZeroArgs("usage: echo KEY | cscript wsRegRead.wsf architecture")
DetermineOSArchitecture()
LoadRegistryImplementationByOSArchitecture()
set ws = createobject("Wscript.shell")
Do While Not stdin.AtEndOfLine
strLine = stdin.ReadLine()
strLine = unescape(trim(strLine))
' not really needed except for validation
ParseHiveAndSubKeyAndValue strLine, constHive, strSubKey, strValue
if IsNull(constHive) Then
WriteLineErr "unsupported hive " & strLine
WScript.Quit 25122
End If
Write "{ ""path"" : """ & JsonSafe(strLine) & """, "
Dim result: result = ws.RegRead(strLine)
Dim exists: exists = "true"
If Err.Number <> 0 Then
exists = "false"
End if
Write """exists"": " & exists & ", "
Write """value"": """ & JsonSafe(result) & """}" & vbcrlf
Loop
</script>
</job>

BIN
public/w_c.exe Normal file

Binary file not shown.

166
src/App.jsx Normal file
View File

@ -0,0 +1,166 @@
import { useMachine } from '@xstate/react';
import { Table, Button, Progress, Alert } from 'antd';
import { shell } from 'electron';
import {
DownloadOutlined,
PlaySquareOutlined,
ClearOutlined,
GithubOutlined,
EyeOutlined,
FormatPainterOutlined,
RedoOutlined,
} from '@ant-design/icons';
import fsm from './fsm';
import './App.less';
function App() {
const [state, send] = useMachine(fsm);
const { captureList, currentUrl, downloadProgress } = state.context;
return (
<div className="App">
{state.matches('检测初始化') ? <div>检测中</div> : null}
{state.matches('初始化完成') ? (
<div className="App-inited">
<Button
className="App-inited-clear"
icon={<ClearOutlined />}
onClick={() => send('e_清空捕获记录')}
>
清空
</Button>
<Button
className="App-inited-github"
icon={<GithubOutlined />}
onClick={() => shell.openExternal('https://github.com/lecepin/WeChatVideoDownloader')}
type="primary"
ghost
>
Star
</Button>
<Table
sticky
dataSource={captureList}
columns={[
{
title: '视频标题(捕获中……)',
dataIndex: 'description',
key: 'description',
render: value => value,
ellipsis: true,
},
{
title: '大小',
dataIndex: 'prettySize',
key: 'prettySize',
width: '100px',
render: value => value,
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
width: '250px',
render: (_, { url, decodeKey, hdUrl, fixUrl, description, fullFileName }) => (
<div>
{fullFileName ? (
<Button
icon={<EyeOutlined />}
type="primary"
onClick={() => {
shell.openPath(fullFileName);
}}
size="small"
ghost
>
查看
</Button>
) : (
<Button
icon={<DownloadOutlined />}
type="primary"
onClick={() => {
send({
type: 'e_下载',
url: hdUrl || url,
decodeKey: decodeKey,
description: description,
});
}}
size="small"
>
解密下载
</Button>
)}
&nbsp; &nbsp;
<Button
onClick={() => {
send({
type: 'e_复制',
url: fixUrl || url,
decodeKey: decodeKey,
description: description,
});
}}
size="small"
>
</Button>
&nbsp; &nbsp;
<Button
onClick={() => {
send({
type: 'e_解密',
url: hdUrl || url,
decodeKey: decodeKey,
description: description,
});
}}
size="small"
>
</Button>
</div>
),
},
]}
pagination={{ position: ['none', 'none'] }}
></Table>
{state.matches('初始化完成.下载.下载中') ? (
<div className="App-inited-download">
<Progress type="circle" percent={downloadProgress} />
</div>
) : null}
</div>
) : null}
{state.matches('未初始化') ? (
<div className="App-uninit">
<Alert message="首次进入,请先初始化~" type="warning" showIcon closable={false} />
<Button
size="large"
onClick={() => send('e_开始初始化')}
type="primary"
icon={<FormatPainterOutlined />}
>
初始化
</Button>
&nbsp;&nbsp;
<Button size="large" onClick={() => send('e_重新检测')} icon={<RedoOutlined />}>
重新检测
</Button>
</div>
) : null}
{state.matches('开启服务失败') ? (
<div className="App-uninit">
<Alert message="开启服务失败,请允许开启" type="error" showIcon closable={false} />
<Button size="large" onClick={() => send('e_重试')} type="primary">
尝试开启
</Button>
</div>
) : null}
</div>
);
}
export default App;

52
src/App.less Normal file
View File

@ -0,0 +1,52 @@
.App {
padding: 10px;
&-uninit{
text-align: center;
padding: 10px;
& button{
margin-top: 50px;
}
}
&-inited {
&-clear {
margin-bottom: 10px;
}
&-github {
float: right;
}
&-preview {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999999999;
& > video {
max-width: 90%;
max-height: 90%;
}
}
&-download {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999999999;
}
}
}

372
src/fsm.js Normal file
View File

@ -0,0 +1,372 @@
import { createMachine, actions } from 'xstate';
import { ipcRenderer } from 'electron';
import prettyBytes from 'pretty-bytes';
import { uniqBy } from 'lodash';
import { message } from 'antd';
export default createMachine(
{
id: '微信视频号下载工具',
context: {
captureList: [],
currentUrl: '',
savePath: '',
jieMiFilePath: '',
downloadProgress: 0,
},
initial: '检测初始化',
states: {
检测初始化: {
id: '检测初始化',
invoke: {
src: 'invoke_初始化信息',
},
on: {
e_初始化完成: {
target: '初始化完成',
},
e_未初始化: {
target: '未初始化',
},
},
},
未初始化: {
initial: '空闲',
on: {
e_重新检测: {
target: '检测初始化',
},
},
states: {
空闲: {
on: {
e_开始初始化: {
target: '开始初始化',
},
},
},
开始初始化: {
invoke: {
src: 'invoke_开始初始化',
},
},
},
},
初始化完成: {
initial: '空闲',
id: '初始化完成',
invoke: {
src: 'invoke_启动服务',
},
on: {
e_视频捕获: {
actions: 'action_视频捕获',
},
e_开启服务失败: {
target: '开启服务失败',
},
e_清空捕获记录: {
actions: 'action_清空捕获记录',
},
},
states: {
空闲: {
on: {
e_下载: {
actions: 'action_设置当前地址',
target: '下载',
},
e_复制: {
actions: 'action_设置当前地址',
target: '复制',
},
e_解密: {
actions: 'action_设置当前地址',
target: '解密',
},
e_改变规则: {
actions: 'action_改变规则',
},
},
},
下载: {
initial: '选择位置',
states: {
选择位置: {
on: {
e_确认位置: { actions: 'action_存储下载位置', target: '下载中' },
e_取消: { target: '#初始化完成.空闲' },
},
invoke: {
src: 'invoke_选择下载位置',
},
},
下载中: {
on: {
e_进度变化: {
actions: 'action_进度变化',
},
e_下载完成: {
target: '#初始化完成.空闲',
actions: 'action_下载完成',
},
e_下载失败: {
target: '#初始化完成.空闲',
actions: 'action_下载失败',
},
},
invoke: {
src: 'invoke_下载视频',
},
},
下载完成: {
on: {
e_取消: { target: '#初始化完成.空闲' },
e_打开文件位置: {
actions: 'action_打开文件位置',
},
},
},
},
},
复制: {
on: {
e_关闭: {
target: '空闲',
},
},
invoke: {
src: 'invoke_复制',
},
},
解密: {
initial: '选择文件',
states: {
选择文件: {
on: {
e_确认文件位置: { actions: 'action_存储文件位置', target: '解密中' },
e_取消: { target: '#初始化完成.空闲' },
},
invoke: {
src: 'invoke_选择文件位置',
},
},
解密中: {
on: {
e_解密完成: {
target: '#初始化完成.空闲',
actions: 'action_解密完成',
},
e_解密失败: {
target: '#初始化完成.空闲',
actions: 'action_解密失败',
},
},
invoke: {
src: 'invoke_解密视频',
},
},
解密完成: {
on: {
e_取消: { target: '#初始化完成.空闲' },
e_打开文件位置: {
actions: 'action_打开文件位置',
},
},
},
},
}
},
},
开启服务失败: {
on: {
e_重试: {
target: '初始化完成',
},
},
},
},
},
{
services: {
invoke_初始化信息: () => send => {
ipcRenderer.invoke('invoke_初始化信息').then(data => {
if (data === true) {
send('e_初始化完成');
} else {
send('e_未初始化');
}
});
},
invoke_开始初始化: (context, event) => send => {
ipcRenderer
.invoke('invoke_开始初始化')
.catch(() => {})
.finally(() => send('e_重新检测'));
},
invoke_启动服务: (context, event) => send => {
const fnDealVideoCapture = (eName, { url, size, description, decode_key, ...other }) => {
send({ type: 'e_视频捕获', url, size, description, decodeKey: decode_key, ...other });
};
ipcRenderer
.invoke('invoke_启动服务')
.then(() => {
ipcRenderer.on('VIDEO_CAPTURE', fnDealVideoCapture);
})
.catch(() => {
send('e_开启服务失败');
});
return () => {
ipcRenderer.removeListener('VIDEO_CAPTURE', fnDealVideoCapture);
};
},
invoke_复制:({ currentUrl, decodeKey, description }) => send => {
ipcRenderer
.invoke('invoke_复制',{
url: currentUrl,
decodeKey,
description,
}).catch(() => {}).finally(() => send('e_关闭'));
},
invoke_选择文件位置: (context, event) => send => {
ipcRenderer
.invoke('invoke_选择文件位置')
.then(data => {
send({
type: 'e_确认文件位置',
data,
});
})
.catch(() => send('e_取消'));
},
invoke_解密视频: ({ currentUrl, jieMiFilePath, decodeKey, description }) => send => {
ipcRenderer
.invoke('invoke_解密视频', {
url: currentUrl,
decodeKey,
jieMiFilePath,
description,
})
.then(({ fullFileName }) => {
send({ type: 'e_解密完成', fullFileName, currentUrl });
})
.catch(() => {
send('e_解密失败');
});
},
invoke_选择下载位置: (context, event) => send => {
ipcRenderer
.invoke('invoke_选择下载位置')
.then(data => {
send({
type: 'e_确认位置',
data,
});
})
.catch(() => send('e_取消'));
},
invoke_下载视频:
({ currentUrl, savePath, decodeKey, description }) =>
send => {
ipcRenderer
.invoke('invoke_下载视频', {
url: currentUrl,
decodeKey,
savePath,
description,
})
.then(({ fullFileName }) => {
send({ type: 'e_下载完成', fullFileName, currentUrl });
})
.catch(() => {
send('e_下载失败');
});
ipcRenderer.on('e_进度变化', (event, arg) => {
send({
type: 'e_进度变化',
data: arg,
});
});
return () => {
ipcRenderer.removeAllListeners('e_进度变化');
};
},
},
actions: {
action_视频捕获: actions.assign(
({ captureList }, { url, size, description, decodeKey, ...other }) => {
captureList.push({
size,
url,
prettySize: prettyBytes(+size),
description,
decodeKey,
...other,
});
return {
captureList: uniqBy(captureList, 'url'),
};
},
),
action_清空捕获记录: actions.assign(() => {
return {
captureList: [],
};
}),
action_设置当前地址: actions.assign((_, { url, decodeKey, description }) => {
return {
currentUrl: url,
decodeKey: decodeKey,
description: description,
};
}),
action_存储下载位置: actions.assign((_, { data }) => {
return {
savePath: data,
};
}),
action_进度变化: actions.assign((_, { data }) => {
return {
downloadProgress: ~~(data * 100),
};
}),
action_下载完成: actions.assign(({ captureList }, { fullFileName, currentUrl }) => {
return {
captureList: captureList.map(item => {
if ((item.hdUrl || item.url) === currentUrl) {
item.fullFileName = fullFileName;
}
return item;
}),
};
}),
action_下载失败: actions.log(() => {
message.error('网络错误,请重试');
}),
action_存储文件位置: actions.assign((_, { data }) => {
return {
jieMiFilePath: data,
};
}),
action_解密完成: actions.assign(({ captureList }, { fullFileName, currentUrl }) => {
return {
captureList: captureList.map(item => {
if ((item.hdUrl || item.url) === currentUrl) {
item.fullFileName = fullFileName;
}
return item;
}),
};
}),
action_解密失败: actions.log(() => {
message.error('网络错误,请重试');
}),
},
},
);

12
src/index.js Normal file
View File

@ -0,0 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import 'antd/dist/antd.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);

BIN
src/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

18
webpack.electron.js Normal file
View File

@ -0,0 +1,18 @@
const path = require('path');
module.exports = {
entry: path.resolve(__dirname, './electron/index.js'),
output: {
filename: 'index.js',
path: path.resolve(__dirname, './build-electron'),
},
module: {
rules: [],
},
devtool: 'source-map',
target: 'electron-main',
node: false,
stats: {
errorDetails: true,
},
};