关于tagLyst工具授权验证的分析报告
本帖最后由 Silent_DXX 于 2020-4-24 12:22 编辑# 破解前的软件界面
# 找到安装目录
`tagLyst`是基于`electron`技术开发的工具,使用这种技术的软件很容易会被解包并查看到程序代码。
在`tagLyst\run\resources`目录下,我们可以看到如下几个文件:
```
app.asar
default_app.asar
electron.asar
```
有关程序逻辑的主要代码在`app.asar`中
# app.asar 解包
`electron`程序在发布的时候会通过`asar`工具对源文件进行打包。首先通过下面的命令安装`asar`工具(需要提前安装nodejs环境)
```
npm install -g asar
```
下面我们进行解包操作
```
asar extract app.asar ./app
```
通过这个命令把`app.asar`文件解包到`./app`下面
# 代码格式化
解包之后虽然我们能够看到代码,但是代码格式上却乱七八糟的,通过安装下面的工具对源码进行格式化操作
```
npm install --g prettier
```
安装成功后, 把我们可能需要分析的源码进行格式化
```
prettier --write app/main.js
prettier --write app/
```
# 开启调试功能
打开package.json文件
```
{
"name": "tagLyst Next",
"version": "3.1.0",
"main": "main.js",
"dependencies": {
"request": "^2.88.0"
}
}
```
通过查看`package.json`文件,得知主程序文件为`main.js`
我们在`main.js`文件中找到`createWindows`函数,然后在合适的位置上添加下面的代码来启动调试功能。调试功能启用之后我们就可以像调试前端的方式去调试`tagLyst`工具,并分析其破解过程。
```
function createWindows() {
...
mainWindow.webContents.openDevTools();
...
}
```
# 注册分析
打开`tagLyst`工具,点击上面的`购买升级`按钮,随便填入一个邮箱和一串激活码,并点击`在线激活`按钮,我们发现会注册失败,对话框提示激活码并不正确。
```
cd tagLyst\run\resources\app
grep "激活码并不正确" . -rn
```
这里使用`grep`工具搜索关键词(你可以使用任何搜索工具来操作)
这里我们找到了很多个选择,怎么确认哪一个才是呢,我也没有很好的办法,只能适当修改下搜索到的地方,并测试查看输出。
一次次尝试,很快就找到了地方,就是在`app1.js`的`Zc3baa406ebb4e97e02569e3c6e7a7a93`中
```
Zc3baa406ebb4e97e02569e3c6e7a7a93 = {
...
MSG_ACTIVATION_FAIL_DENIED:
"<h2>很遗憾!</h2>您输入的激活码并不正确 xxxx。请检查拼写。<br/>如有问题,请联系我们获得支持。",
...
}
```
我们继续搜索`MSG_ACTIVATION_FAIL_DENIED`
```
grep "MSG_ACTIVATION_FAIL_DENIED" . -rn
```
随后我们看到一个很关键的地方
而这段代码的else部分是这样的
所以这个变量`e`就是成功的关键,那么这个`e`是怎么过来的呢?
答案是`runActivation`
我们在`runActivation`上面添加一个断点或者使用`debugger`
```
debugger;
```
一步一步执行到`runActivation`函数里面
服务器返回数据
经过分析,我大概理解了这个代码,下面用注释的方式来解释:
```
function runActivation(e, t, n) {
// e: 邮箱地址
// t: 激活码
// n: 返回函数
try {
saveActivationEvidence(
"evidence1",
"from " + new Date(fs.statSync(__exePath).birthtime).format("yyyyMMdd")
);
} catch (e) {}
try {
saveActivationEvidence("evidence2", JSON.stringify(tglData.dashboard));
} catch (e) {}
if (SSO.noTrack) return console.log("No Track for ACT");
var a = nz(e, "").toLowerCase(),
i = nz(t, {});
try {
var l = nz(n, function () {}),
s = getDeviceInfo(a),// 获取设备ID信息
o = s.deviceID,
r = s.deviceTextEnc,
c = require("http"), // 发送POST请求使用
d = { // 把机器信息组包为json数据
data: JSON.stringify({
activationCode: a,
deviceTextEnc: r,
deviceID: o,
userID: nz(i.userID),
tag1: i.tag1,
tag2: i.tag2,
tag3: i.tag3,
evidence1: loadActivationEvidence("evidence1", ""),
evidence2: loadActivationEvidence("evidence2", ""),
evidence3: loadActivationEvidence("evidence3", ""),
}),
};
d = JSON.stringify(d);
// 构建POST请求数据
var f = {
method: "POST",
host: Pd5e58e4fab1ba92ff7d0bb4ce2459058.activationHost, // activation.taglyst.com
port: Pd5e58e4fab1ba92ff7d0bb4ce2459058.activationPort, // 12050
path: "/activate.do",
headers: {
"Content-Type": "application/json",
"Content-Length": d.length,
},
},
// 这个函数很关键,它是发送POST请求服务器的回复数据
// 当激活码无效时的返回数据{"error":"denied"}
// denied 是不是比较眼熟?没错就是提示激活码不正确前的那个e的值
g = function (e) {
try {
console.log('request: ' + e);
var t = JSON.parse(e); // 服务器返回的数据
n = nz(t.userID, ""); // 用户ID
i = nz(t.expiringDate, ""); // 激活码到期日
s = nz(t.error, ""); // error为denied
// 下面就是判断是不是Business的版本,该值是由服务器返回,并把数据写入到配置文件中
(Pd5e58e4fab1ba92ff7d0bb4ce2459058.bizc = nz(t.isBusinessClass, 0)
? 1
: 0),
Pd5e58e4fab1ba92ff7d0bb4ce2459058.bizc
? writeProjectData("vcls", "bizc")
: removeProjectData("vcls"); // 把bizc写入到[计算机名.vcls]文件中
nz(t.userRole, "").toLowerCase();
var o = nz(t.teamID, "");// teamID 暂时没有理解是干啥的
// 下面就是保存数据并赋值服务器的数据
o &&
(writeProjectData(
"temd",
nz(Pd5e58e4fab1ba92ff7d0bb4ce2459058.temd, "")
),
(Pd5e58e4fab1ba92ff7d0bb4ce2459058.temd = o)),
n && // 保存用户ID
(writeProjectData("usrd", n),
(Pd5e58e4fab1ba92ff7d0bb4ce2459058.usrd = n)),
i &&
((Pd5e58e4fab1ba92ff7d0bb4ce2459058.avdt = new Date(i)),
(Pd5e58e4fab1ba92ff7d0bb4ce2459058.actd = 1),
(Pd5e58e4fab1ba92ff7d0bb4ce2459058.lckd = 0),
ckacv2 && ckacv2(),
writeProjectData("avdt", i), // 保存到期时间
writeProjectData("actc", a)),// 保存邮箱地址
s &&
"locked" == s && // 感觉是服务器封杀用户用的
((Pd5e58e4fab1ba92ff7d0bb4ce2459058.lckd = 1),
writeProjectData("actc", ""),
writeProjectData(
"avdt",
new Date("2001-1-1").format("yyyy-MM-dd")
)),
l(s); // 执行回调函数
} catch (e) {
console.log(e);
}
};
if (i.isOffline) {
try {
var u = parseOfflineActivationCode(a, o);
} catch (e) {
l("offlineFailed");
}
return (
cerr("OfflineActivationResData", JSON.stringify(u)),
void g(JSON.stringify(u))
);
}
(req = c.request(f, function (e) { // 发送POST请求
if (!a)
return console.log(
"* Activation Res will not work at startup without ActivationCode."
);
e.setEncoding("utf8"),
e.on("data", g), // 服务器回复数据,并调用上面的g函数
e.on("end", function () {
console.log("res ends.");
}),
e.on("error", function (e) {
console.error("res error:", e), l("failure");
});
})),
req.setTimeout(1e4, function () {
console.log("req timed out"), l("failure");
}),
req.on("error", function (e) {
console.log("req error", e), l("failure");
}),
req.write(d),
req.end();
} catch (e) {
console.log(e);
}
}
```
# 构思破解
1. 当我们点击`在线激活`按钮时,会发送一个POST数据到`http://activation.taglyst.com:12050/activate.do`服务器中,这些数据包含邮箱号,激活码,机器ID等信息。
2. 随后服务器会返回一个`json`数据,如果激活码是无效的就返回 `{"error":"denied"}`, 否则就返回正确的验证数据
3. 所以我们可以尝试在服务器返回时,构造一个假数据来欺骗软件说服务器通过验证了,正确的验证信息是某某某
# 尝试破解
```
...
g = function (e) {
try {
console.log('request: ' + e);
var t = JSON.parse(e);
t.userID = "52pojie"; // 构造假ID
t.expiringDate = "2999/1/1"; // 到期时间
t.error = ""; // 清空error
t.isBusinessClass = 1; // 商业版本
n = nz(t.userID, "");
i = nz(t.expiringDate, "");
s = nz(t.error, "");
console.log("avdt:", i, s);
...
```
通过测试居然发现成功的破解了软件!!令人诧异。。。一个简单的欺骗就结束了??
# 重新打包
```
asar pack app app.asar
```
# 破解成果图
完结,撒花~~~
# 后语
通过此次分析,我们可想而知软件安全的重要性,开发者在考虑软件授权时应该要考虑的更多一些才行。`tagLyst`虽然使用了服务器验证,但是返回数据居然是`json`,而且是没有加密的数据,这是一个很严重的问题。
对于此次分析,我给出的建议是:
1. 软件授权部分不应该使用容易被还原代码的语言设计(例如:JavaScript、python、java等),而且使用c/c++之类不容易被还原代码的语言设计(electron支持c语言开发的模块)
2. 网络授权的api应该要加密数据,最好采用非对称加密方式来提高数据安全
3. 授权部分的代码采用代码混淆技术来提高被反破解的难度
4. 授权代码不仅仅采用一个api来验证,最好采用不用的接口在不同的地方或时间来验证
5. c/c++语言设计授权验证,可以添加反调试机制、花指令、动态代码等多种方式来提高安全性
本帖最后由 uzcool 于 2022-3-5 02:00 编辑
20150605101006 发表于 2022-3-3 12:47
tagLyst大佬求一下,我一直pj不成功
试了下最新的版本好像还可以..
https://uzcool.118pan.com/b1078834
https://uzcool.118pan.com/b1078835
替换之后需要输入下邮箱和随便输入个注册码应该就行了
最新版本的 tagLyst ES 也就是 tagLyst 5
方法需要一点小改动,来交作业了,代码写的很好
https://wwk.lanzoue.com/b03vg9hna
密码:hq3q 亲测成功,给大佬点赞! 感谢大佬分享,已经成功。 其实不需要对称加密,HTTPS公钥扎钉好像就可以 修改后还是要确保联网,订阅才能激活。
怎么样不联网激活 改了启动程序后空白 学习下,感谢分享 冥界3大法王 发表于 2020-4-24 16:35
也就是说还是修改的js文件?
动态调试和伪造数据是关键的两步。
修改js文件是一种办法,一劳永逸。也可以伪造一个服务器来完成欺骗。 这是什么语言写的软件? 朱朱你堕落了 发表于 2020-4-24 15:28
这是什么语言写的软件?
应该是node.js 朱朱你堕落了 发表于 2020-4-24 15:28
这是什么语言写的软件?
语言是JavaScript,技术是nodejs+electron实现的,有兴趣可以搜素electron,跨平台的桌面应用开发。 也就是说还是修改的js文件?
动态调试和伪造数据是关键的两步。 按大神思路操作,打开后是空白,无法进入程序,不知道是哪里出错了。
https://ae01.alicdn.com/kf/U0ca613984d634b1a8cad0a31dca7db17n.jpg Proxifier+fiddler = ok