吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5025|回复: 84
上一主题 下一主题
收起左侧

[分享] snipaste V2.11.3 Pro

  [复制链接]
跳转到指定楼层
楼主
yzy1226357697 发表于 2026-6-25 22:17 回帖奖励

Snipaste v2.11.3 离线授权分析

声明

本项目基于:Snipaste-2.10.8-x64 离线激活记录 - DirWangK - 博客园 文章中的代码进行更新来兼容snipaste v2.11.3 版本。
项目地址: https://github.com/1226357697/snipaste_keygen/tree/main

效果图:

离线授权方案

0. 总览

授权码(用户输入的序列号)形如:

0X-<Base64载荷>
│└┬┘ └────────── 载荷
│ └─ 校验字符
└─── 类型/版本标记('0')

载荷解码后是一段 gzip 压缩 + XOR 混淆的 JSON,JSON 经 Ed25519 签名保护,内含设备绑定信息。客户端离线校验时:验证格式 → 校验位 → 解码 → 验签 → 解析字段 → 逐项比对设备环境。

完整数据流:

明文 JSON(license info)
  │  Ed25519 私钥签名 → sig(64B)
  │  body = gzip(JSON) ^ sig          (单次 XOR, 在 gzip 之后)
  │  data = sig(64B) + body
  │  Base64 编码
  │  前缀: '0' + 校验字符 + '-'
  ▼
授权码字符串

客户端逆向还原后做 Ed25519 验签,再把 JSON 字段与本机环境比对。


1. 授权码外层格式

1.1 结构
位置 内容 说明
[0] '0' 类型/版本标记(固定 0
[1] 校验字符 对 Base64 载荷算出的校验字符
[2] '-' 分隔符(ASCII 45),客户端强制要求
[3:] Base64 载荷 真正的数据

客户端入口 setLicenseKeysub_14024C9C0):

  • 要求 len > 3code[2] == '-',否则直接判废。
  • code[1] 作为校验字符,与对载荷算出的校验字符比对。
