2017-10-13 279 views
0

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

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工具“处理”加载模块外,我没有验证其他打开的句柄。

+1

我猜数据只是放在内存中,但文件不保持打开状态。 https://blogs.msdn.microsoft.com/oldnewthing/20141120-00/?p=43573可能是相关的。 –

+0

我觉得还有比这更多的东西。它显然仍然被拥有进程引用(如果你在SysInternals Process Explorer中查看,它将被列为进程生命周期的DLL)。您的链接还声明它已映射到进程地址空间。 另外,为什么downvotes没有解释?这很容易重现。如果这是一个“愚蠢的”问题,请解释原因。 – ConfusedDeveloper

+0

Raymond的博客建议,如果仅作为数据文件打开,则不应将其列为DLL。顺便说一句,downvotes不是我的。 –

回答

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描述更清晰显示:

如果用户想要写访问文件,确保没有一个 流程映射此文件作为图像

调用MmFlushImageSectionMmFlushForWrite失败,你有STATUS_SHARING_VIOLATION - 正是在这条线。

LoadLibraryEx与标志LOAD_LIBRARY_AS_DATAFILESEC_COMMIT创建文件部分时,在标志0 - 与SEC_IMAGE - 使图像部分

的特权在这里绝对没有关系

如何将一个确定你是否真的在加载库时可以独占访问文件 ?

只需打开您需要的访问权限并执行您所需的操作。是的,当您尝试截断映射文件时,可能会收到错误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(); 
} 
+0

非常感谢您的详细回复。作为解决方法,我已决定尝试通过SetFileInformationByHandle设置文件标记的结尾(相同大小),并检查错误1224(ERROR_USER_MAPPED_FILE),以此来检测此问题。有没有更好的方法来确定一个进程是否具有映射文件段? – ConfusedDeveloper

+0

@ConfusedDeveloper - 你需要什么来检测这个?你的目标是什么?甚至你以某种方式检测到这一点,之后它随时会改变情况。那么这种检测有什么意义呢?你真正的目标是什么? – RbMm

+0

并将文件结尾设置为小于部分的大小 - 这不是检查,而是文件数据损坏 – RbMm