吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 19767|回复: 83
收起左侧

[原创] 关于tagLyst工具授权验证的分析报告

  [复制链接]
Silent_DXX 发表于 2020-4-24 12:20
本帖最后由 Silent_DXX 于 2020-4-24 12:22 编辑

破解前的软件界面

892d5c1c-273d-4ca6-b03e-b06dca475028.jpg

找到安装目录

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工具,点击上面的购买升级按钮,随便填入一个邮箱和一串激活码,并点击在线激活按钮,我们发现会注册失败,对话框提示激活码并不正确。
17b93f17-5c0b-4cdb-8c9d-b743fefcb9ba.png
6e1f3243-2b8e-4f7c-9e23-cf258bb30248.png

cd tagLyst\run\resources\app
grep "激活码并不正确" . -rn

这里使用grep工具搜索关键词(你可以使用任何搜索工具来操作)
4afe60f4-41ba-4306-aae8-5d769ccce421.png
这里我们找到了很多个选择,怎么确认哪一个才是呢,我也没有很好的办法,只能适当修改下搜索到的地方,并测试查看输出。
b2a98f17-f08e-4f4e-96f6-702673c87f43.png
一次次尝试,很快就找到了地方,就是在app1.jsZc3baa406ebb4e97e02569e3c6e7a7a93

Zc3baa406ebb4e97e02569e3c6e7a7a93 = {
...
      MSG_ACTIVATION_FAIL_DENIED:
        "<h2>很遗憾!</h2>您输入的激活码并不正确 xxxx。请检查拼写。<br/>如有问题,请联系我们获得支持。",
...
}

我们继续搜索MSG_ACTIVATION_FAIL_DENIED

grep "MSG_ACTIVATION_FAIL_DENIED" . -rn

随后我们看到一个很关键的地方
ecce6b22-e4ac-4a9f-87d2-61262fa32c15.png
而这段代码的else部分是这样的
3559bee2-c127-4e4d-869e-7ee9f4c9ef81.png
所以这个变量e就是成功的关键,那么这个e是怎么过来的呢?
答案是runActivation
我们在runActivation上面添加一个断点或者使用debugger

debugger;

c78e20f5-fe68-4b99-995d-f4d5c9e2d79d.png
一步一步执行到runActivation函数里面
93cd478a-8a80-4a44-b177-bac1eba0c188.png
服务器返回数据
25c4ec30-3936-40d8-9100-18c057ccf690.png
经过分析,我大概理解了这个代码,下面用注释的方式来解释:

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

破解成果图

49ec0b3e-404e-45f1-8d49-3f02a4607ae0.jpg
完结,撒花~~~

后语

通过此次分析,我们可想而知软件安全的重要性,开发者在考虑软件授权时应该要考虑的更多一些才行。tagLyst虽然使用了服务器验证,但是返回数据居然是json,而且是没有加密的数据,这是一个很严重的问题。
对于此次分析,我给出的建议是:

  1. 软件授权部分不应该使用容易被还原代码的语言设计(例如:JavaScript、python、java等),而且使用c/c++之类不容易被还原代码的语言设计(electron支持c语言开发的模块)
  2. 网络授权的api应该要加密数据,最好采用非对称加密方式来提高数据安全
  3. 授权部分的代码采用代码混淆技术来提高被反破解的难度
  4. 授权代码不仅仅采用一个api来验证,最好采用不用的接口在不同的地方或时间来验证
  5. c/c++语言设计授权验证,可以添加反调试机制、花指令、动态代码等多种方式来提高安全性

免费评分

参与人数 16威望 +2 吾爱币 +116 热心值 +13 收起 理由
夺笋 + 1 + 1 谢谢@Thanks!
WAzmq + 1 + 1 谢谢@Thanks!
fengtertor + 1 + 1 修改完显示试用版的,需要点注册,然后随便输入邮箱注册码,点击注册即可成.
92013 + 1 这个软件卡卡的
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
padao + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
yxl0520 + 1 用心讨论,共获提升!
bovod + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
冥界3大法王 + 1 用心讨论,共获提升!
朱朱你堕落了 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
生有涯知无涯 + 1 我很赞同!
antiol + 3 + 1 我很赞同!
hez + 1 鼓励转贴优秀软件安全工具和文档!
uzcool + 3 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
RS水果 + 1 我很赞同!
xzgxp + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

uzcool 发表于 2022-3-5 01:57
本帖最后由 uzcool 于 2022-3-5 02:00 编辑
20150605101006 发表于 2022-3-3 12:47
tagLyst大佬求一下,我一直pj不成功

试了下最新的版本好像还可以..
https://uzcool.118pan.com/b1078834
https://uzcool.118pan.com/b1078835
替换之后需要输入下邮箱和随便输入个注册码应该就行了


Snipaste_2022-03-05_01-59-26.jpg

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
wbx103 + 1 + 1 我很赞同!

查看全部评分

麦米尔加弗德 发表于 2022-11-11 18:40
最新版本的 tagLyst ES 也就是 tagLyst 5
方法需要一点小改动,来交作业了,代码写的很好
https://wwk.lanzoue.com/b03vg9hna
密码:hq3q

免费评分

参与人数 2吾爱币 +2 热心值 +1 收起 理由
小叔sir + 1 + 1 谢谢@Thanks!
wwwfunk + 1 谢谢@Thanks!

查看全部评分

古墓卓洛 发表于 2020-5-2 16:41
林小轩112 发表于 2020-4-30 13:09
感谢大佬分享,已经成功。
xieyi1393 发表于 2020-4-25 10:12
其实不需要对称加密,HTTPS公钥扎钉好像就可以
5ipj2007 发表于 2020-4-25 10:00
修改后还是要确保联网,订阅才能激活。
怎么样不联网激活
5ipj2007 发表于 2020-4-25 09:47
改了启动程序后空白
斩风 发表于 2020-4-24 19:02
学习下,感谢分享
头像被屏蔽
bingmoer 发表于 2020-4-24 17:25
提示: 作者被禁止或删除 内容自动屏蔽
 楼主| Silent_DXX 发表于 2020-4-24 16:45
冥界3大法王 发表于 2020-4-24 16:35
也就是说还是修改的js文件?
动态调试和伪造数据是关键的两步。

修改js文件是一种办法,一劳永逸。也可以伪造一个服务器来完成欺骗。
朱朱你堕落了 发表于 2020-4-24 15:28
这是什么语言写的软件?
blsn3548 发表于 2020-4-24 15:56
朱朱你堕落了 发表于 2020-4-24 15:28
这是什么语言写的软件?

应该是node.js
 楼主| Silent_DXX 发表于 2020-4-24 15:58
朱朱你堕落了 发表于 2020-4-24 15:28
这是什么语言写的软件?

语言是JavaScript,技术是nodejs+electron实现的,有兴趣可以搜素electron,跨平台的桌面应用开发。

免费评分

参与人数 1热心值 +1 收起 理由
朱朱你堕落了 + 1 大神好厉害!!!

查看全部评分

冥界3大法王 发表于 2020-4-24 16:35
也就是说还是修改的js文件?
动态调试和伪造数据是关键的两步。
bovod 发表于 2020-4-24 17:34
按大神思路操作,打开后是空白,无法进入程序,不知道是哪里出错了。
blsn3548 发表于 2020-4-24 19:09
Proxifier+fiddler = ok
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-18 10:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表