普通网友 2026-02-28 02:00 采纳率: 98.4%
浏览 1
已采纳

PsGetProcessImageFileName为何常返回截断的进程路径?

`PsGetProcessImageFileName` 常返回截断的进程路径(如 `"C:\Program Files\..."` 被截为 `"C:\Program Fil"`),根本原因在于其底层依赖内核对象 `EPROCESS->SeAuditProcessCreationInfo.ImageFileName->Buffer`,该字段在Windows 8.1+中被限制为**最多256字节(含Unicode空终止符)**,且由系统在进程创建时静态拷贝——若原始映像路径超长(如含深层符号链接、AppContainer沙箱路径或长UNC重定向),则直接截断而非扩展缓冲区。此外,该API不处理重解析(如`\\?\`前缀)、不解析硬链接/软链接,也不回退到`ObQueryNameString`等更健壮接口。驱动开发者若直接使用该函数获取完整路径用于日志、白名单或HIPS判断,极易因路径不完整导致误判。正确做法是结合`PsGetProcessImageFilePointer`获取`FILE_OBJECT`后调用`IoQueryFileDosDeviceName`(Win10 1809+)或`SeLocateProcessImageName`(需SE_DEBUG_PRIVILEGE),并妥善处理Unicode与符号链接。
  • 写回答

1条回答 默认 最新

  • 泰坦V 2026-02-28 02:00
    关注
    ```html

    一、现象层:PsGetProcessImageFileName 返回截断路径的典型表现

    • 常见日志中出现 "C:\Program Fil""\\?\C:\Users\A...\AppData\Local\Temp\..."(仅前256字节)
    • 白名单引擎误判:因 "C:\Windows\System32\svchost.exe" 被截为 "C:\Windows\System32\svchost"(缺 .exe),触发误告警
    • AppContainer 进程(如 Microsoft Store 应用)路径常以 "AppxBundle://..." 或长重定向 UNC 形式存在,极易超限

    二、机制层:内核对象设计与硬性约束溯源

    根本原因在于 Windows 内核结构演进:

    字段路径数据类型长度限制填充时机
    EPROCESS->SeAuditProcessCreationInfo.ImageFileName->BufferUNICODE_STRING≤256 字节(含 \0\0进程创建时一次性静态拷贝
    PsGetProcessImageFileName() 封装逻辑仅返回该 Buffer 指针不校验完整性、不尝试扩展无重试/回退机制

    三、缺陷层:API 能力缺失的技术全景图

    • ❌ 不解析符号链接:对 \\?\Volume{...}\...C:\ symlink -> D:\deep\path\ 完全忽略
    • ❌ 不处理重解析点(Reparse Points):跳过 IO_REPARSE_TAG_SYMLINK/_APPCONTAINER
    • ❌ 不追溯硬链接(Hard Link)原始 inode,无法还原真实映像位置
    • ❌ 不调用 ObQueryNameString()IoGetFileObjectFileName() 等动态查询接口

    四、验证层:复现截断行为的驱动级代码片段

    // 示例:在 IRP_MJ_CREATE 钩子中观测截断
    UNICODE_STRING usPath;
    PsGetProcessImageFileName(PsGetCurrentProcess(), &usPath);
    DbgPrint("Raw path len=%u, buf=%.*S\n", 
             usPath.Length, usPath.Length / sizeof(WCHAR), usPath.Buffer);
    // 输出:len=254, buf=L"C:\Program Files\WindowsApps\Microsoft.Microsof..."
    

    五、方案层:多版本兼容的完整路径获取路径树

    graph TD A[获取 EPROCESS] --> B{Windows Version} B -->|Win8.1–Win10 1803| C[SeLocateProcessImageName
    需 SE_DEBUG_PRIVILEGE] B -->|Win10 1809+| D[PsGetProcessImageFilePointer → FILE_OBJECT →
    IoQueryFileDosDeviceName] B -->|All Versions| E[FILE_OBJECT → ObQueryNameString +
    RtlDosPathNameToNtPathName_U] C --> F[处理 AppContainer Token 映射] D --> G[自动解析 \\?\ 前缀与符号链接] E --> H[需手动展开 ReparsePoint]

    六、实践层:生产环境推荐的健壮实现流程

    1. 调用 PsGetProcessImageFilePointer() 获取 FILE_OBJECT*
    2. 检查 FILE_OBJECT->Flags & FO_FILE_OPENED_WITH_DOS_PATH
    3. Win10 1809+:优先使用 IoQueryFileDosDeviceName()(自动解析符号链接)
    4. 降级路径:调用 ObQueryNameString() 获取 NT 路径,再经 RtlVolumeDeviceToDosName() 转换
    5. 对返回路径执行 IoResolveFileName()(需 IRQL ≤ APC_LEVEL)处理深层重定向
    6. 最终结果统一做 RtlPrefixUnicodeString(&prefix, &full, TRUE) 白名单匹配

    七、风险层:错误使用引发的安全后果矩阵

    场景误判类型影响等级典型案例
    HIPS 进程路径白名单漏报(恶意进程伪装为截断合法路径)严重PowerShell.exe → "PowerShel"
    EDR 进程溯源日志信息丢失导致无法归因Edge WebView2 子进程路径完全不可识别

    八、演进层:Windows 内核路径管理的设计权衡

    微软将 ImageFileName 限制为 256 字节是明确的性能/内存权衡:

    • 避免每个进程在 EPROCESS 中分配可变长缓冲区(破坏结构体布局稳定性)
    • 审计子系统(SeAuditProcessCreationInfo)需快速序列化,禁止阻塞式文件系统遍历
    • 真正完整的路径属于“按需查询”语义,而非“始终可用”状态 —— 符合微内核设计理念

    九、工具层:内核调试辅助验证方法

    !process <pid> 0
    // 查看 SeAuditProcessCreationInfo.ImageFileName.Buffer 地址
    dt nt!_EPROCESS <addr> SeAuditProcessCreationInfo
    dc <buffer_addr> L100 // 观察实际存储内容
    !object <file_object_addr> // 验证 FILE_OBJECT 是否有效

    十、架构层:面向未来的路径抽象建议

    • 构建 IMAGE_PATH_CONTEXT 结构体:封装 NT 路径、DOS 路径、签名哈希、PackageFamilyName(UWP)
    • 引入异步路径解析队列:避免在 APC 或 DPC 中执行耗时 IO
    • 与 Windows Defender Application Control(WDAC)策略联动,利用 SiPolicy 提供的 FileAttributes 元数据增强判断
    • 在 ETW 事件中新增 ProcessImageFullPathResolved provider,替代旧有 ImageFileName 字段
    ```
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 3月1日
  • 创建了问题 2月28日