Files
dlp-io/docs/DLP_ENVIRONMENT_NOTES.md
T
p40000043244@byd.com 457e4eed3f docs: DLP 环境特征调研记录与判定/写入逻辑说明
- docs/DLP_ENVIRONMENT_NOTES.md: 白名单矩阵/密文样本/属性不可区分等实测结论与探索过程
- README: DLP 程序化特征与 is_encrypted/写入通道实现逻辑摘要
2026-07-31 18:32:14 +08:00

151 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DLP 环境特征调研与检测/写入实现逻辑
本文记录在本机(启用透明加密 DLP 的 Windows)上对 `tests/DLP加密文件.pptx`
`tests/DLP解密文件.pptx` 两个样本的实测结论、探索过程,以及 `dlp_io` 据此实现的
`is_encrypted()` 判定逻辑和未加密写入通道选择逻辑。所有结论都来自实测,不同 DLP
产品/策略可能不同,库的应对方式是运行时探测而非硬编码假设。
## 一、样本事实
两个样本文件内容相同(解密后 SHA-256 均为
`968b5db1…d2947100`),大小完全相同(2,466,572 字节)。加密文件在磁盘上存密文,
前 16 字节对比:
```
明文视图: 504b0304 0a000000 0000874e e2400000 (标准 ZIP/PPTX 头 "PK\x03\x04")
密文视图: 181b031a 1510197c 0a19 0e7c 6f726c5f (流式加密, 相同明文 0x00 在不同偏移密文不同)
```
特征:同尺寸、全文流式加密、无附加文件头/尾、无静态 magic 可识别。
## 二、文件属性层面无法区分(重要结论)
对两个样本逐项对比,全部一致:
- `attrib`:都只有 `A` 属性(无 EFS 的 E 属性等差异)
- `dir /r`:均无附加数据流(ADS
- 文件大小、时间戳:一致
**结论:无法通过任何文件元数据判断加密状态,唯一可靠的判别特征是内容。**
这正是 `is_encrypted()` 采用内容对比的原因。
## 三、进程白名单模型(实测矩阵)
DLP 透明加密按「进程身份」决定是否介入读写。用「读取加密样本看到什么」实测:
| 进程 | 父进程 | 读加密样本视图 | 结论 |
|------|--------|----------------|------|
| `python.exe` | 任意(含非白名单 bash) | 明文 (PK 头) | **身份白名单**,不继承、不降级 |
| `cmd.exe``type`) | 任意 | 明文 | 白名单 |
| `powershell.exe` | 任意 | 明文 | 白名单(读透明) |
| `certutil.exe` | bash / cmd | 密文 | **非白名单, raw 视图** |
| Git Bash msys 工具(`head`/`sha256sum`/`cat`) | bash | 密文 | 非白名单 |
| `git.exe``hash-object`) | bash | 明文 | 白名单(两样本 blob hash 相同) |
关键推论:
1. **helper 桥接设计成立**:打包 EXE(非白名单)拉起 `python.exe` 子进程,python 按身份
仍是白名单,读到的就是明文——这是库读取链路的根基。
2. **`cmd /c type` 不能当密文通道**:cmd 是白名单,读到的同样是明文。社区直觉「用 cmd
读原始内容」在本 DLP 上不成立。
3. **certutil 是本机可用的 raw 视图工具**:但 DLP 拦截白名单进程直接拉起它
python → certutil 报 `WinError 786`),必须经 `cmd /c certutil …` 中转。
4. **`cmd /c` 中文路径坑**:把整条命令作为单个字符串传参会触发 cmd 的引号剥离规则,
中文文件名丢失(`ERROR_FILE_NOT_FOUND`);用列表参数(subprocess 自动逐个加引号)
则中文路径正常。
写入侧实测:
| 写入方 | 落盘结果(raw 视图校验) |
|--------|--------------------------|
| `python.exe` 直写(.bin / .pptx 后缀) | 明文(本机策略不加密 python 写入) |
| `powershell.exe`stdin 管道 → `[IO.File]` | 明文 |
| `cmd /c more > f` | **破坏二进制**0x00→CRLF、TAB→空格),不可用 |
| `cmd /c copy /b con f` | 挂起等待控制台输入,不可用 |
| `certutil -decode - f` | 不支持 stdin 输入,不可用 |
| `findstr ^ f` | raw 密文视图,但按行重新包装(0x0A→CRLF 注入),只能看头部不能还原内容 |
注意:白名单读透明 ≠ 写加密。本机 python/powershell 读透明但写入落盘明文;
「白名单进程写入会被加密」的策略在其他机器上仍可能存在——所以写入通道必须
运行时探测,不能假设。
## 四、探索过程(方法记录)
1. **多通道哈希对比**`certutil -hashfile`(非白名单视图)对两样本哈希不同
33d3… vs 968b…),python 读取两样本哈希相同(968b…)——证明 DLP 生效、
python 白名单、certutil 非白名单,一条命令同时确认三件事。
2. **头部字节对比**`head -c 64 | xxd`(msys 工具,raw 视图)看到密文头,确认
同尺寸流加密、无附加头。
3. **文件属性对比**`attrib` / `dir /r` 无差异——排除元数据检测路线。
4. **写入通道候选实验**:逐一验证 more/copy con/certutil -decode/findstr 全部
有二进制安全性或可用性问题,最终 `powershell [Console]::OpenStandardInput()`
管道方案通过全字节(含 0x00/0x1A/0xFF)哈希校验。
5. **误报纠正**`python → cmd /c type` 一度显示明文,先怀疑「白名单继承」,
后用 `bash → cmd /c type`(仍是明文)和 `bash → python`(明文)交叉验证,
确认 python/cmd 均为身份白名单而非继承。
## 五、is_encrypted() 判定逻辑
判据:文件已加密 ⟺ 白名单视图 ≠ 非白名单(raw)视图。
难点:当前进程自身是否白名单是未知的(Python/pyd 是白名单,打包 EXE 不是),
单次对比存在歧义。`dlp_io.is_encrypted(path)` 用两阶段消歧:
```
h_raw = certutil -hashfile(path) # 非白名单 raw 视图(经 cmd /c 中转)
h_native = hashlib(当前进程读 path)
if h_raw != h_native:
return True # 视图不一致: 文件已加密, 且当前进程是白名单
# 歧义分支: 文件未加密, 或文件已加密但当前进程是非白名单 EXE
head_helper = helper(白名单 python)读前 64 KiB
head_native = 当前进程读前 64 KiB
return head_helper != head_native
```
三种运行环境全部正确:
| 环境 | 加密文件 | 未加密文件 |
|------|----------|------------|
| Python / pyd(白名单) | 阶段一即判定 True | 阶段二两视图一致 → False |
| 打包 EXE(非白名单) | 阶段一相等(都看到密文)→ 阶段二 helper 明文头 ≠ 本地密文头 → True | 阶段二一致 → False |
代价:未加密文件需一次完整 certutil 哈希 + 一次本地完整读取 + helper 前 64 KiB
读取;大文件应缓存结果。库故意不引入「期望 magic 表」——内容对比与文件类型无关。
## 六、未加密写入通道选择逻辑
目标:任何环境下 `dlp_io.open(path, "w"/"a"/"x")` 落盘都是未加密文件。
```
首次写某后缀时:
1. 环境变量 DLP_IO_WRITE_CHANNEL=direct|powershell → 强制, 跳过探测
2. 直写探测: temp 目录写探针文件(同后缀, 含 0x00/0x1A/0xFF 等边界字节)
→ certutil raw 哈希 == 内容哈希 ?
- 一致 → DIRECTEXE 环境恒真; 本机 python 也真)
- certutil 不可用 → 视为无 DLP → DIRECT
3. 否则 PowerShell 中转探测: 同 payload 经 stdin 管道由 powershell 子进程
([Console]::OpenStandardInput → [IO.File]::Open) 写盘 → 再校验 raw 哈希
- 一致 → POWERSHELL
4. 都失败 → raise DlpWriteError(绝不静默写出密文)
结果按后缀缓存(DLP 写策略常按文档类型区分)。
```
中转通道要点:字节只走 stdin 内存管道,不落地任何临时文件(白名单进程写的临时
文件本身可能被加密,会造成「密文套娃」);`FileMode` 映射 `w→Create`
`a→Append``x→CreateNew``x` 模式在通道选择前先做本地存在性检查,保证
`FileExistsError` 语义与原生一致。
已知边界:探测文件写在 temp 目录,若 DLP 策略按目录区分,探针结论可能与目标
目录不符——此时用 `DLP_IO_WRITE_CHANNEL` 显式指定。
## 七、相关已知问题
- 保护区内 `git push` 必现 `fatal: not a git repository`DLP 拦截 push 进程
`.git` 的访问模式),解法见 `.kimi-code/skills/git-push-dlp/`robocopy
镜像 `.git` 到 TEMP 后从 stage 目录推送)。
- 提交样本 fixture 时注意:本机 `git.exe` 是白名单视图,commit 会把加密样本按
明文存入仓库。测试用例已按环境自适应编写(依据本机 raw 视图与本地视图是否
一致判断期望),CI(无 DLP)与 DLP 开发机都能通过。