You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中如何通过文件映射对象句柄获取对应对象名称

问题背景
  • 开发目标为解密某进程映射到内存中的媒体文件,目前已成功Hook CreateFileW() 与 MapViewOfFile() 函数,其余进程读写函数的Hook逻辑已调试通过、运行正常。
  • 现存异常:其他进程通过当前Hook链路中的文件向其他文件执行写入操作时,会出现运行失败的问题。
  • 待实现逻辑:需要在Hook的MapViewOfFile()函数内部校验映射对象名称,识别到目标对象MYOBJECT后,对内存缓冲区中的数据做解密更新。
  • 当前卡点:MapViewOfFile()的Hook回调中仅持有传入的文件映射对象句柄hFileMappingObject,无法直接获取该句柄对应的对象名称,现有Hook代码框架如下:
LPVOID WINAPI MapViewHooked(  
    __in HANDLE hFileMappingObject,
    __in DWORD  dwDesiredAccess,
    __in DWORD  dwFileOffsetHigh,
    __in DWORD  dwFileOffsetLow,
    __in SIZE_T dwNumberOfBytesToMap   
) 
{ 
    // string pszobjectnamefrom handle=some_function(hFileMappingObject);// 此处逻辑卡住
    // if(pszobjectnamefrom=="MYOBJECT") {
    //     拿到缓冲区后执行解密逻辑
    // }
}
实现方案

通过句柄获取文件映射对象名称

Windows未提供公开的用户态API直接从句柄获取内核对象名称,可调用ntdll.dll导出的原生系统函数NtQueryObject实现,该函数虽未在官方文档中公开,但从Win2000到最新Win11版本的调用逻辑保持稳定,具体实现步骤如下:

  • 动态加载ntdll.dll,获取NtQueryObject的函数地址,提前定义好函数需要的枚举、结构类型
  • 第一次调用NtQueryObject,传入信息类为ObjectNameInformation,缓冲区参数传NULL,获取存储对象名称需要的缓冲区长度
  • 按返回的长度申请堆内存,第二次调用NtQueryObject将对象名称信息写入缓冲区,从返回的OBJECT_NAME_INFORMATION结构中读取Unicode格式的对象名
  • 注意:不要使用ObjectAllTypesInformation等需要遍历全系统句柄的信息类,会引发性能问题甚至Hook死锁;名称读取完成后及时释放申请的缓冲区。

对应实现代码如下:

// 提前定义原生API所需结构
typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectNameInformation = 1,
} OBJECT_INFORMATION_CLASS;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_NAME_INFORMATION {
    UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

typedef NTSTATUS(NTAPI* pNtQueryObject)(
    HANDLE                   Handle,
    OBJECT_INFORMATION_CLASS ObjectInformationClass,
    PVOID                    ObjectInformation,
    ULONG                    ObjectInformationLength,
    PULONG                   ReturnLength
);

// 从句柄获取对象名的工具函数
std::wstring GetObjectNameFromHandle(HANDLE hObject) {
    pNtQueryObject NtQueryObject = (pNtQueryObject)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryObject");
    if (!NtQueryObject) return L"";

    ULONG retLen = 0;
    NTSTATUS status = NtQueryObject(hObject, ObjectNameInformation, NULL, 0, &retLen);
    if (retLen == 0) return L"";

    POBJECT_NAME_INFORMATION pNameInfo = (POBJECT_NAME_INFORMATION)HeapAlloc(GetProcessHeap(), 0, retLen);
    if (!pNameInfo) return L"";

    std::wstring objName;
    status = NtQueryObject(hObject, ObjectNameInformation, pNameInfo, retLen, &retLen);
    if (status >= 0 && pNameInfo->Name.Buffer != NULL && pNameInfo->Name.Length > 0) {
        objName.assign(pNameInfo->Name.Buffer, pNameInfo->Name.Length / sizeof(WCHAR));
    }

    HeapFree(GetProcessHeap(), 0, pNameInfo);
    return objName;
}

匹配目标对象后的解密逻辑实现

拿到对象名完成匹配后,按以下逻辑执行解密,避免引发访问异常或执行流错误:

  • 所有分支必须优先调用原始的MapViewOfFile()函数,传入全部收到的参数,拿到真实的映射内存基址,禁止在调用原函数前直接操作内存。
  • 非目标对象的分支直接返回原函数的执行结果,不要篡改参数、返回值,这是之前跨文件写入失败的核心原因——非目标对象的执行流被Hook逻辑拦截,没有透传原函数的正确返回值。
  • 匹配到MYOBJECT对象后,根据dwNumberOfBytesToMap确定解密长度:如果该参数为0,调用VirtualQuery()查询映射段的完整区域长度。
  • 调用VirtualProtect()将待解密的映射内存区域临时修改为PAGE_READWRITE属性,记录原保护属性,在映射基址上原地执行解密逻辑,解密完成后恢复内存的原保护属性即可。

完整Hook逻辑框架参考:

// 保存原始MapViewOfFile函数地址
LPVOID(WINAPI* OriginalMapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T) = MapViewOfFile;

LPVOID WINAPI MapViewHooked(  
    __in HANDLE hFileMappingObject,
    __in DWORD  dwDesiredAccess,
    __in DWORD  dwFileOffsetHigh,
    __in DWORD  dwFileOffsetLow,
    __in SIZE_T dwNumberOfBytesToMap   
) 
{ 
    // 先调用原函数拿到真实映射地址
    LPVOID pMapBase = OriginalMapViewOfFile(hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap);
    if (!pMapBase) return NULL;

    // 读取对象名做匹配
    std::wstring objName = GetObjectNameFromHandle(hFileMappingObject);
    if (objName.find(L"MYOBJECT") != std::wstring::npos) {
        SIZE_T decryptLen = dwNumberOfBytesToMap;
        if (decryptLen == 0) {
            MEMORY_BASIC_INFORMATION mbi = {0};
            VirtualQuery(pMapBase, &mbi, sizeof(mbi));
            decryptLen = mbi.RegionSize;
        }

        // 临时修改内存保护属性
        DWORD oldProtect = 0;
        VirtualProtect(pMapBase, decryptLen, PAGE_READWRITE, &oldProtect);
        
        // 此处调用自己实现的解密函数,直接在pMapBase指向的内存上原地更新
        // DecryptBuffer(pMapBase, decryptLen);

        // 恢复原保护属性
        VirtualProtect(pMapBase, decryptLen, oldProtect, &oldProtect);
    }

    return pMapBase;
}

注意:如果创建文件映射时传入的名称带\Sessions\X\BaseNamedObjects\这类系统路径前缀,做字符串匹配时不要只做全字匹配,判断名称包含目标字符串即可,避免漏判。

内容的提问来源于stack exchange,提问作者Deepak Pathak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:21:35