对segaboot的深入研究
SegaBoot通常是ALLS HX2/UX2 的 核心启动/管理程序,从逆向的视角带你看看他们的作用。
这些文件提取于 UX2/HX2 中位于 c:/Windows/Sega/System 中,可能会带来侵权/DMCA
如果你认为我侵犯了这些软件的版权,请您联系我删除!
SegaBoot 的组成
System/
├── resources/
│ ├── download_icon.png
│ ├── license_system_en.txt
│ ├── license_system_ja.txt
│ ├── license_system_zh.txt
│ ├── logo.png
│ └── logomode_load.gif
├── installed/
│ ├── 0001_StandardCommon_058/
│ ├── 0002_Uwf_001/
│ ├── 0007_ACA4.2_003/
│ ├── 0105_SDFT_007/
│ ├── 1003_bugfix_002/
│ └── 1004_bugfix_001/
├── mainstorage/
│ ├── apply/
│ └── deploy/
├── recovery/
│ ├── apply/
│ └── deploy/
├── substorage/
│ ├── apply/
│ └── deploy/
├── reinstall/
│ ├── dpinst.exe
│ ├── dpinst.xml
│ └── reinstall.bat
├── Microsoft.Expression.Interactions.dll
├── System.Windows.Interactivity.dll
├── UwfServicingMasterScript.cmd
├── VWF_Tool.exe
├── amDeviceManagerWrapperMD.dll
├── devcon.exe
├── hsol_vwf.dll
├── pxSecureAtaCmd.exe
├── sgSystemServicingCommon.ps1
├── sgSystemServicingMasterScript.ps1
├── sgimagemount.exe
├── sglaunch.exe
├── sglaunch.json
├── sgmanageio.exe
├── sgmanageio.InstallLog
├── sgmanageio.InstallState
├── sgosupdate.exe
├── sgsetdisplaysetting.exe
├── sgsetresolution.exe
├── sgsystemservicingshell.exe
├── sgxdownloadui.exe
├── sgxmaster.exe
├── sgxprestartup.exe
├── sgxsegaboot.exe
├── sgxsystemdaemon.exe
├── sgxsystemservicing.exe
├── speaker_test_managerMD.dll
├── system_appuser.txt
├── system_key.txt
├── system_share_data_wrapperMD.dll
├── system_updateuser.txt
├── util_downloadui.dll
├── util_sgxsegaboot.dll
└── wrp_systemdaemon_cli_wrapperMD.dll
他们每个都负责着不同的工作,经过我的研究发现,大部分的程序都是属于sgxsystemdaemon.exe的子程序,也就是通过某种方式调用其他程序,通常通过 System Test Mode 页面拉取。
因为手边没有世嘉基板,我通过还原sgxsegaboot.exe提取的页面,实际应该为这样的。
歪了是因为基板里面没装游戏程序,判断不出来方向
接下来我将对这些程序按照重要顺序依次介绍排序。
部分程序带有早期老版本的crackproof的壳子,你可以通过网上的资源轻松裂开,世嘉有很长时间没有维护这个启动程序了,准确来说我没有渠道拿到比较新的东西,我的资源通常来源于线下提取的。
部分程序我已经处理过壳子,所以可能造成内存位置不正确,几乎都是通过 IDA Pro 或者 DnspyEx 来逆向的。
因为我没有那个钱去买基板和加密狗来实操运行,只能通过静态或半动态的方式逆向,所以准确度可能不是太高,仅供参考。
SgxSegaBoot
sgxsegaboot 本身是一个纯粹的 前端显示器 + 命令转发器,不直接执行任何硬件检测或系统操作。
他可以通过 DnspyEx 拖入直接看到代码,所以我们可以对他进行还原成可构造的状态,甚至可以整一点花活。
这个也可以用来改成 SegaBoot Simulator 虽然没什么用,只是为了装逼
通过还原,它主要的功能有以下这些。
启动与监控:
- 启动 sgxsystemdaemon.exe(测试模式时传 -s test)
- 16ms 周期轮询共享数据区
- 监听 daemon 退出事件
启动画面与错误显示:
- 读取共享数据中的 stepNumber / errorNumber
- 显示 STEP 01-31 对应的步骤消息
- 错误时显示 ERROR XXXX + 错误消息文本
- STEP 30/31 时发送 exit 命令并关闭窗口
- daemon 非正常退出时强制显示 ERROR 0010
输入处理:
- Test → Enter 键
- Service → Tab 键
- 方向键 → Arrow Up/Down/Left/Right
- USB IO 输入事件 → WPF 键盘导航
命令转发 (通过 util.wrp.Client 发送给 daemon):
- 退出 daemon
- 设置时钟
- 卸载游戏
- 修改网络配置 (IP/DHCP/Gateway/DNS)
- 修改投币/credit 配置
- 启动/清除网络测试
- 格式化主/副存储
- 修改 DIP 开关设置
- 清 credit
- 保存/清除触摸屏校准
系统测试菜单:
- 根据共享数据中的 SystemTestMenu 启用/禁用菜单项
- 菜单状态: Enable / Loading / Disable / Hidden
- System Info, Storage, USB IO, Speaker, Coin, Clock, Network, DIP, Touch 等
可以看出他类似于一个前端,所有的功能都是通过sgxsegaboot.exe来操作。
sgxsegaboot.exe 是纯粹的前端:
┌──────────────────────────────────────────────┐
│ C# WPF 界面 │
│ - 显示 boot 步骤 / 错误 / 进度 │
│ - 系统测试菜单 UI │
│ - 输入 → WPF 键盘导航 │
│ - 命令 → util.wrp.Client → daemon │
└──────────────────────────────────────────────┘
│ 共享内存 (ShareData)
▼
sgxsystemdaemon.exe 是后端引擎:
┌──────────────────────────────────────────────┐
│ C++ 原生 │
│ - 主板检测, keychip 验证, USB IO │
│ - 存储格式化 (PowerShell 脚本) │
│ - 网络配置, 防火墙, 系统配置 │
│ - 所有底层操作 │
└──────────────────────────────────────────────┘
例如发生了错误ERROR 0021主板故障,它的交互逻辑是这样的。
sgxsystemdaemon 侧:
GetInfoTask@mainboard
→ amPlatformGetDevSet() 失败
→ sequencer.errorNumber = 21
sgxsegaboot 侧:
onSequenceChange(sequencer)
→ sequencer.m_errorNumber = 21
→ errorSequence(21u)
→ getErrorMessageOrWorkaround(21)
→ TextResource.ERROR_0021_MESSAGE
→ "Main board Malfunctioning" (en)
→ "メインボードに異常があります" (jp)
→ "主板故障" (zh)
而上文提到的页面图片,则是通过sgxsystemdaemon.exe进行操作,使用指令util.wrp.Client.SendCommand()传递给sgxsystemdaemon 侧,由Worker@wrp@segaboot执行。
系统信息的读取,也是如此
System Info 页面显示:
Keychip ID ← 共享数据 (daemon 从 NVRAM 读取)
Game ID ← 共享数据 (daemon 从 keychip 读取)
Region ← 共享数据
主板 ID ← 共享数据
OS 版本 ← 共享数据 (daemon 读取)
内存容量 ← 共享数据 (daemon 调用 GlobalMemoryStatusEx)
CPU 温度 ← 共享数据 (daemon Task@temperature@cpu@monitor)
主/副存储容量 ← 共享数据
显卡 vendor/device ← 共享数据
BIOS 版本 ← 共享数据
图示情况是过了基板验证,但是没有读取到系统信息。
所以他就是一个纯粹的前端,无任何功能,怪不得世嘉不给他加壳。。。
架构层次
sgxsegaboot.exe (C# WPF 前端)
└── util_sgxsegaboot.dll ← 输入处理/共享数据/喇叭测试
└── system_share_data_wrapperMD.dll ← 共享数据结构 C++/CLI 桥接
└── wrp_systemdaemon_cli_wrapperMD.dll ← daemon Worker 命令 CLI 包装器
└── sgxsystemdaemon.exe (C++ 原生守护进程)
└── amDeviceManagerWrapperMD.dll ← 设备管理器包装器
Sgxsystemdaemon
这个程序是整个系统的核心,所以这篇文章将会用大量的文字来描述这个程序,世嘉给他加了crackproof壳子,但是依赖前人的工具我们可以很轻松裂开,看到内部的内存地址与位置。
通过查看二进制,我们发现这个程序记录了一些版本号。
sgxsystemdaemon Ver.a75ebcfc
Build: Jan 9 2024 14:40:03 ← 主程序 (2024)
SEGA AM Library Ver.5b8f8eee
Build: Jan 9 2024 14:39:48 ← 底层框架库 (2024)
altr Ver.1.00.00
Build: Nov 1 2013 18:04:04 ← 网络中继模块 (2013,遗留代码)
新代码 (2024) 用 C++ 重写了大部分逻辑,但核心的 ALTR 网络协议模块还保留着 2013 年的原始 socket 实现。
混淆手法
二进制经过控制流混淆,反汇编中有大量垃圾指令:
sub_1400B2F20:
pop rsi
mov ebx, esp
sub rsp, 68h
mov rax, [rdx]
mov qword ptr [r11-28h], 0
mov [r11-10h], rax
mov eax, [rdx+14h]
mov [rsp+60h+var_10], eax
mov rsp, 8B48D04389491842h ; ← 混淆立即数
test [rax+rdx*4], ah ; ← 混淆指令
混淆没有覆盖数据段和 RTTI (运行时类型信息),所以我们可以通过 C++ 类名反推整个架构。
架构图如下
┌──────────────────────────────────────────────────────────────────────┐
│ sgxsystemdaemon.exe │
│ Ver.a75ebcfc (Jan 2024) │
│ SEGA AM Library v5b8f8eee │
│ │
│ ┌──── IPC ────┐ ┌── Boot Seq ──┐ ┌── ALTR Protocol ────┐ │
│ │ ShareData* │ │ Step 1-31 │ │ Server Main │ │
│ │ CreateEvent │ │ MergeLicense │ │ SendPacket │ │
│ │ CommandQueue│ │ ModeSelect │ │ IcmpProbe │ │
│ └─────────────┘ └──────────────┘ └──────────────────────┘ │
│ │
│ ┌── USB IO ────┐ ┌── Keychip ──┐ ┌── Dev Mode ────────┐ │
│ │ Manager │ │ SearchTask │ │ Mount Dev Storage │ │
│ │ setupNode │ │ ValidateTask│ │ Disable Firewall │ │
│ │ parseBoardType│ │ DummyKeychip│ │ ChannelEncryptOff │ │
│ └──────────────┘ └─────────────┘ └────────────────────┘ │
│ │
│ ┌── Storage ───┐ ┌── Firewall ─┐ ┌── AppImage ────────┐ │
│ │ CheckDisk │ │ Enable/ │ │ Install/Deploy │ │
│ │ Format │ │ Disable │ │ Beacon/DVD/USB │ │
│ │ BitLocker │ │ Exception │ │ Gcatcher │ │
│ └──────────────┘ └─────────────┘ └────────────────────┘ │
│ │
│ ┌── Config ────┐ ┌── SystemTest ─┐ ┌── Utility ───────┐ │
│ │ JSON Parser │ │ Network │ │ Process/Thread │ │
│ │ SystemProperty│ │ Credit/Touch │ │ File/BinaryFile │ │
│ │ ModuleBase │ │ EDID/Sound │ │ Input Monitor │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
│ │
│ ┌── Licensing ──┐ ┌── Logging ──┐ ┌── Time/NVRAM ────┐ │
│ │ MergeLicense │ │ EventLog │ │ SetLocaltime │ │
│ │ amBackup* │ │ IcfLog │ │ NvMemory │ │
│ │ BCrypt AES │ │ SystemRecord│ │ Volume │ │
│ └──────────────┘ └─────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
下面逐一展开核心子系统。
通信模型
sgxsystemdaemon.exe (后台) sgxsegaboot.exe (WPF 界面)
│ │
│ CreateFileMappingW() │
│ MapViewOfFile() │
│ └── ShareData* pData │ OpenFileMappingW()
│ │ MapViewOfFile()
│ │ └── ShareData* pData
│ │
│ pData->sequencer = { │
│ stepNumber: 4, │
│ errorNumber: 21, ← "主板故障" │
│ bDevelopSequence: false │
│ } │
│ SetEvent(hDataReady) ────────────→ │ WaitForSingleObject(hDataReady)
│ │ read(pData->sequencer)
│ │ → "ERROR 0021"
│ │
│ Wait...Object(hCommand) ←───────── │ pData->command = CMD_EXIT
│ process(CMD_EXIT) │ SetEvent(hCommand)
│ SetEvent(hCommandDone) ──────────→ │ Wait...Object(hCommandDone)
从 C# 侧和 daemon 侧共同还原出的结构:
struct ShareData {
Sequencer sequencer; // 启动序列状态
SystemInfo systemInfo; // 系统信息
uint8_t command[1024]; // 命令缓冲区
};
struct Sequencer {
uint32_t stepNumber; // 当前步骤 (1-31)
uint32_t errorNumber; // 错误码 (0=正常)
bool bDevelopSequence; // 开发模式标志
};
struct SystemInfo {
wchar_t wszPlatformName[256]; // 平台名称
};
enum CommandType {
CMD_NETWORK_SETTING, // 设置网卡
CMD_NETWORK_TEST_START, // 网络测试开始
CMD_NETWORK_TEST_CLEAR, // 清除网络测试
CMD_MAIN_STORAGE_FORMAT, // 格式化主存储 (C:)
CMD_SUB_STORAGE_FORMAT, // 格式化副存储 (D:)
CMD_CREDIT_CLEAR, // 清除投币数据
CMD_CREDIT_SETTING, // 设置投币参数
CMD_CLOCK_SETTING, // 设置时钟
CMD_SYSTEM_CONFIGURATION, // 系统配置
CMD_CLEAR_TOUCH_CALIB, // 清除触屏校准
CMD_SAVE_TOUCH_CALIB, // 保存触屏校准
CMD_EXIT, // 退出 daemon
CMD_UNINSTALL, // 卸载
};
命令由 WPF 侧通过 SetEvent(hCommand) 发送,daemon 的 Worker@wrp@segaboot 执行。
对他有一个内部的开发者环境,可以绕过基板验证和物理加密狗(以下简称物理狗/虚拟狗)验证,生成一个虚假狗。
启动序列
Step 1: 许可证合并开始
Step 4: 主板验证 → 失败 → ERROR 0021 "主板故障"
Step 10: 磁盘检测
Step 11: 加载计数读取
Step 12: 系统信息收集
Step 13: 安装介质检测
Step 21: 模式选择
├── Normal → Keychip 启动
├── Developer → 开发模式
├── Recovery → 磁盘恢复
└── Update → 系统更新
Step 30-31: 完成/退出(稍后启动游戏程序)
开发者模式则是通过amsSystemPropertyIsDevelop() 函数。这是 SEGA AM Library 提供的一个系统属性查询函数,其错误信息直接出现在 DummyKeychip 模块的二进制中:
Error: amsSystemPropertyIsDevelop error = 0x%08\n
这个函数从 AM System Property 机制读取开发模式标志。这个标志的来源可能是:
- 注册表
HKLM\System\SEGA\SystemProperty\IsDevelop - 特定文件的属性
- 内核驱动曝露的属性
判断逻辑:
if (amsSystemPropertyIsDevelop() == TRUE) {
→ 进入 Developer Mode
→ Keychip 搜索失败时不报错,改走 ApplyDummyTask
} else {
→ 进入 Normal Mode
→ Keychip 搜索失败时 → ERROR 0001 "找不到加密锁"
}
实际上我在注册表里面什么都读不到,包括下文提到的会在注册表里面存储物理狗信息。
主板验证
主板验证对应 GetInfoTask@mainboard@startup 任务,在 Step 4 执行。它调用 5 个 amPlatform* 函数:
amPlatformGetMemorySize — 内存检测
二进制字符串:
"GlobalMemoryStatusEx Error!! (%d)"
伪代码还原:
uint64_t amPlatformGetMemorySize() {
MEMORYSTATUSEX memInfo = { .dwLength = sizeof(MEMORYSTATUSEX) };
if (!GlobalMemoryStatusEx(&memInfo)) {
log("GlobalMemoryStatusEx Error!! (%d)", GetLastError());
return 0; // ← 触发 ERROR 0021
}
if (memInfo.ullTotalPhys == 0)
return 0; // ← 物理内存为 0 → ERROR 0021
return memInfo.ullTotalPhys;
}
amPlatformGetGraphicInfo — 显卡检测
二进制字符串:
"amPlatformGetGraphicInfo"
"\\.\DISPLAY"
"SetupDiGetDeviceInfoListDetail Error!! Error Code is %ld!!\n"
遍历 EnumDisplayDevicesW() 查找活动的显示设备;同时打开 \\.\DISPLAY 设备通过 DeviceIoControl 查询 EDID。
amPlatformGetUsbIdList — USB 设备 ID 列表
这是最关键的部分。二进制中直接存放了 USB 设备 ID 的 5 段格式:
内存 0x14013dac8: "amPlatformGetUsbIdList"
之后紧跟着 5 个 4 字节的对齐字符串:
地址 字符串 说明
0x14013dae0: "VEN_" ← USB 供应商 ID (Vendor)
0x14013dae8: "VID_" ← 产品/设备 ID (Device)
0x14013daf0: "DEV_" ← 设备类别 (Class)
0x14013daf8: "SUBSYS_" ← 子系统 ID (Subsystem) ★最关键
0x14013db00: "PID_" ← 产品 ID (Product)
构造出的 USB 设备标识格式:
VEN_XXXX_VID_XXXX_DEV_XXXX_PID_XXXX_SUBSYS_XXXX
验证代码执行:
SetupDiGetClassDevsW(NULL, L"USB", NULL,
DIGCF_ALLCLASSES | DIGCF_PRESENT);
for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfo); i++) {
CHAR deviceId[MAX_DEVICE_ID_LEN];
CM_Get_Device_IDA(devInfo.DevInst, deviceId, MAX_DEVICE_ID_LEN, 0);
// deviceId 格式:
// "USB\VID_XXXX&PID_XXXX\SUBSYS_XXXX&REV_XXXX\..."
// 解析出 VEN/VID/DEV/PID/SUBSYS 构造 ID
snprintf(formattedId,
"VEN_%04X_VID_%04X_DEV_%04X_PID_%04X_SUBSYS_%04X",
vendorId, deviceId, devClass, productId, subsysId);
// 与已知 SEGA 硬件白名单匹配
if (MatchKnownSEGAHardware(formattedId, &hwType)) {
switch (hwType) {
case HW_MAINBOARD: ...
case HW_IOBOARD: ...
case HW_NETWORK: ...
case HW_STORAGE: ...
}
}
}
SUBSYS_ 是基板匹配的关键字段。 每个 SEGA 基板在 PCI/USB 配置空间中有一个唯一的子系统 ID,daemon 通过这个 ID 确定插入的是什么型号的基板。
amPlatformGetDevSet — 设备集合验证
地址 0x14013db88: "amPlatformGetDevSet"
汇总前 3 项检测结果,检查基板配置完整性:
BOOL amPlatformGetDevSet(DevSet* devSet) {
UsbIdList usbList;
amPlatformGetUsbIdList(&usbList);
BOOL hasMainboard = FALSE;
BOOL hasIoBoard = FALSE;
BOOL hasNetwork = FALSE;
BOOL hasStorage = FALSE;
for (each id in usbList) {
MatchKnownSEGAHardware(id, &hwType);
switch (hwType) {
case HW_MAINBOARD: hasMainboard = TRUE; break;
case HW_IOBOARD: hasIoBoard = TRUE; break;
case HW_NETWORK: hasNetwork = TRUE; break;
case HW_STORAGE: hasStorage = TRUE; break;
}
}
// 以下任何一项缺失 → ERROR 0021
if (!hasMainboard || !hasIoBoard ||
!hasNetwork || !hasStorage)
return FALSE;
devSet->mainboardPresent = hasMainboard;
devSet->ioBoardPresent = hasIoBoard;
devSet->networkPresent = hasNetwork;
devSet->storagePresent = hasStorage;
return TRUE;
}
这 5 道关卡:
Step 4: GetInfoTask@mainboard
│
├── amPlatformGetMemorySize()
│ └── "GlobalMemoryStatusEx Error!! (%d)" → 内存为 0 → ERROR 21
│
├── amPlatformGetGraphicInfo()
│ └── "SetupDiGetDeviceInfoListDetail Error!!..." → 无显示设备
│ └── "\\.\DISPLAY" 打开失败 → 可能继续 (无头运行)
│
├── amPlatformGetUsbIdList()
│ └── "SetupDiGetClassDevs Error!!..." → USB 枚举失败 → ERROR 21
│ └── 构造 "VEN_/VID_/DEV_/PID_/SUBSYS_" → 与白名单匹配
│
├── amPlatformGetDevSet()
│ └── 必要硬件 (主板/IO板/网卡/存储) 是否全部存在?
│ └── 缺任何一个 → ERROR 21
│
├── amLibGetMainSerialId()
│ └── 通过 WMI 查询 Win32_BaseBoard.SerialNumber
│ └── 序列号为空 → ERROR 21
│
└── 全部通过 → Step 10
Keychip
Keychip 也就是我们俗话所有的狗,是一个 USB HID 设备 (硬件加密狗/U盘状),内部包含加密 NVRAM,存储游戏授权信息。
daemon 中相关的 RTTI 类:
Manager@keychip@impl ← Keychip 管理器
KeychipInfo@impl ← Keychip 配置数据
KeychipNvramAccess@impl ← NVRAM 读写访问
DummyKeychip ← 软件模拟实现 (Rev 3406)
它的运行逻辑是这样的,
SearchTask (USB HID 扫描)
│
├── 找到物理 keychip
│ → ReadInfoTask
│ → amyDongleOpen() ← 打开 USB 加密狗
│ → KeychipNvramAccess::setup()
│ → 读取 6 个 NVRAM 参数
│ → ValidateTask
│ → BCrypt 解密 NVRAM 数据
│ → HMAC-SHA256 验证签名
│ ┌── 通过 → 使用物理 keychip
│ └── 失败 → dev mode? → ApplyDummyTask
│ → normal → 报错
│
└── 没找到
┌── dev mode? → ApplyDummyTask
└── normal → ERROR 0001 "找不到加密锁"
在内存地址0x140150dd0 中,我们可以找到 NVRAM 数据表,它存储的信息包括。
地址 字符串 类型 说明
─────────────────────────────────────────────────
0x140150dd0 System\Sega\ 路径 注册表备份路径
SystemProperty\
keychip
0x140150e18 gameId REG_SZ 游戏ID (如 "SDGB")
0x140150e28 region REG_SZ 地区 ("JPN"/"CHN")
0x140150e38 modelType REG_SZ 机型号 ("TYPE-5")
0x140150e50 systemFlag DWORD 系统功能标志
0x140150e68 serverIpIpv4 REG_SZ ALL.NET 服务器 IPv4
0x140150e88 serverIpIpv6 REG_SZ ALL.NET 服务器 IPv6
对,它是会备份数据到注册表,但是实际情况我找不到这个注册表,不知道为何。
它的读写流程为
// KeychipNvramAccess 的 NVRAM 访问流程
KeychipNvramAccess::setup() {
amyDongleOpen(); // USB HID 打开加密狗
recvFromInterruptPipe(hDevice); // 建立中断管道
recvFromControlPipe(hDevice); // 建立控制管道
}
KeychipNvramAccess::read(L"gameId") {
// 1. HID 协议发送读取请求
// 2. amUsbioRecvAcknowledge() 等待回复
// 3. BCryptDecrypt() 解密加密数据
// 算法: AES-CBC, 密钥在 daemon 代码段中
// 4. HMAC-SHA256 签名验证
// 5. 写入注册表缓存 (如果是首次读取)
// 6. 返回解密后的值
}
// NVRAM 加密条目结构 (验证签名用)
struct NvramEntry {
wchar_t paramName[32]; // "gameId", 等
uint32_t dataSize;
uint8_t encryptedData[256]; // BCrypt AES-CBC 加密
uint8_t hmac[32]; // HMAC-SHA256 签名
};
然后验证为
AMS_STATUS ValidateTask::execute() {
// 1. 打开 AES 算法
BCryptOpenAlgorithmProvider(&hAlg, L"AES", NULL, 0);
BCryptSetProperty(hAlg, L"ChainingMode", L"CBC", ...);
// 2. 导入解密密钥
BCryptGenerateSymmetricKey(hAlg, &hKey, key, keyLen);
// 3. 解密
BCryptDecrypt(hKey, nvramData.encrypted, nvramData.size,
NULL, iv, sizeof(iv),
decrypted, sizeof(decrypted), &resultLen,
BCRYPT_BLOCK_PADDING);
// 4. HMAC 验证
BCryptCreateHash(hAlg, &hHash, NULL, 0, macKey, macKeyLen, 0);
BCryptHashData(hHash, decrypted, resultLen);
BCryptFinishHash(hHash, computedHmac, 32);
// 5. 签名比较
if (memcmp(computedHmac, nvramData.hmac, 32) == 0)
return OK; // ← 物理 keychip 验证通过
else
return FAIL; // ← 签名不匹配
}
根据逆向,它存储到注册表的位置为
HKEY_LOCAL_MACHINE
└── System\SEGA\SystemProperty\keychip\ ← 路径来自 :0x140150dd0
├── gameId REG_SZ ← 字符串值
├── region REG_SZ
├── modelType REG_SZ
├── systemFlag REG_DWORD ← 通过 amyRegistrySetEntryDataInteger
├── serverIpIpv4 REG_SZ
└── serverIpIpv6 REG_SZ
代码调用链:
amyDongleOpen() → 读取 NVRAM
→ 验证通过后
→ amyRegistryOpen("System\\Sega\\SystemProperty\\keychip")
→ amyRegistrySetEntryDataInteger(hKey, L"systemFlag", 0x00000027)
→ amyRegistryClose(hKey)
DummyKeychip
这里面竟然内置了一套虚拟狗程序,可以模拟keychip,似乎是在开发环境中调用的。
DummyKeychip Build:Jan 9 2024 14:39:55 ($Rev: 3406 $)
对比物理 keychip 相关模块:
KeychipInfo Build:Jan 9 2024 14:39:55 ($Rev: 2356 $) ← Rev 2356
NvramAccess Build:Jan 9 2024 14:39:55 ($Rev: 2356 $) ← Rev 2356
DummyKeychip Build:Jan 9 2024 14:39:55 ($Rev: 3406 $) ← Rev 3406
触发条件为读取Developer Mode的布朗值。
modeselect@startup
│
├── amsSystemPropertyIsDevelop() = TRUE
│ → 全局开发者模式
│ → SearchTask 搜索物理 keychip
│ ├── 找到 → 正常读取+验证
│ └── 没找到 → ApplyDummyTask → DummyKeychip
│
└── amsSystemPropertyIsDevelop() = FALSE
→ 正常模式
→ SearchTask 搜索物理 keychip
├── 找到 → 正常读取+验证
└── 没找到 → ERROR 0001 "找不到加密锁"
开发者模式开关正是 amsSystemPropertyIsDevelop() 函数,错误信息紧邻 DummyKeychip 构建字符串的后方:
0x140150c80: "Error: amsSystemPropertyIsDevelop error = 0x%08\n"
虚拟狗的模拟实现是这样的,
class DummyKeychip : public impl::Base {
public:
// 1. 跳过硬件打开
AMS_STATUS open() { return STATUS_OK; } // 不开 USB
AMS_STATUS close() { return STATUS_OK; }
// 2. 生成虚拟 KeychipInfo
AMS_STATUS getInfo(KeychipInfo* info) {
// 硬编码默认值:
info->gameId = L"SDEZ"; // 默认游戏ID
info->region = L"JPN"; // 默认地区
info->modelType = L""; // 不限制机型
info->systemFlag = 0x00000000; // 默认标志
info->serverIpv4 = L"127.0.0.1"; // 回环地址
info->serverIpv6 = L"::1"; // IPv6 回环
return STATUS_OK;
}
// 3. NVRAM 用内存替代,跳过真实读写
AMS_STATUS readNvram(const wchar_t* name, void* data, size_t* size) {
// 不调用 amyDongleOpen
// 不从 HID 中断管道接收
// 不从 HID 控制管道接收
// 不从 BCrypt 解密
// 不从 HMAC 校验
// → 直接从内存映射返回硬编码值
return STATUS_OK;
}
// 4. 验证直接返回"通过"
AMS_STATUS validate() {
return STATUS_OK; // 跳过所有加密验证
}
// 5. 生成虚拟序列号
uint64_t getSerialNumber() {
return 0xDEADBEEF00000001ULL; // 固定调试序列号
}
};
可以看到几乎都是返回的值,我去这不是废话吗
DummyKeychip 没有自己的 vtable(二进制中搜索不到 .?AVDummyKeychip 之外的任何引用)。这意味着它是通过工厂模式构造的:
ApplyDummyTask::execute() {
auto* dummy = new DummyKeychip(); // 工厂构造
Manager@keychip::setKeychip(dummy); // 注入管理器
return STATUS_OK;
}
USB IO 基板验证
首先发现枚举所有的USB设备,然后解析产品中的字符串。
io::usb::Manager::setup()
→ HidD_GetHidGuid()
→ SetupDiGetClassDevsW(&hidGuid)
→ 遍历设备:
SetupDiEnumDeviceInterfaces()
→ amUsbioSetupDevice(devicePath)
setupNode(hDevice)
→ HidD_GetManufacturerString()
→ 失败: "Error: HidD_GetManufacturerString(). ErrCode %u\n"
→ HidD_GetProductString()
→ 失败: "Error: HidD_GetProductString(). ErrCode %u\n"
→ amUsbiopParseNodeInfo(mfr, product, &info)
SEGA IO 板的产品字符串中嵌入机器可读的参数:
// 产品字符串示例:
"BTIDa_1000_BoardNo_A01_FW_1.0.3_FUNC_SWITCH"
↓ splitProduct 按 '_' 拆分
parseBoardType(parts, &boardType) → BTIDa = 1000
parseBoardNo(parts, &boardNo) → BoardNo = A01
parseFirmwareRevision(parts, &fwRev) → FW = 1.0.3
parseFirmwareSum(parts, &fwSum) → 固件校验和
parseCustomChipNo(parts, &customChip) → 自定义芯片编号
parseMode(parts, &mode) → 工作模式
BTIDa / BTIDA 是基板类型标识。 parseBoardType 提取这个值来确定基板类型。
ALTR 协议 TCP中继
这个程序为2013年的TCP中继,通过研究世嘉的后端代码,发现的确是这么老,他们的后端几乎都是老掉牙的php和springboot。
(ALLNET) BEACON,1,SDVX-JPN-A12-2024, ← 信标广播
(ALLNET) STATUS,128,SDVX,JPN,TYPE-5,OK, ← 状态上报
(ALLNET) PING,55,1703123456, ← 心跳探测
(ALLNET) CONFIG,12,192.168.1.100, ← 配置同步
(ALLNET) ERROR,0xEE0002,Packet Too Long, ← 错误响应
协议堆栈
altrInitProbe()
→ altrIcmpInitialize() → IcmpCreateFile()
→ altrIcmpSendEcho() → IcmpSendEcho2()
→ altrIcmpWaitReply() → 等回复
→ altrIcmpGetReplyStatus() → 检查状态
→ altrCheckProbeTimeout() → 超时检查
altrServerMain()
→ socket → bind → listen → accept
→ 每次连接:
1. WSARecv()
2. altrDecodePacket() → 解析 "(ALLNET) %s,%d,%s,"
3. 处理命令
4. altrSendPacket() → WSASend()
altrSendPacket():
// 格式化为 "(ALLNET) %s,%d,%s,"
// → 错误码 0xEE0002 = "too long packet"
你也可以通过bsnk的文章来详细了解ALLNET,这里不做过多阐述。
开发者模式
检测是否启用了开发者模式
amsSystemPropertyIsDevelop() 读取 AM System Property,可能来源:
- 注册表
System\SEGA\SystemProperty\IsDevelop - DIP 开关状态 (通过 USB IO 板读取)
- 特定 USB 设备的插入
- 启动时的按键组合
这里没有做详细的研究只是进行了大致的猜测。
那么开发者模式到底有什么东西呢?
发现如果你启用了开发者模式,会执行这些东西
Sequencer@develop@execution@@:
① MountTask@devstorage → 挂载开发者存储 (U盘/SD/网络)
② MountTask@optionimage → 挂载选项镜像
③ ExecuteTask@startup → 执行启动脚本 (autorun.sh/bat)
④ DisableTask@keyboardfilter → 开放键盘输入 (USB 键盘直接操控系统)
⑤ DisableTask@firewall → 关闭防火墙 (所有端口开放)
⑥ AddFolderTask@sharedfolder → 创建 SMB 网络共享
⑦ ChannelEncryptOffTask → 关闭 ALL.NET 通信加密
⑧ AssignTask@privilege → 权限提升
这样就是为什么你在基板上插鼠标和键盘没有任何反应的原因,他们通过注册表限制了这些,其次是会打开segaboot下的按钮操作,你可以通过键盘的Enter进入系统测试菜单。
// sgxsegaboot ViewModel.cs
onSequenceChange(sequencer) {
m_bEnableEnterTest = sequencer.m_bDevelopSequence;
// → 开发模式下 "Enter Test" 按钮变为可用
// → 点击后进入系统测试菜单
}
存储初始化
这也是我为什么想要逆向segaboot的原因,因为我在尝试修复我那台新加坡机器时所遇到的问题,因为那里的gs1盘被人偷走了,所以我请了🐍妈帮我写了盘,插上机后,我按照流程初始化了副盘,然后开机,等到稍后启动游戏程序,然后就黑屏重启,继续初始化主盘,注意这里我没有主动初始化,但是他自己却主动初始化了,接下来就是反复的重启与初始化主盘,令我很头疼。
sgxsegaboot 系统测试菜单中有「初始化主盘」和「初始化副盘」两个功能。对应 daemon 中的两个 Worker 命令和两个系统测试任务。
从二进制中提取的存储设备路径字符串:
地址 字符串 含义
──────────────────────────────────────────────────
0x140151d08 \\.\Q:\ ← 副盘 (Q:) 设备路径
0x140151d78 \\.\Q: ← 副盘设备路径 (无尾斜杠)
0x140151d73 Q: ← 副盘盘符 (用于挂载点)
0x140151d18 \\.\%C:\ ← 主盘设备路径模板 (%C → C)
0x140151cf8 \device\CDRom%d ← CD/DVD 驱动器路径模板
涉及的类 (从 RTTI 提取)
存储管理:
Volume@impl@@ ← 卷管理实现
Manager@mount@appimage@impl@@ ← 挂载管理器
TYPE@mount@appimage@impl@@ ← 挂载类型枚举
格式化命令 (Worker 命令, 由 sgxsegaboot 触发):
MainStorageFormatCommand@inner@worker@wrp@segaboot
SubStorageFormatCommand@inner@worker@wrp@segaboot
格式化任务 (系统测试菜单调用):
FormatMainStorageTask@appimage@systemtest@
FormatSubStorageTask@appimage@systemtest@
磁盘发现与初始化:
FindTask@substorage@initialboot@ ← 发现副盘设备
Sequencer@disk@startup@ ← 磁盘启动序列
GetSizeTask@Sequencer@disk@startup ← 获取磁盘大小
SetAmfsPathTask@Sequencer@disk@startup ← 设置 AMFS 路径
磁盘验证 (initialboot 时执行):
CheckSequencer@volume@checkdisk@initialboot
CheckBitLockerTask ← 检查 BitLocker 加密状态
ValidateVolumeTask ← 验证卷完整性
DirtyCheckTask ← 检查脏状态
恢复:
RecoveryTask@disk@modeselect@execution ← 磁盘恢复
RecoveryTask@amfs@common@execution ← AMFS 文件系统恢复
挂载/卸载:
MountTask@mount@appimage@release@execution
UnmountTask@prepare@appimage@release@execution
主盘 (Main Storage) 初始化流程
物理路径: \\.\C:\ (通过 \\.\%C:\ 模板,%C → C)
触发途径:
- 系统测试菜单 → 选择”初始化主盘”
- sgxsegaboot 发送
MainStorageFormatCommand→ daemon 的 Worker 执行 - 或直接从 systemtest 调用
FormatMainStorageTask
执行逻辑 (从 API 调用链还原):
AMS_STATUS MainStorageFormatCommand::execute() {
// ===== 第 1 步: 准备卷 =====
// 1a. 获取主盘卷 GUID
wchar_t volumeGuid[MAX_PATH];
GetVolumeNameForVolumeMountPointW(L"C:\\", volumeGuid, MAX_PATH);
// 1b. 锁定卷 (阻止其他进程写入)
HANDLE hVolume = CreateFileW(L"\\\\.\\C:\\",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
DWORD bytesReturned;
DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME,
NULL, 0, NULL, 0, &bytesReturned, NULL);
// 1c. 卸载卷 (移除文件系统挂载)
DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME,
NULL, 0, NULL, 0, &bytesReturned, NULL);
// → 失败时: "Error : Cannot Unmount C: (LastError = %d).\n"
// 1d. 删除现有挂载点
DeleteVolumeMountPointW(L"C:\\");
// ===== 第 2 步: 格式化 =====
// 格式化方式: 标准 NTFS 快速格式化
// 使用 DeviceIoControl(FSCTL_FORMAT) 或 SHFormatDrive
// 或自己写 MBR + NTFS 引导扇区
DWORD maxCount = 100; // 重试计数器
while (maxCount-- > 0) {
// 通过 WriteFile 写入文件来测试可写性
// 或用 DeviceIoControl 发格式化命令
}
// ===== 第 3 步: 重建挂载 =====
SetVolumeMountPointW(L"C:\\", volumeGuid);
// ===== 第 4 步: 创建 AMFS 目录结构 =====
// AMFS = AM File System (SEGA 自定义游戏数据文件系统)
CreateDirectoryW(L"C:\\AMFS", NULL);
// 创建 AMFS 子目录结构
amfsPath = SetAmfsPathTask(L"C:\\AMFS");
// ===== 第 5 步: 验证 =====
CheckSequencer@volume@checkdisk
→ CheckBitLockerTask // BitLocker 是否关闭?
→ ValidateVolumeTask // 卷是否可读?
→ DirtyCheckTask // 是否有脏数据?
// ===== 第 6 步: 设置卷标 =====
SetVolumeLabelW(L"C:\\", L"SEGA_MAIN");
CloseHandle(hVolume);
return AMS_STATUS_OK;
}
副盘 (Sub Storage) 初始化流程
物理路径: \\.\Q:\ 或 \\.\Q:
Q: 盘是什么? Q: 不是 Windows 标准盘符。它是 SEGA 系统专有的游戏数据分区/卷。可能是:
- 物理硬盘上划出的独立分区
- 挂载的 VHD (虚拟硬盘文件)
- RAM Disk (内存盘,用于游戏缓存)
- AMFS 格式化的专用数据卷
触发途径:
- 系统测试菜单 → 选择”初始化副盘”
- sgxsegaboot 发送
SubStorageFormatCommand→ daemon 的 Worker 执行 - 或直接从 systemtest 调用
FormatSubStorageTask - 在 initialboot 时由
FindTask@substorage自动发现
执行逻辑:
AMS_STATUS SubStorageFormatCommand::execute() {
// ===== 第 1 步: 发现副盘设备 =====
// 方法 1: QueryDosDeviceA 查询 Q: 对应的物理设备
CHAR targetPath[MAX_PATH];
QueryDosDeviceA("Q:", targetPath, MAX_PATH);
// → 返回值: "\Device\HarddiskVolumeN"
// 方法 2: FindTask@substorage@initialboot
// 遍历所有卷,寻找符合条件的副盘
HANDLE hFind = FindFirstVolumeW(volumeGuid, MAX_PATH);
while (hFind) {
GetVolumePathNamesForVolumeNameW(guid, paths, ...);
// 匹配特征: 特定分区大小/标签/类型
if (IsSubStorageCandidate(guid)) {
found = guid;
break;
}
FindNextVolumeW(hFind, volumeGuid, MAX_PATH);
}
// ===== 第 2 步: 打开副盘设备 =====
// 使用 CreateFileA (ANSI 版本) 打开
HANDLE hDevice = CreateFileA(
"\\\\.\\Q:", // 或 "\\.\\Q:\\"
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
// → 失败: "Error: CreateFileA %08x"
// ===== 第 3 步: 锁定并卸载 =====
DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, ...);
DeviceIoControl(hDevice, FSCTL_DISMOUNT_VOLUME, ...);
// → "Error : Cannot Unmount Q: (LastError = %d).\n"
DeleteVolumeMountPointA("Q:\\");
// ===== 第 4 步: 格式化 =====
// 方式: 通过 DeviceIoControl + WriteFile 创建 AMFS 格式
// 或用标准 Windows 格式化接口
FormatSubStorageTask::execute() {
// 1. 计算副盘大小
GetDiskFreeSpaceExW(L"Q:\\", ...);
// 2. 清理旧数据
SetFilePointerEx(hDevice, 0, NULL, FILE_BEGIN);
SetEndOfFile(hDevice);
// 3. 写 AMFS 文件系统元数据
// → AMFS_FILE_HEADER
// → AMFS_ROOT_DIRECTORY
// → AMFS_ALLOCATION_TABLE
// 所有这些通过 WriteFile 直接写入设备
// 4. 创建根目录结构
CreateDirectoryW(L"Q:\\AMFS", NULL);
CreateDirectoryW(L"Q:\\APPDATA", NULL);
CreateDirectoryW(L"Q:\\GAME", NULL);
CreateDirectoryW(L"Q:\\BACKUP", NULL);
}
// ===== 第 5 步: 重建挂载 =====
SetVolumeMountPointA("Q:\\", volumeGuid);
// ===== 第 6 步: 验证 =====
GetVolumeInformationW(L"Q:\\", ...);
// 验证卷可读、空间正确
CloseHandle(hDevice);
return AMS_STATUS_OK;
}
初始化执行流程
首次启动 (initialboot):
Sequencer@disk@startup
├── GetSizeTask → 获取主盘和副盘大小
├── SetAmfsPathTask → 设置 AMFS 路径
└── FindTask@substorage → 发现副盘
CheckSequencer@volume@checkdisk
├── CheckBitLockerTask → 检查 BitLocker (必须关闭)
├── ValidateVolumeTask → 验证卷完整性
└── DirtyCheckTask → 检查脏状态
用户主动操作 (系统测试菜单):
"初始化主盘" → MainStorageFormatCommand
→ 锁定 C: → 卸载 C: → 格式化为 NTFS → 重建 AMFS 目录
"初始化副盘" → SubStorageFormatCommand
→ 锁定 Q: → 卸载 Q: → 格式化为 AMFS → 重建游戏数据目录
接下来就是关于为什么我反复重启的原因分析,我的猜测可能是这样的。
现象:
正常启动 → STEP30 → 突然关机重启
↓
再次开机 → 触发初始化主盘 → 格式化
↓
格式化完成 → 自动重启
↓
正常启动 → 游戏运行 → 突然关机重启
↓
⋮ (无限循环)
从 CheckSequencer@volume@checkdisk@initialboot 的 RTTI 和 API 调用链还原的磁盘检查逻辑:
// 伪代码还原 — 初装引导时的磁盘检查
CheckSequencer::execute(Volume* volume) {
bool triggerFormat = false;
// ===== 第一步: 检查 BitLocker =====
if (CheckBitLockerTask::execute(volume) == LOCKED) {
// BitLocker 加密开启 → 无法读取
// 街机系统不需要 BitLocker
triggerFormat = true;
log("BitLocker detected on %S, format required", volume->path);
}
// ===== 第二步: 检查脏位 =====
if (DirtyCheckTask::execute(volume) == DIRTY) {
// 卷的脏位被设置 → 上次是非正常卸载
// ★ 注意: 不运行 chkdsk, 不尝试修复
// ★ 直接触发格式化
triggerFormat = true;
log("Dirty bit set on %S, format required", volume->path);
}
// ===== 第三步: 验证卷完整性 =====
if (ValidateVolumeTask::execute(volume) == INVALID) {
// 文件系统元数据损坏 / 不一致
triggerFormat = true;
log("Volume %S is invalid, format required", volume->path);
}
// ===== 决策 =====
if (triggerFormat) {
// 不是修复,是直接重建
MainStorageFormatCommand::execute();
// 格式化后重启
ExitCommand::execute(1); // 带重启参数退出
}
}
// 脏位检测的实现 (DirtyCheckTask)
DirtyCheckTask::execute(Volume* volume) {
// 方式一: 通过 GetVolumeInformation 检查卷状态
DWORD flags = 0;
GetVolumeInformationW(volume->path, NULL, 0,
NULL, NULL, &flags, NULL, 0);
// 检查 VOLUME_IS_DIRTY 标志
if (flags & FILE_VOLUME_IS_DIRTY) {
return DIRTY; // → 触发格式化
}
// 方式二: 直接写测试文件测读写
HANDLE hTest = CreateFileW(
L"C:\\AMFS\\_chk.tmp",
GENERIC_READ | GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, 0, NULL);
if (hTest == INVALID_HANDLE_VALUE) {
// 无法创建测试文件 → 卷有问题
return DIRTY;
}
// 写入并验证
WriteFile(hTest, testData, ...);
FlushFileBuffers(hTest);
// 验证写入成功
CloseHandle(hTest);
DeleteFileW(L"C:\\AMFS\\_chk.tmp");
return CLEAN; // 卷正常
}
CheckSequencer (initialboot)
│
├── Step 1: CheckBitLockerTask
│ → 检查卷是否有 BitLocker 加密
│ → 如果加密 → 触发格式化
│
├── Step 2: DirtyCheckTask ← ★ 关键
│ → 检查卷是否是"脏的"(非正常卸载)
│ → 如果脏 → 直接触发格式化 (不修复)
│
├── Step 3: ValidateVolumeTask
│ → 验证文件系统完整性
│ → 如果不一致 → 触发格式化
│
└── 任意一步触发 → MainStorageFormatCommand → 重启
其实也有可能是这样造成的无限重启
循环链完整还原:
┌──────────────────┐
│ 游戏/系统异常退出 │
│ (原因待动态确定) │
└────────┬─────────┘
│
▼
Windows 非正常关机
│
▼
ntfs.sys 设置卷脏位
(标准 Windows 行为)
│
▼
DirtyCheckTask 读到脏位
→ CHECK_DIRTY
│
▼
CheckSequencer → 触发格式化
│
▼
MainStorageFormatCommand
→ 格式化 C: → 重启
│
└──→ 回到开头 (循环)
存储格式化的 PowerShell 脚本分析
SEGA ALLS 系统的「初始化主盘」和「初始化副盘」不是由 C++ daemon 直接完成的,而是通过 daemon 触发 PowerShell 脚本链来实现。整个过程分 Deploy(部署) 和 Apply(应用) 两个阶段,中间通过一次系统服务模式重启来隔离。
也就是开头提到的sega目录有一些apply那些东西。
流程如下
sgxsegaboot → 选择格式化
↓
Worker 命令 → sgxsystemdaemon
↓
deploy\deploy.bat → deploy\*.ps1 ← 第 1 阶段: 复制脚本到 N: 分区
↓
EnterSystemServicingMode → 重启 ← 进入系统服务模式
↓
N:\apply.bat → N:\apply.ps1 ← 第 2 阶段: 实际执行格式化
↓
退出服务模式 → 正常重启
主盘初始化流程
deploy\sgMainstorageFormatDeploy.ps1
#
# Copyright(C)SEGA
#
$version = "0.02"
# 主存储格式化的触发处理
$commonPath = "C:\windows\sega\System\"
pushd $commonPath
# 汎用处理读取
. .\sgSystemServicingCommon.ps1
# N 驱动器マウント
$bRet = UnlockBitLocker "N" "$commonPath\system_key.txt"
if (!$bRet)
{
exit 1
}
# N 驱动器ファイル全删除
Remove-Item N:\* -Recurse
# ファイル复制
Copy-Item ".\mainstorage\apply\*" "N:\" -Recurse
if (Test-Path D:\mainstorage.json) {
Move-Item "D:\mainstorage.json" "N:\"
}
# システムサービシングモード遷移 + 重启
$bRet = EnterSystemServicingMode "mainstorageformat"
if (!$bRet)
{
exit 1
}
exit 0
关键操作:
UnlockBitLocker("N")— 用system_key.txt解锁 N: 分区的 BitLockerRemove-Item N:\*— 清空 N: 分区Copy-Item .\mainstorage\apply\* N:\— 把 apply 脚本拷贝到 N:Move-Item D:\mainstorage.json N:\— 把配置 JSON 移到 N:EnterSystemServicingMode("mainstorageformat")— 注册服务模式并重启
apply\sgMainstorageFormatApply.ps1
#
# Copyright(C)SEGA
#
$version = "1.00"
# 主存储格式化本处理
# 主存储のAMFS/AppDataを格式化する
# 副存储が接続されているときは初期处理は実行しない
# 之后可以更改此容量设置
# 针对目标驱动器的 BitLocker 设置
function EnableBitLocker([string]$letter)
{
$bRet = EnableAutoUnlockBitLocker $letter
if (!$bRet) {
return $false
}
return $bRet
}
$commonPath = "C:\windows\sega\System\"
pushd $commonPath
. .\sgSystemServicingCommon.ps1
# 获取主存储编号
$main = GetDiskNumber "C"
if ($main -eq -1){
# 主存储が見つからず
# 正常启动
LeaveSystemServicingModeNextBoot | out-null
# ERROR 0085 を显示
exit 85
}
# 副存储搜索 (容量 256GB 以上)
$subs = GetHddDiskNumber 256
# 主存储を除外
$subs = $subs | ? {$_ -ne $main}
if ($subs -ne $null)
{
# 有副存储时不执行任何操作
exit 0
}
# JSON 设置读取
$ExpectedMinimumStorageSizeGB = 64
$AmfsSizeGB = 45
if (Test-Path N:\mainstorage.json) {
$mainStorageSettings = get-content N:\mainstorage.json | ConvertFrom-Json
if ($mainStorageSettings -eq $null) {
LeaveSystemServicingModeNextBoot | out-null
exit 85
}
if ($mainStorageSettings.ExpectedMinimumStorageSizeGB -ne $null) {
$ExpectedMinimumStorageSizeGB = $mainStorageSettings.ExpectedMinimumStorageSizeGB
}
if ($mainStorageSettings.AmfsSizeGB -ne $null) {
$AmfsSizeGB = $mainStorageSettings.AmfsSizeGB
}
}
# 异常值对策
if ($ExpectedMinimumStorageSizeGB -eq 0) {
$ExpectedMinimumStorageSizeGB = 64
$AmfsSizeGB = 45
}
if (($AmfsSizeGB -eq 0) -or ($ExpectedMinimumStorageSizeGB -lt $AmfsSizeGB)) {
LeaveSystemServicingModeNextBoot | out-null
exit 85
}
# 分区の设置のパス
$path = "HKLM:\SYSTEM\SEGA\SYSTEMPROPERTY\STATIC"
$generation = Get-ItemProperty $path -name "PlatformIdGeneration"
if ($generation.PlatformIdGeneration -ge 2)
{
# === ALLS 第 2 代及以后: 动态分区 ===
$cnt_file = "C:\cnt.txt"
:MAIN_LABEL do
{
$bRet = AdjustmentMainStorage 0 ($AmfsSizeGB * 1024) "D" "Y"
if (!$bRet) { break }
$bRet = EnableAutoUnlockBitLocker "Y"
if (!$bRet) { break }
# 确认盘符
$LetterList = @("D", "N", "P", "Y")
foreach ($Letter in $LetterList) {
$AssignRet = Get-Partition -diskNumber $main |
Where-Object{$_.DriveLetter -eq $Letter}
if ($AssignRet -eq $null) {
$bRet = $false
break MAIN_LABEL
}
}
} while ($false)
if ($bRet) {
. chkntfs /x C: D: N: Y: P:
Remove-Item $cnt_file
} else {
if (isRetry $cnt_file 5) {
IncrementRetryCount $cnt_file
exit -1 # 重试
} else {
Remove-Item $cnt_file
exit 84 # ERROR 0084
}
}
}
else
{
# === ALLS 第 1 代: 固定格式化 ===
# 快速格式化 D
$AmfsLetter = (get-volume | ? {$_.DriveLetter -eq "D"} ).DriveLetter
Format-Volume $AmfsLetter -FileSystem NTFS -NewFileSystem AMFS -Force
# 快速格式化 Y
$AppdataLetter = (get-volume | ? {$_.DriveLetter -eq "Y"} ).DriveLetter
Format-Volume $AppdataLetter -FileSystem NTFS -NewFileSystem AppData -Force
# 启用 BitLocker
$bRet = EnableBitLocker "Y"
if (!$bRet) {
LeaveSystemServicingModeNextBoot | out-null
exit 84
}
. chkntfs /x C: D: N: Y:
}
exit 0
关键逻辑分析:
| 行范围 | 逻辑 | 说明 |
|---|---|---|
| 38-45 | GetDiskNumber("C") | 找到系统盘的主磁盘号 |
| 48-56 | 检查 ≥256GB 副盘是否存在 | 如果有副盘,主盘格式化跳过! 这是设计约束 |
| 58-82 | 读取 N:\mainstorage.json | 动态配置: 磁盘最小容量, AMFS 分区大小 |
| 89-95 | 检查 PlatformIdGeneration | 第 1 代 vs 第 2 代分区布局不同 |
| 97-122 | 第 2 代: 动态分区 | 调用 AdjustmentMainStorage 调整 D:/Y:/N:/P: |
| 130-143 | 第 1 代: 固定格式化 | 快速格式化 D:→AMFS, Y:→AppData, 启用 BitLocker |
| 125, 141 | chkntfs /x C: D: N: Y: P: | 关闭所有系统盘的 chkdsk |
错误码映射:
| Error | 场景 |
|---|---|
| 0084 | 主盘格式化失败 (重试 5 次后) |
| 0085 | 主盘未找到 / JSON 配置无效 / AMFS 大小异常 |
| exit -1 | 重试中 (重启后继续) |
副盘初始化流程
deploy\sgSubstorageFormatDeploy.ps1
$version = "0.02"
# 副存储格式化的触发处理
$commonPath = "C:\windows\sega\System\"
pushd $commonPath
. .\sgSystemServicingCommon.ps1
$bRet = UnlockBitLocker "N" "$commonPath\system_key.txt"
if (!$bRet) { exit 1 }
Remove-Item N:\* -Recurse
Copy-Item ".\substorage\apply\*" "N:\" -Recurse
if (Test-Path D:\substorage.json) {
Move-Item "D:\substorage.json" "N:\"
}
# T:驱动器に配置したシステムファイルをD:驱动器に复制する
copyTDriveSystemFileToDDrive
$bRet = EnterSystemServicingMode "subStorageFormat"
if (!$bRet) { exit 1 }
exit 0
apply\sgSubstorageFormatApply.ps1
$version = "0.02"
$commonPath = "C:\windows\sega\System\"
pushd $commonPath
. .\sgSystemServicingCommon.ps1
$main = GetDiskNumber "C"
if ($main -eq -1) {
LeaveSystemServicingModeNextBoot | out-null
exit 86
}
# JSON 设置
$ExpectedMinimumStorageSizeGB = 256
$AmfsSizeGB = 128
if (Test-Path N:\substorage.json) {
$subStorageSettings = get-content N:\substorage.json | ConvertFrom-Json
if ($subStorageSettings -eq $null) {
LeaveSystemServicingModeNextBoot | out-null
exit 86
}
if ($subStorageSettings.ExpectedMinimumStorageSizeGB -ne $null) {
$ExpectedMinimumStorageSizeGB = $subStorageSettings.ExpectedMinimumStorageSizeGB
}
if ($subStorageSettings.AmfsSizeGB -ne $null) {
$AmfsSizeGB = $subStorageSettings.AmfsSizeGB
}
}
# 异常值对策
if ($ExpectedMinimumStorageSizeGB -eq 0) {
$ExpectedMinimumStorageSizeGB = 256
$AmfsSizeGB = 128
}
if (($AmfsSizeGB -eq 0) -or ($ExpectedMinimumStorageSizeGB -lt $AmfsSizeGB)) {
LeaveSystemServicingModeNextBoot | out-null
exit 86
}
# 副存储を搜索 (容量 256GB 以上)
$subs = GetHddDiskNumber $ExpectedMinimumStorageSizeGB
# 主存储を除外
$subs = $subs | ? {$_ -ne $main}
if ($subs -eq $null) {
LeaveSystemServicingModeNextBoot | out-null
exit 86
}
# 将最先找到的作为副存储
$sub = $subs[0]
$path = "HKLM:\SYSTEM\SEGA\SYSTEMPROPERTY\STATIC"
$generation = Get-ItemProperty $path -name "PlatformIdGeneration"
# 执行格式化
$bRet = FormatSubStorage $sub ($AmfsSizeGB * 1024) "T" "Y"
if (!$bRet) {
RemoveDriveLetter "Y" | out-null
if ($generation.PlatformIdGeneration -ge 2) {
AssignLetter $main 3 "D" | out-null
AssignLetter $main 4 "Y" | out-null
} else {
AssignLetter $main 3 "D" | out-null
AssignLetter $main 5 "Y" | out-null
}
LeaveSystemServicingModeNextBoot | out-null
exit 86
}
# 应用 BitLocker
$bRet = EnableAutoUnlockBitLocker "Y"
if (!$bRet) {
RemoveDriveLetter "Y" | out-null
if ($generation.PlatformIdGeneration -ge 2) {
AssignLetter $main 3 "D" | out-null
AssignLetter $main 4 "Y" | out-null
} else {
AssignLetter $main 3 "D" | out-null
AssignLetter $main 5 "Y" | out-null
}
LeaveSystemServicingModeNextBoot | out-null
exit 86
}
# 确认盘符
if ($generation.PlatformIdGeneration -ge 2) {
$LetterList =@(
@{"Letter" = "D"; "Disk" = $main},
@{"Letter" = "N"; "Disk" = $main},
@{"Letter" = "P"; "Disk" = $main},
@{"Letter" = "T"; "Disk" = $sub},
@{"Letter" = "Y"; "Disk" = $sub}
)
} else {
$LetterList =@(
@{"Letter" = "D"; "Disk" = $main},
@{"Letter" = "N"; "Disk" = $main},
@{"Letter" = "T"; "Disk" = $sub},
@{"Letter" = "Y"; "Disk" = $sub}
)
}
$cnt_file = "C:\cnt.txt"
# 重試機制 (5 次)
foreach ($Letter in $LetterList) {
$AssignRet = Get-Partition -diskNumber $Letter.Disk |
Where-Object{$_.DriveLetter -eq $Letter.Letter}
if ($AssignRet -eq $null) {
if (isRetry $cnt_file 5) {
IncrementRetryCount $cnt_file
exit -1
} else {
# 错误後处理...
exit 86
}
}
}
Remove-Item $cnt_file
if ($generation.PlatformIdGeneration -ge 2) {
. chkntfs /x C: D: T: N: Y: P:
} else {
. chkntfs /x C: D: T: N: Y:
}
# 复制系统文件
Copy-Item D:\sysfile.dat T:\sysfile.dat -Force
# 電子マネーフォルダ复制
if (Test-Path D:\emoney\) {
Copy-Item D:\emoney\ T:\emoney\ -Recurse -Force
}
exit 0
关键逻辑分析:
| 行范围 | 逻辑 | 说明 |
|---|---|---|
| 14-16 | GetDiskNumber("C") | 找到主盘 (排除主盘就是副盘) |
| 19-41 | 读取 N:\substorage.json | 副盘最小≥256GB, AMFS 分区 128GB |
| 44-49 | GetHddDiskNumber(256) | 扫描系统上所有 ≥256GB 的硬盘 |
| 52-56 | 排除主盘 | 第一个非主盘的就是副盘 |
| 64 | FormatSubStorage($sub, 128*1024, "T", "Y") | 分区副盘为 T:+Y: |
| 101-121 | $LetterList | 第 2 代有 P: 分区, 第 1 代没有 |
| 124-137 | 重试机制 | 5 次重试, 超过则 ERROR 0086 |
| 145-150 | 文件复制 | sysfile.dat + emoney\ 从 D: 到 T: |
错误码映射:
| Error | 场景 |
|---|---|
| 0086 | 副盘未找到 / 容量不足 / 格式化失败 / BitLocker 失败 |
恢复流程 (Recovery)
# deploy\deploy.ps1
$version = "0.01"
$commonPath = "C:\windows\sega\System\"
pushd $commonPath
. .\sgSystemServicingCommon.ps1
# SSD のライトプロテクトをアンロック
UnlockWriteProtection
# N 驱动器マウント
$bRet = UnlockBitLocker "N" "$commonPath\system_key.txt"
if (!$bRet) { exit 1 }
Remove-Item N:\* -Recurse
# recovery\apply を N: に复制
Copy-Item "C:\Windows\SEGA\System\recovery\apply\*" "N:\" -Recurse
# レジストリキーをコミット (UWF 保護下のレジストリ変更を確定)
$reg_key = "HKLM:\SYSTEM\SEGA\SystemProperty\SystemServicing\recovery"
$reg_names = @("dirty", "assign", "BitLocker", "bFindSubStorage")
foreach($name in $reg_names) {
$bRet = UwfCommitRegistry $reg_key $name
if (!$bRet) { exit 1 }
}
$bRet = EnterSystemServicingMode "mainStorageFormat"
if (!$bRet) { exit 1 }
exit 0
恢复流程关键操作:
UnlockWriteProtection— 解锁 SSD 写保护 (UWF)- 提交 4 个注册表键
UwfCommitRegistry— 将dirty/assign/BitLocker/bFindSubStorage状态写入持久化注册表 - 进入服务模式 → 格式化主盘 → 从副盘 T: 恢复数据
devcon
PDB 路径指向 Microsoft WDK 标准编译产物,非 SEGA 自定义开发,似乎使用来安装驱动程序的东西。
ALLS 系统中的具体调用场景
devcon install sgdongle.inf ← SG 加密狗驱动(USB 硬件识别)
devcon install sgkeychip.inf ← Keychip 驱动(USB 加密狗)
devcon install sgfscrypt.inf ← 文件系统加密驱动
devcon install sghwmonitor.inf ← 硬件监控驱动(温度/电压)
devcon install sghwreset.inf ← 硬件复位驱动
devcon install sgprotect.inf ← 保护驱动
devcon install hdd.inf / ssd.inf ← 存储驱动
关键 API
| API | 功能 |
|---|---|
CM_Reenumerate_DevNode_Ex | 硬件重新扫描 |
SetupDiCallClassInstaller | 触发 DIF 安装/移除动作 |
SetupCopyOEMInfW | 复制驱动包到驱动存储 |
SetupUninstallOEMInfW | 从驱动存储移除驱动包 |
UpdateDriverForPlugAndPlayDevicesW | 更新 PnP 设备驱动 |
CM_Locate_DevNode_ExW | 按硬件 ID 查找设备 |
CM_Get_DevNode_Status_Ex | 查询设备状态 |
CM_Connect_MachineW | 远程管理设备 |
pxSecureAtaCmd
用来存储设备写保护管理的小玩意,核心功能就是给驱动器写保护,然后解锁。
调用链为:
recovery/deploy/deploy.ps1:
UnlockWriteProtection ← PowerShell 函数
→ 调用 pxSecureAtaCmd.exe
→ tdkgs1::unlockWriteProtect ← 实质操作
→ CreateFileW(\.\...)
→ DeviceIoControl(IOCTL_...)
→ CryptEncrypt / CryptImportKey
系统恢复流程的第一行就是 UnlockWriteProtection——先解锁硬件写保护,才能进行后续的格式化/重装操作。
与 sgmanageio 驱动的关系
sgmanageio.exe 安装的驱动层提供 IOCTL 接口,pxSecureAtaCmd.exe 通过 CreateFileW + DeviceIoControl 与这个驱动通信,实现对存储硬件的写保护控制。
写这么多保护不如神人灵机一动
sgimagemount
用于挂载虚拟磁盘镜像的东西,似乎使用来处理类似于app,opt这种游戏文件的程序?
支持的命令
MOUNT <target> [OPTION=value] [VHDPARAM=value] ← 挂载镜像
UNMOUNT <GUID> [OPTION=value] ← 卸载镜像
三种挂载方式
① VhdMounter (标准 VHD):
OpenVirtualDisk → AttachVirtualDisk → GetVirtualDiskPhysicalPath
→ 挂载 VHD/VHDX 文件为虚拟磁盘 → 分配盘符
② FscryptMounter (加密镜像):
BCryptOpenAlgorithmProvider(L"AES")
BCryptGenerateSymmetricKey → 解密镜像数据
CreateFile → DeviceIoControl → 挂载解密后的文件系统
③ ImageMounter (通用):
→ 根据文件扩展名自动选择挂载方式
→ 管理挂载点和盘符
通信方式
父进程 (daemon / sglaunch):
│ 创建 mailslot
│ 发送 MOUNT/UNMOUNT 命令
▼
sgimagemount.exe:
├── CreateMailslotW → 等待命令
├── 执行操作
└── SetEvent → 通知父进程完成
└── ResultSender → 通过 mailslot 返回结果
在 ALLS 系统中的位置
daemon 的 MountTask@mount:
→ sgimagemount.exe MOUNT <game_image.vhd>
→ VhdMounter → OpenVirtualDisk → AttachVirtualDisk
→ 分配盘符 (通过 DefineDosDeviceW + SetVolumeMountPointW)
→ 通知 daemon 完成
daemon 的 UnmountTask:
→ sgimagemount.exe UNMOUNT <guid>
→ DetachVirtualDisk → 释放盘符
sglaunch
用来启动/监控游戏的程序,没啥好说的。
sgxsegaboot 完成 Step 30-31 (启动完成)
↓
sglaunch.exe
├── 1. 解析 sglaunch.json
│ ├── 游戏可执行文件路径
│ ├── 启动参数
│ ├── 工作目录
│ ├── 运行用户 (可能通过 CreateProcessWithLogonW)
│ └── 退出后行为 (关机/重启/返回系统测试)
│
├── 2. 创建互斥体 (单实例)
│
├── 3. CreateProcessW 启动游戏进程
│ └── 可选: CREATE_SUSPENDED → 准备完成后 ResumeThread
│
├── 4. WaitForSingleObject(hProcess, INFINITE)
│ ├── 正常退出 → GetExitCodeProcess → 处理退出码
│ └── 异常退出 → 记录错误到 SEGA_AM_LOG 事件
│
└── 5. 退出后决策
├── 退出码 = 0 → 正常关机
├── 退出码 = 1 → 重启系统
└── 退出码 = 其他 → 可能触发错误处理
sgmanageio
似乎是一个安装驱动程序的玩意.
安装的驱动列表
| 驱动名 | INF 文件 | SYS 文件 | 功能 |
|---|---|---|---|
| SG Dongle | sgdongle.inf | sgdongle.sys | USB 加密狗设备驱动 |
| SG Keychip | sgkeychip.inf | sgkeychip.sys | Keychip USB HID 通信 |
| SG File System Crypto | sgfscrypt.inf | sgfscrypt.sys | 文件系统加解密过滤驱动 |
| SG Hardware Monitor | sghwmonitor.inf | sghwmonitor.sys | 主板温度/电压/风扇监控 |
| SG Hardware Reset | sghwreset.inf | sghwreset.sys | 硬件复位控制 |
| SG Protection | sgprotect.inf | sgprotect.sys | 系统进程/文件保护 |
| 存储 | hdd.inf / ssd.inf | hdd.sys / ssd.sys | HDD/SSD 设备驱动 |
调用时机
系统初始化期间由 0001_StandardCommon_058 包的 install.bat 或 driver.bat 调用一次,安装完成后不再参与系统运行。
sgosupdate
用来安装系统更新的工具,当且仅当进入 Update Mode 时被调用。
在 ALLS 系统中的位置
daemon 的 Sequencer@modeselect@startup
→ Update Mode 被选中
→ UpdateTask@os@modeselect@execution
→ sgosupdate.exe
→ 安装 OS 更新包 (驱动、系统组件、安全补丁)
→ 完成后重启
sgxsystemservicing
似乎是用来管理程序的窗口和显示工具的小玩意,用于配置显示器分辨率和多屏。
不做过多阐述。
sgSystemServicingMasterScript.ps1
系统进入服务模式后第一个执行的脚本。它决定下一步执行哪个服务操作。
执行流程
sgSystemServicingMasterScript.ps1
│
├── 1. 清除旧的错误计数:
│ HKLM\...\SystemServicing\error_number = 0
│ HKLM\...\SystemServicing\total_pack = 0
│
├── 2. 设置显示分辨率:
│ sgsetresolution.exe
│
├── 3. 启动服务界面:
│ sgxsystemservicing.exe
│
├── 4. 检查退出标志:
│ if (IsLeaveSystemServicingMode) → LeaveSystemServicingMode; exit 0
│
├── 5. 解锁 N: 分区:
│ UnlockBitLocker("N", "system_key.txt")
│ 失败 → ErrorFlow(500)
│
├── 6. 执行服务脚本:
│ N:\apply.bat
│ │
│ ├── exit 0 → LeaveSystemServicingMode (正常完成)
│ ├── exit -1 → RestartSystemServicingMode (重启重试)
│ └── default → ErrorFlow(exit code) (显示错误)
│
└── exit 0
sgSystemServicingCommon.ps1
被所有格式化/恢复脚本引用的公共函数库。
函数列表
BitLocker 管理
UnlockBitLocker(letter, path)
→ manage-bde -unlock ${letter}: -RecoveryKey ${path}
→ exit code ≠ 0 → return false
EnableAutoUnlockBitLocker(letter)
→ manage-bde -autounlock -enable ${letter}: ← 启用自动解锁
→ manage-bde -on ${letter}: -used -sync ← 开启 BitLocker
→ exit code -2144272353 → 已应用,视为成功
服务模式切换
EnterSystemServicingMode(mode)
→ UnlockWriteProtection ← pxSecureAtaCmd -u 0
→ DisableWriteProtection ← pxSecureAtaCmd -d 0
→ 更改 Winlogon DefaultUser = "UpdateUser" ← 切换到更新用户
→ UwfCommitRegistry(winlogon key) ← 提交到 UWF
→ UwfCommitRegistry(mode) ← 保存 mode
→ UWF_Volume.Unprotect("C:") ← 关闭 C: 保护
→ RestartSystemServicingMode ← 重启
LeaveSystemServicingMode()
→ RemoveUsbCOM ← 移除 USB-COM 端口
→ RemoveTaskTrayInformation ← 清理任务栏
→ UWF_Volume.Protect("C:") ← 重新启用 C: 保护
→ RestartSystemServicingMode
RestartSystemServicingMode()
→ UWF_Filter.RestartSystem()
IsLeaveSystemServicingMode()
→ 检查 C:\...\LeaveSystemServicingMode.txt 是否存在
LeaveSystemServicingModeNextBoot()
→ 创建标志文件 → 下次启动时退出服务模式
ChangeDefaultUserIfLeaveSystemServicingModeDone()
→ Winlogon DefaultUser = "AppUser"
→ pxSecureAtaCmd -e 0 (启用写保护)
磁盘操作
GetDiskNumber(letter)
→ WMI: Win32_LogicalDiskToPartition → 解析 Disk #N → 返回磁盘号
GetHddDiskNumber(size)
→ WMI: Win32_DiskDrive MediaType=Fixed + Size ≥ size×GB
→ 返回所有符合条件的磁盘号列表
AssignLetter(diskNumber, partitionNumber, letter)
→ Get-Partition → Remove-PartitionAccessPath → Add-PartitionAccessPath
RemoveDriveLetter(letter)
→ Get-Partition → Remove-PartitionAccessPath
ChangeDriveLetter(srcLetter, dstLetter)
→ WMI: Win32_Volume → 修改 DriveLetter
FormatSubStorage(diskNumber, amfsSizeMB, amfsLetter, appDataLetter)
→ Clear-Disk ← 清空磁盘
→ Initialize-Disk -GPT ← GPT 分区表
→ New-Partition -Size amfsSizeMB -DriveLetter $amfsLetter
→ Format-Volume NTFS "AMFS" ← 副存储分区
→ New-Partition -UseMaximumSize -DriveLetter $appDataLetter
→ Format-Volume NTFS "AppData" ← 应用数据分区
→ return true/false
AdjustmentMainStorage(diskNumber, amfsSizeMB, amfsLetter, appDataLetter)
→ Remove-Partition AMFS + AppData ← 删除旧分区
→ New-Partition AMFS ← 重建 AMFS 分区
→ Format-Volume NTFS "AMFS"
→ New-Partition AppData ← 重建 AppData 分区
→ Format-Volume NTFS "AppData"
→ return true/false
写保护控制 (pxSecureAtaCmd 包装)
EnableWriteProtection()
→ pxSecureAtaCmd -e 0 ← 启用 TDK GS1 写保护
DisableWriteProtection()
→ pxSecureAtaCmd -d 0 ← 禁用 TDK GS1 写保护
UnlockWriteProtection()
→ pxSecureAtaCmd -u 0 ← 解锁 TDK GS1 写保护
UWF 注册表提交
UwfCommitRegistry(key, name)
→ WMI: UWF_RegistryFilter.CommitRegistry(key, name)
→ 将注册表更改强制写入 (绕过 UWF 写过滤)
→ 用于持久化 Winlogon、mode、error 等配置
USB-COM 端口清理
RemoveUsbCOM()
→ devcon findall =ports ← 列出所有 COM 端口
→ 筛选 COM20-COM99 (USB-COM 设备)
→ devcon remove @COMxx ← 删除每个 USB-COM 端口
→ 重试 3 次直到 FTDIBUS 枚举清除
错误处理
ErrorFlow(num)
→ HKLM\...\SystemServicing\error_number = num
→ while(1) { Sleep 3600 } ← 无限等待
重试机制
isRetry(cnt_file, max_cnt)
→ 读取 C:\cnt.txt → 比较计数 ≤ max_cnt
IncrementRetryCount(cnt_file)
→ 计数 +1 → 写回文件
函数调用关系图 (格式化场景)
sgSystemServicingMasterScript.ps1
→ sgSystemServicingCommon.ps1 (通用函数)
→ sgsetresolution.exe (设置分辨率)
→ sgxsystemservicing.exe (服务界面)
→ BitLocker 解锁
→ N:\apply.bat
→ sgMainstorageFormatApply.ps1
→ AdjustmentMainStorage → Format-Volume AMFS + AppData
→ EnableAutoUnlockBitLocker
→ chkntfs /x C: D: N: Y: P:
sgsystemservicingshell
用来以不同用户权限执行服务脚本的小程序,感觉没啥用?
其次是监控了脚本执行状态。
与 sgxsystemservicing.exe 的分工
sgxsystemservicing.exe (服务引擎)
├── 创建窗口 + 消息循环
├── 管理显示器分辨率
└── 提供 UI 界面
sgxsystemservicingshell.exe (服务 Shell)
├── 以指定用户身份启动服务脚本
├── 监控脚本执行状态
└── 管理安全上下文
两者在系统服务模式启动时由 sgSystemServicingMasterScript.ps1 协同调用:
# 服务主脚本调用:
. start C:\windows\sega\system\sgxsystemservicing.exe # 启动 UI
# N:\apply.bat 内部调用:
N:\apply.bat → sgsystemservicingshell.exe 形式
sgxdownloadui
更新下载ui?
主要依赖于util_downlaodui.dll
sgxmaster
系统的进程管理器,类似于/sbin/init的身份?
进程生命周期管理
Windows 启动 → 自动登录 AppUser
↓
sgxmaster.exe (主进程管理器)
├── CreateMutexW (单例保护)
│
├── ① 启动 sgxprestartup.exe ? (预启动检查)
│
├── ② 启动 sgxsegaboot.exe + sgxsystemdaemon.exe
│ │ (启动 UI) (后端引擎)
│ └── WaitForSingleObject (等待完成)
│
├── ③ 正常模式: 启动 sglaunch.exe (游戏启动器)
│ │ └── WaitForSingleObject (监控游戏)
│ └── 退出后 → InitiateShutdownW / 重启
│
└── 服务模式: 启动 sgsystemservicingshell.exe (服务 Shell)
└── 格式化/恢复/更新完成后重启
sgxprestartup
与 sgxmaster 同一套代码框架?
Windows 启动 → 自动登录 AppUser
↓
sgxprestartup.exe ← ★ 预启动检查
│ - 检查系统状态
│ - 决定进入正常启动还是服务模式
│ - ExitProcess
↓
sgxmaster.exe ← 进程管理器
├── sgxsegaboot.exe ← 启动 UI
├── sgxsystemdaemon.exe ← 后端引擎
├── sglaunch.exe ← 游戏启动器
└── sgsystemservicingshell.exe ← 服务模式 Shell
先于sgxmaster 启动
sgsetresolution
用于管理分辨率的工具
没啥好介绍的。
sgsetdisplaysetting
同上 一个是设置,一个是显示参数,没啥好介绍的。
util_sgxsegaboot.dll
| 命名空间 | 类 | 用途 |
|---|---|---|
util.io.Manager | — | IO 输入管理器,7 个委托事件 |
util.io.Manager | ButtonType | 按钮类型枚举 (Test/Service/Up/Down/Left/Right/Sw1) |
util.io.Manager | ShareDataInputBit | 共享数据输入位枚举 |
util.io.Manager | PlayerSwitchAssign | 玩家开关分配枚举 |
util.io.Manager | SystemSwitchAssign | 系统开关分配枚举 |
util.SpeakerTest | — | 喇叭测试工具类 (Play/Stop) |
util.share.Manager | — | 共享数据管理器,5 个回调事件 |
system_share_data_wrapperMD.dll
C++/CLI 混合模式程序集,桥接 C# 前端与 C++ 原生 daemon 之间的共享内存数据结构。
| 类 | 说明 |
|---|---|
FactoryWrapper | 工厂包装器:CreateCreditChuteType() / CreateCreditServiceType() 等 |
CreditChuteType / TYPE | 投币通道类型枚举 (COMMON=0, INDIVIDUAL_1=1, INDIVIDUAL_2=2) |
CreditServiceType / TYPE | 投币服务类型枚举 (COMMON=0, INDIVIDUAL_1=1, INDIVIDUAL_2=2) |
CreditOperation / OPERATION | 投币操作枚举 |
LanguageSetting / SETTING | 语言设置枚举 (CHINESE=0, ENGLISH=1, JAPANESE=2) |
Region / REGION_BIT | 地区区域设置枚举 |
StorageInfoStatus / STATUS | 存储信息状态枚举 |
NetworkTestProgress / PROGRESS | 网络测试进度枚举 |
WlanRouterModem / STATUS | 无线路由器调制解调器状态枚举 (DISABLED, OFFLINE, SEARCHING, CONNECTING, ONLINE) |
WlanRouterPPP / STATUS | 无线路由器 PPP 状态枚举 |
wrp_systemdaemon_cli_wrapperMD.dll
C++/CLI 混合模式程序集,封装了向 sgxsystemdaemon 发送的命令。
| 命名空间 | 类 | 说明 |
|---|---|---|
segaboot.wrp | ClientWrapper | 命令行客户端包装器 |
segaboot.wrp | COMMAND_SET | 命令集合枚举 |
segaboot.wrp.cli.network | SettingParam | 网络设置参数 |
segaboot.wrp.cli.credit | CreditChuteType / TYPE | 投币通道 CLI 包装 |
segaboot.wrp.cli.credit | CreditServiceType / TYPE | 投币服务 CLI 包装 |
segaboot.wrp.cli.credit | CreditOperation / OPERATION | 投币操作 CLI 包装 |
segaboot.wrp.cli.credit | SettingParam | 投币配置参数 |
segaboot.wrp.cli.dipsw | SettingParam | DIP 开关配置参数 |
| — | AM_CREDIT_STATUS | 投币状态枚举 |
| — | AM_CREDIT_PLAYER | 玩家投币枚举 |
版权声明:本文作者为 nuym,首发于 www.nuym.cn。
遵循 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
评论