Fluent-Steam-Lua 如何提取 Denuvo 授权?appticket 与 eticket 子进程提取全流程揭秘

【免费下载链接】Fluent-Steam-Lua

基于 WPF + Fluent Design 开发的现代化轻量级 Steam Lua 入库管理工具,目前仅适配OpenSteamTool

项目地址: https://gitcode.com/gh_mirrors/fl/Fluent-Steam-Lua
点击查看 免费下载

Fluent-Steam-Lua 是一款基于 WPF + Fluent Design 开发的现代化轻量级 Steam Lua 入库管理工具(目前仅适配 OpenSteamTool)。它内置了 Denuvo 授权提取能力:一键从你当前登录的 Steam 账号中提取游戏的 appticket(所有权票据)与 eticket(加密票据),并自动落盘为可直接导入的 tickets.txt,让 Denuvo 加密的游戏在 Lua 入库后顺利启动。这篇文章带你快速跑通提取流程,并完整揭秘背后"主进程 + 子进程"的双进程设计。

1 分钟搞懂 Denuvo 授权提取:appticket 与 eticket 是什么?

使用 Denuvo(D 加密)的游戏无法直接入库游玩。你需要先用一个拥有该游戏正版的账号提取授权票据,再在游戏端导入票据完成授权。整个过程涉及两张票据:

  • appticket(AppTicket,所有权票据):由 Steam 客户端的 GetAppOwnershipTicketData 接口返回,内嵌 AppID 与 SteamID(偏移 8 处的 64 位 SteamID),用于证明"这个账号拥有这款游戏"。
  • eticket(ETicket,加密票据):通过 RequestEncryptedAppTicket 异步请求获得,是服务端签发的加密授权凭据,两者缺一不可。

⚠️ 两个关键限制,请务必记住:

  1. 有效期约 30 分钟:票据文件中会写入失效时间,过期需重新提取,提取后请尽快启动游戏。
  2. 授权与机器特征码和游戏版本绑定:授权后不能更新游戏、系统或驱动,否则授权失效。

另外,该功能仅支持明文 ticket 授权文件,不支持 CW、Liubang 等带额外加密壳的授权文件。

提取前的 3 步准备:快速检查清单

准备项要求原因
运行环境Windows 10/11 + .NET 8,64 位程序提取必须运行在 64 位进程,接口布局按 x64 定义
Steam 状态已启动,并登录拥有该游戏的账号需要连接本地 Steam 客户端读取票据
入库状态游戏已通过 Lua 清单入库(清单含 addappid)票据只写注册表,供内核读取授权

AppID 可以在 AppID 查询窗口 中按游戏目录名搜索获取,也可以直接在手输入。

手把手提取 Denuvo 授权:3 步拿到 tickets.txt

第 1 步:进入「授权」页面。打开 Fluent-Steam-Lua,切到"授权"标签页,页面说明会提示"为游戏提取或导入 Denuvo 授权票据(有效期 30 分钟)"。界面定义见 Views/AuthorizationView.xaml。

第 2 步:输入 AppID 并点击「提取」。在提取卡片中填入游戏 AppID(如 3357650),确认后点击提取按钮。此时主进程会启动一个隐藏窗口的一次性子进程,界面显示"正在提取…请确认 Steam 已运行并登录拥有该游戏的账号"。

第 3 步:查看结果。成功后页面会展示 AppTicket / ETicket 的字节数与保存位置,票据默认保存在:

程序目录\cache\denuvo\{AppID}\tickets.txt

文件内容形如(hex 为示例数据,仅展示格式):

appid:3357650
appticket(104bytes):a1b2c3d4...
eticket(360bytes):e5f6a7b8...

# 游戏名称:示例游戏
# 提取时间:2026-01-01 12:00:00
# 失效时间:2026-01-01 12:30:00(授权有效期 30 分钟,过期后需重新提取)

文件采用"临时文件 + 原子移动"方式写入,避免中途失败留下半截文件。

为什么必须用子进程?--ticket-worker 内部全流程揭秘

这是本功能最有意思的设计。为什么不让主进程直接调 Steam 客户端接口,而要绕一圈启动子进程?

核心原因:Steam 客户端的上下文是"进程级"的。 steamclient64.dll 在首次加载前就要求环境变量 SteamAppId / SteamGameId 已设置,一旦加载后上下文即被固定。主程序作为常驻 GUI 进程不能为此"牺牲"自己的进程环境,所以 Fluent-Steam-Lua 选择:让主进程把"自己"再次启动为一次性 worker 子进程,在干净的子进程里完成原生提取,用完即走。

