步骤 1:下载并安装 Typora
首先访问 Typora 官方发布页面下载:
安装路径可以随意选择,在后续的代码中修改为对应的安装路径即可。
注意:安装完成后暂时不要打开 Typora,取消勾选 Launch Typora 选项,然后点击 Finish 。
步骤 2:创建破解工作目录
在任意位置创建 typora-crack 文件夹,本教程以 E:\app\program\typora-crack 为例。
步骤 3:创建破解脚本
在 typora-crack 文件夹内新建 crack.js 文件,粘贴以下代码:
javascript
const asar = require("asar");
const chalk = require("chalk");
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const readlineSync = require("readline-sync");
const WinReg = require("winreg");
const { flipFuses, FuseV1Options, FuseVersion } = require("@electron/fuses");
// 从代码1引入:固定配置和扩展端点
const FIXED_REG_CODE = "JayCrack2026";
const FIXED_DATE = "1/1/2099";
const FIXED_IDATE = "12/1/2025";
function getInsertCode(EnableHookDebug, atobMachineCode, email, nowDateStr) {
return `
/** Hook破解开始 */
const electron = require("electron");
const { execFileSync } = require("child_process");
// 是否启用劫持调试
const HookDebug = ${EnableHookDebug ? "true" : "false"};
// 从代码1引入:固定配置
const FIXED_REG_CODE = "${FIXED_REG_CODE}";
const FIXED_DATE = "${FIXED_DATE}";
const FIXED_IDATE = "${FIXED_IDATE}";
// 调试日志定义
const LOG_PATH = ".\\\\Typora_Hook_Log.txt";
//fs.rmSync(LOG_PATH, { force: true });
function writeLog(...data) {
if (!HookDebug) return;
try {
const log = \`[\${new Date().toLocaleString()}] [Log] \${data.join(
" "
)}\\n------------------\\n\`;
fs.appendFileSync(LOG_PATH, log);
} catch (e) {}
}
// 调试模式只记录窗口创建,不改写 app.quit,也不自动打开 DevTools。
// 改写 app.quit 会让前台窗口关闭后主进程残留,双击 md 时 second-instance 无法正常唤起窗口。
if (HookDebug) {
electron.app.on("browser-window-created", (_event, win) => {
writeLog("【👀 监控】检测到 BrowserWindow 实例化!");
});
}
// 从代码1引入:注册表修复函数(延迟执行)
function fixRegistryLater() {
try {
const regPath = "HKCU\\\\Software\\\\Typora";
const encodedRegCode = Buffer.from(FIXED_REG_CODE).toString("base64");
const sLicense = encodedRegCode + "#0#" + FIXED_DATE;
execFileSync("reg", ["add", regPath, "/v", "SLicense", "/t", "REG_SZ", "/d", sLicense, "/f"], {
windowsHide: true,
timeout: 3000,
});
execFileSync("reg", ["add", regPath, "/v", "IDate", "/t", "REG_SZ", "/d", FIXED_IDATE, "/f"], {
windowsHide: true,
timeout: 3000,
});
} catch (e) {
// Silent fail - don't crash the app
}
}
// Hook fs 模块,重定向对 resources/app 目录的访问
// resources/app/ → resources/app.bak/
const fsPathFrom = /resources[\\\\/]app[\\\\/]/i;
const fsPathTo = "resources\\\\app.bak\\\\";
const fsHook = {};
[
"readFileSync",
"readFile",
"statSync",
"stat",
"Stats",
"StatsFs",
"open",
"openSync",
].forEach((property) => {
fsHook[property] = fs[property];
fs[property] = function (filePath, ...args) {
if (typeof filePath == "string" && fsPathFrom.test(filePath)) {
const redirectPath = filePath.replace(fsPathFrom, fsPathTo);
if (HookDebug) {
writeLog(
\`[🛡️ fsHook] 程序试图 fs.\${property} 重定向 \${filePath} --> \${redirectPath}\`
);
}
return fsHook[property].call(this, redirectPath, ...args);
}
if (HookDebug) writeLog(\`[🛡️ fsHook] 程序试图 fs.\${property} \${filePath}\`);
return fsHook[property].call(this, filePath, ...args);
};
});
const fsPromisesHook = {};
["readFile", "open", "stat"].forEach((property) => {
fsPromisesHook[property] = fs.promises[property];
fs.promises[property] = async function (filePath, ...args) {
if (typeof filePath == "string" && fsPathFrom.test(filePath)) {
const redirectPath = filePath.replace(fsPathFrom, fsPathTo);
if (HookDebug) {
writeLog(
\`[🛡️ fsHook/Promises] 程序试图 fs.promises.\${property} 重定向 \${filePath} --> \${redirectPath}\`
);
}
return fsPromisesHook[property].call(this, redirectPath, ...args);
}
if (HookDebug) writeLog(
\`[🛡️ fsHook/Promises] 程序试图 fs.promises.\${property} \${filePath}\`
);
return fsPromisesHook[property].call(this, filePath, ...args);
};
});
// IPC 通信进行监控
if (HookDebug) {
const invokeFilter = ["document.addSnapAndLastSync", "document.setContent"];
const originalIpcMainHandle = electron.ipcMain.handle;
electron.ipcMain.handle = function (channel, listener) {
const filter = !invokeFilter.includes(channel);
return originalIpcMainHandle.call(this, channel, async (event, ...args) => {
filter &&
writeLog(
\`[👀IPC 请求] 收到 .invoke("\${channel}") 参数:\`,
JSON.stringify(args)
);
try {
const result = await listener(event, ...args);
filter &&
writeLog(
\`[👀IPC 响应] .handle("\${channel}") 返回结果:\`,
JSON.stringify(result)
);
return result;
} catch (error) {
filter && writeLog(\`[👀IPC 错误] .handle("\${channel}") 执行出错:\`, error);
throw error;
}
});
};
}
const crypto = require("crypto");
const originalPublicDecrypt = crypto.publicDecrypt;
crypto.publicDecrypt = function (key, buffer) {
if (HookDebug) {
writeLog("-------------------------------------------");
writeLog("【👀 监控】 crypto.publicDecrypt 被调用");
writeLog("Key:", key);
writeLog("Buffer (Hex):", buffer.toString("hex"));
}
// 从代码1引入:使用固定注册码
return Buffer.from(
JSON.stringify({
deviceId: "",
fingerprint: "",
email: "",
license: FIXED_REG_CODE,
version: "",
date: FIXED_DATE,
type: "JayCrack2026",
})
);
};
// 劫持联网验证
electron.app.whenReady().then(() => {
// 从代码1引入:延迟执行注册表修复 + 周期性修复
setTimeout(() => {
fixRegistryLater();
setInterval(fixRegistryLater, 30 * 1000);
}, 5000);
electron.protocol.handle("https", async (request) => {
if (HookDebug) {
writeLog(\`[👀electron.net Request] \${request.method} \${request.url}\`);
writeLog("request.url typeof:", typeof request.url, "value:", request.url);
}
// 从代码1引入:扩展拦截端点
const verificationEndpoints = [
"/api/client/renew",
"/api/client/activate",
"/api/client/status",
"/api/client/validate",
"/api/client/deactivate"
];
const shouldIntercept = verificationEndpoints.some(endpoint => request.url.includes(endpoint));
if (shouldIntercept) {
if (HookDebug) {
writeLog(\`[🛡️ 拦截] 伪造激活验证响应\`);
}
// 从代码1引入:使用 Buffer.from 替代 btoa
const encodedLicense = Buffer.from(FIXED_REG_CODE).toString("base64");
return new Response(
JSON.stringify({
success: true,
code: 0,
retry: true,
msg: encodedLicense,
status: "activated",
license: FIXED_REG_CODE,
expire_date: FIXED_DATE
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
}
if (HookDebug) {
// 尝试打印 Request Body
try {
const reqClone = request.clone();
const reqBody = await reqClone.text();
if (reqBody) {
writeLog('[electron.net Request Body]:', reqBody);
}
} catch { }
// 其他请求正常转发
const response = await electron.net.fetch(request, { bypassCustomProtocolHandlers: true });
// 克隆响应用于日志
const resClone = response.clone();
resClone
.text()
.then((resText) => {
writeLog(\`[👀electron.net Response] \${response.status} \${request.url}\`);
writeLog('[electron.net Response Body]:', resText.substring(0, 500));
})
.catch((err) => {
console.error('[electron.net Response Error]:', err);
});
return response;
}
// 非调试模式下直接转发
return electron.net.fetch(request, { bypassCustomProtocolHandlers: true });
});
});
// 从代码1引入:Hook winreg 模块防止 SLicense 被清空
try {
const winregModule = require("winreg");
if (winregModule && winregModule.prototype && typeof winregModule.prototype.set === "function") {
const originalSet = winregModule.prototype.set;
winregModule.prototype.set = function(name, type, value, callback) {
if (name === "SLicense" && (!value || value === "" || value === "null" || String(value).startsWith("null"))) {
if (HookDebug) {
writeLog(\`[🛡️ winreg Hook] 阻止清空 SLicense, 尝试值: \${value}\`);
}
if (typeof callback === "function") {
setImmediate(() => callback(null));
}
return;
}
return originalSet.apply(this, arguments);
};
if (HookDebug) {
writeLog("[🛡️ winreg Hook] 成功 Hook winreg.prototype.set");
}
}
} catch (e) {
// winreg 模块可能还不可用,静默忽略
}
/** Hook破解结束 */
`;
}
= ;
= ;
() {
possiblePaths = [
,
,
,
process.. +
];
( p possiblePaths) {
(fs.(path.(p, ))) {
p;
}
}
;
}
_Installation_Path = ();
(!_Installation_Path) {
.(chalk.());
.(chalk.());
_Installation_Path = readlineSync.().().(, );
(!fs.(path.(_Installation_Path, ))) {
.(chalk.());
.(chalk.());
readlineSync.();
process.();
}
}
.(chalk.());
resourcesPath = path.(_Installation_Path, );
asarPath = path.(resourcesPath, );
appDir = path.(resourcesPath, );
appBakDir = path.(resourcesPath, );
asarBakPath = path.(resourcesPath, );
= path.(_Installation_Path, );
= path.(appDir, );
() {
chars = ;
code = ;
( i = ; i < ; i++) {
code += chars.(.(.() * chars.));
}
code += ;
code;
}
() {
{
(, {
: ,
:
});
.(chalk.());
} (e) {
.(chalk.());
}
.(
chalk.(
)
);
.(chalk.());
readlineSync.();
}
() {
( {
regKey.(name, ., value, () {
(err) (err);
();
});
});
}
() {
now = ();
dd = (now.()).(, );
mm = (now.() + ).(, );
yyyy = now.();
;
}
nowDateStr = ();
.(chalk.());
machineCode = readlineSync.();
.(chalk.());
email = readlineSync.();
.(chalk.());
.(chalk.());
backupAnswer = readlineSync.();
.(chalk.());
debugAnswer = readlineSync.();
= backupAnswer.() === ;
= debugAnswer.() === ;
() {
.(str, ).();
}
atobMachineCode = .((machineCode));
.(chalk.( + atobMachineCode.));
.(chalk.( + atobMachineCode.));
.(chalk.( + atobMachineCode.));
();
.(chalk.());
() {
.(chalk.());
(!fs.(asarPath)) {
.(chalk.());
.(chalk.());
.(chalk.());
.(chalk.());
.(chalk.());
(fs.(asarBakPath)) {
.(chalk.());
restoreAnswer = readlineSync.();
(restoreAnswer.() === ) {
fs.(asarBakPath, asarPath);
.(chalk.());
} {
.(chalk.());
;
}
} {
.(chalk.());
;
}
}
(fs.(appDir)) {
.(chalk.());
.(chalk.());
reExtractAnswer = readlineSync.();
(reExtractAnswer.() === ) {
.(chalk.());
fs.(appDir, { : , : });
} {
.(chalk.());
}
}
(!fs.(appDir)) {
.(chalk.());
{
asar.(asarPath, appDir);
.(chalk.());
} (err) {
.(chalk.());
.(chalk.());
;
}
} {
.(chalk.());
}
.(
chalk.()
);
() {
(!fs.(dest)) fs.(dest, { : });
( entry fs.(src, { : })) {
srcPath = path.(src, entry.);
destPath = path.(dest, entry.);
(entry.()) {
(srcPath, destPath);
} {
fs.(srcPath, destPath);
}
}
}
(!fs.(appBakDir)) {
(appDir, appBakDir);
.(chalk.());
} {
.(chalk.());
}
.(chalk.());
(fs.(asarPath)) {
() {
(!fs.(asarBakPath)) {
fs.(asarPath, asarBakPath);
.(chalk.());
} {
fs.(asarPath, { : });
.(chalk.());
}
} {
fs.(asarPath, { : });
.(chalk.());
}
} {
.(chalk.());
}
.(
chalk.()
);
( && !fs.()) {
fs.(, );
.(chalk.());
}
{
(, {
: .,
[.]: ,
});
.(chalk.());
} (err) {
.(chalk.());
.(chalk.());
;
}
.(chalk.());
.(chalk.());
(!fs.()) {
.(chalk.());
.(chalk.());
;
}
content = fs.(, );
= ;
= ;
insertCode = (
,
atobMachineCode,
email,
nowDateStr
);
(content.() && content.()) {
.(chalk.());
startIdx = content.();
endIdx = content.();
afterEndIdx = endIdx + .;
(startIdx >= && endIdx > startIdx) {
content = content.(, startIdx) + insertCode + content.(afterEndIdx);
fs.(, content, );
.(chalk.());
} {
.(chalk.());
}
} {
requireRegex = ;
match = requireRegex.(content);
(match) {
insertPos = match. + match[].;
content =
content.(, insertPos) + insertCode + content.(insertPos);
fs.(, content, );
.(chalk.());
} {
.(
chalk.()
);
}
}
.(chalk.());
.(chalk.());
regKey = ({
: .,
: ,
});
{
encodedRegCode = .().();
targetVal = ;
(regKey, , targetVal);
.(chalk.());
(regKey, , );
.(chalk.());
} (err) {
.(chalk.(), err);
}
.(chalk.());
.(chalk.());
regCode = ();
.(chalk.());
.(chalk.());
.(
chalk.(
)
);
}
().( {
.(chalk.(), err);
.(chalk.());
readlineSync.();
});
修改代码中的安装路径
使用 Ctrl + F 搜索 possiblePaths ,将实际 Typora 安装目录添加进去。
步骤 4:安装依赖包
在 typora-crack 文件夹内右键选择在终端中打开,依次执行以下命令:
4.1 初始化 npm 项目
javascript
npm init -y
4.2 安装所需依赖
javascript
npm install asar chalk@4 readline-sync winreg @electron/fuses
注意:这里必须使用旧版
chalk@4,否则下一步运行时会报错。
4.3 运行破解脚本
javascript
node crack.js
执行后,脚本会要求输入机器码(稍后获取)。
步骤 5:获取机器码并完成激活
打开 Typora,在激活页面选择离线激活,如果没有离线激活则关闭 Typora 后重新打开。
在离线激活页面复制机器码然后关闭 Typora,回到终端粘贴机器码。按照后续终端提示即可完成激活。
声明:本教程参考 L站 文章(
关于 Typora 最新激活脚本-优化版【2026-05-23有效】 - 资源荟萃 - LINUX DO),仅供学习交流使用,请支持正版软件。
