【问题标题】:CreateFileW with exclusive access succeeds when LoadLibraryEx called with LOAD_LIBRARY_AS_DATAFILE当使用 LOAD_LIBRARY_AS_DATAFILE 调用 LoadLibraryEx 时,具有独占访问权限的 CreateFileW 成功
【发布时间】:2017-10-13 16:33:30
【问题描述】:

我的假设是,如果您使用 CreateFileW 请求独占访问,如果它已经打开,我应该会收到共享冲突。但是,我遇到了一个并非如此的示例。如果调用 LoadLibraryEx 以加载设置了 LOAD_LIBRARY_AS_DATAFILE 标志的 DLL,则 CreateFileW 成功返回。但是有些操作会被拒绝(例如设置文件结尾等属性)。有没有更好的方法来检测这些文件是否打开?我是否错误地使用了 CreateFileW?在没有 LOAD_LIBRARY_AS_DATAFILE 标志的情况下加载 DLL 会导致 CreateFileW 失败,表明它无法访问该文件,因为它正在被另一个进程使用。

HMODULE hModule = LoadLibraryEx(L"\\Pathto\\module.dll", NULL, NULL);
DWORD access = GENERIC_READ | GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY;
DWORD creation = OPEN_EXISTING;
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
HANDLE file = CreateFileW(L"\\Pathto\\module.dll", access, 0, NULL, creation, flags, 0);

以上将导致 CreateFileW 失败,错误代码为 32(无法访问,因为它正在被另一个进程使用),这是预期的结果。如果我将标志添加到 LoadLibraryEx:

HMODULE hModule = LoadLibraryEx(name, NULL, LOAD_LIBRARY_AS_DATAFILE);    

然后对 CreateFileW 使用完全相同的调用,然后我被告知它是成功的,尽管稍后在尝试设置文件结尾时我会遇到问题(例如)。此外,删除文件将失败并出现拒绝访问错误(没有应用程序或任何其他具有打开句柄的应用程序)。

其他奇怪的行为涉及与加载的库的硬链接。如果我为加载的模块生成一个新的硬链接,并尝试删除替代的新创建的硬链接,它也会失败(没有打开文件句柄,只是加载了模块)。为什么?它不应该只是删除链接并取消引用链接计数,或者在模块关闭时安排删除,因为我之前可以获得独占访问权限吗?

另外值得注意的是,该进程本身在特权用户帐户中运行,并启用了以下附加权限: SE_SECURITY_NAME、SE_BACKUP_NAME、SE_RESTORE_NAME、SE_TAKE_OWNERSHIP_NAME

如何确定在加载库时您是否真正拥有对文件的独占访问权限?

编辑:只是为了仔细检查,除了通过 SysInternals 工具“句柄”加载模块之外,我没有验证其他打开的句柄。

【问题讨论】:

  • 我猜数据只是放在内存中,但文件没有保持打开状态。 blogs.msdn.microsoft.com/oldnewthing/20141120-00/?p=43573 可能是相关的。
  • 我认为还有更多。它显然仍被拥有进程引用(如果您在 SysInternals Process Explorer 中查看,则在该进程的生命周期内列为 DLL)。您的链接还指出它已映射到进程地址空间。另外,为什么没有解释就投反对票?这很容易重现。如果这是一个“愚蠢”的问题,请解释原因。
  • Raymond 的博客建议,如果它仅作为数据文件打开,则不应将其列为 DLL。顺便说一句,反对票不是我的。
  • @JonathanPotter - 文件并非在所有情况下都保持打开状态。这里的所有任务都是如何映射文件 - 作为图像或数据。并请求对文件的写访问权。正是在这一点上出错 - docs.microsoft.com/en-us/windows-hardware/drivers/ifs/…
  • 句柄引用文件对象,该对象引用文件的文件/流控制块。为了将文件映射为图像/数据,内存管理器使用 FCB 的section object pointers。当打开文件进行写访问或设置文件的删除处置时,文件系统使用MmFlushForWriteMmFlushForDelete 调用MmFlushImageSection。为硬链接打开的文件对象使用来自底层 SCB/FCB 的节对象指针。

标签: c++ winapi


【解决方案1】:

