关于 Typora 最新激活脚本-优化版【2026-05-23有效】

b1ac3 2026-05-22 11:20 1

Windows 用户看这里


参考这篇文章,昨天在 Windows 上对最新版 Typora(v1.13.6.0)进行了激活。



最新帖子解决掉激活等问题

前戏
之前在论坛中安装佬友的 Typor1.11.7-x64-CN 虽然能成功激活,但是后面过一阵子就只能同时打开一个markdown文件
在吾爱上刷到大佬的破解分享
https://www.52pojie.cn/forum.php?mod=viewthread&tid=2084047&extra=page%3D1&page=1
看不懂,遂来L站找无脑教程,…




但是我遇到了几个问题:

1、首次(后台没有 Typora 进程)打开 Typora 或使用 Typora 打开 md 文件,会自动弹出开发者工具。

2、我把前台窗口关闭后,再次点击打开其他 md 文件,无法打开(无法弹窗),任务管理器查看有 Typora 后台进程,把所有后台进程关闭后,再次打开其他 md 文件,这时才可以打开。

3、md 文件所在目录下会自动生成 hook log 日志




分析了下,确实脚本有些小问题:



  • 自动弹 DevTools:来自 win.webContents.openDevTools({ mode: “detach” })

  • 关闭窗口后后台残留:来自把 electron.app.quit 覆盖成空函数,导致 Typora 主进程不退出




相关问题已经修好了,自取:


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");

function getInsertCode(EnableHookDebug, atobMachineCode, email, nowDateStr) {
return `
/** Hook破解开始 */
const electron = require("electron");

// 是否启用劫持调试
const HookDebug = ${EnableHookDebug ? "true" : "false"};

// 调试日志定义
const LOG_PATH = ".\\\\Typora_Hook_Log.txt";
//fs.rmSync(LOG_PATH, { force: true });
function writeLog(...data) {
const log = \`[\${new Date().toLocaleString()}] [Log] \${data.join(
" "
)}\\n------------------\\n\`;
fs.appendFileSync(LOG_PATH, log);
}

// 调试模式只记录窗口创建,不改写 app.quit,也不自动打开 DevTools。
// 改写 app.quit 会让前台窗口关闭后主进程残留,双击 md 时 second-instance 无法正常唤起窗口。
if (HookDebug) {
electron.app.on("browser-window-created", (_event, win) => {
writeLog("【👀 监控】检测到 BrowserWindow 实例化!");
});
}

// 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);
writeLog(
\`[🛡️ fsHook] 程序试图 fs.\${property} 重定向 \${filePath} --> \${redirectPath}\`
);
return fsHook[property].call(this, redirectPath, ...args);
}
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);
writeLog(
\`[🛡️ fsHook/Promises] 程序试图 fs.promises.\${property} 重定向 \${filePath} --> \${redirectPath}\`
);
return fsPromisesHook[property].call(this, redirectPath, ...args);
}
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) {
// writeLog(\`[IPC 注册] .handle 监听频道: "\${channel}"\`);
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"));
}
// return originalPublicDecrypt.call(this, key, buffer);
// 直接返回伪造的明文 Buffer
return Buffer.from(
JSON.stringify({
deviceId: "${atobMachineCode.l}",
fingerprint: "${atobMachineCode.i}",
email: "${email}",
license: "Cracked_By_DreamNya",
version: "${atobMachineCode.v}",
date: "${nowDateStr}",
type: "DreamNya",
})
);
};

// 劫持联网验证
electron.app.whenReady().then(() => {
electron.protocol.handle("https", async (request) => {
writeLog(\`[👀electron.net Request] \${request.method} \${request.url}\`);

writeLog("request.url typeof:", typeof request.url, "value:", request.url);
// 拦截目标请求,伪造响应
if (request.url === "https://store.typora.io/api/client/renew") {
if (HookDebug){
writeLog(\`[🛡️ 拦截] 伪造激活验证响应: {success:true, msg: \${btoa("DreamNya")}}\`);
}
return new Response(
JSON.stringify({ success: true, msg: btoa("DreamNya") }),
{
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;
}

});
});
/** Hook破解结束 */
`;
}

let EnableBackup = false; // 是否备份原始文件
let EnableHookDebug = false; // 是否启用调试日志

const Typora_Installation_Path = "D:\\App\\Tool\\Typora";
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 {
execSync("taskkill /F /IM Typora.exe");
console.log(chalk.green("已关闭所有 Typora.exe 进程"));
} catch (e) {
console.log(chalk.red("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("一、正在进行反反调试操作..."));
console.log(chalk.yellow("解包 asar"));
await asar.extractAll(asarPath, appDir);

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);
}
}
}
copyDir(appDir, appBakDir);

console.log(chalk.yellow("移除 app.asar 文件"));
// 3. 重命名 app.asar 为 app.asar.bak
if (EnableBackup) {
fs.renameSync(asarPath, asarBakPath);
} else {
fs.rmSync(asarPath, { force: true });
}

console.log(
chalk.yellow("修改 Typora.exe 的 fuse 配置,允许加载未打包的 app 目录")
);

if (EnableBackup) {
// 修改前先备份
fs.copyFileSync(TyporaEXE, `${TyporaEXE}.bak`);
}

