【steam自动登录解密】02-用CDP查询Steam状态

▒ 目录 ▒

    • [🛫 导读](#🛫 导读)
    • [1️⃣ 先看我们要连接哪里](#1️⃣ 先看我们要连接哪里)
    • [2️⃣ 步骤1:在 PowerShell 找到 Steam](#2️⃣ 步骤1:在 PowerShell 找到 Steam)
    • [3️⃣ 步骤2:确保调试端口可用](#3️⃣ 步骤2:确保调试端口可用)
    • [4️⃣ 步骤3:进入正确的 Console](#4️⃣ 步骤3:进入正确的 Console)
      • [没有 Console:用配套只读查询器](#没有 Console:用配套只读查询器)
    • [5️⃣ 步骤4:读取账号列表](#5️⃣ 步骤4:读取账号列表)
    • [6️⃣ 步骤5:读当前账号是否在线](#6️⃣ 步骤5:读当前账号是否在线)
    • [🛬 文章小结](#🛬 文章小结)

🛫 导读

在 Steam 的浏览器环境里运行两条只读查询,看看客户端实际报告了什么。

CDP 是 Chrome DevTools Protocol,也就是 Chromium 的调试协议。Steam 用嵌入式 Chromium 显示界面,因此在开启调试后,可以从浏览器连接到它的调试目标。

本篇只查询账号列表和实时状态,不执行切号、登出或修改配置。读者只需会打开 PowerShell 和浏览器。

环境 本文基线
系统 Windows x64
Steam 本研究构建10.96.30.42;其他构建的目标名称和接口可能变化
浏览器 可访问本机地址;Console 入口是否可用取决于客户端
备用查询 Python 3 与本目录 requirements-cdp.txt;没有 Console 时使用
输入位置 PowerShell 用于启动/检查;DevTools Console 用于 JavaScript

1️⃣ 先看我们要连接哪里

#mermaid-svg-ieSJACzUyP7kiPfp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ieSJACzUyP7kiPfp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ieSJACzUyP7kiPfp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ieSJACzUyP7kiPfp .error-icon{fill:#552222;}#mermaid-svg-ieSJACzUyP7kiPfp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ieSJACzUyP7kiPfp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ieSJACzUyP7kiPfp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ieSJACzUyP7kiPfp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ieSJACzUyP7kiPfp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ieSJACzUyP7kiPfp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ieSJACzUyP7kiPfp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ieSJACzUyP7kiPfp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ieSJACzUyP7kiPfp .marker.cross{stroke:#333333;}#mermaid-svg-ieSJACzUyP7kiPfp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ieSJACzUyP7kiPfp p{margin:0;}#mermaid-svg-ieSJACzUyP7kiPfp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ieSJACzUyP7kiPfp .cluster-label text{fill:#333;}#mermaid-svg-ieSJACzUyP7kiPfp .cluster-label span{color:#333;}#mermaid-svg-ieSJACzUyP7kiPfp .cluster-label span p{background-color:transparent;}#mermaid-svg-ieSJACzUyP7kiPfp .label text,#mermaid-svg-ieSJACzUyP7kiPfp span{fill:#333;color:#333;}#mermaid-svg-ieSJACzUyP7kiPfp .node rect,#mermaid-svg-ieSJACzUyP7kiPfp .node circle,#mermaid-svg-ieSJACzUyP7kiPfp .node ellipse,#mermaid-svg-ieSJACzUyP7kiPfp .node polygon,#mermaid-svg-ieSJACzUyP7kiPfp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ieSJACzUyP7kiPfp .rough-node .label text,#mermaid-svg-ieSJACzUyP7kiPfp .node .label text,#mermaid-svg-ieSJACzUyP7kiPfp .image-shape .label,#mermaid-svg-ieSJACzUyP7kiPfp .icon-shape .label{text-anchor:middle;}#mermaid-svg-ieSJACzUyP7kiPfp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ieSJACzUyP7kiPfp .rough-node .label,#mermaid-svg-ieSJACzUyP7kiPfp .node .label,#mermaid-svg-ieSJACzUyP7kiPfp .image-shape .label,#mermaid-svg-ieSJACzUyP7kiPfp .icon-shape .label{text-align:center;}#mermaid-svg-ieSJACzUyP7kiPfp .node.clickable{cursor:pointer;}#mermaid-svg-ieSJACzUyP7kiPfp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ieSJACzUyP7kiPfp .arrowheadPath{fill:#333333;}#mermaid-svg-ieSJACzUyP7kiPfp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ieSJACzUyP7kiPfp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ieSJACzUyP7kiPfp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ieSJACzUyP7kiPfp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ieSJACzUyP7kiPfp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ieSJACzUyP7kiPfp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ieSJACzUyP7kiPfp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ieSJACzUyP7kiPfp .cluster text{fill:#333;}#mermaid-svg-ieSJACzUyP7kiPfp .cluster span{color:#333;}#mermaid-svg-ieSJACzUyP7kiPfp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ieSJACzUyP7kiPfp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ieSJACzUyP7kiPfp rect.text{fill:none;stroke-width:0;}#mermaid-svg-ieSJACzUyP7kiPfp .icon-shape,#mermaid-svg-ieSJACzUyP7kiPfp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ieSJACzUyP7kiPfp .icon-shape p,#mermaid-svg-ieSJACzUyP7kiPfp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ieSJACzUyP7kiPfp .icon-shape .label rect,#mermaid-svg-ieSJACzUyP7kiPfp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ieSJACzUyP7kiPfp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ieSJACzUyP7kiPfp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ieSJACzUyP7kiPfp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 浏览器打开 127.0.0.1:8080
调试目标列表
选择 SharedJSContext
打开 Console
查询 SteamClient.User

SharedJSContext 可以理解为 Steam 共享的 JavaScript 执行环境。你自己打开的商店网页不是同一个环境,普通浏览器页面里也不会自然出现 SteamClient。

2️⃣ 步骤1:在 PowerShell 找到 Steam

按 Win 键,搜索并打开"Windows PowerShell"。复制下面的整段代码。$steamExe 是本段创建的变量,保存的是程序路径。

powershell 复制代码
$steamExe = (Get-ItemProperty -LiteralPath 'HKCU:\Software\Valve\Steam' -ErrorAction Stop).SteamExe
if (-not (Test-Path -LiteralPath $steamExe)) {
    throw '注册表中的 Steam 程序路径不存在,请先核对安装位置。'
}
$steamExe

预期最后一行显示以 steam.exe 结尾的真实路径。读取失败就先停止,不要把文章示例目录当成你机器上的目录。

3️⃣ 步骤2:确保调试端口可用

先在浏览器地址栏打开:

text 复制代码
http://127.0.0.1:8080/json

如果已能看到一组调试目标,直接进入步骤3。若连接被拒绝,在 Steam 中正常退出应用,然后回到刚才同一个 PowerShell 窗口执行:

powershell 复制代码
& $steamExe -silent -cef-enable-debugging

等待启动,再刷新浏览器。-silent 可能只让托盘图标出现,没有自动弹出主窗口;双击托盘图标即可查看。

4️⃣ 步骤3:进入正确的 Console

打开 http://127.0.0.1:8080/。如果这个客户端显示带有 inspect 链接的列表,点击 SharedJSContext 旁边的链接,再切换到 Console。

若根页面只有 JSON 或没有可点击入口,打开 /json,在 SharedJSContext 项中检查是否有可用的 devtoolsFrontendUrl。前端链接这条分支没有在本研究中单独实测,不保证每个构建都有。打不开就使用下面的备用方式,不要选择一个随便能打开的商店页面继续。

在 Console 输入下行并按回车:

javascript 复制代码
typeof SteamClient

预期是可用对象的类型,通常为 "object"。如果是 "undefined",先检查所选目标。Console 的粘贴保护若阻止输入,按浏览器自身提示处理,或手动键入这条短命令,不在未识别的页面粘贴长脚本。

没有 Console:用配套只读查询器

这是与底稿实测相同的 CDP 连接方式:从 /json 取得 SharedJSContext 的 webSocketDebuggerUrl,再发送 Runtime.evaluate。查询器只内置四个固定选项,不接受任意脚本、不触发登录或切号。

先确认 python --version 可用。在资源管理器找到本系列文件夹 steam-login-lab,点击地址栏复制完整路径。把下面 $seriesRoot 的字符串换成你自己的位置,然后在 PowerShell 执行:

powershell 复制代码
$seriesRoot = 'J:\_ALL\CODE\gitcode.com\ShyanZh\gitcode_knowledge\ai\mp-sy\steam\doc\steam-login-lab'
Set-Location -LiteralPath $seriesRoot -ErrorAction Stop
if (-not (Test-Path -LiteralPath '.\scripts\query_steam.py')) {
    throw '没有找到查询器,请检查系列目录。'
}
python -m pip install -r .\requirements-cdp.txt
if ($LASTEXITCODE -ne 0) { throw '依赖安装失败,先停止查询。' }
python .\scripts\query_steam.py targets

预期列表中有 title: SharedJSContext 和 hasWebSocket: true。查询器只允许连接本机目标;找不到唯一的 SharedJSContext、超时或接口异常都会报错停止,不会拿其他网页凑数。targets 成功仅说明目标可见,接下来仍要完成实际查询。

本轮已用这份查询器完成本机只读验证:目标列表、账号字段、在线状态与原生函数描述都能返回。没有执行登录或切换账号。

requirements-cdp.txt 文件内容如下:

txt 复制代码
websocket-client==1.9.0

5️⃣ 步骤4:读取账号列表

接着在同一个 Console 输入。下面整段是一个立即执行的异步函数,重复运行也不会因顶层 const 重复声明而报错。

javascript 复制代码
(async () => {
  const users = await SteamClient.User.GetLoginUsers();
  console.log('条目数:', users.length);
  console.table(users.map((u, i) => ({
    序号: i + 1,
    记住状态: u.rememberPassword,
    有PIN: u.hasPin,
    最近登录时间戳: u.rtLastLogin
  })));
  console.log('字段名:', users.length ? Object.keys(users[0]) : []);
})();

你应能看到条目数、表格以及字段名。字段包括 accountName / personaName / avatarUrl / rememberPassword / rtLastLogin / hasPin,但这段示例刻意不打印真实账号名。原始列表中的名字与昵称不同,重复条目也可能存在,因此不要把条目数直接当成唯一账号数。

本机最早一次查询是37条、12条记住状态为 true。你的数量不同完全正常。该 API 不返回 token 原文,内部却可能检查缓存,后面的 Hook 实验会看到这一点。

使用备用方式时,不需要打开 Console,直接在刚才的 PowerShell 窗口执行:

powershell 复制代码
python .\scripts\query_steam.py accounts

输出的 count 对应条目数,fields 对应字段名,rows 对应脱敏表格。它与上面 JavaScript 查询的是同一个接口。

6️⃣ 步骤5:读当前账号是否在线

仍在 SharedJSContext 的 Console 中执行:

javascript 复制代码
(() => {
  if (typeof App === 'undefined' || typeof App.GetCurrentUser !== 'function') {
    console.log('当前上下文没有 App.GetCurrentUser,先核对目标或等待界面就绪。');
    return;
  }
  const u = App.GetCurrentUser();
  console.table([{
    有有效用户ID: !!(u && u.strSteamID && String(u.strSteamID) !== '0'),
    当前登录名: u?.strAccountName ?? '',
    离线模式: u ? !!u.bIsOfflineMode : null,
    原始登录状态: typeof loginStore !== 'undefined' ? loginStore.m_eLoginState : null
  }]);
})();

这一步会打印自己的登录名,便于你与 Steam 界面中的目标身份核对。截图时遮挡该列。判断在线成功要同时考虑:有有效用户ID、身份是预期账号、离线模式为 false。本构建曾观察到原始状态5,但不要把5当成所有版本都成立的常量。

备用方式的命令如下,仍在系列目录执行:

powershell 复制代码
python .\scripts\query_steam.py status

返回 ready: false 时先等待客户端就绪,不继续判定。validUserId / accountName / offline / loginState 依次对应有效ID、当前登录名、离线模式和原始状态,判断标准与 Console 相同。截图同样要遮住 accountName。
#mermaid-svg-HsaH6Squ0bZ2EBHs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HsaH6Squ0bZ2EBHs .error-icon{fill:#552222;}#mermaid-svg-HsaH6Squ0bZ2EBHs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HsaH6Squ0bZ2EBHs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .marker.cross{stroke:#333333;}#mermaid-svg-HsaH6Squ0bZ2EBHs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HsaH6Squ0bZ2EBHs p{margin:0;}#mermaid-svg-HsaH6Squ0bZ2EBHs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .cluster-label text{fill:#333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .cluster-label span{color:#333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .cluster-label span p{background-color:transparent;}#mermaid-svg-HsaH6Squ0bZ2EBHs .label text,#mermaid-svg-HsaH6Squ0bZ2EBHs span{fill:#333;color:#333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .node rect,#mermaid-svg-HsaH6Squ0bZ2EBHs .node circle,#mermaid-svg-HsaH6Squ0bZ2EBHs .node ellipse,#mermaid-svg-HsaH6Squ0bZ2EBHs .node polygon,#mermaid-svg-HsaH6Squ0bZ2EBHs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HsaH6Squ0bZ2EBHs .rough-node .label text,#mermaid-svg-HsaH6Squ0bZ2EBHs .node .label text,#mermaid-svg-HsaH6Squ0bZ2EBHs .image-shape .label,#mermaid-svg-HsaH6Squ0bZ2EBHs .icon-shape .label{text-anchor:middle;}#mermaid-svg-HsaH6Squ0bZ2EBHs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HsaH6Squ0bZ2EBHs .rough-node .label,#mermaid-svg-HsaH6Squ0bZ2EBHs .node .label,#mermaid-svg-HsaH6Squ0bZ2EBHs .image-shape .label,#mermaid-svg-HsaH6Squ0bZ2EBHs .icon-shape .label{text-align:center;}#mermaid-svg-HsaH6Squ0bZ2EBHs .node.clickable{cursor:pointer;}#mermaid-svg-HsaH6Squ0bZ2EBHs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .arrowheadPath{fill:#333333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HsaH6Squ0bZ2EBHs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HsaH6Squ0bZ2EBHs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HsaH6Squ0bZ2EBHs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HsaH6Squ0bZ2EBHs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HsaH6Squ0bZ2EBHs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HsaH6Squ0bZ2EBHs .cluster text{fill:#333;}#mermaid-svg-HsaH6Squ0bZ2EBHs .cluster span{color:#333;}#mermaid-svg-HsaH6Squ0bZ2EBHs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HsaH6Squ0bZ2EBHs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HsaH6Squ0bZ2EBHs rect.text{fill:none;stroke-width:0;}#mermaid-svg-HsaH6Squ0bZ2EBHs .icon-shape,#mermaid-svg-HsaH6Squ0bZ2EBHs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HsaH6Squ0bZ2EBHs .icon-shape p,#mermaid-svg-HsaH6Squ0bZ2EBHs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HsaH6Squ0bZ2EBHs .icon-shape .label rect,#mermaid-svg-HsaH6Squ0bZ2EBHs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HsaH6Squ0bZ2EBHs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HsaH6Squ0bZ2EBHs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HsaH6Squ0bZ2EBHs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
否
是
查询实时用户
有有效用户ID?
尚不能确认已登录
身份是目标账号?
登录了其他账号
离线模式为 false?
不是已确认的在线会话
本次在线结果成立

🛬 文章小结

现在你能从客户端查询候选账号和实时会话。下一篇追问一个更具体的问题:03-点击切号后代码去了哪里?

相关推荐
缘友一世17 天前
银河护卫队在steam(linux Proton GE-Proton10-34)上启动无窗口问题修复
linux·ubuntu·steam·proton
gjjwh24 天前
PUBG作弊封禁申诉思路分享
steam·pubg·vac
技术小甜甜4 个月前
[软件商业化] Steam 游戏更新怎么发公告?预告、上传新包和正式升级说明的顺序怎么安排?
游戏·steam·独立游戏·steamworks·游戏更新·软件商业化
ShyanZh5 个月前
从0开始做自己的免费游戏聚合站(三):接入 GamerPower API,聚合 Steam 免费游戏
游戏·steam·喜加一
缘友一世5 个月前
Ubuntu24安装Steam客户端NVIDIA 显卡驱动异常:从诊断到修复的全过程
ubuntu·steam·nvidia drive
alwaysrun6 个月前
Python获取Steam平台安装游戏信息
python·游戏·steam·vdf
ITKEY_7 个月前
Steam 下载历史版本的Terraria 并禁用自动更新
steam·terraria
sulikey9 个月前
Steam《妹居物语》接入deepseek API教程
游戏·api·steam·deepseek·妹居物语
华硕之声9 个月前
HIFI、OWS耳机,磁轴键盘等ROG外设纷纷亮相
科技·游戏·steam