某MarkDown编辑器在线激活流程简易分析
## 在线激活注册码本地合法校验:
``` javascript
const Z = e => {
const r = "L23456789ABCDEFGHJKMNPQRSTUVWXYZ";
if (!/^({6}-){3}{6}$/.exec(e)) return !1;
var e = e.replace(/-/g, ""),
t = e.substr(22);
return !e.replace(//g, "") && t == (e => {
for (var t = "", n = 0; n < 2; n++) {
for (var o = 0, i = 0; i < 16; i += 2) o += r.indexOf(e);
o %= r.length, t += r
}
return t
})(e)
}
```
使用网传代码轻松过:
``` javascript
function randomSerial() {
var $chars = 'L23456789ABCDEFGHJKMNPQRSTUVWXYZ';
var maxPos = $chars.length;
var serial = '';
for (i = 0; i < 22; i++) {
serial += $chars.charAt(Math.floor(Math.random() * maxPos));
}
serial += (e => {
for (var t = "", i = 0; i < 2; i++) {
for (var a = 0, s = 0; s < 16; s += 2) a += $chars.indexOf(e);
t += $chars
}
return t
})(serial)
return serial.slice(0, 6) + "-" + serial.slice(6, 12) + "-" + serial.slice(12, 18) + "-" + serial.slice(18, 24);
}
```
然后发送给远端服务器 `api/client/activate` 提交激活信息
``` javascript
t = {
v: A() + "|" + s.getVersion(), //系统平台
license: t, //激活码
email: e, //邮箱
l: await G(), //hostname 以及用户名信息
f: await M(), //机器唯一识别码
u: s.setting.generateUUID(), //UUID
type: global.devVersion ? "dev" : "",//当前环境是否是开发环境
force: n //
};
```
获取机器名以及当前用户名信息:
``` javascript
function G() {
var o = (o = process.env.USER) || a(842).userInfo().username;
switch (process.platform) {
case "win32":
return process.env.COMPUTERNAME + " | " + o + " | Windows";
case "darwin":
return new Promise(n => {
a(620).exec("scutil --get ComputerName", {
timeout: 5e3
}, (e, t) => {
n(!e && t ? t.toString().trim() + " | " + o + " | darwin" : a(842).hostname() + " | " + o + " | darwin")
})
});
default:
return a(842).hostname() + " | " + o + " | Linux"
}
}
```
机器码获取:
``` javascript
const M = async() => {
if (!i) {
if (w) {
const t = C("native-reg");
var e = t.openKey(t.HKEY.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", t.Access.WOW64_64KEY | t.Access.READ);
i = t.getValue(e, null, "MachineGuid"), t.closeKey(e)
} else i = await a(560).machineId({
original: !0
});
i || r.captureMessage(" Failed to get fingerPrint"), i = T(i, "typora").substr(0, 10).replace(/[/=+-]/g, "a"), b && (i += "darwin")
}
return i
};
```
然后会根据返回值进行判断
``` javascript
if (JSON.stringify(o.data), console.log(" response code is " + o.data.code), o.data.code == D.SUCCESS) return await Y(o.data.msg) ? [!0, ""] : [!1, "Please input a valid license code"];
if (o.data.code == D.OUT_OF_LIMIT) return n ? await Y(o.data.msg) ? [!0, "Your license has exceeded the max devices numbers.\nThe oldest device was unregistered automatically."] : o.data.msg ? [!1, "Please input a valid license code"] : [!1, "Your license has exceeded the max devices numbers."] : ["confirm", 'Your license has exceeded the max devices numbers.\nIf you click "Continue Activation", this device will be activated and the oldest device will be unregistered automatically.'];
if (o.data.code == D.INVALIDATE) return [!1, "Please input a valid license code"];
if (o.data.code == D.WRONG_USER) return [!1, "This license code has been used with a different email address."]
```
当其返回为`D.SUCCESS`的时候,会写入注册表,保存注册信息
``` javascript
function Y(e) {
try {
var {
fingerprint: t,
email: n,
license: o,
type: i
} = I(e) || {};
return t == await M() && n && o ? (H(n, o, i), d().put("SLicense", e + "#0#" + (new Date).toLocaleDateString("en-US")), l = !0) : (console.log(" validate server return fail"), V(), !1)
} catch (e) {
throw console.error(e.stack), new Error("WriteActivationInfoFail")
}
}
```
这里的`H`是本地稍加检测授权,并刷新当前开启的授权状态(有一定猜测在里面),而`l`则是当前全局的激活状态
``` javascript
function H(e, t, n) {
c = t, S = n, (l = !(!(x = e) || !c)) && re()
}
```
紧接着就将注册信息利用`d()`写入注册表了
``` javascript
const d = function () {
var n;
return O = null == O ? w ? function () {
const o = C("native-reg"),
i = "Software\\Typora";
return {
get: function (e) {
var t = o.openKey(o.HKCU, i, o.Access.READ);
if (null == t) return "";
e = o.getValue(t, null, e);
return o.closeKey(t), e
}, put: function (e, t) {
var n = o.createKey(o.HKCU, i, o.Access.WRITE);
o.setValueSZ(n, e, t), o.closeKey(n)
}
}
}() : (n = s.setting.prepDatabase(i), {
put: function (e, t) {
console.log("ls put " + e), n.getState() = t, n.write()
}, get: function (e) {
return n.getState()
}
}) : O
};
```
同时这里要注意返回的json数据经过了`I()`进行处理
``` javascript
const I = e => {
if (!e) return e;
var t;
try {
t = Buffer.from(e, "base64");
const n = a(289).publicDecrypt(`-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7nVoGCHqIMJyqgALEUrc 5JJhap0+HtJqzPE04pz4y+nrOmY7/12f3HvZyyoRsxKdXTZbO0wEHFIh0cRqsuaJ PyaOOPbA0BsalofIAY3mRhQQ3vSf+rn3g+w0S+udWmKV9DnmJlpWqizFajU4T/E4 5ZgMNcXt3E1ips32rdbTR0Nnen9PVITvrbJ3l6CI2BFBImZQZ2P8N+LsqfJsqyVV wDkt3mHAVxV7FZbfYWG+8FDSuKQHaCmvgAtChx9hwl3J6RekkqDVa6GIV13D23LS qdk0Jb521wFJi/V6QAK6SLBiby5gYN6zQQ5RQpjXtR53MwzTdiAzGEuKdOtrY2Me DwIDAQAB-----END PUBLIC KEY-----`, t);
return JSON.parse(n.toString("utf8"))
} catch (e) {
return null
}
}
```
这里使用的RSA算法,对返回的授权信息进行解密。
另外,启动的时候有授权合法检测,不合法仍然会掉授权。
``` javascript
const j = e => {
console.log(" firstValidateLicense"), L = !0;
var t = W(),
{
license: n,
email: o,
type: i
} = t || {};
n && o ? (H(o, n, i), B(t, e), console.log(" pass validateLicenseInfoStr")) : V()
}
```
里面调用`V()`函数,一般用作授权删除或者清理非法授权使用。相较于最初的验证版本,个人感觉在线验证更加复杂了一些,不过新增了离线激活模式。
``` javascript
function V(e) {
l || (e = ""), c = x = "", l = !1, d().put("SLicense", ""), e && $(p.getPanelString("Typora is now deactivated"), p.getPanelString(e)), ae()
}
``` 刚刚瞧了一下,typora.io被g?w屏蔽了。。。可惜 涛之雨 发表于 2022-2-21 19:23
刚刚瞧了一下,typora.io被g?w屏蔽了。。。可惜
它刚一收费就被屏蔽了,原因不知道,然后在国际官网中加了国内官网的链接 感谢楼主的分享 本帖最后由 涛之雨 于 2022-2-23 07:30 编辑
刚刚又跟了一下离线激活的代码,验证甚至更少。。。
只要rsa公钥可以解开,并且指纹和生存的机器码的一部分一样,并且用户名,密钥和类型不为空就行。。。。
所以理论上可以搞布丁一键解包,修改公钥成自己的,再替换,就可以写注册机了(g?w还帮我们把那个验证服务器给屏蔽了) 涛之雨 发表于 2022-2-23 07:28
刚刚又跟了一下离线激活的代码,验证甚至更少。。。
只要rsa公钥可以解开,并且指纹和生存的机器码的一部 ...
对 离线的蛮简单的,但是一直在更新从1.1.2开始分析,几天就升级到1.1.5,打算等他稳定了之后在仔细跟跟。感觉单靠fw不太行,他在网不通的时候会尝试切换国内服务器,可以把域名替换掉。 CRoot 发表于 2022-2-23 19:30
对 离线的蛮简单的,但是一直在更新从1.1.2开始分析,几天就升级到1.1.5,打算等他稳定了之后在仔细跟跟 ...
那就再替换的时候多一步,
正则表达式把这两个域名全都replace掉
{:301_986:}
此外他这个保存的时候会自动url编码,关掉再打开的时候不会解码回来显示{:301_1001:}
(算不算bug?去提一下issue给他们说一下{:301_1001:}) 涛之雨 发表于 2022-2-23 19:35
那就再替换的时候多一步,
正则表达式把这两个域名全都replace掉
{:301_1009:} 这-- 还是不要吧,会被挨打。另做好对应版本的atom.js就行,然后放到node_modules,会优先就近加载。论坛那位最早搞aes密钥的github上的issue上提到过这一点。自动化的话就用github的action全自动拉去对应版本替换发布就行。 涛之雨 发表于 2022-2-23 19:35
那就再替换的时候多一步,
正则表达式把这两个域名全都replace掉
ee = function() {
var e = Object(s.a)(l.a.mark((function e(t) {
var n, a, r, i, c, o, s, u, m, f, p;
return l.a.wrap((function(e) {
for (;;) switch (e.prev = e.next) {
case 0:
if ("+" == t || "#" == t) {
e.next = 2;
break
}
return e.abrupt("return");
case 2:
t = t.substr(1, t.length - 2), e.prev = 3, window.webkit && (n = t.split("|") || ["", ""], a = Object(d.a)(n, 2), r = a, i = a, (c = JSON.parse(window.atob(r))).sig = i, t = JSON.stringify(c)), e.next = 11;
break;
case 7:
return e.prev = 7, e.t0 = e.catch(3), window.alert("Invalid Activation Token"), e.abrupt("return");
case 11:
return U(!0), e.next = 14, window.Setting.invokeWithCallback("offlineActivation", t);
case 14:
o = e.sent, s = Object(d.a)(o, 4), u = s, m = s, f = s, p = s, U(!1), u ? (q(m), j(!0), b(0), C(f), H(p), R("off")) : (window.alert("Invalid Activation Token"), q(m || "Unknown Error"));
case 22:
case "end":
return e.stop()
}
}), e, null, [
])
})));
return function(t) {
return e.apply(this, arguments)
}
}
不知道大佬注意到这段代码了没有,离线调试了许久,才发现在页面前端还有一处判断,我说我的keygen死活生成不出来。 CRoot 发表于 2022-2-26 17:49
ee = function() {
var e = Object(s.a)(l.a.mark((function e(t) {
...
对最前面加一个+就行了
然后可以shift+f12调试
弹出的注册窗口要用代码有一个xxx.debug,调用就有控制台了
页:
[1]
2