- name
- dma-cardkey-evo-crack
- description
- DMA 硬件外挂(Evo / EVO PASS / scheats.club / freakluke.me 系)卡密授权链的完整逆向与本地化绕过工作流。当用户要求分析 DMA 外挂的卡密校验、破解 Evo_Crack.exe / evo.exe 授权、抓取或伪造授权服务器协议、复现本地 TLS 中间人 + hosts 劫持 + 根证书植入的绕过手法、从加壳内存 dump 中恢复明文逻辑、或把该流程沉淀为可复用工具链时使用。触发词:DMA、Evo、EVO_PASS、卡密、卡密破解、Evo_Crack、evo.exe、scheats.club、freakluke.me、授权服务器、本地代理、hosts 劫持、根证书、user.dat、VMProtect、Themida、leechcore、PCILeech、内存 dump、frida、DMA卡。注意:本技能沉淀的是 Evo 系「本地授权服务器伪造 + TLS 中间人」这一种破解思路,并非所有 DMA 外挂都采用该方案,套用前必须先判定目标的授权校验形态。
- agent_created
- true
- x-alice-class
- crack
## 零、适用范围声明(重要 —— 先读这一节)
> **本技能记录的是 DMA 外挂卡密破解的其中一种思路,不是全部 DMA 软件都这么干。**
> 其它厂商/其它产品的授权链可能完全不同,切勿照搬。
本案例(Evo 系:`Evo_Crack.exe` + `evo.exe` + `scheats.club`)走的是
**「本地授权服务器伪造 + TLS 中间人」** 路线。它成立需要同时满足 4 个前提:
1. 授权校验走**网络请求**(HTTPS 到远端授权服务器);
2. 客户端**信任本机根证书库**(没有做 certificate pinning);
3. 校验结果最终落在**本地文件**或可预测的内存态(这里 `C:\Evo\user.dat`);
4. 主程序**有 CRC / 虚拟机保护**,直接 patch 二进制不现实。
**其它 DMA 软件可能是完全不同的形态,例如:**
| 形态 | 特征 | 对应思路(**不在**本技能覆盖内) |
|---|---|---|
| 纯本地离线算法 | 无网络请求,卡密 = 序列号 / 签名 | 逆算法 + keygen |
| 硬件绑定卡 | 卡密绑 DMA 卡序列号 / FTDI 芯片 ID | 伪造硬件指纹读取 |
| 服务端计算 + 强制心跳 | 每次启动必须联网,本地不留凭证文件 | 伪造 / 重放心跳 |
| 驱动级校验 | 校验在内核驱动里,用户态看不到 | 驱动逆向 / 内核绕过 |
| 双向 TLS(mTLS) | 客户端证书 + 证书固定 | 需先解 pinning |
| 卡密 + 机器码双因子 | 服务端把机器码写死绑定 | 伪造 HWID 上报 |
**判定流程**:
1. 先做「二、静态侦察」:看导入表有没有 `WinHttp*` / `WS2_32` / `bcrypt`;
2. 再做「三、动态侦察」:钩 `WinHttpSendRequest` / `connect`,**看它到底联不联网**;
3. 联网 → 本技能适用;不联网 → 转入**离线算法逆向 + keygen** 路线(另一套技能);
4. 联网但证书 pinning → 先解决 pinning,再用本技能;
5. 联网但校验在内核 → 先做驱动逆向,再用本技能。
**只有确认是「网络授权 + 本地落盘」型,本技能的思路才可直接套用。**
本技能的所有脚本(探针 / mock / dump 分析)都是**通用**的,
但「hosts 劫持 + 根证书 + 本地 TLS」这条**具体绕过链**只对上述第 1~4 条全都成立的样本有效。
---
## 一、目标画像(先认产品,再动手)
DMA 外挂(Direct Memory Access cheat)的典型文件布局:
| 文件 | 作用 | 识别特征 |
|---|---|---|
| 主程序(evo.exe / EC1.1.exe) | 外挂本体 | 段名含 `.themida` / `.boot` / 空段名;熵 7.9+ |
| 破解/加载器(Evo_Crack.exe) | 卡密绕过工具 | 随机段名(`.Oh5`/`.3p[`)+ 每 DLL 只留 1 个导入 + 高熵 |
| `vmm.dll` | PCILeech VMM(内存读写引擎) | 导出 `VMMDLL_*` |
| `leechcore.dll` | LeechCore(DMA 通信层) | 导出 `LcCreate`/`LcRead`/`LcWrite`/`LcCommand` |
| `leechcore_driver.dll` | 内核驱动 | 20 KB 级 |
| `FTD3XX.dll` | FTDI FT60x USB3 驱动 | 导出 `FT_*`(47 个) |
| `tinylz4.dll` | LZ4 | 导出 `LZ4_compress_default` / `LZ4_decompress_safe` |
| `info.db` | 数据缓存 | `SQLite format 3` |
| `cloudflared.exe` | 穿透隧道 | Cloudflare Quick Tunnel |
**判据**:见到 `vmm.dll + leechcore.dll + FTD3XX.dll` 三件套 = DMA 卡外挂,不是普通软件。
判据:见到 `Evo_Crack.exe` 这类"卡密破解器" = 它不会真去改主程序,而是**伪造授权服务端**。
---
## 二、静态侦察(Phase 1)
### 2.1 PE 结构快筛
```python
import pefile
pe = pefile.PE(path)
for s in pe.sections:
print(s.Name, hex(s.VirtualAddress), hex(s.Misc_VirtualSize),
hex(s.PointerToRawData), hex(s.SizeOfRawData), hex(s.Characteristics))
```
**关键判读**:
- `RAW=0 / RS=0` 的段 → 运行时才填充(VM 解包目标区 / 壳数据)
- 段名随机化(`.Oh5` `.2d8ld` `.ASLpfR`)→ VMProtect 3.x 或自研壳
- 段名 `.themida` / `.boot` → Themida / WinLicense
- 熵 `>7.9` 的代码段 → 代码加密
- 熵 `≈6.0` 的大块 → **典型 VM 字节码区**(本案例 `.3p[` 的 0x8 万-0x200 万区间)
### 2.2 导入表伪造识别(关键指纹)
逐 DLL 统计导入数量:
```python
for e in pe.DIRECTORY_ENTRY_IMPORT:
print(e.dll, len(e.imports), [i.name for i in e.imports][:3])
```
**每个系统 DLL 只有 1 个导入**(且是随机的、通常不重要的那个函数)→
壳把真实 IAT 抹掉,运行时由 VM 解密后 `LoadLibrary` + `GetProcAddress` 重建。
本案例实测(23 个 DLL 各 1 个):
```
ADVAPI32.dll -> GetTokenInformation
KERNEL32.dll -> AreFileApisANSI
WS2_32.dll -> accept
WINHTTP.dll -> WinHttpCloseHandle
bcrypt.dll -> BCryptCloseAlgorithmProvider
MSVCP140.dll -> ??0?$basic_ios@...
```
### 2.3 字符串侦察的陷阱
**加壳文件里搜不到明文是正常的,但要注意编码:**
- 本案例 `Evo_Crack.exe` 静态文件:25419 条 ASCII 串、**0 条 UTF-16 串**、0 条 GBK 中文
- 但内存 dump 后:中文提示串全部是 **UTF-16LE**(`请输入卡密` = `c7ebcae4c8ebbfa8c3dc` 的 utf-16le 编码)
- **不要只搜 GBK**。中文 Windows 程序有用 GBK 也有用 UTF-16 的,两个都要试:
```python
for enc in ("gbk", "utf-16le", "utf-8"):
hits = [hex(i) for i in [0] ] # d.find(s.encode(enc))
print(enc, d.count("卡密".encode(enc)))
```
### 2.4 上行动力:找"破解开关"的名字
加壳程序的特征字符串(错误消息、环境变量名、事件名)通常**不加密**,
因为它们在壳的自解密 stub 里就要用到。搜这些模式命中率极高:
```python
import re
for m in sorted(set(re.findall(rb'EVO_[A-Z0-9_]+', d))): print(m)
for m in sorted(set(re.findall(rb'[a-z][a-z0-9_]{4,40}(?:_mode|_fixture|_fallback|_accept|_failed|_ready|_skipped)', d))): print(m)
for m in sorted(set(re.findall(rb'/[a-z][a-z0-9_./-]{3,50}', d))): print(m) # API 路径
```
本案例一击命中:
```
EVO_PASS_ACCEPT_ANY_AUTH ← 破解总开关
EVO_PASS_PRESERVE_INVALID_AUTH ← 诊断开关
EVO_PASS_DEV_ONLY
/user/authorize /session/validate /v2/user/subscription/ /v2/product/
```
---
## 三、动态侦察(Phase 2)—— 本技能的重心
### 3.1 提权运行(必须)
这类工具 manifest 是 `requireAdministrator`。
非提权 `Start-Process` 报「请求的操作需要提升」;`frida.spawn` 报 `NotSupportedError 0x2e4`。
**三种可用提权方式**(按可靠性排序):
**A. schtasks 计划任务(最可靠,推荐)**
```bat
schtasks /create /tn ALICE_Run /tr "cmd.exe /c <ASCII工作目录>\scripts\run.cmd" /sc once /st 23:59 /rl HIGHEST /f
rem 本包 scripts\boot_schtasks.cmd 已封装: 传工作目录当第一个参数即可
schtasks /run /tn ALICE_Run
```
用一次性 `Start-Process -Verb RunAs boot.cmd`(用户 UAC 静默放行)去创建并触发任务,
任务本体跑在 HIGHEST 上下文且**完全脱离会话**,父进程被中断也不影响。
**B. Start-Process -Verb RunAs**(简单,但生命周期绑定调用者)
```powershell
Start-Process -FilePath 'app.cmd' -Verb RunAs -PassThru -WindowStyle Hidden
```
**C. Frida 提权**(要做插桩时)
```python
dev = frida.get_local_device()
pid = dev.spawn([EXE], cwd=CWD, stdio="pipe", env={"EVO_PASS_ACCEPT_ANY_AUTH": "1"})
```
> ⚠️ **中文路径陷阱**:Windows PowerShell 5.1 处理含中文的路径会乱码
> (`F:\alice破甲` → `F:\alice鐮寸敳`),`Out-File`/`schtasks`/`Start-Process` 全部失败。
> **所有提权脚本必须放在纯 ASCII 路径**(本案例用 `C:\alice_evoc\`),
> 且 `.cmd` 文件用 **GBK(936)** 编码保存、`.ps1` 用 **UTF-8 with BOM**。
### 3.2 Frida 探针(Frida 17+ API 必读)
> ⚠️⚠️ **Frida 17 移除了 `Module.getExportByName()`**。
> 旧代码用它会导致**所有钩子静默失败**(脚本不报错,但一个事件都不产生)。
> 这是本案例最初两轮 frida 只输出模块列表、零行为事件的根本原因。
正确写法:
```js
function resolve(modName, expName){
if (modName) {
try { const m = Process.getModuleByName(modName);
const p = m.getExportByName(expName); if (p) return p; } catch(e){}
}
try { const p2 = Module.getGlobalExportByName(expName); if (p2) return p2; } catch(e){}
return null;
}
```
其它要点:
- `spawn` 必须带 `stdio="pipe"`,否则 `dev.input()` 喂不进 stdin(程序卡在"请输入卡密:")
- RPC 导出名 `script.exports_sync.dump_range()` 里的下划线会被自动转 camelCase;
建议 RPC 用**无下划线名**(`dumprange` / `saverange` / `findkey`)
- exit gate(在 `ExitProcess`/`exit` 里 `while(!released){}`)能冻结进程供 dump,
但**冻结期间脚本会超时销毁**,dump 必须在 JS 侧用 `File` 直接落盘,不能走 RPC
### 3.3 必钩清单(本案例 96 钩子,命中率见右)
```
【输入】 getchar / _getch / fgets / gets / scanf / ReadConsoleW/A ← 抓卡密输入
【输出】 WriteConsoleW/A / WriteFile(stdout) ← 抓提示文本
【文件】 CreateFileW/A / WriteFile / ReadFile / CopyFileW / DeleteFileW
【内存】 VirtualProtect / VirtualAlloc / WriteProcessMemory ← 抓自解密/自补丁
【模块】 LoadLibraryW/A/ExW / GetProcAddress ← 抓延迟导入
【网络】 connect / getaddrinfo / DnsQuery_A/W / send / WSASend / recv / WSARecv
WinHttpOpen / WinHttpConnect / WinHttpOpenRequest / WinHttpSendRequest / WinHttpReadData
【进程】 CreateProcessW/A / ShellExecuteExW / WinExec / system
【注册表】 RegCreateKeyExW / RegSetValueExW / RegOpenKeyExW
【加密】 BCryptOpenAlgorithmProvider / BCryptEncrypt / BCryptDecrypt / BCryptGenerateSymmetricKey
【反调试】 IsDebuggerPresent / NtQueryInformationProcess
【凭据】 GetComputerNameW / GetVolumeInformationW / GlobalMemoryStatusEx / GetAdaptersInfo
【证书】 CertAddEncodedCertificateToStore ← 抓根证书植入
```
完整探针见 `scripts/hook_agent.js`(24 KB,可直接复用)。
### 3.4 WriteProcessMemory 自我补丁的解读
壳启动时会对**自身进程**(`proc = 0xffffffffffffffff`)做几十次 `WriteProcessMemory`:
```
WPM(proc=0xffffffffffffffff, addr=0x7fffxxxxxxx, size=5) e9 ab 17 ef ff ← jmp rel32
WPM(proc=0xffffffffffffffff, addr=..., size=6) ff 25 00 00 00 00 ← jmp [rip+0]
VirtualProtect(addr=..., size=0x1000, prot=0x20) ← PAGE_EXECUTE_READ
```
这是 **VM/壳的 IAT 混淆**:把已加载 DLL 的 IAT 槽改写成跳到 VM 桩的 `jmp`。
**看到这个模式就能确认壳类型,且说明所有 API 调用都会经过 VM。**
---
## 四、突破口:Evo_Crack.exe 的真实工作原理
> 下列链条是 **Evo 系(本例)特有**的破解链。换一个产品,链条可能完全不同——
> 但"先判定授权形态,再选绕过路线"的方法论是通用的。
### 4.1 完整链条(内存 dump 还原)
```
[1] 自检提权 OpenProcessToken + GetTokenInformation(20)
未提权 -> 日志 elevation_skipped + 提示 "run elevated or add 127.0.0.1 scheats.club"
[2] hosts 劫持 改写 %WINDIR%\System32\drivers\etc\hosts
把 scheats.club / www.scheats.club / api.scheats.club 指向 127.0.0.1
随后执行 ipconfig /flushdns >nul 2>&1
失败 -> hosts_override_failed
[3] 证书植入 从 PE 资源取内嵌 CER(日志 "embedded CER win32="),
装入 local-machine ROOT store(本机受信任根)
已存在 -> certificate_already_trusted
失败 -> certificate_trust_failed
[4] 本地 TLS bind/listen 127.0.0.1:443
用 SSPI 实现 TLS 服务端:
AcquireCredentialsHandleW / AcceptSecurityContext /
CompleteAuthToken / QueryContextAttributes(STREAM_SIZES) / DecryptMessage
就绪 -> tls_listener_ready
[5] 拉起本体 找同目录 EVO.exe / evo.exe -> CreateProcessW(EVO)
找不到 -> "same-directory EVO.exe/evo.exe not found"
[6] 伪造授权 拦截并伪造以下请求的响应:
/user/authorize 登录授权(读 authorization: Bearer + password)
/session/validate 会话校验
/v2/user/subscription/ 订阅状态(决定 VIP 是否有效)
/v2/product/ 产品信息
重写响应体: "Hwid":"..." / "UserId":"..." / "server_time":"..."
[7] 发放凭据 本地生成 token + user_id -> local_auth_identity_issued
EVO_PASS_ACCEPT_ANY_AUTH 时:接受任意非空凭据 -> local_auth_any_accept
[8] 写授权文件 create C:\Evo -> 写临时 user.dat -> 原子替换 C:\Evo\user.dat
失败链:create C:\Evo failed / create temporary user.dat failed /
write temporary user.dat failed / replace C:\Evo\user.dat failed
[9] 记日志 evo_pass.jsonl(JSON Lines)
```
### 4.2 破解开关
| 开关 | 形式 | 作用 |
|---|---|---|
| `EVO_PASS_ACCEPT_ANY_AUTH` | 环境变量 / `--accept-any-auth` | 本地接受任意非空凭据,不要求上游通过 |
| `EVO_PASS_PRESERVE_INVALID_AUTH` | 环境变量 / `--preserve-invalid-auth` | 保留上游无效响应(诊断,不伪造) |
| `EVO_PASS_DEV_ONLY` | 环境变量 | 开发模式(触发 `DecryptMessage failed` 分支) |
| `--elevated` | CLI | 标记已提权 |
内部标识:`EVO_PASS`(用于识别自身环境/子进程)、UA `EVO_PASS/1.0`
### 4.3 上游协议(实测)
```
POST /1 HTTP/1.1
connection: close
content-length: 378
host: freakluke.me:8880
<378 字节二进制 body>
```
body 结构(三次采样一致):
```
off 0 uint32 LE = 378 自描述长度
off 4 uint32 LE = 变化值 nonce / tick
采样: 0x00067429 / 0x0006a4e3 / 0x000892a0 / 0x000900a0
off 8.. 高熵密文 AES-256-CBC
证据: BCryptOpenAlgorithmProvider("AES") + BCryptSetProperty(ChainingModeCBC)
+ BCryptGenerateSymmetricKey + BCryptEncrypt/Decrypt
尾部 8 字节 0 对齐 + 24~32 字节尾块(HMAC/校验)
```
响应 JSON 字段:`Hwid` / `UserId` / `server_time` / `user_id` / `username` / `token`
### 4.4 中文提示串全表(UTF-16LE,dump 0x28190-0x28400)
```
请输入卡密:
未配置服务器信息 连接服务器失败 发送失败 接收失败
无效数据包 无效数据 数据损坏 客户端版本停止服务
客户端版本已过期 未知错误 失败: 错误码
初始化 卡密不能为空。 卡密长度超过 511 字节。
卡密必须以 EVO 开头。 卡密验证 登录完整性校验 登录成功
```
### 4.5 错误码对照(实测)
| 错误码 | 触发条件 |
|---|---|
| **4** | 无网络 / 服务器不可达(接收失败) |
| **8** | 服务器返回非法响应(响应解析失败) |
| **1201** | 真实服务器响应但业务拒绝,或响应解密失败 |
**判读技巧**:错误码从 4 → 8 → 1201 变化,说明链路在推进(DNS → TCP → 业务),
可以用来定位卡在哪一环。
---
## 五、复现与验证流程(Phase 3)
### 5.1 Mock 上游服务器
```python
# scripts/mock8880.py —— 捕获 + 应答
import socket, threading
def build_resp(body, ctype="application/octet-stream", status="200 OK"):
hdr = ("HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %d\r\nConnection: close\r\n\r\n"
% (status, ctype, len(body))).encode()
return hdr + body
s = socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("0.0.0.0", 8880)); s.listen(16)
GitHub에서 보기