Preserve WeChat original entitlements when re-signing (v0.2.3)

- Extract existing entitlements before re-signing
- Merge get-task-allow into original entitlements instead of replacing
- Prevents breaking WeChat features like Search after init
This commit is contained in:
canghe 2026-04-04 16:11:17 +08:00
parent b794ad1370
commit 7158422618
5 changed files with 59 additions and 31 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@canghe_ai/wechat-cli-darwin-arm64", "name": "@canghe_ai/wechat-cli-darwin-arm64",
"version": "0.2.2", "version": "0.2.3",
"description": "wechat-cli binary for macOS arm64", "description": "wechat-cli binary for macOS arm64",
"os": ["darwin"], "os": ["darwin"],
"cpu": ["arm64"], "cpu": ["arm64"],

View File

@ -1,6 +1,6 @@
{ {
"name": "@canghe_ai/wechat-cli", "name": "@canghe_ai/wechat-cli",
"version": "0.2.2", "version": "0.2.3",
"description": "WeChat data query CLI — chat history, contacts, sessions, favorites, and more. Designed for LLM integration.", "description": "WeChat data query CLI — chat history, contacts, sessions, favorites, and more. Designed for LLM integration.",
"bin": { "bin": {
"wechat-cli": "bin/wechat-cli.js" "wechat-cli": "bin/wechat-cli.js"
@ -13,7 +13,7 @@
"install.js" "install.js"
], ],
"optionalDependencies": { "optionalDependencies": {
"@canghe_ai/wechat-cli-darwin-arm64": "0.2.2" "@canghe_ai/wechat-cli-darwin-arm64": "0.2.3"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=14"

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "wechat-cli" name = "wechat-cli"
version = "0.2.2" version = "0.2.3"
description = "WeChat data query CLI for LLMs" description = "WeChat data query CLI for LLMs"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [

View File

@ -2,24 +2,13 @@
import os import os
import platform import platform
import plistlib
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
from .common import collect_db_files, cross_verify_keys, save_results, scan_memory_for_keys from .common import collect_db_files, cross_verify_keys, save_results, scan_memory_for_keys
# Entitlements needed for task_for_pid to work on WeChat
_ENTITLEMENTS_XML = """\
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
"""
def _find_binary(): def _find_binary():
"""查找对应架构的 C 二进制。""" """查找对应架构的 C 二进制。"""
@ -52,8 +41,34 @@ def _find_binary():
) )
def _get_original_entitlements(app_path):
"""提取 app 当前的签名 entitlements返回 dict 或 None。"""
try:
result = subprocess.run(
["codesign", "-d", "--entitlements", ":-", app_path],
capture_output=True,
timeout=15,
)
if result.returncode == 0 and result.stdout:
return plistlib.loads(result.stdout)
except Exception:
pass
return None
def _build_entitlements_xml(app_path):
"""构建 entitlements保留原有权限 + 添加 get-task-allow。"""
entitlements = _get_original_entitlements(app_path)
if entitlements is None:
entitlements = {}
entitlements["com.apple.security.get-task-allow"] = True
return plistlib.dumps(entitlements, fmt=plistlib.FMT_XML)
def _resign_wechat(): def _resign_wechat():
"""Re-sign WeChat with get-task-allow entitlement so task_for_pid works.""" """Re-sign WeChat: 保留原有 entitlements仅添加 get-task-allow。"""
wechat_paths = [ wechat_paths = [
"/Applications/WeChat.app", "/Applications/WeChat.app",
os.path.expanduser("~/Applications/WeChat.app"), os.path.expanduser("~/Applications/WeChat.app"),
@ -67,15 +82,20 @@ def _resign_wechat():
if wechat_app is None: if wechat_app is None:
return False, "未找到 WeChat.app已搜索 /Applications 和 ~/Applications" return False, "未找到 WeChat.app已搜索 /Applications 和 ~/Applications"
# Write entitlements to temp file
ent_fd, ent_path = tempfile.mkstemp(suffix=".xml")
try:
with os.fdopen(ent_fd, "w") as f:
f.write(_ENTITLEMENTS_XML)
print(f"\n[*] 检测到 task_for_pid 权限不足,正在对微信重新签名...") print(f"\n[*] 检测到 task_for_pid 权限不足,正在对微信重新签名...")
print(f" 目标: {wechat_app}") print(f" 目标: {wechat_app}")
# 提取并合并 entitlements
try:
ent_data = _build_entitlements_xml(wechat_app)
except Exception as e:
return False, f"提取微信原始权限失败: {e}"
ent_fd, ent_path = tempfile.mkstemp(suffix=".plist")
try:
with os.fdopen(ent_fd, "wb") as f:
f.write(ent_data)
result = subprocess.run( result = subprocess.run(
["codesign", "--force", "--sign", "-", "--entitlements", ent_path, wechat_app], ["codesign", "--force", "--sign", "-", "--entitlements", ent_path, wechat_app],
capture_output=True, capture_output=True,
@ -88,7 +108,9 @@ def _resign_wechat():
if result.returncode != 0: if result.returncode != 0:
return False, f"codesign 失败: {result.stderr.strip()}" return False, f"codesign 失败: {result.stderr.strip()}"
print("[+] 签名完成!请重新启动微信后再执行 init。") print("[+] 签名完成(已保留微信原有权限,仅添加调试访问权限)。")
print("[+] 请重新启动微信后再执行 init。")
print("[!] 注意:如果微信自动更新,可能需要重新签名。")
return True, None return True, None
@ -145,23 +167,29 @@ def extract_keys(db_dir, output_path, pid=None):
combined_output = (result.stdout or "") + (result.stderr or "") combined_output = (result.stdout or "") + (result.stderr or "")
if "task_for_pid" in combined_output: if "task_for_pid" in combined_output:
print("\n[!] task_for_pid 失败macOS 安全策略阻止了进程内存访问。") print("\n[!] task_for_pid 失败macOS 安全策略阻止了进程内存访问。")
print("[!] 需要对微信重新签名以允许调试访问") print("[!] 需要对微信重新签名以允许调试访问(不影响微信正常功能)")
ok, err = _resign_wechat() ok, err = _resign_wechat()
if ok: if ok:
raise RuntimeError( raise RuntimeError(
"已对微信重新签名。请执行以下步骤后重试:\n" "已对微信重新签名(保留原有权限)。请执行以下步骤后重试:\n"
" 1. 退出微信(完全退出,不是最小化)\n" " 1. 退出微信(完全退出,不是最小化)\n"
" 2. 重新打开微信并登录\n" " 2. 重新打开微信并登录\n"
" 3. 再次执行: sudo wechat-cli init" " 3. 再次执行: sudo wechat-cli init"
) )
else: else:
# 手动命令也需要保留原有权限
raise RuntimeError( raise RuntimeError(
f"自动签名失败: {err}\n" f"自动签名失败: {err}\n"
"请手动执行以下命令后重试:\n" "请手动执行以下命令后重试:\n"
' codesign --force --sign - --entitlements /dev/stdin /Applications/WeChat.app <<\'EOF\'\n' " # 1. 提取微信原有权限\n"
+ _ENTITLEMENTS_XML + " codesign -d --entitlements wechat_ent.plist /Applications/WeChat.app\n"
"EOF\n" " # 2. 用 PlistBuddy 添加 get-task-allow\n"
' /usr/libexec/PlistBuddy -c "Add :com.apple.security.get-task-allow bool true" wechat_ent.plist\n'
" # 3. 重新签名\n"
" codesign --force --sign - --entitlements wechat_ent.plist /Applications/WeChat.app\n"
" # 4. 清理\n"
" rm wechat_ent.plist\n"
"然后重启微信,再执行: sudo wechat-cli init" "然后重启微信,再执行: sudo wechat-cli init"
) )

View File

@ -6,7 +6,7 @@ import click
from .core.context import AppContext from .core.context import AppContext
_VERSION = "0.2.2" _VERSION = "0.2.3"
@click.group() @click.group()