fix: 增加sip文案

This commit is contained in:
Wxw-Gu
2026-09-18 09:38:33 +08:00
parent 774346d503
commit 12b8fb34c6
7 changed files with 106 additions and 19 deletions
+60 -3
View File
@@ -1,5 +1,62 @@
# macOS 数据访问说明(兼容入口)
# macOS 关闭 SIP 教程
完整内容已移到[macOS 数据访问与系统权限](./platform/macos.md)。
SIP(System Integrity Protection,系统完整性保护)是 macOS 的系统安全机制。关闭 SIP 会降低系统安全性,只建议在确实需要读取或调试本地微信数据时临时关闭;操作完成后,建议重新开启。
保留此文件是为了兼容应用内已经发布的帮助链接。请不要把“关闭 SIP”当作默认安装步骤;只有当当前连接页面明确要求时才处理,并在完成后恢复系统安全设置。
> 只在连接页面明确提示需要关闭 SIP 时才处理。首次连接失败时,先确认微信版本、账号目录和登录时机,再按本文操作。关闭 SIP 不是 TraceMemo 的常规安装步骤,也不应长期保持关闭。
## 准备
- 一台 Mac 电脑,Intel 芯片和 Apple Silicon 芯片均可。
- 需要进入 macOS 恢复模式。
- 请先保存正在编辑的文件,并预留一次重启时间。
## 关闭 SIP
### Intel Mac
1. 关机。
2. 按下开机键后,立刻按住 `Command + R`。
3. 保持按住,直到进入 macOS 恢复模式。
### Apple Silicon Mac(M1/M2/M3/M4/M5)
1. 关机。
2. 长按开机键不放。
3. 直到出现启动选项界面后松开。
4. 选择"选项",进入 macOS 恢复模式。
### 在恢复模式中执行命令
1. 进入恢复模式后,点击顶部菜单栏的 **Utilities(实用工具)**。
2. 选择 **Terminal(终端)**。
3. 在终端中输入:
```bash
csrutil disable
```
4. 按回车执行。
5. 看到关闭成功提示后,重启电脑。
## 确认是否生效
重启回到正常桌面后,打开"终端",执行:
```bash
csrutil status
```
看到 `System Integrity Protection status: disabled.` 才算关闭成功。
若仍显示 `enabled`,说明没有生效。常见原因是没在恢复模式里执行,或系统刚做过大版本更新——
macOS 大版本更新会把 SIP 重置回开启状态,此前关过也会失效,需要重新按上面的步骤操作。
## 重新开启 SIP
拿到数据库密钥后,建议重新进入恢复模式,在终端中执行:
```bash
csrutil enable
```
然后重启电脑,恢复系统安全设置。
+1 -1
View File
@@ -8,7 +8,7 @@ TraceMemo 需要读取微信本地数据。macOS 会根据系统版本、微信
1. 先启动 TraceMemo,阅读连接页面显示的当前前置条件。
2. 确认微信数据目录指向当前账号。
3. 只在页面明确要求时处理系统授权或 SIP;按页面提示完成密钥获取后,恢复你平时使用的安全设置。
3. 只在页面明确要求时处理系统授权或 SIP;关闭 SIP 的具体步骤见[关闭 SIP 教程](../mac-disable-sip.md),按页面提示完成密钥获取后,恢复你平时使用的安全设置。
4. 返回应用重新检测账号、数据库和图片资源状态。
不要直接复制网上针对其他微信版本的命令。系统授权失败时,记录 macOS 版本、微信版本和页面错误,再按[排障文档](../user-guide/troubleshooting.md#连接微信失败)处理。
+11 -5
View File
@@ -134,6 +134,15 @@ export function parseXkeyHelperOutput(output: string): DatabaseKeyResult {
return mapXkeyHelperFailure(rawError)
}
export const SIP_ENABLED_ERROR =
'macOS 系统完整性保护(SIP)已开启,无法自动获取数据库密钥。请先关闭 SIP,或改用手动粘贴。'
export function parseSipEnabled(statusOutput: string): boolean {
const status = statusOutput.match(/status:\s*([a-z]+)/i)?.[1]?.toLowerCase()
if (status) return status === 'enabled'
return statusOutput.toLowerCase().includes('enabled')
}
export class KeyServiceMac {
private getMacKeyRuntimeDir(): string {
return path.join(app.getPath('userData'), 'key-runtime')
@@ -306,7 +315,7 @@ export class KeyServiceMac {
private async isSipEnabled(): Promise<boolean> {
try {
const { stdout } = await execFileAsync('/usr/bin/csrutil', ['status'])
return stdout.toLowerCase().includes('enabled')
return parseSipEnabled(stdout)
} catch {
return false
}
@@ -346,10 +355,7 @@ export class KeyServiceMac {
return { success: false, error: '自动获取密钥目前仅支持 macOS' }
}
if (await this.isSipEnabled()) {
return {
success: false,
error: '当前系统还未完成连接环境准备,请按页面提示完成设置。'
}
return { success: false, code: 'SIP_ENABLED', error: SIP_ENABLED_ERROR }
}
try {
@@ -491,14 +491,8 @@ export function DatabaseConnectionPage({
<p className="database-login-platform-note">
{isMac ? (
<>
{isIntelMac
? 'Intel Mac 首次使用需要先准备连接环境,按页面提示完成即可。'
: 'macOS Apple 芯片首次获取密钥需要关闭 SIP,并在监听期间点击微信登录。'}{' '}
<a
href={isIntelMac ? GUIDE_URL : macKeyFaqUrl}
target="_blank"
rel="noreferrer"
>
macOS 首次获取密钥需要关闭 SIP,并在监听期间点击微信登录。{' '}
<a href={macKeyFaqUrl} target="_blank" rel="noreferrer">
查看说明
</a>
<br />第 4 步之前不要登录微信,只需保留微信登录页面窗口;授权后再点「登录」。
+3 -1
View File
@@ -143,7 +143,9 @@ describe('DatabaseConnectionPage', () => {
expect(screen.getByRole('button', { name: '开始获取密钥' })).toBeVisible()
expect(screen.queryByText(/管理员授权|电脑密码/)).not.toBeInTheDocument()
expect(screen.queryByText(/Frida|Python|SIP|重新签名/)).not.toBeInTheDocument()
// SIP is a user prerequisite, not an implementation detail: seeing it in the
// UI is expected whenever the key cannot be captured without it.
expect(screen.queryByText(/Frida|Python|重新签名/)).not.toBeInTheDocument()
})
it('renders a discovered nickname and avatar before connection', () => {
@@ -84,7 +84,8 @@ describe('database key controls', () => {
expect(screen.getByText('Intel Mac 自动获取')).toBeVisible()
expect(screen.getByText(/按页面提示操作即可/)).toBeVisible()
expect(screen.queryByText(/Frida|Python|SIP|签名/)).not.toBeInTheDocument()
// SIP is a user prerequisite, not an implementation detail.
expect(screen.queryByText(/Frida|Python|签名/)).not.toBeInTheDocument()
await user.click(screen.getByRole('button', { name: '自动获取密钥' }))
expect(onDetect).toHaveBeenCalledOnce()
})
+27
View File
@@ -7,9 +7,11 @@ vi.mock('electron', () => ({
}))
import {
SIP_ENABLED_ERROR,
buildAppleSiliconXkeyInvocation,
buildXkeyHelperArguments,
mapXkeyHelperFailure,
parseSipEnabled,
parseXkeyHelperOutput,
resolveXkeyHelperMode
} from '../../src/main/key-service-mac'
@@ -133,3 +135,28 @@ describe('parseXkeyHelperOutput', () => {
})
})
})
describe('parseSipEnabled', () => {
it.each<[string, boolean]>([
['System Integrity Protection status: enabled.', true],
['System Integrity Protection status: disabled.', false],
['System Integrity Protection status: unknown (Custom Configuration).', false],
['System Integrity Protection status: enabled (Apple Internal).', true],
['System Integrity Protection status: disabled (Apple Internal).', false]
])('reads "%s" as %s', (statusOutput, expected) => {
expect(parseSipEnabled(statusOutput)).toBe(expected)
})
it('only reads the status field, not the word enabled elsewhere in the output', () => {
expect(
parseSipEnabled('System Integrity Protection status: disabled.\nKernel Extensions: enabled')
).toBe(false)
})
})
describe('SIP blocking message', () => {
it('names the prerequisite and the next action', () => {
expect(SIP_ENABLED_ERROR).toContain('SIP')
expect(SIP_ENABLED_ERROR).toMatch(/关闭 SIP|手动粘贴/)
})
})