mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-09 00:16:32 +08:00
fix: 增加sip文案
This commit is contained in:
+60
-3
@@ -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
|
||||||
|
```
|
||||||
|
|
||||||
|
然后重启电脑,恢复系统安全设置。
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ TraceMemo 需要读取微信本地数据。macOS 会根据系统版本、微信
|
|||||||
|
|
||||||
1. 先启动 TraceMemo,阅读连接页面显示的当前前置条件。
|
1. 先启动 TraceMemo,阅读连接页面显示的当前前置条件。
|
||||||
2. 确认微信数据目录指向当前账号。
|
2. 确认微信数据目录指向当前账号。
|
||||||
3. 只在页面明确要求时处理系统授权或 SIP;按页面提示完成密钥获取后,恢复你平时使用的安全设置。
|
3. 只在页面明确要求时处理系统授权或 SIP;关闭 SIP 的具体步骤见[关闭 SIP 教程](../mac-disable-sip.md),按页面提示完成密钥获取后,恢复你平时使用的安全设置。
|
||||||
4. 返回应用重新检测账号、数据库和图片资源状态。
|
4. 返回应用重新检测账号、数据库和图片资源状态。
|
||||||
|
|
||||||
不要直接复制网上针对其他微信版本的命令。系统授权失败时,记录 macOS 版本、微信版本和页面错误,再按[排障文档](../user-guide/troubleshooting.md#连接微信失败)处理。
|
不要直接复制网上针对其他微信版本的命令。系统授权失败时,记录 macOS 版本、微信版本和页面错误,再按[排障文档](../user-guide/troubleshooting.md#连接微信失败)处理。
|
||||||
|
|||||||
@@ -134,6 +134,15 @@ export function parseXkeyHelperOutput(output: string): DatabaseKeyResult {
|
|||||||
return mapXkeyHelperFailure(rawError)
|
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 {
|
export class KeyServiceMac {
|
||||||
private getMacKeyRuntimeDir(): string {
|
private getMacKeyRuntimeDir(): string {
|
||||||
return path.join(app.getPath('userData'), 'key-runtime')
|
return path.join(app.getPath('userData'), 'key-runtime')
|
||||||
@@ -306,7 +315,7 @@ export class KeyServiceMac {
|
|||||||
private async isSipEnabled(): Promise<boolean> {
|
private async isSipEnabled(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('/usr/bin/csrutil', ['status'])
|
const { stdout } = await execFileAsync('/usr/bin/csrutil', ['status'])
|
||||||
return stdout.toLowerCase().includes('enabled')
|
return parseSipEnabled(stdout)
|
||||||
} catch {
|
} catch {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -346,10 +355,7 @@ export class KeyServiceMac {
|
|||||||
return { success: false, error: '自动获取密钥目前仅支持 macOS' }
|
return { success: false, error: '自动获取密钥目前仅支持 macOS' }
|
||||||
}
|
}
|
||||||
if (await this.isSipEnabled()) {
|
if (await this.isSipEnabled()) {
|
||||||
return {
|
return { success: false, code: 'SIP_ENABLED', error: SIP_ENABLED_ERROR }
|
||||||
success: false,
|
|
||||||
error: '当前系统还未完成连接环境准备,请按页面提示完成设置。'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -491,14 +491,8 @@ export function DatabaseConnectionPage({
|
|||||||
<p className="database-login-platform-note">
|
<p className="database-login-platform-note">
|
||||||
{isMac ? (
|
{isMac ? (
|
||||||
<>
|
<>
|
||||||
{isIntelMac
|
macOS 首次获取密钥需要关闭 SIP,并在监听期间点击微信登录。{' '}
|
||||||
? 'Intel Mac 首次使用需要先准备连接环境,按页面提示完成即可。'
|
<a href={macKeyFaqUrl} target="_blank" rel="noreferrer">
|
||||||
: 'macOS Apple 芯片首次获取密钥需要关闭 SIP,并在监听期间点击微信登录。'}{' '}
|
|
||||||
<a
|
|
||||||
href={isIntelMac ? GUIDE_URL : macKeyFaqUrl}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
查看说明
|
查看说明
|
||||||
</a>
|
</a>
|
||||||
<br />第 4 步之前不要登录微信,只需保留微信登录页面窗口;授权后再点「登录」。
|
<br />第 4 步之前不要登录微信,只需保留微信登录页面窗口;授权后再点「登录」。
|
||||||
|
|||||||
@@ -143,7 +143,9 @@ describe('DatabaseConnectionPage', () => {
|
|||||||
|
|
||||||
expect(screen.getByRole('button', { name: '开始获取密钥' })).toBeVisible()
|
expect(screen.getByRole('button', { name: '开始获取密钥' })).toBeVisible()
|
||||||
expect(screen.queryByText(/管理员授权|电脑密码/)).not.toBeInTheDocument()
|
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', () => {
|
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('Intel Mac 自动获取')).toBeVisible()
|
||||||
expect(screen.getByText(/按页面提示操作即可/)).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: '自动获取密钥' }))
|
await user.click(screen.getByRole('button', { name: '自动获取密钥' }))
|
||||||
expect(onDetect).toHaveBeenCalledOnce()
|
expect(onDetect).toHaveBeenCalledOnce()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ vi.mock('electron', () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
SIP_ENABLED_ERROR,
|
||||||
buildAppleSiliconXkeyInvocation,
|
buildAppleSiliconXkeyInvocation,
|
||||||
buildXkeyHelperArguments,
|
buildXkeyHelperArguments,
|
||||||
mapXkeyHelperFailure,
|
mapXkeyHelperFailure,
|
||||||
|
parseSipEnabled,
|
||||||
parseXkeyHelperOutput,
|
parseXkeyHelperOutput,
|
||||||
resolveXkeyHelperMode
|
resolveXkeyHelperMode
|
||||||
} from '../../src/main/key-service-mac'
|
} 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|手动粘贴/)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user