吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 764|回复: 7
收起左侧

[原创] 新手分析Electron应用AI漫剧提示词助手的激活验证

[复制链接]
a3341736201 发表于 2026-5-14 13:08
本帖最后由 a3341736201 于 2026-5-15 19:40 编辑

我是小白,大家看个乐子,今天在一个公众号看到有人写了吸引人的标题“小白也能快速入门做ai漫剧”
点进去一看,要钱,而且是会员制,并api还是用的中转,费用还挺贵的,于是我打开逆向环境虚拟机 开干!



1.png

我们看整体的包结构,很明显就是Electron的产物,特别是在\resources 里面 有他app.asar
2.png
所以我可以很明确的确定 他就是Electron的产物
Electron 应用打包后,核心代码都压缩在一个叫 `app.asar` 的文件里(405MB)。这个 asar 其实就是个特殊格式的压缩包,用官方工具一行命令就能解开。
我们先解包

安装npm(至于如何安装,网上教程很多,不赘述)
帮我安装这个:npm install --engine-strict asar
安装好npm后执行命令安装asar:npm install asar -g
然后目录下 就解包出源文件了
3.png

然后我们看package.json

"name": "ai-manju-prompt-helper",
"version": "1.10.0",
"private": true,
"description": "AI漫剧提示词助手 - Electron + Vue desktop app",
"main": "electron/main.js",
"type": "module",
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"archiver": "^5.3.2",
"axios": "^1.15.0",
"builder-util-runtime": "^9.2.4",
"electron-updater": "^6.8.3",
"element-plus": "^2.13.7",
"jimp": "^1.6.1",
"three": "^0.184.0",
"vue": "^3.5.13"



可以看到 "main": "electron/main.js", 这里就是我们的入口文件,我们重点看这里
然后我们就搜索 http