// 修改fuse配置(同时会修改程序hash)
flipFuses(TyporaEXE, {
version: FuseVersion.V1,
[FuseV1Options.OnlyLoadAppFromAsar]: false,
});

console.log(chalk.green("反反调试操作完成!"));

// 二、注入破解代码
console.log(chalk.yellow("二、正在注入破解代码到 launch.dist.js..."));

// 读取原文件内容
let content = fs.readFileSync(LaunchDistJS, "utf-8");

// 查找第一个require语句后的分号
const requireRegex = /require\([^)]+\);/;
const match = requireRegex.exec(content);

if (match) {
const insertPos = match.index + match[0].length;
const insertCode = getInsertCode(
EnableHookDebug,
atobMachineCode,
email,
nowDateStr
);
// 插入代码
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("三、正在注册激活..."));
// try {
// execSync(`start "" "${TyporaEXE}"`);
// console.log(chalk.green("Typora 已启动!"));
// } catch (e) {
// console.log(chalk.red("Typora 启动失败,请手动打开。"));
// }

// const regCode = generateRegCode();
// console.log(chalk.green(`您的注册码为:${regCode}`));
// console.log(chalk.yellow("请复制并用于激活。"));
// console.log(chalk.cyan("请按回车键继续..."));

// // 关闭Typora进程
// closeTyporaProcesses();

// 三、修改注册表
console.log(chalk.yellow("三、正在修改注册表以关闭联网验证..."));
// 修改注册表,尽量关闭联网验证
// 注册表路径
const regKey = new WinReg({
hive: WinReg.HKCU,
key: "\\Software\\Typora",
});

try {
await setRegValue(regKey, "SLicense", "RHJlYW1OeWE=#0#1/1/2029");
console.log(chalk.green("SLicense 注册表字段写入成功"));
await setRegValue(regKey, "IDate", nowDateStr);
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();




详细激活过程可以参考原帖。感谢大佬提供的脚本。



很简单,就是要确保后台没有 Typora 进程;拿到机器码。









macOS 用户看这里


另外,有些佬友可能是 mac 用户,mac 上 的破解也很简单。


有一个老牌破解站点(macked)。但是需要注册才可以使用。


我注册了账号。


为了方便佬友,可以把 UUID 发给我,我直接生成激活命令给你。


UUID 获取方法。


ioreg -rd1 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $4}'

最新回复 (19)
  • Heart Queen 05-22 11:26
    1

    用最新的也会出现这个问题吗?我自测没有说的这种情况呀

  • b1ac3 楼主 05-22 11:30
    2

    不清楚… 昨晚我是遇到了这两个问题,然后想着就优化一下

  • 小炒肉 05-22 11:40
    3

    会不会掉激活啊,而且为啥我每次打开都会弹窗已激活

  • b1ac3 楼主 05-22 14:26
    4

    不会,激活一次就可以了。我这边目前使用都正常。

  • topsage 05-22 14:27
    5

    有linux版本的吗?现在切到了linux系统,还是用的那个最后的免费版

  • b1ac3 楼主 05-22 16:27
    6

    还是



    这个目前没有,稍微参考 js 改一下 linux 的应该也没啥问题

  • topsage 05-22 17:35
    7


    感谢大佬。。。

    哈哈哈哈,我用mimo直接按你那个教程和脚本,直接给我修改激活成功了

  • b1ac3 楼主 05-22 17:39
    8

    现在有了 AI 改脚本方便多了。。。

  • topsage 05-22 17:40
    9



    超级吊啊


    修改js,然后修复激活过程的错误,还给我写了个脚本自动激活

  • lostffking 05-22 17:40
    10

    之前一直停留在1.9.5 版本,没找到合适的破解办法,试试你的方法; ^-^

  • huanyi 05-22 17:46
    11

    请问下mac可以用吗?还是目前只支持windows啊?

  • topsage 05-22 17:48
    12

    用ai去改下脚本应该就可以。。

    我linux的,用佬的脚本,用ai去修改了下,已经激活上了

  • 戴仙笙 05-22 17:54
    13

    我现在用的是Typedown,算是弱化版的Typora,常用的一些功能都有,主要是免费

  • b1ac3 楼主 05-22 17:56
    14

    之前一直停留在 1.9.5 版本,没找到合适的破解办法,试试你的方法; ^-^



    mac 有个最简单的办法 不需要脚本


    ioreg -rd1 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $4}'


    佬你到命令行把这个 uuid 找到,发给我,我直接发你激活码

  • lostffking 05-22 17:58
    15

    我是win11/10 , 我先自己摸索一下下;

  • b1ac3 楼主 05-22 18:21
    16

    mac 有个最简单的办法 不需要脚本


    ioreg -rd1 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $4}'


    佬你到命令行把这个 uuid 找到,发给我,我直接发你激活码

  • Rhine. 05-22 18:26
    17

    我也是用这个佬的脚本激活的,刚开始激活的时候没什么问题,就前段时间不知道为什么打开了一段时间就掉激活,重新进去两次好了,之后挂久了又掉激活,到L站找问题,用另一个佬的法子在安装目录加了一个文件就没掉了,不过还是谢谢佬优化激活问题 ^-^

  • baby 05-22 18:46
    18



    激活成功1.13.6

  • 银河快递 05-22 18:52
    19

    佬,是加什么文件啊,我也是偶尔突然弹出来需要激活

* 帖子来源Linux.do
返回