Typora安装&激活&主题配置(保姆级教程)

方式一

1. 安装nodejs(自行解决)

2. 安装Typroa

[!Tip]

安装好后打开Tyroa,选择输入序列号->选择离线激活->复制机器码到记事本->关闭程序

复制下面的脚本到安装目录下D:\Typora\resources文件夹内创建pj.js文件

修改209行的路径为自己的安装路径const Typora_Installation_Path = “D:\Typora”;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
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 调用
// Hook Electron 模块,监控 BrowserWindow 实例化及阻止 app.quit 调用
// Node模块require后会进行缓存,即使再次require会指向同一个对象
if (HookDebug) {
Object.defineProperty(electron.app, "quit", {
value: function () {
writeLog("[🛡️ 拦截] 程序试图调用 app.quit(),已阻止。");
},
writable: true,
configurable: true,
});
electron.app.on("browser-window-created", (_event, win) => {
writeLog("【👀 监控】检测到 BrowserWindow 实例化!");

// 确保dom-ready后再打开DevTools 否则第一个窗口可能会无法打开
win.webContents.once("dom-ready", () => {
writeLog("【🔧】打开 DevTools...");
win.webContents.openDevTools({ mode: "detach" });
});
});
}

// 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:\\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.开始激活

  1. D:\Typora\resources路径下打开命令行窗口 【鼠标点击一下地址栏直接输入CMD即可打开命令行窗口】
  2. 安装chalk@4 asar readline-sync winreg @electron/fuses
1
npm install chalk@4 asar readline-sync winreg @electron/fuses
  1. 运行脚本
1
node pj.js
  1. 按照提示输入 安装Typora时复制的到记事本中的机器码

  2. 输入邮箱

    1
    admin@admin.com
  3. 按照提示输入

1
2
3
4
【建议开启】是否开启备份?(Y/N):
Y
【建议关闭】是否开启调试?(Y/N):
N
  1. 敲回车继续

1

  1. 回到命令行后安装 native-reg
1
npm install native-reg

方式二 (推荐)

一、官网地址

官网地址:https://typora.io/

中文官网:https://typoraio.cn/

官网下载地址:https://typoraio.cn/releases/all

二、激活工具

激活工具已整合打包,上传至云盘。

提取地址: https://www.alipan.com/s/73vzNVG7GoK

提取码:g95a

三、安装Typora

点击下载好的Typora的exe文件,这里跳过,安装过程选择推荐进行安装即可,需要修改安装路径,尽量不要安装在C盘。

安装好以后,关闭Typora运行窗口,不要选择15天试用,直接关闭激活窗口。

四、激活Typora

  1. 将激活工具node_inject.exe和license-gen.exe复制到Typora安装目录下,如图所示:
    img

  2. 在Typora安装目录输入cmd打开终端或快捷键Win+R打开运行窗口输入cmd进入终端。
    img

  3. 在终端输入命令。

    输入node_inject.exe,显示了done即生成成功。
    该命令是自动配置相关文件,在安装目录下会生成node目录。

    输入license-gen.exe,生成激活序列号。

    生成激活序列号后,此时不要关闭终端,复制序列号

  4. 运行Typora,在激活窗口输入邮箱和生成的激活序列号,即可激活。

image-20260606162336757

主题配置

[Typora主题导入]

要在Typora中导入主题,您可以按照以下步骤操作:

  1. 下载主题:首先,访问Typora的主题网站,选择并下载您喜欢的主题。下载后,您会得到一个压缩文件。(网盘里已经上传了一些主题,个人比较喜欢typora-theme-phycat系列下的phycat Radiation)
  2. 解压缩文件:将下载的压缩文件解压缩。通常,解压后会得到一个包含*.css*文件和其他资源文件的文件夹。
  3. 打开主题文件夹:在Typora中,点击菜单栏的“文件” -> “偏好设置” -> “外观”,然后点击“打开主题文件夹”按钮。
  4. 复制主题文件:将解压后的*.css*文件和相关资源文件复制到打开的主题文件夹中。
  5. 重启Typora:关闭并重新打开Typora。在菜单栏的“主题”选项中,您应该能看到新导入的主题。

通过以上步骤,您可以轻松地在Typora中导入和使用新的主题,使您的Markdown编辑体验更加个性化和美观。