async function lt(e, t = "") {
if ((Ce("loadRendererWindow:start", { isDev: ue, search: t }), ue))
return (
await e.loadURL(http://localhost:5173${t}),
e.webContents.openDevTools({ mode: "detach" }),
void Ce("loadRendererWindow:dev-loaded")
);
const r = new URL("../dist/index.html", import.meta.url);
((r.search = t.startsWith("?") ? t.slice(1) : t),
Ce("loadRendererWindow:loading-url", r.toString()),
await e.loadURL(r.toString()),
//e.webContents.openDevTools(),
Ce("loadRendererWindow:loaded"));
}



这里的这里的代码意思呢 就是 看以下ai的回答

// 定义一个异步函数 lt,接收两个参数:
// e = Electron 窗口对象(BrowserWindow)
// t = 路由/查询参数(默认空字符串)
async function lt(e, t = "") {

// 1. 埋点日志:记录开始加载渲染窗口
Ce("loadRendererWindow:start", { isDev: ue, search: t });

// 2. 判断是否是【开发环境】(ue = isDev,开发环境标识)
if (ue) {
// 开发环境走这里:
await e.loadURL(http://localhost:5173${t}); // 加载本地 Vite 开发服务器
e.webContents.openDevTools({ mode: "detach" }); // 自动弹出独立的开发者工具
Ce("loadRendererWindow:dev-loaded"); // 埋点:开发环境加载完成
return; // 结束函数
}

// 3. 非开发环境 = 生产环境,走这里
// 拼接打包后的 HTML 文件路径:项目根目录/dist/index.html
const r = new URL("../dist/index.html", import.meta.url);

// 给文件 URL 拼接查询参数(比如 ?id=123)
r.search = t.startsWith("?") ? t.slice(1) : t;

// 埋点日志:正在加载生产环境页面
Ce("loadRendererWindow:loading-url", r.toString());

// 真正加载本地打包好的 HTML 文件
await e.loadURL(r.toString());

// 埋点日志:生产环境页面加载完成
Ce("loadRendererWindow:loaded");
}



ok既然知道意思,那就很简单,因为我们要调试嘛,肯定要正式环境页面,那就要开调试模式,我们用到
e.webContents.openDevTools()

你们看代码我已经贴了上去,然后重新打包回去替换 ,我们运行看看

4.png

调试模式已经打开了,现在就很好做啦
然后我们重点关于俩个文件:
`index-BGUn5-_A.js` — 前端所有逻辑,包括验证判断都在里面
- `electron/licensing.js` — 后端的授权模块
进去后
代码很多,12w行,肯定不可能一行的看,然后我就打开调试工具
利用关键词定位大法,他既然提示失败 我就看提示失败代码

async function Kw() {
var V, ue, Le;
if (
!(
(V = window.desktopAPI) != null && V.getUnifiedActivationStatus
) &&
!((ue = window.desktopAPI) != null && ue.getActivationStatus)
) {
((S.loading = !1), (S.activated = !0));
return;
}
S.loading = !0;
const O =
(Le = window.desktopAPI) != null && Le.getUnifiedActivationStatus
? await window.desktopAPI.getUnifiedActivationStatus()
: await window.desktopAPI.getActivationStatus();
((S.source = O.source || ""),
(S.reason = O.reason || "not_activated"),
(S.entryMode =
O.activationEntryMode || S.entryMode || "license_key"),
(S.registrationCode =
O.registrationCode || S.registrationCode || ""),
(S.licenseKeyMasked = O.licenseKeyMasked || ""),
(S.expiresAtMs = O.expiresAtMs || 0),
(S.activated = !!O.activated),
(S.locked = O.reason === "time_tampered"),
(S.usingOfflineCache = O.usingOfflineCache === !0),
(S.lastOnlineCheckSucceeded = O.lastOnlineCheckSucceeded === !0),
!S.activated &&
S.source === "license_key" &&
(S.entryMode = "license_key"),
An(
O.activated
? ""
: O.message ||
(S.entryMode === "license_key"
? "请输入卡密完成激活。"
: "请复制注册码并输入与你的授权时长匹配的激活码。"),
),
(S.loading = !1),
S.activated && (await sp()));
}


然后审查代码:
第1段:检查 API 是否存在

Javascript

if (!(window.desktopAPI?.getUnifiedActivationStatus) &&
    !(window.desktopAPI?.getActivationStatus))
如果两个激活检测 API 都不存在(比如 preload 没注入),直接设置 loading=false, activated=true,当作已激活放行
第2段:调用 API 获取激活状态

Javascript

S.loading = true;  // 显示加载中
const O = await window.desktopAPI.getUnifiedActivationStatus()  // 优先用统一激活接口
        ?? await window.desktopAPI.getActivationStatus();         // 回退到旧接口
第3段:解析返回结果

Javascript

S.source = O.source || ""              // 激活来源
S.reason = O.reason || "not_activated" // 未激活原因
S.entryMode = O.activationEntryMode    // 激活方式:卡密 or 注册码
S.registrationCode = O.registrationCode // 注册码
S.licenseKeyMasked = O.licenseKeyMasked // 卡密(脱敏显示)
S.expiresAtMs = O.expiresAtMs || 0     // 过期时间
S.activated = !!O.activated            // 是否已激活(核心字段)
S.locked = O.reason === "time_tampered" // 时间被篡改则锁定
第4段:处理未激活提示

Javascript

if (!S.activated && S.source === "license_key")
    S.entryMode = "license_key";

An(O.activated ? "" : O.message ||
    S.entryMode === "license_key"
    ? "请输入卡密完成激活。"
    : "请复制注册码并输入与你的授权时长匹配的激活码。");
未激活时显示对应提示文案
第5段:激活成功后

Javascript

if (S.activated) await sp();  // 执行激活成功后的逻辑
一句话总结:检查激活状态 → 显示加载 → 拿结果 → 判断激活/未激活 → 显示对应 UI。

翻译成大白话就是:

1. 先检查有没有验证方法 → 没有就直接当已激活(搞笑吧)
2. 有的话调用 API → 问服务器"这个用户激活了没?
3. 根据返回结果→ `S.activated = !!O.activated`,一句话决定你的命运
4. 最后 → 只有 `S.activated` 为 true 才加载用户数据(设置、库、项目等)
ok知道逻辑了 Kw() 是核心验证函数
找的时候  顺藤摸瓜,找到在线定时验证,
找到 $g() 函数(定时器调度)

找到 $g() 函数(定时器调度)

function $g() {
  Fg();                    // 清除上一个定时器
  const O = hL();          // 获取随机间隔时间
  C = window.setTimeout(async () => {
    try {
      await mL();          // 执行在线验证
    } finally {
      S.activated && $g(); // 如果还是激活状态,继续安排下一次验证
    }
  }, O);
}

就是个 setTimeout 递归调用,每隔一段时间执行一次 mL()

javascript
async function mL() {
var V, ue, Le, it, Ye;

// 检查有没有在线验证的 API 方法
if (
!(
(V = window.desktopAPI) != null && V.verifyActivationStatusOnline
) &&
!(
(ue = window.desktopAPI) != null && ue.getUnifiedActivationStatus
) &&
!((Le = window.desktopAPI) != null && Le.getActivationStatus)
)
return;

// 调用在线验证(三种 API,哪个有就用哪个)
const O =
(it = window.desktopAPI) != null && it.verifyActivationStatusOnline
? await window.desktopAPI.verifyActivationStatusOnline()
: (Ye = window.desktopAPI) != null &&
Ye.getUnifiedActivationStatus
? await window.desktopAPI.getUnifiedActivationStatus()
: await window.desktopAPI.getActivationStatus();

if (
((S.source = O.activated
? O.source || S.source
: String(O.source || "").trim()),
(S.reason = O.reason || "not_activated"),
(S.entryMode = V.activated
? V.activationEntryMode || S.entryMode || "license_key"
: jw(V)),
(S.registrationCode =
V.registrationCode || S.registrationCode || ""),
(S.licenseKeyMasked = V.licenseKeyMasked || ""),
(S.expiresAtMs = V.expiresAtMs || 0),
(S.activated = !!V.activated),              // ← 又是这句!直接覆盖本地状态
(S.locked = V.reason === "time_tampered"),
(S.usingOfflineCache = V.usingOfflineCache === !0),
(S.lastOnlineCheckSucceeded =
V.lastOnlineCheckSucceeded === !0),
!V.activated)
) {
// 验证失败 → 设为未激活,踢回登录页
((S.activated = !1),
(S.locked = O.reason === "time_tampered"),
An(O.message || "授权已失效。"),
);
return;
}
// 验证成功
((S.activated = !0), (S.locked = !1));
}

找到 uL() 函数(手动触发的在线验证)


async function uL() {
  var O;
  if ((O = window.desktopAPI) != null && O.verifyActivationStatusOnline)
    try {
      const V = await window.desktopAPI.verifyActivationStatusOnline();
      ((S.source = V.activated
        ? V.source || S.source
        : String(V.source || "").trim()),
        (S.reason = V.reason || "not_activated"),
        (S.entryMode = V.activated
          ? V.activationEntryMode || S.entryMode || "license_key"
          : jw(V)),
        (S.registrationCode =
          V.registrationCode || S.registrationCode || ""),
        (S.licenseKeyMasked = V.licenseKeyMasked || ""),
        (S.expiresAtMs = V.expiresAtMs || 0),
        (S.activated = !!V.activated),            // ← 同样直接覆盖
        (S.locked = V.reason === "time_tampered"),
        (S.usingOfflineCache = V.usingOfflineCache === !0),
        (S.lastOnlineCheckSucceeded =
          V.lastOnlineCheckSucceeded === !0),
        // 未激活时的处理逻辑...
        An(
          V.activated
            ? ""
            : V.message || "激活状态校验失败,请重新登录。"
        ),
        V.activated && (await sp()));
    } catch (V) {
      An(V instanceof Error ? V.message : "激活状态校验失败,请重新登录。");
    }
}


以上就是找的流程,并且我使用ai注释了 ,反正大体要学会看代码,然后我们就开始手动改啦,下面是我的踩坑过程
因为我打开了调试模式,
Console 里输入:

```javascript
S.activated = true;
sp();
```

报错了:

```
Uncaught ReferenceError: S is not defined
5.png
好吧,不懂换 !
ps:事后找资料复盘,为什么?因为这个 JS 是用 `type="module"` 加载的,`S` 是模块内部的变量,Console 在全局作用域,访问不到。

> 这不是什么安全措施,只是 ES Module 的特性。模块里的 `var`、`const`、`let` 都不会挂到 `window` 上。
那就改源码!我们流程就是这样:从asar 里解出 JS ---改 JS---改 JS---重新打包成 asar---替换原来的 asar

好的,流程理清楚了
开始改

第 1 处:Kw() 函数 — 跳过启动验证**

// ===== 修改前 =====
async function Kw() {
  var V, ue, Le;
  if (
    !(...

// ===== 修改后 =====
async function Kw() {
  ((S.loading = !1), (S.activated = !0)); return;    // ← 加了这一行
  var V, ue, Le;
  if (
    !(...

效果:函数一进来就设 activated = true 然后 return,后面的验证逻辑全都不执行。



第二处:


第 2 处:uL() 函数 — 跳过手动在线验证

// ===== 修改前 =====
async function uL() {
  var O;
  if ((O = window.desktopAPI) != null && O.verifyActivationStatusOnline)

// ===== 修改后 =====
async function uL() { return;    // ← 加了这一行
  var O;
  if ((O = window.desktopAPI) != null && O.verifyActivationStatusOnline)

第 3 处:mL() 函数 — 跳过定时轮询验证

// ===== 修改前 =====
async function mL() {
  var V, ue, Le, it, Ye;
  if (

// ===== 修改后 =====
async function mL() { return;    // ← 加了这一行
  var V, ue, Le, it, Ye;
  if (


把修改后的 JS 复制进去覆盖原文件,源文件记得备份

然后我们开始运行看看 !
6.png
其实我觉得这个只是把相关提示词写灵活了,也算辅助,但是者github 不是有很多吗,这都要圈钱,ai生成费用还是单独另算的.......
{:301_1010:}
有点黑 圈小白
总结:花了俩小时学习的,我太菜了 也是第一次发帖希望得到各位师傅的指教,软件就不放了,就是很简单的提示,ai还是用别人中转的,咱们就不要给别人引流消费,,谢谢各位老师,希望能得到各位的评分!!

免费评分

参与人数 1威望 +1 吾爱币 +20 热心值 +1 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

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

xiongshu 发表于 2026-5-16 23:48
Github上一大把比这个优秀的,纯圈钱的
Huanxian 发表于 2026-5-17 09:17
Lxfdcxh 发表于 2026-5-17 10:19
大佬,能不能说说虚拟环境怎么搞?我这些天在学逆向,AI说要ARM真机,模拟器有部分无法逆,真机是真可以,但我那手机都给ROOT了。。。
 楼主| a3341736201 发表于 2026-5-17 10:28
Lxfdcxh 发表于 2026-5-17 10:19
大佬,能不能说说虚拟环境怎么搞?我这些天在学逆向,AI说要ARM真机,模拟器有部分无法逆,真机是真可以, ...

你好,所谓虚拟环境就是,用的vmware虚拟机建立一个镜像,然后里面放了以及一些常用工具包,这个看自己习惯收集啦,建议自己装个,不要用别人的,慢慢收集工具丢进去
xyb205205 发表于 2026-5-17 15:25
感谢老大辛勤付出,方便私发一份不?
绿箭口香糖 发表于 2026-6-14 13:54
好想要这个软件
本人很懒 发表于 2026-6-14 16:43
能否私发一份吗
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-6-16 04:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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