解决Typora1.13.7 掉激活问题,可以重新激活

emperor.bei096 2026-07-03 02:32 1

*Windows 用户看这里*


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


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


** 初版 **



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:\\software_tools\\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('打开 Typora,在菜单栏点击 帮助 (Help) -> 我的许可证 (My License...)->在弹出的许可证窗口中,点击“离线激活”(Enter License -> Offline Activation)。此时窗口中会显示一串字符,那就是你的机器码 (Machine Code)。'))

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()

得到ActivatedTypora.js文件,


安装依赖npm install asar [email protected] readline-sync winreg @electron/fuses


搜索 const Typora_Installation_Path =


const Typora_Installation_Path = ‘D:\software_tools\Typora’


将 D:\software_tools\Typora 值为Typora的实际安装路径


执行node .\ActivatedTypora.js然后输入机器码


(路径-> 获取机器码最直接的方法就是通过 Typora 的离线激活窗口:


打开 Typora,在菜单栏点击 帮助 (Help) → 我的许可证 (My License…)。


在弹出的许可证窗口中,点击“离线激活”(Enter License → Offline Activation)。


此时窗口中会显示一串字符,那就是你的机器码 (Machine Code)。


后续按照脚本提示操作,将获取到的这串字符粘贴回去即可。)


(推荐,最简单):手动关闭调试日志


1.由于你只是不想再看到日志文件,并不需要重新执行整个破解流程,直接修改启动文件即可:


2.用文本编辑器(如 VSCode、Notepad++)打开


D:\software_tools\Typora\resources\app\launch.dist.js(注意:路径是 app 目录下的,不是 app.bak)


3.搜索 const HookDebug =,将后面的值从 true 改为 false。


4.搜索 function writeLog(…data) {


function writeLog(...data) {

if (!HookDebug) return; // 增加这一行

const log = `[${new Date().toLocaleString()}] [Log] ${data.join(" ")}\n------------------\n`;

fs.appendFileSync(LOG_PATH, log);

}

5.保存文件,重启 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("【&#128064; 监控】检测到 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(

​ \`[&#128737;&#65039; fsHook] 程序试图 fs.\${property} 重定向 \${filePath} --> \${redirectPath}\`

​ );

​ return fsHook[property].call(this, redirectPath, ...args);

​ }

​ writeLog(\`[&#128737;&#65039; 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(

​ \`[&#128737;&#65039; fsHook/Promises] 程序试图 fs.promises.\${property} 重定向 \${filePath} --> \${redirectPath}\`

​ );

​ return fsPromisesHook[property].call(this, redirectPath, ...args);

​ }

​ writeLog(

​ \`[&#128737;&#65039; 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(

​ \`[&#128064;IPC 请求] 收到 .invoke("\${channel}") 参数:\`,

​ JSON.stringify(args)

​ );

​ try {

​ const result = await listener(event, ...args);

​ filter &&

​ writeLog(

​ \`[&#128064;IPC 响应] .handle("\${channel}") 返回结果:\`,

​ JSON.stringify(result)

​ );

​ return result;

​ } catch (error) {

​ filter && writeLog(\`[&#128064;IPC 错误] .handle("\${channel}") 执行出错:\`, error);

​ throw error;

​ }

​ });

};

}

const crypto = require("crypto");

const originalPublicDecrypt = crypto.publicDecrypt;

crypto.publicDecrypt = function (key, buffer) {

if (HookDebug) {

​ writeLog("-------------------------------------------");

​ writeLog("【&#128064; 监控】 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(\`[&#128064;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(\`[&#128737;&#65039; 拦截] 伪造激活验证响应: {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(\`[&#128064;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:\\software_tools\\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()

/**

\* 强制关闭 launch.dist.js 中的调试日志功能

\* @param {string} filePath - launch.dist.js 的完整路径

*/

function disableHookDebug(filePath) {

if (!fs.existsSync(filePath)) {

console.log(chalk.red(`错误:找不到 ${filePath},无法关闭日志。`))

return

}

let content = fs.readFileSync(filePath, 'utf-8')

// 1. 将 const HookDebug = true; 强制改为 false(若原本为 false 则不变)

content = content.replace(/const HookDebug\s*=\s*true;/, 'const HookDebug = false;')

// 2. 在 writeLog 函数体内插入 if (!HookDebug) return;

const writeLogRegex = /function writeLog\s*\([^)]*\)\s*\{/

const match = writeLogRegex.exec(content)

if (match) {

const insertPos = match.index + match[0].length

// 避免重复插入

if (!content.includes('if (!HookDebug) return;')) {

content = content.slice(0, insertPos) + '\n if (!HookDebug) return;' + content.slice(insertPos)

}

} else {

console.log(chalk.yellow('警告:未找到 writeLog 函数定义,跳过插入。'))

}

fs.writeFileSync(filePath, content, 'utf-8')

console.log(chalk.green('已成功关闭调试日志(HookDebug = false,writeLog 添加了返回判断)。'))

}

// 要求输入机器码和邮箱

console.log(chalk.cyan('打开 Typora,在菜单栏点击 帮助 (Help) -> 我的许可证 (My License...)->在弹出的许可证窗口中,点击“离线激活”(Enter License -> Offline Activation)。此时窗口中会显示一串字符,那就是你的机器码 (Machine Code)。'))

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.error(chalk.red(`错误:找不到 ${asarPath},请确认 Typora 安装路径正确,且 app.asar 文件存在。`))

console.error(chalk.red('如果之前已手动处理过,请恢复 app.asar 或重新安装 Typora。'))

process.exit(1)

}

if (!fs.existsSync(TyporaEXE)) {

console.error(chalk.red(`错误:找不到 ${TyporaEXE},请确认安装路径。`))

process.exit(1)

}

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('三、正在关闭调试日志(防止生成日志文件)...'))

disableHookDebug(LaunchDistJS)

// 四、修改注册表

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()
最新回复 (4)
  • ZenDust 07-03 02:35
    1

    佬,这个掉激活确实是很烦恼。

    只是能不能排版稍微。。。。

  • 清风 07-03 02:36
    2

    我也觉得,代码块用三引号包起来吧,看的好难受

  • emperor.bei096 楼主 07-03 02:56
    3

    帖子更新了,你可以看了,应该没有问题。。。。。。。。。。。。。。

  • emperor.bei096 楼主 07-03 02:56
    4

    帖子更新了,你可以看了,应该没有问题。。。。。。。。。。。。。。

* 帖子来源Linux.do
返回