WWW.YOUINFO.SITE
标签聚合 Typora

/tag/Typora

v2ex · 2026-06-02 00:54:01+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍?

v2ex · 2026-06-01 23:54:01+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍?

v2ex · 2026-06-01 23:47:23+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍?

v2ex · 2026-06-01 23:12:34+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍?

v2ex · 2026-06-01 23:12:34+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍?

v2ex · 2026-06-01 23:05:08+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍?

v2ex · 2026-06-01 22:52:20+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍?

v2ex · 2026-06-01 22:47:27+08:00 · tech

花钱买正版 Typora 好几年了,今天成功把我心态搞崩。 这软件防盗版防得像防贼一样,每次自动升级完,必定自动退出登录,强制要求重新输入 License 。今天就在我急着用它码字的关键时刻,升级后直接弹窗:不输序列号就只能退出。 找验证码有多麻烦,用过的人都懂。交了钱支持正版,结果工作思路隔三差五被它的激活弹窗促不及防地打断。Typora 的产品经理到底知不知道,对于一个生产力工具来说,“随时可用”比“严格防盗”重要一万倍? https://imgur.com/a/iRXcDzt

LinuxDo 最新话题 · 2026-05-24 21:35:16+08:00 · tech

Typora 离线许可证激活工具(Windows EXE,开箱即用)【截至 2026-05-24 v1.13.6 有效】 站在前人肩膀上,感谢两位佬友的思路与原始脚本: 原始方案: Typora 安装与激活教程 脚本优化版(解决日志、DevTools、进程残留问题): 关于 Typora 最新激活脚本-优化版 背景 两篇文章的思路已经很完整,但对非技术用户门槛仍然存在: 需要手动配置 Node.js 环境并 npm install Typora 安装路径需要手动修改脚本 运行出错时窗口直接消失,看不到任何提示 多次操作可能出现重复注入,导致 Typora 无法启动 于是基于 b1ac3 佬的优化版 ,用 AI 重新设计交互流程,打包成单个 EXE,降低使用门槛。 技术实现原理(来自上述两篇文章) Typora 基于 Electron,许可证校验逻辑打包在 app.asar 中,工具分三层处理: 绕过完整性校验 :解包 app.asar → 复制到 app.bak/ → 修改 Typora.exe 的 Electron Fuse 配置,允许加载未打包的 app/ 目录代替 asar 包 Hook 许可证校验 :在 launch.dist.js 开头注入 JS,劫持 crypto.publicDecrypt ,返回构造好的许可证 JSON,绕过 RSA 签名验证 拦截联网续签 :劫持 electron.protocol.handle("https") ,对 store.typora.io/api/client/renew 返回构造好的成功响应 注册表写入 :写入 SLicense 、 IDate ,使 Typora 启动时读取到有效许可证 相比原始脚本的改进: 彻底去除日志文件 :原脚本 fsHook 无论调试开关状态如何,都会在打开文件的目录下生成 Typora_Hook_Log.txt ,本工具注入代码中无任何日志逻辑 去除 DevTools 自动弹出 防重复注入 :工具始终从未修改的 app.bak/ 副本读取源文件进行注入,检测到已有注入标记时会提示先还原再重新注入 使用步骤 前置条件: Windows 系统(Win10 / Win11) 官方版 Typora(推荐 v1.13.6) 以管理员身份运行 (Typora 默认安装在 C:\Program Files ,需要写入权限) Step 1:获取机器码 打开 Typora → 帮助 → 我的许可证 → 输入序列号 → 离线激活 ,复制弹窗中的机器码(Base64 字符串) Step 2:运行工具 右键 TyporaCrack.exe → 以管理员身份运行 Typora 许可证工具 v1.0 ────────────────────────────── ✓ 检测到 Typora: C:\Program Files\Typora 使用此路径?(Y/N,默认Y): 当前状态: 干净(未激活) 请选择操作: [1] 激活 [2] 还原原始文件 选择 (默认1): 1 [提示] 机器码:Typora → 帮助 → 我的许可证 → 输入序列号 → 离线激活 中复制 机器码: <粘贴机器码> deviceId: xxx fingerprint: xxx version: win|1.13.6 邮箱 (回车默认 [email protected]): ✓ 解包 app.asar ✓ 备份原始文件 ✓ 修改 Electron Fuse 配置 ✓ 写入许可证校验 Hook ✓ 写入注册表 ────────────────────────────── ✓ 完成!直接打开 Typora 即可使用。 提示: 建议关闭【自动检查更新】和【使用国内服务器】选项 Step 3:打开 Typora 直接启动,无需输入任何序列号,已处于已授权状态。 还原功能 工具会自动备份所有修改( app.asar.bak 、 Typora.exe.bak ),随时可以选择 [2] 还原原始文件 一键恢复,同时清除注册表中写入的字段。 关于 EXE 与 Node.js 不需要安装 Node.js。 使用 pkg 将 Node.js 运行时与依赖一同打包进 EXE(约 38MB),双击即可运行。 注意事项 关闭自动更新 :Typora 自动更新会覆盖修改的文件 关闭国内服务器选项 : 偏好设置 → 通用 → 使用国内服务器 建议关闭,否则可能绕过网络验证失败 换设备或重装后需重新操作 :机器码与设备绑定,重装后会变化 下载地址:蓝奏云 https://wwbkr.lanzoul.com/iB16P3q9rz8j 密码:3j3r 感谢 horrah 佬 和 b1ac3 佬 的原始思路,本工具是在其基础上的工程化封装与体验优化。 5 个帖子 - 4 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-05-22 11:20:44+08:00 · tech

参考这篇文章,昨天对最新版 Typora 进行了激活。 Typora【超便捷md文本编辑器】 安装与激活教程 但是我遇到了两个问题: 1、首次(后台没有 Typora 进程)打开 Typora 或使用 Typora 打开 md 文件,会自动弹出开发者工具。 2、我把前台窗口关闭后,再次点击打开其他 md 文件,无法打开(无法弹窗),任务管理器查看有 Typora 后台进程,把所有后台进程关闭后,再次打开其他 md 文件,这时才可以打开。 分析了下,确实脚本有些小问题: 自动弹 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(); 详细激活过程可以参考原帖。感谢大佬提供的脚本。 3 个帖子 - 2 位参与者 阅读完整话题

v2ex · 2026-05-13 21:08:00+08:00 · tech

之前一直使用破解的 typora ,最近一直弹窗激活。正好 claude 还有点额度,就模仿了 typora 的实时渲染风格写了一个 web 端的编辑器。 提供三个模式 源码:方便复制,编辑态出现渲染问题时,可以直接修改源码 编辑:类似 typora 实时渲染 只读:方便鼠标点选滑动复制等。编辑态点选时,光标所在的行会变成编辑态,不方便阅读 使用浏览器的存储,不与后端交互,不持久存储 可以新增,也可以将已有的文件拖拽进去 快捷键: 快捷键 快捷键 ctrl + / 切换源码模式/编辑模式 ctrl + shift + r 切换只读模式/编辑模式 (主要是方便滑动复制) ctrl + shift + e 下载到本地 ctrl + \ 隐藏左侧文件目录 github: https://github.com/KysonGeek/ZenMark