步骤 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: "${atobMachineCode.l}",
fingerprint: "${atobMachineCode.i}",
email: "${email}",
license: FIXED_REG_CODE,
version: "${atobMachineCode.v}",
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破解结束 */
`;
}
// 从代码1引入:多路径自动检测
let EnableBackup = false; // 是否备份原始文件
let EnableHookDebug = false; // 是否启用调试日志
// 从代码1引入:自动检测安装路径
function findTyporaPath() {
const possiblePaths = [
"E:\\app\\install\\Typora",
"C:\\Program Files\\Typora",
"E:\\Program Files\\Typora",
process.env.LOCALAPPDATA + "\\Programs\\Typora"
];
for (const p of possiblePaths) {
if (fs.existsSync(path.join(p, "Typora.exe"))) {
return p;
}
}
return null;
}
let Typora_Installation_Path = findTyporaPath();
if (!Typora_Installation_Path) {
console.log(chalk.red("Typora 未在默认位置找到"));
console.log(chalk.cyan("请输入 Typora 安装路径: "));
Typora_Installation_Path = readlineSync.question().trim().replace(/^"|"$/g, '');
// 验证输入的路径
if (!fs.existsSync(path.join(Typora_Installation_Path, "Typora.exe"))) {
console.log(chalk.red("错误:指定路径中未找到 Typora.exe"));
console.log(chalk.cyan("按回车键退出..."));
readlineSync.question();
process.exit(1);
}
}
console.log(chalk.green(`找到 Typora: ${Typora_Installation_Path}\n`));
const resourcesPath = path.join(Typora_Installation_Path, "resources");
const asarPath = path.join(resourcesPath, "app.asar");
const appDir = path.join(resourcesPath, "app");
const appBakDir = path.join(resourcesPath, "app.bak");
const asarBakPath = path.join(resourcesPath, "app.asar.bak");
const TyporaEXE = path.join(Typora_Installation_Path, "Typora.exe");
const LaunchDistJS = path.join(appDir, "launch.dist.js");
// 随机生成一个符合前端验证格式的注册码
function generateRegCode() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let code = '+';
for (let i = 0; i < 8; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
}
code += '#';
return code;
}
function closeTyporaProcesses() {
try {
// 修复:添加 encoding 选项解决乱码问题
execSync("taskkill /F /IM Typora.exe", {
windowsHide: true,
encoding: 'utf-8' // 添加编码设置
});
console.log(chalk.green("已关闭所有 Typora.exe 进程"));
} catch (e) {
// 修复:捕获错误时不显示乱码的错误信息
console.log(chalk.yellow("Typora.exe 未运行或已关闭"));
}
console.log(
chalk.yellow(
"已尝试自动关闭所有 Typora.exe 进程,如果未关闭请手动关闭后再运行此程序。"
)
);
// 回车继续
console.log(chalk.cyan("请按回车键继续..."));
readlineSync.question();
}
function setRegValue(regKey, name, value) {
return new Promise((resolve, reject) => {
regKey.set(name, WinReg.REG_SZ, value, function (err) {
if (err) reject(err);
else resolve();
});
});
}
function getNowDateStr() {
const now = new Date();
const dd = String(now.getDate()).padStart(2, "0");
const mm = String(now.getMonth() + 1).padStart(2, "0");
const yyyy = now.getFullYear();
return `${mm}/${dd}/${yyyy}`;
}
const nowDateStr = getNowDateStr();
// 要求输入机器码和邮箱
console.log(chalk.cyan("请输入机器码: "));
const machineCode = readlineSync.question();
console.log(chalk.cyan("请输入邮箱: "));
const email = readlineSync.question();
// 询问是否开启备份(默认开启)与调试(默认关闭)
console.log(chalk.cyan("请选择是否开启备份与调试选项:"));
console.log(chalk.cyan("【建议开启】是否开启备份?(Y/N): "));
const backupAnswer = readlineSync.question();
console.log(chalk.cyan("【建议关闭】是否开启调试?(Y/N): "));
const debugAnswer = readlineSync.question();
EnableBackup = backupAnswer.toLowerCase() === "y";
EnableHookDebug = debugAnswer.toLowerCase() === "y";
// Base64 解码
function atob(str) {
return Buffer.from(str, "base64").toString("utf-8");
}
const atobMachineCode = JSON.parse(atob(machineCode));
console.log(chalk.yellow("deviceId: " + atobMachineCode.l));
console.log(chalk.yellow("fingerprint: " + atobMachineCode.i));
console.log(chalk.yellow("version: " + atobMachineCode.v));
// 关闭所有 Typora.exe 进程
closeTyporaProcesses();
console.log(chalk.green("==== 开始破解... ===="));
async function main() {
// 一、反反调试
console.log(chalk.yellow("一、正在进行反反调试操作..."));
// 检查必要文件是否存在
if (!fs.existsSync(asarPath)) {
console.log(chalk.red(`错误:未找到 app.asar 文件 (${asarPath})`));
console.log(chalk.yellow("可能的原因:"));
console.log(chalk.yellow("1. Typora 版本不兼容"));
console.log(chalk.yellow("2. app.asar 已被移动或删除"));
console.log(chalk.yellow("3. Typora 安装不完整"));
// 检查是否有备份文件可以恢复
if (fs.existsSync(asarBakPath)) {
console.log(chalk.cyan("发现备份文件 app.asar.bak,是否恢复?(Y/N): "));
const restoreAnswer = readlineSync.question();
if (restoreAnswer.toLowerCase() === 'y') {
fs.copyFileSync(asarBakPath, asarPath);
console.log(chalk.green("已从备份恢复 app.asar"));
} else {
console.log(chalk.red("无法继续,缺少必要文件"));
return;
}
} else {
console.log(chalk.red("未找到备份文件,请重新安装 Typora"));
return;
}
}
// 检查 app 目录是否已存在(可能是之前破解过的)
if (fs.existsSync(appDir)) {
console.log(chalk.yellow("发现已存在的 app 目录(可能已破解过)"));
console.log(chalk.cyan("是否删除并重新解包?(Y/N): "));
const reExtractAnswer = readlineSync.question();
if (reExtractAnswer.toLowerCase() === 'y') {
console.log(chalk.yellow("删除旧的 app 目录..."));
fs.rmSync(appDir, { recursive: true, force: true });
} else {
console.log(chalk.yellow("将使用现有的 app 目录"));
}
}
// 解包 asar(如果 app 目录不存在)
if (!fs.existsSync(appDir)) {
console.log(chalk.yellow("解包 asar..."));
try {
await asar.extractAll(asarPath, appDir);
console.log(chalk.green("解包完成"));
} catch (err) {
console.log(chalk.red(`解包失败: ${err.message}`));
console.log(chalk.yellow("可能原因:文件损坏或权限不足"));
return;
}
} else {
console.log(chalk.gray("跳过解包(app 目录已存在)"));
}
console.log(
chalk.yellow("复制 app 到 app.bak(递归复制)【应对完整性校验】")
);
// 2. 复制 app 到 app.bak(递归复制)
function copyDir(src, dest) {
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
copyDir(srcPath, destPath);
} else {
fs.copyFileSync(srcPath, destPath);
}
}
}
// 只有在 app.bak 不存在时才复制
if (!fs.existsSync(appBakDir)) {
copyDir(appDir, appBakDir);
console.log(chalk.green("备份完成"));
} else {
console.log(chalk.gray("app.bak 已存在,跳过备份"));
}
console.log(chalk.yellow("移除 app.asar 文件"));
// 3. 重命名 app.asar 为 app.asar.bak
if (fs.existsSync(asarPath)) {
if (EnableBackup) {
if (!fs.existsSync(asarBakPath)) {
fs.renameSync(asarPath, asarBakPath);
console.log(chalk.green("已备份 app.asar"));
} else {
fs.rmSync(asarPath, { force: true });
console.log(chalk.green("已删除 app.asar(备份已存在)"));
}
} else {
fs.rmSync(asarPath, { force: true });
console.log(chalk.green("已删除 app.asar"));
}
} else {
console.log(chalk.gray("app.asar 不存在,跳过删除"));
}
console.log(
chalk.yellow("修改 Typora.exe 的 fuse 配置,允许加载未打包的 app 目录")
);
if (EnableBackup && !fs.existsSync(`${TyporaEXE}.bak`)) {
// 修改前先备份
fs.copyFileSync(TyporaEXE, `${TyporaEXE}.bak`);
console.log(chalk.green("已备份 Typora.exe"));
}
// 修改fuse配置(同时会修改程序hash)
try {
flipFuses(TyporaEXE, {
version: FuseVersion.V1,
[FuseV1Options.OnlyLoadAppFromAsar]: false,
});
console.log(chalk.green("Fuse 配置修改成功"));
} catch (err) {
console.log(chalk.red(`Fuse 配置修改失败: ${err.message}`));
console.log(chalk.yellow("如果 Typora 正在运行,请先关闭后再试"));
return;
}
console.log(chalk.green("反反调试操作完成!"));
// 二、注入破解代码
console.log(chalk.yellow("二、正在注入破解代码到 launch.dist.js..."));
// 检查文件是否存在
if (!fs.existsSync(LaunchDistJS)) {
console.log(chalk.red(`错误:未找到 launch.dist.js (${LaunchDistJS})`));
console.log(chalk.yellow("解包可能失败,请检查 app 目录"));
return;
}
// 读取原文件内容
let content = fs.readFileSync(LaunchDistJS, "utf-8");
// 从代码1引入:检测已有注入,支持更新
const HOOK_START = "/** Hook破解开始 */";
const HOOK_END = "/** Hook破解结束 */";
const insertCode = getInsertCode(
EnableHookDebug,
atobMachineCode,
email,
nowDateStr
);
if (content.includes(HOOK_START) && content.includes(HOOK_END)) {
console.log(chalk.yellow("检测到已有注入代码,正在更新..."));
const startIdx = content.indexOf(HOOK_START);
const endIdx = content.indexOf(HOOK_END);
const afterEndIdx = endIdx + HOOK_END.length;
if (startIdx >= 0 && endIdx > startIdx) {
content = content.slice(0, startIdx) + insertCode + content.slice(afterEndIdx);
fs.writeFileSync(LaunchDistJS, content, "utf-8");
console.log(chalk.green("成功更新破解代码到 launch.dist.js"));
} else {
console.log(chalk.red("Hook 标记位置无效"));
}
} else {
// 查找第一个require语句后的分号
const requireRegex = /require\([^)]+\);/;
const match = requireRegex.exec(content);
if (match) {
const insertPos = match.index + match[0].length;
// 插入代码
content =
content.slice(0, insertPos) + insertCode + content.slice(insertPos);
fs.writeFileSync(LaunchDistJS, content, "utf-8");
console.log(chalk.green("成功插入破解代码到 launch.dist.js"));
} else {
console.log(
chalk.red("未找到 require 语句,破解代码未插入launch.dist.js。")
);
}
}
console.log(chalk.green("注入破解代码完成!"));
// 三、修改注册表
console.log(chalk.yellow("三、正在修改注册表以关闭联网验证..."));
// 修改注册表,尽量关闭联网验证
// 注册表路径
const regKey = new WinReg({
hive: WinReg.HKCU,
key: "\\Software\\Typora",
});
try {
// 从代码1引入:使用固定配置写入注册表
const encodedRegCode = Buffer.from(FIXED_REG_CODE).toString('base64');
const targetVal = `${encodedRegCode}#0#${FIXED_DATE}`;
await setRegValue(regKey, "SLicense", targetVal);
console.log(chalk.green("SLicense 注册表字段写入成功"));
await setRegValue(regKey, "IDate", FIXED_IDATE);
console.log(chalk.green("IDate 注册表字段写入成功"));
} catch (err) {
console.log(chalk.red("写入注册表失败:"), err);
}
console.log(chalk.green("==== 破解完成!使用愉快!===="));
console.log(chalk.yellow("后续操作建议:\n"));
const regCode = generateRegCode();
console.log(chalk.green(`\t1.您的注册码为:${regCode} 请复制并用于激活。`));
console.log(chalk.yellow("\t2. 关闭【自动检查更新】功能,防止被覆盖。"));
console.log(
chalk.yellow(
"\t3. 关闭【Typora服务器使用国内服务器】功能,避免绕过联网验证失败。"
)
);
}
main().catch(err => {
console.error(chalk.red("错误:"), err);
console.log(chalk.cyan("\n按回车键退出..."));
readlineSync.question();
});
修改代码中的安装路径
使用 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),仅供学习交流使用,请支持正版软件。
