恋の歌的logo
分类 归档 标签

记一次对StarUML-3.2.2的解包

发表于2020-06-12
更新于2023-02-13
分类于编程
总字数1.6K
阅读时长 ≈6 分钟

前言 🔗

感觉很久没写帖子了…
老是把上学期的实训还没有做完当借口感觉也不是很行
索性就随便写写吧
(项目还没有做完,cao!)

准备 🔗

上系统分析与设计的课需要用到一个StartUML的软件
虽然老师已经提供了完整的工具了
但是人嘛,总是喜新厌旧的
不是最新版这手就不舒服…
直接上官网下载了最新版的
高高兴兴的替换
What the f**k? 直接黑屏

不行,上网找找看看有没有新的解决方法
emmm,找了一会,找到了好东西

吾爱论坛的一个帖子: 另一种 StarUML 3.x 完美**的思路

感觉能行,是我最擅长的js 直接开搞

StartXML官网:StarUML 3

这次使用的版本为最新版,版本号为3.2.2
除此之外需要node环境,需要npm的一个asar的一个包
这个就不细讲,因为我也不懂emmm…

过程 🔗

看完吾爱那个帖子还是蛮吃惊的
这应用好像是Electron的…
简单讲就是套了个谷歌浏览器的桌面应用

ok那开始,先把最新的app.asar文件解包出来
asar extract app.asar app
extract解包,app.asar为需要解包的文件 app为解包的目录

解包出来
emmm,挺熟悉的…

马不停蹄,直接奔着about-dialog.js文件去
src/dialogs文件下
在这个文件下的showDialog函数下果然找到了这一段代码

这段代码判断根据凭证管理器返回的状态来给模态框设置内容

javascript
if (app.licenseManager.getStatus() === true) {
  // ...
} else {
  // ...
}

所以现在首要的是找到app这个变量
这个变量有着licenseManager的实例
ctrl这个变量发现有两处存在这个变量
分别是src/main-process/application.jssrc/index.js

其中application.js文件的代码段为

metadataManager?
很明显不是我们的licenseManager
直接不管
src/index.js文件

new AppContxt() 按照字面意思应该是app的上下文
应该是这个不会错
点进去发现是一个类
ctrl+f搜索licenseManager
果然在38行发现了引入的凭证管理器

ok定位到src/enginer/license-manager.js这个文件
继续ctrl+f搜索getStatus
找到代码,是LicenseManager的一个方法
这个方法返回了一个定义在文件中的变量status

根据前面我们知道,当status为true,就会往模态框渲染已注册的内容了
所以继续搜索status
发现一个setStatus函数
这个函数不在类中
(截图好累,直接贴代码吧….)

javascript
/**
 * Set Registration Status
 * This function is out of LicenseManager class for the security reason
 * (To disable changing License status by API)
 * @private
 * @param {boolean} newStat
 * @return {string}
 */
function setStatus(licenseManager, newStat) {
  if (status !== newStat) {
    status = newStat;
    licenseManager.emit("statusChanged", newStat);
  }
}

注释也给出了没写在类方法中的原因

To disable changing License status by API
译过来就是:防止通过api的方式去改变凭证

也就是不想让license管理器对象直接设置status状态
这也反过来证明这个status状态确实决定注册的状态
(有点心虚的味道…)

这里还可以发现设置状态会广播一个statusChanged的事件 licenseManager.emit('statusChanged', newStat) 状态根据传进来的newStat决定

接下来看到底谁调用了setStatus这个函数了
搜索发现checkLicenseValidity函数和register函数都调用了

怎么有两个函数调用…
看了看方法,找到猫腻
类中有一个方法appReady
代码是这样的

javascript
class LicenseManager extends EventEmitter {
  // ...

  appReady() {
    this.checkLicenseValidity();
  }
}

看方法名就知道了,在应用准备的时候调用这个函数
那八九不离十是checkLicenseValidity这个函数了
而且register函数的注释也让我更加确定

Check the license key in server and store it as license.key file in local 通过服务器检查凭证并将它以license.key文件进行储存

不难分析,刚安装的软件肯定是没有注册的
需要通过键入密钥来激活
也就是这几种情况

  • 开启app - 有本地文件 - 进行检验 - 显示检验状态
  • 开启app - 没有本地文件 - 显示未注册
  • 开启app - 没有本地文件 - 输入密钥 - 成功 - 生成文件方便下次启动的验证

当然我们手头是没有密钥的,所以register函数关系不大
目的是在启动检测的时候假装存在检验文件, 并检测成功

观察checkLicenseValidity函数,它调用了validate函数进行验证
但是validate函数其实已经不重要了
为啥呢?
因为validate函数没有调用setStatus这个函数了
也就是注册状态的变化和validate函数块无关了
validate函数执行的对应回调有关
也就是checkLicenseValidity函数中的代码

javascript
class LicenseManager extends EventEmitter {
  // ...

  checkLicenseValidity() {
    this.validate().then(
      () => {
        setStatus(this, true);
      },
      () => {
        setStatus(this, false);
        UnregisteredDialog.showDialog();
      },
    );
  }
}

这里可以看到成功就设置状态为true
失败设置状态为false并展示一个未注册的展示框

OK大体上摸清楚了
只要把代码这个函数的代码改为

javascript
class LicenseManager extends EventEmitter {
  // ...

  checkLicenseValidity() {
    this.validate().then(
      () => {
        setStatus(this, true);
      },
      () => {
        // 失败也设置未注册成功
        setStatus(this, true);
        // 失败不展示失败的模态框
        // UnregisteredDialog.showDialog()
      },
    );
  }
}

其实大部分工作都已经完成了
接下来回到最开始的if判断

javascript
if (app.licenseManager.getStatus() === true) {
  var info = app.licenseManager.getLicenseInfo();
  var licenseTypeName = "Unknown";
  switch (info.licenseType) {
    case "PS":
      licenseTypeName = "Personal";
      break;
    case "CO":
      licenseTypeName = "Commercial";
      break;
    case "ED":
      licenseTypeName = "Educational";
      break;
    case "CR":
      licenseTypeName = "Classroom";
      break;
  }
  $license.html("Licensed to " + info.name);
  $licenseType.html(licenseTypeName + " License");
  $quantity.html(info.quantity + " User(s)");
} else {
  $license.html("UNREGISTERED");
}

判断成功后会获取凭证的信息
在LicenseManager类中也可以找到这个方法
也就是只要构造出符合结构的对象就可以了
也可以直接在方法体里面改
我的话通过返回的对象来改
也就是

javascript
class LicenseManager extends EventEmitter {
  // ...

  getLicenseInfo() {
    // 不返回lincenseInfo了
    return licenseInfo;
    //这里获取密钥所包含的信息,具体可以看validate函数,里面有对密钥的解析,也就是json格式
    return {
      name: "17软件工程",
      product: "系统分析与设计",
      licenseType: "PS",
      quantity: "INDEX",
      timestamp: "1583395020",
      licenseKey: "仅供学习,请勿用于商业用途",
      crackedAuthor: "Dedicatus545",
    };
  }
}

到这里其实基本就完成了
最后就是重新打包这个文件
asar pack app app.asar
替换之后就完成了

附上一张截图

仅供学习使用,请勿使用于商业用途。

哦呐该,如果没有评论的话,瓦达西...