【问题标题】:Winapi: Get the process which has specific handle of a fileWinapi:获取具有文件特定句柄的进程
【发布时间】:2018-05-10 11:50:56
【问题描述】:

目前我有一个具有文件过滤驱动程序的软件,在安装软件期间,驱动程序以这种方式作为服务启动:

CreateService(serviceManager, name, displayName,
                          SERVICE_START | DELETE | SERVICE_QUERY_STATUS | SERVICE_STOP,
                              SERVICE_FILE_SYSTEM_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
                                  path, NULL, NULL, NULL, NULL, NULL);

路径是C:\Program Files(x86)\TSU\driver\TSUfsd.sys

我遇到的问题是在卸载软件期间。当软件尝试删除 TSUfsd.sys 文件时,它会拒绝我访问。

我检查了软件如何删除驱动程序,结果发现它使用DeleteService函数将其删除,并等待服务将其状态从SERVICE_STOP_PENDING更改为SERVICE_STOPPED,如果之后没有发生一段时间后,它会获取服务PID 并使用ProcessTerminate 将其杀死,然后尝试使用rmdir /S /Q C:\Program Files(x86)\TSU\ 删除该文件。

我试图找到可能拥有文件句柄的进程(使用进程资源管理器)但找不到任何文件。然后我想也许服务还活着,所以我输入了sc query TSUfsd,但服务也消失了。

我也尝试更改权限并向我的用户授予完全权限,但仍然出现同样的错误。

所以我的问题是:

  1. 还有其他方法可以检查哪个进程(或其他任何东西)可以持有文件吗?

  2. 我还注意到,每当我尝试使用 Cygwin(rm TSUfsd.sys) 删除文件时,它都会毫无问题地删除文件。用 cmd(del /f <filename>) 和 cygwin 删除文件有什么区别?

【问题讨论】:

  • Mark Russinovich 有一个名为hanlde.exe 的不错的实用程序。除了你想在代码中自己做,那么你可以枚举所有进程并检查它们的句柄?
  • 在 Windows 10 中 - 系统保留已加载的驱动程序,在卸载驱动程序之前不要删除它

标签: c windows winapi cmd cygwin


【解决方案1】:

对于这个任务,从 Windows Vista 开始特别存在 FileProcessIdsUsingFileInformation FILE_INFORMATION_CLASS

所以我们需要使用FILE_READ_ATTRIBUTES 打开文件(即使有人以 0 共享模式打开文件也是可能的。如果我们无权访问文件(通过它DACL)但有读取权限到父目录)。并使用FileProcessIdsUsingFileInformation 致电NtQueryInformationFile。返回时,我们得到FILE_PROCESS_IDS_USING_FILE_INFORMATION 结构(在wdm.h 中定义),其中包含此文件的ProcessId 列表(打开文件句柄或映射部分。假设该文件是exe/dll - 我们在它加载的位置获得了进程ID) .每个 id 的打印进程名称也很好:

volatile UCHAR guz = 0;

NTSTATUS PrintProcessesUsingFile(HANDLE hFile)
{
    NTSTATUS status;
    IO_STATUS_BLOCK iosb;

    ULONG cb = 0, rcb = FIELD_OFFSET(FILE_PROCESS_IDS_USING_FILE_INFORMATION, ProcessIdList[64]);

    union {
        PVOID buf;
        PFILE_PROCESS_IDS_USING_FILE_INFORMATION ppiufi;
    };

    PVOID stack = alloca(guz);

    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (0 <= (status = NtQueryInformationFile(hFile, &iosb, ppiufi, cb, FileProcessIdsUsingFileInformation)))
        {
            if (ppiufi->NumberOfProcessIdsInList)
            {
                PrintProcessesUsingFile(ppiufi);
            }
        }

        rcb = (ULONG)iosb.Information;

    } while (status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}

NTSTATUS PrintProcessesUsingFile(POBJECT_ATTRIBUTES poa)
{
    IO_STATUS_BLOCK iosb;
    HANDLE hFile;
    NTSTATUS status;
    if (0 <= (status = NtOpenFile(&hFile, FILE_READ_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
    {
        status = PrintProcessesUsingFile(hFile);
        NtClose(hFile);
    }

    return status;
}

NTSTATUS PrintProcessesUsingFile(PCWSTR FileName)
{
    UNICODE_STRING ObjectName;
    NTSTATUS status = RtlDosPathNameToNtPathName_U_WithStatus(FileName, &ObjectName, 0, 0);
    if (0 <= status)
    {
        OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
        status = PrintProcessesUsingFile(&oa);
        RtlFreeUnicodeString(&ObjectName);
    }

    return status;
}

NTSTATUS PrintProcessesUsingFile(PFILE_PROCESS_IDS_USING_FILE_INFORMATION ppiufi)
{
    NTSTATUS status;

    ULONG cb = 0x8000;

    do 
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (PVOID buf = new BYTE[cb])
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                union {
                    PVOID pv;
                    PBYTE pb;
                    PSYSTEM_PROCESS_INFORMATION pspi;
                };

                pv = buf;
                ULONG NextEntryOffset = 0;

                do 
                {
                    pb += NextEntryOffset;

                    ULONG NumberOfProcessIdsInList = ppiufi->NumberOfProcessIdsInList;

                    PULONG_PTR ProcessIdList = ppiufi->ProcessIdList;
                    do 
                    {
                        if (*ProcessIdList++ == (ULONG_PTR)pspi->UniqueProcessId)
                        {
                            DbgPrint("%p %wZ\n", pspi->UniqueProcessId, &pspi->ImageName);
                            break;
                        }
                    } while (--NumberOfProcessIdsInList);

                } while (NextEntryOffset = pspi->NextEntryOffset);
            }

            delete [] buf;
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}

【讨论】:

  • 非常感谢您的回答 :) 因为构建您的代码给了我error: #error Compiler version not supported by Windows DDK #error Compiler version not supported by Windows DDK ,我无法真正更新我的编译器,因为它安装在我的大学计算机上并且更新它需要许可和大量时间。 @A 一个建议的handle.exe来解决这个问题,你的代码和handle.exe做同样的事情吗?
  • @user3503143 - 是的。同样-FileProcessIdsUsingFileInformation-您的编译器版本是什么?你可以简单地复制粘贴 wdk 定义 (NtQueryInformationFile ) 到拥有 src
  • (GCC) 4.9.2 我们正在使用 Cygwin。我试过handle.exe,它给出了No Matching Handle Found。还有什么可能是它的原因?用户配置文件和文件本身没有损坏。
  • @user3503143 你绝对可以通过复制/粘贴一些定义来编译这段代码。我只是按原样粘贴自己准备好的。但是这里只有NtQueryInformationFileFILE_INFORMATION_CLASSFILE_PROCESS_IDS_USING_FILE_INFORMATION 是强制性的。比如说打开文件你可以使用CreateFileW,将进程ID转换为你不能使用的名称。
  • @user3503143 - 像用户模式应用程序一样包含 wdm.h(对于 windows.h 也是如此)很难。需要将它包含在命名空间中+几个技巧。不要尝试将 wdm.h 包含到项目中。只需复制/粘贴 FILE_INFORMATION_CLASS 和 FILE_PROCESS_IDS_USING_FILE_INFORMATION 和 NtQueryInformationFile。使用 CreateFileW(FILE_READ_ATTRIBUTESFILE_FLAG_BACKUP_SEMANTICS) 打开文件 - 真正的任务是绝对可行的
猜你喜欢
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
相关资源
最近更新 更多