你的情况可以在下一个测试中更清楚地显示

ULONG TestImageAccess(PCWSTR name, BOOL bImage, BOOL bRequestWriteAccess)
{
    SetLastError(0);

    HANDLE hFile = CreateFileW(name, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        HANDLE hSection = CreateFileMappingW(hFile, 0, 
            bImage ? PAGE_READONLY|SEC_IMAGE : PAGE_READONLY|SEC_COMMIT, 0, 0, 0);

        CloseHandle(hFile);

        if (hSection)
        {
            hFile = CreateFileW(name, 
                bRequestWriteAccess ? GENERIC_WRITE : GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);

            CloseHandle(hSection);

            if (hFile != INVALID_HANDLE_VALUE)
            {
                CloseHandle(hFile);
            }
        }
    }

    return GetLastError();
}

void TestImageAccess(PCWSTR filename)
{
    TestImageAccess(filename, TRUE, TRUE);  // ERROR_SHARING_VIOLATION
    TestImageAccess(filename, FALSE, TRUE); // NOERROR
    TestImageAccess(filename, TRUE, FALSE); // NOERROR
    TestImageAccess(filename, FALSE, FALSE);// NOERROR
}

当我们请求写入权限到文件和文件映射作为图像

时,我们得到ERROR_SHARING_VIOLATION

这个案例在Executable Images:

中有所描述

如果用户想要对文件进行写访问,请确保没有 将此文件映射为图像的过程

MmFlushForWrite 调用MmFlushImageSection 失败,你得到STATUS_SHARING_VIOLATION - 就在这一行。

LoadLibraryEx with flag LOAD_LIBRARY_AS_DATAFILE create section on file with SEC_COMMIT when with 0 in flag - with SEC_IMAGE - so as image section

这里的权限绝对不相关

如何确定您是否真正拥有对文件的独占访问权限 什么时候加载库?

只需使用您需要的访问权限打开文件并执行您需要的操作。是的,当您尝试截断映射文件时,您可能会收到错误ERROR_USER_MAPPED_FILE。但这里无能为力。示例 2

ULONG TestImage2(PCWSTR name)
{
    HANDLE hFile = CreateFileW(name, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        HANDLE hSection = CreateFileMappingW(hFile, 0, PAGE_READONLY|SEC_COMMIT, 0, 0, 0);

        CloseHandle(hFile);

        if (hSection)
        {
            PVOID pv = MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0);

            CloseHandle(hSection);

            if (pv)
            {
                hFile = CreateFileW(name,GENERIC_WRITE|GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);

                if (hFile != INVALID_HANDLE_VALUE)
                {
                    FILE_END_OF_FILE_INFO eof = {  };
                    // ERROR_USER_MAPPED_FILE will be here
                    SetFileInformationByHandle(hFile, FileEndOfFileInfo, &eof, sizeof(eof));
                    CloseHandle(hFile);
                }

                UnmapViewOfFile(pv);
            }
        }
    }

    return GetLastError();
}

【讨论】:

  • 非常感谢您的详细回复。作为一种解决方法,我决定我可以尝试通过 SetFileInformationByHandle 设置文件结束标记(相同大小)并检查错误 1224 (ERROR_USER_MAPPED_FILE) 作为检测此问题的一种方法。有没有更好的方法来确定进程是否具有映射文件部分?
  • @ConfusedDeveloper - 你需要检测什么?你的目标是什么?甚至您以某种方式检测到这一点,之后情况随时可能发生变化。那么这种检测有什么意义呢?你真正的目标是什么?
  • 并将文件结尾设置为小于节的大小 - 这不是检查,而是文件数据损坏
  • 我打算将大小设置为现有大小,不更改文件大小。不幸的是,这不起作用,因为事实证明您可以设置相同的大小而不会出错。至于用例,请考虑在需要覆盖它们的情况下检测正在使用的文件。在这种情况下,可以通过 MoveFileEx 安排在重新启动时更换它们。
猜你喜欢
  • 1970-01-01
  • 2017-10-08
  • 2011-10-10
  • 2021-11-01
  • 1970-01-01
  • 2017-03-31
  • 1970-01-01
  • 2020-06-26
  • 2014-04-15
相关资源
最近更新 更多