1.2 校验字符算法(sub_140288C00

对 Base64 载荷字符串求"相邻字符 ASCII 差的绝对值之和",再取 载荷[sum % len] 作为校验字符:

def calculate_adjacent_char_diff_sum(s: str) -> str:
    if not s: return '0'
    if len(s) == 1: return s[0]
    total = sum(abs(ord(s[i]) - ord(s[i-1])) for i in range(1, len(s)))
    return s[total % len(s)]

仅防手误/防随手伪造,无密码学强度。

1.3 Base64 变体(sub_140289400

客户端自动探测:载荷含 '+''/' → 标准 Base64;否则 → URL-safe Base64。
生成端用 base64.standard_b64encode(标准表)。


2. 载荷的解码/解密链

2.1 客户端解码(postProcessBody = sub_14024AB20
data = Base64解码结果
if len(data) < 65: 原样返回(无效)
header = data[0:64]      # = Ed25519 签名 sig,同时被当作 XOR 密钥
body   = data[64:]

(若非调试) body ^= header        # XOR #1  —— gzip 之前
body = gunzip(body)               # zlib inflate, wbits=31 (gzip 格式)
(若非调试) body ^= header        # XOR #2  —— gzip 之后

return header + body              # body 段即明文 JSON
2.2 关键:三次 XOR 与抵消

客户端从原始字节到"验签 message",对 body 共做 3 次 XOR(密钥都是 sig):

位置 函数 操作
postProcess gzip 前 sub_14024AB20 body ^= sig
postProcess gzip 后 sub_14024AB20 body ^= sig
验签前 sub_14024B1D0 body ^= sig

后两次相互抵消,验签 message = gunzip(原始body ^ sig)

因此生成端只需一次 XOR(在 gzip 之后):

body = gzip_compress(json_bytes)   # wbits=31
body = s_xor(sig, body)            # 仅一次
data = sig + body
2.3 XOR(sub_14024A190

循环密钥 XOR(密钥 = 64 字节 sig):

def s_xor(key: bytes, data: bytes) -> bytes:
    return bytes(b ^ key[i % len(key)] for i, b in enumerate(data))
2.4 gzip(sub_140289270

zlib inflateInit2(wbits=31) = gzip 格式(zlib 1.2.11,Qt 自带)。

def gzip_compress(data: bytes) -> bytes:
    co = zlib.compressobj(9, zlib.DEFLATED, 31)
    return co.compress(data) + co.flush()

3. 数字签名

3.1 算法

Ed25519(CryptoPP ed25519Verifier / ed25519PublicKey)。

  • 签名 64 字节,公钥 32 字节,私钥 32 字节。
  • license 里前 64 字节 = 签名,同时复用为 XOR 密钥。
3.2 签名对象

签名的 message = 明文 JSON 字节(即 make_json 输出的 UTF-8)。
客户端验签 message = gunzip(body ^ sig),必须等于签名时的明文 JSON。

3.3 公钥(客户端硬编码)

客户端硬编码的官方公钥(经 AES-128-CTR 解密后):

86a6313855512c692b7f44a7041a86d02890544923714acaeeb29e52883c9060
  • 存储位置:sub_140249FA0 函数体内,以 8 个 mov 立即数形式嵌入(58 字节密文)。
  • 加密方式:AES-128-CTR(块函数 sub_14002ECA0,密钥扩展 sub_14002EA60),key/nonce 在 sub_14024FE70 懒解密。
  • 该公钥对应的私钥在官方手里,无法伪造签名(Ed25519 设计目的)。
3.4 破解方式(二选一)

方式 A:替换公钥
用自己的密钥对,把硬编码公钥密文替换成自己公钥的密文(同一 AES-CTR keystream,因已知明文+密文可异或得 keystream)。

方式 B:patch 验签逻辑(已采用,更干净)
真正执行 Ed25519 验签的是 sub_140250020(在 sub_14024B630 内,地址 0x14024B87B)。其返回值经 movzx ebx, al0x14024B880)保存。

Patch:

地址 0x14024B880
原: 0F B6 D8        movzx ebx, al
改: B3 01 90        mov bl, 1 ; nop

定位字节序列:0F B6 D8 48 89 6C 24 48 48 8D 05 → 把开头 0F B6 D8 改为 B3 01 90
效果:验签结果恒为 true,接受任意签名。

本项目使用方式 B,故 main.py 中的密钥对可为自签密钥。

3.5 自签密钥对(main.py)
PRIVATE_KEY = 951743f1381818ec1af9a32a2eafce42c6261f5089468ef863e7b903c183b3f6
PUBLIC_KEY  = 1ebf8f1197d33a99aee6c625e306739eb9bf1c2806324eb7b83a09933062aa04

4. 授权信息 JSON

4.1 结构(make_json
{
  "nam": "XXXX",
  "eml": "XXXX",
  "lic": "XXXX",
  "dev": XXXX,
  "pln": "XXXX",
  "dom": "XXXX",
  "api": 0,
  "iat": "TIME",
  "exp": "TIME",
  "exx": "TIME",
  "ref": "TIME",
  "hwi": {
    "machineid": "XXXX-XXXXXXXX-XXXXXXXX",
    "platform": 1,
    "domain": ""
  }
}
4.2 客户端解析(LicenseInfo::fromJson = sub_1402442E0
  • 顶层键名经 sub_14002B9B0 等解码器从混淆常量运行时还原(语义已对齐上表)。
  • 时间字段用 QDateTime::fromString(s, Qt::ISODateWithMs) 解析。
  • 解析结果存入 LicenseInfo 结构体(偏移见下)。
4.3 结构体字段偏移(Qt6,QByteArray=24 字节)
字段 JSON 键 结构体偏移
nam nam +0
eml eml +24
lic lic +48
dev dev +72 (int)
pln pln +80
dom dom +104
iat iat +128 (QDateTime)
exp exp +136
exx exx +144
ref ref +152
platform hwi.platform +160 (int)
machineid hwi.machineid +168
domain hwi.domain +216
api api +240 (byte)

5. 离线校验逻辑(validate = sub_140245880

返回状态码,映射到提示文案(sub_140245EA0)。状态码:0=成功,12=无效凭证,8=时钟过早,31=已过期。

// 门槛1: eml 非空 且 platform == 1
if (eml.isEmpty() || platform != 1) return 12;

// 门槛2: dom 非空时, decode(dom)==工作组 或 dom==hex(blake2s(工作组)).upper()
if (!dom.isEmpty()) {
    wg = getWorkgroup();                         // NetWkstaGetInfo
    if (compare(decode(dom), wg)) goto ok;       // way1: 明文工作组
    if (compare(dom, hex(blake(wg)).upper())) goto ok;  // way2: 工作组哈希
    return 12;
}
ok:
// 门槛3: decode(machineid) == 现算设备码 genId(L)
//        L = compute_len(decode(machineid))
if (!compare(genId(L), decode(machineid))) return 12;

// 门槛4: 时间
if (now < iat.addSecs(-1200)) return 8;          // iat 提前 20 分钟容差
if (exp < now)                return 31;
return 0;                                          // 成功
5.1 各字段约束总结
字段 约束
eml 非空
platform 必须 == 1
dom == 本机工作组明文(way1),或 == 工作组 blake2s 的 hex(way2)
machineid decode 后 == 客户端现算设备码(见第 6 节)
iat ≤ now + 20min
exp ≥ now

注:licdevplnapi 在 validate 中不被检查,可任意填。


6. 设备码 / machineid

6.1 三种形态(核心易混点)
形态 示例 用途
界面展示码 X-XXXXX-XXXXX-XXXXX-XXXXX 给用户看(字母映射后)
比较用明文 XXXX-XXXXXXXX-XXXXXXXX validate 实际比较的值
字段存储态 encode 后的字节 JSON 内部存储(客户端自行编解码)

结论:make_jsonhwi.machineid 直接填比较用明文 XXXX-XXXXXXXX-XXXXXXXX 即可(JSON 层是明文,客户端内部自行 encode/decode)。

6.2 比较用明文的生成(genId = sub_140288D10
genId(len) = blake2s('Snipaste 2', '1', MachineUniqueId)[-len:]
           → hex 大写
           → 4-8 分段插 '-'          (sub_140288B00, 首段4 后续8)
           → 追加校验字符             (sub_140288C00)
           → 追加 '1'
  • lencompute_len(decode(machineid字段)) 决定(sub_140242000(strlen - dash数 - 2) / 2)。
  • 本机方案:len = 9,得 XXXX-XXXXXXXX-XXXXXXXX

MachineUniqueIdQSysInfo::machineUniqueId / sub_1402754A0)来源优先级:

  1. HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid
  2. HKLM\SOFTWARE\Microsoft\SQMClient\MachineId
6.3 Blake2s 哈希(sub_140288F90
def x_Blake2s_128(data: bytes, r_sz: int) -> bytes:
    h = hashlib.blake2s(digest_size=16)   # Blake2s_128 (Qt alg id = 19)
    h.update(b'Snipaste 2')               # 盐1
    h.update(b'1')                        # 盐2
    h.update(data)
    return h.digest()[-r_sz:]             # 取右 r_sz 字节
6.4 界面展示码的生成(x_gen_device_id,仅展示用)
01 | blake2s(MachineGuid)[-4:] | blake2s(工作组)[-4:]
→ hex 大写 + 校验字符
→ 字符映射 "3679EFHKMNPRTWXY"           (sub_140277190)
→ 1-5-5-5-5 分段

本机 = X-XXXXX-XXXXX-XXXXX-XXXXX

6.5 字段 encode/decode(sub_14026DB40 / sub_14026DC80

客户端内部对 machineid 字段做的对称编码(JSON 层不需要,仅供理解):

encode: key = rand % 251
        rotate_right(body, rot)
        body[i] ^= key ^ i
        prepend(key)
decode: key = data[0]
        body[i] ^= key ^ i
        rotate_left(body, rot)
其中 rot = abs(len ^ key) % len

rotate 由三次 reverse 实现(sub_14026DE00,标准 std::rotate)。


7. 工作组(dom 字段)

客户端用 NetWkstaGetInfo(level=100)wki100_langroupsub_1402AC550),不是 WMI。

def get_workgroup():
    # netapi32.dll NetWkstaGetInfo, level 100, 读 langroup 字段
    ...  # 本机返回 'XXXX'

早期用 wmi.Win32_ComputerSystem.Workgroup 在本机返回 None,导致 dom 错误 —— 必须用 NetWkstaGetInfo 对齐。

dom 字段填本机工作组明文 "XXXX" 即可(走 way1)。


8. 保护机制汇总

保护手段 实现 位置 强度
格式校验 ??-载荷 + 相邻差校验字符 setLicenseKey 防手误
Base64 自动探测标准/URL-safe sub_140289400 编码
循环 XOR 64字节 sig 当密钥 sub_14024A190 混淆(密钥同包,≈0)
gzip 压缩 zlib wbits=31 sub_140289270 非安全
Ed25519 签名 CryptoPP sub_14024B1D0 等 (真正防线)
公钥 AES-CTR 加密 硬编码密文运行时解密 sub_140249FA0 防静态提取公钥
字符串/常量混淆 Huffman/trie 查表解码 clz32 + trie 循环 拖慢逆向 + 完整性自检
反调试陷阱 CheckRemoteDebuggerPresent sub_140275930 调试时悄悄走错解密分支
字段 encode XOR+rotate 对称编码 sub_14026DB40/DC80 混淆
设备绑定 machineid/dom 比对本机 validate 防跨机复用
8.1 字符串/常量混淆机制

关键字符串、字段名、配置常量不以明文存储,而是编码成 trie(Huffman 式) 结构,运行时按一个 32 位魔数逐 bit 走表解码:

  • clz32sub_14002C3C0)定位魔数最高有效位。
  • 从 MSB 逐 bit 在 trie 表(unk_...,每节点 4 字节:bit/is_leaf/cur/next)中走边,到叶子得结果。
  • 表被篡改 → 走不到叶子 → 抛 std::exception 自毁(兼作完整性自检)。
8.2 反调试陷阱

sub_140275930(底层 CheckRemoteDebuggerPresent)被用作 XOR 的门控,逻辑反写:

没调试 → 执行 XOR → 解密正确 → 正常
被调试 → 跳过 XOR → 数据错乱 → gzip 失败 / 验签失败(不报错,悄悄失败)

离线生成脚本无视此项,按"正常路径"执行即可。


9. 生成端完整流程(main.py)

1. dom        = get_workgroup()                    # NetWkstaGetInfo → 'XXXX'
2. machineid  = gen_expected_machineid(guid, 9)    # blake2s[-9:] → XXXX-XXXXXXXX-XXXXXXXX
3. json_bytes = make_json(nam, eml, dom, machineid)
                # platform=1 必须; iat/exp 时间窗口
4. sig        = Ed25519_sign(PRIVATE_KEY, json_bytes)      # 64B
5. body       = gzip_compress(json_bytes)
6. body       = s_xor(sig, body)                   # 单次 XOR
7. data       = sig + body
8. b64        = base64.standard_b64encode(data)
9. checkchar  = calculate_adjacent_char_diff_sum(b64)
10. code      = '0' + checkchar + '-' + b64

客户端patch 验签后,自签密钥即可通过。

免费评分

参与人数 30威望 +1 吾爱币 +52 热心值 +28 收起 理由
zzzzzzzzzzzzzz1 + 1 + 1 谢谢@Thanks!
liyitong + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
贼将休走 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
andy_wang425 + 1 + 1 我很赞同!
learnnovo + 1 + 1 谢谢@Thanks!
那些年打的飞机 + 1 + 1 谢谢@Thanks!
冥界3大法王 + 4 + 1 还有IDA数据库,好评!点赞!
紫色 + 2 + 1 我很赞同!
shiyue2925 + 1 + 1 谢谢@Thanks!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
9898a + 1 + 1 我很赞同!
thghx + 1 + 1 谢谢@Thanks!
lf619 + 1 + 1 谢谢@Thanks!
springlu + 1 我很赞同!
yifubei + 1 谢谢@Thanks!
eer4 + 1 + 1 我很赞同!
papapo + 1 + 1 热心回复!
萧然行者 + 1 + 1 谢谢@Thanks!
JonesDean + 1 + 1 谢谢@Thanks!
yyb414 + 1 + 1 热心回复!
52rap + 1 + 1 谢谢@Thanks!
wuai3456 + 1 + 1 谢谢@Thanks!
唐小样儿 + 1 + 1 我很赞同!
抱薪风雪雾 + 1 + 1 谢谢@Thanks!
Issacclark1 + 1 谢谢@Thanks!
超逗的二哈少爷 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
LuckyClover + 1 + 1 谢谢@Thanks!
kwan8888 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
tutu2 + 1 + 1 牛逼啊 成功激活
yulai3230 + 1 + 1 我很赞同!

查看全部评分

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

推荐
gy0715 发表于 2026-6-28 22:47

隔壁有 https://www.lanzoui.com/iQ96n3r83yri

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
chaohao1988 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
thghx + 1 + 1 谢谢@Thanks!

查看全部评分

推荐
83835332 发表于 2026-6-27 16:36
本帖最后由 83835332 于 2026-7-1 00:27 编辑



感谢到老,已经激活成功,随手做个补丁

生成的激活码为:
0g-CrYIw3YJfFUk5JP4gNlEZv33hmRCSCT7cMnYUqANTJTQu2S+ceAcrnUTPdwURgkQ6otw1biqtHIOM96fsWV1BBU9AMN2CXxVJu7ut0vThlbtCxP28dnILU4bSwRwjkvRwJTZekclXFTFSSgYa3GSzrcN4Kx4RMgqpSlImh19Yl6Oc5CjAnjT+YT88LSFQkEqymGkzdmtyc1fcl4Gy0celZPZ8cBLxnWynn52AlVJ/r6/XXfvB00ITeCFGWJYJe/uh2WMOG/8XEIhIs88nHpEVu4XZms6v7fQVd44QqBpUgcrRYewyx7/8Cpn/Fsh7LWKvBD6ZxOO8mygQVd2l4SkHishsUyBX0G/SkS1yPWLglGYKGlrjuqlOzvI2FI=
推荐
冥界3大法王 发表于 2026-7-1 18:44
14006AFCE:  0F94C3  sete bl
我每次都是这样改的:
14006AFCE:  B301  mov bl, 01
14006AFD0:  90  nop
注册号都省了。
沙发
yulai3230 发表于 2026-6-26 17:58
感谢大佬,成功了,但是文字识别闪退,是我的问题吗

Snipaste_2026-06-26_17-58-12.png (29.58 KB, 下载次数: 2)

Snipaste_2026-06-26_17-58-12.png

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
thghx + 1 + 1 谢谢@Thanks!

查看全部评分

3#
 楼主| yzy1226357697 发表于 2026-6-26 18:20 |楼主
yulai3230 发表于 2026-6-26 17:58
感谢大佬,成功了,但是文字识别闪退,是我的问题吗

这个原因我不知道耶,没有遇到过。我这边不会闪退。
4#
ceciliaaii 发表于 2026-6-26 18:21
yulai3230 发表于 2026-6-26 17:58
感谢大佬,成功了,但是文字识别闪退,是我的问题吗

OCR的结果已经到剪切板了
PS:OCR大概率为本地,准确率堪忧
5#
 楼主| yzy1226357697 发表于 2026-6-26 18:23 |楼主
ceciliaaii 发表于 2026-6-26 18:21
OCR的结果已经到剪切板了
PS:OCR大概率为本地,准确率堪忧

首选项可以设置。
6#
LuckyClover 发表于 2026-6-26 19:44
确实牛掰,感谢分享
7#
zggzwanghong 发表于 2026-6-26 20:39
厉害,我跟着操作学习一下
8#
抱薪风雪雾 发表于 2026-6-27 01:44
棒,要学着试试
9#
bF6eUh3 发表于 2026-6-27 02:53
这软件我记得还有闪退的暗桩,例如运行一定时间后静默退出
10#
shan16531017 发表于 2026-6-27 10:13
激活给ikun, 原来是家人
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-7-26 05:30

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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