整个流程如下:

  1. 主进程调度:Services/SteamTicketExtractor.cs 中的 ExtractAsync 先检查"是否 64 位""Steam 是否在运行",然后以 --ticket-worker {AppID} {结果JSON路径} {日志开关} 参数拉起自身,并设置 45 秒超时保护(超时自动终止子进程)。
  2. 子进程入口:启动参数在 App.xaml.cs 的 OnStartup 中被识别(e.Args[0] == "--ticket-worker"),随后调用 Services/TicketWorker.cs 的 TicketWorker.Run,全程不走界面逻辑。
  3. 设置游戏身份:子进程在加载 DLL 之前先写入 SteamAppId 与 SteamGameId 环境变量,再设置 DLL 搜索目录并加载 Steam 安装目录下的 steamclient64.dll(Steam 路径优先从注册表 HKCU\Software\Valve\Steam 的 SteamPath 读取)。
  4. 建立客户端会话:依次调用 CreateInterface("SteamClient023") → CreateSteamPipe → ConnectToGlobalUser,完成与本地 Steam 客户端的连接。任一步失败都会给出针对性错误,例如"请确认 Steam 正在运行""请确认已登录 Steam 账号"。
  5. 提取 appticket:通过 GetISteamGenericInterface("STEAMAPPTICKET_INTERFACE_VERSION001") 拿到 AppTicket 接口,调用 GetAppOwnershipTicketData(2048 字节缓冲区)一次性读回所有权票据,并同时拿到 AppID / SteamID 在票据中的偏移;票据短于 20 字节直接判定无效。当前账号 SteamID 从票据内 steamId 偏移处读取(与 OpenSteamTool 的做法一致,规避了 GetSteamID 在 x64 下的崩溃风险)。
  6. 提取 eticket:从 ISteamUser("SteamUser023") 发起 RequestEncryptedAppTicket 异步请求,然后每 50ms 轮询一次 IsApiCallCompleted(最长 15 秒),成功后经 GetApiCallResult 校验响应状态码为 OK,最后调用 GetEncryptedAppTicket(先问长度、再取内容)读出加密票据。若返回非成功状态码,通常意味着当前账号不拥有该游戏。
  7. 结果回传:票据属于敏感数据,不通过 stdout 传输,而是序列化为 base64 JSON 写入临时结果文件(steam_lua_ticket_{AppID}_{Guid}.json),主进程读取并删除它。
  8. 落盘与补全:主进程校验两张票据齐全后,离线读取游戏名(优先 appmanifest 的 acf,兜底解析 appinfo.vdf 中文名),最终原子写入 cache\denuvo\{AppID}\tickets.txt。票据数据模型见 Models/TicketData.cs。

一句话总结:"主进程管调度与落盘,子进程管原生提取",互不干扰,也天然规避了单进程上下文固定问题。

提取之后:导入授权时票据如何被写入注册表

拿到 tickets.txt 后(可以是别人帮你提取的),在授权页右侧「导入授权」卡片中拖入文件或点击选择即可,导入逻辑见 ViewModels/AuthorizationViewModel.cs 与 Services/AuthorizationService.cs:

  1. 解析与多重校验:文件上限 2MB;hex 必须为偶数长度且与实际字节数一致;appticket 内嵌 AppID 定位校验(常规偏移 16 优先,否则按 4 字节对齐扫描);偏移 8 处 SteamID 必须非零。任何一项不符都会明确报错,且票据内容不会写入日志。
  2. 检查 Lua 清单:确认该游戏已入库且清单中存在 addappid({AppID});若清单里残留旧版 setappticket/seteticket 语句,会提示确认后自动清理。
  3. 写入注册表:Services/SteamCredentialStore.cs 将 AppTicket / ETicket 以 REG_BINARY 写入 HKCU\Software\Valve\Steam\Apps\{AppID}。不再经过 Lua 清单中转,内核直接从注册表读取完成授权。
  4. 30 分钟内启动游戏完成授权闭环。

常见问题:Denuvo 授权提取失败怎么排查?

报错 / 现象可能原因解决办法
"请先启动 Steam 并登录拥有该游戏的账号"Steam 未运行或未登录启动 Steam 并用拥有该游戏的账号登录后重试
"提取授权超时(45 秒)"Steam 未在线或网络异常确认 Steam 在线后重试
"未获取到所有权票据"账号未拥有该游戏,或本机未缓存授权信息换拥有该游戏的账号;或先在正版库中启动过该游戏
"请求加密票据返回非成功状态"账号不拥有该游戏确认 AppID 与账号游戏库匹配
授权后游戏又无法启动更新了游戏 / 系统 / 驱动授权与机器特征码和游戏版本绑定,需重新提取
导入时报"文件无法识别"使用了 CW / Liubang 等加密壳授权文件本工具仅支持明文 ticket,需重新提取

更多说明可参考界面内置的常见问题(Views/FaqWindow.xaml.cs)与中文语言文件 Resources/Lang/zh.json。

相关源码模块一览

总结

Fluent-Steam-Lua 的 Denuvo 授权功能把繁琐的原生接口调用封装成了"输入 AppID → 一键提取 → 拖入导入"三步体验:主进程负责调度、校验与落盘,--ticket-worker 子进程负责在干净的进程环境中加载 steamclient64.dll 提取 appticket 与 eticket,票据最终直接写入注册表供 OpenSteamTool 内核读取。只要记住"30 分钟有效期、授权后不更新系统/游戏/驱动、仅支持明文 ticket"这三点,你就能顺畅完成 Denuvo 游戏的入库与授权。

【免费下载链接】Fluent-Steam-Lua

基于 WPF + Fluent Design 开发的现代化轻量级 Steam Lua 入库管理工具,目前仅适配OpenSteamTool

项目地址: https://gitcode.com/gh_mirrors/fl/Fluent-Steam-Lua
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值