4

Currently I have a software which has a file filter driver, during installation of software the driver is started as service in this way:

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);

Where path is C:\Program Files(x86)\TSU\driver\TSUfsd.sys.

The problem I'm having is during uninstallation of the software. It gives me access denied when software tries to delete TSUfsd.sys file.

I've checked how the software deletes the driver, and turns out it deletes it with DeleteService function, and waits for service to change its state from SERVICE_STOP_PENDING to SERVICE_STOPPED and if it doesn't happen after some time, it gets the service PID and kills it with ProcessTerminate and then tries to delete the file with rmdir /S /Q C:\Program Files(x86)\TSU\.

I've tried to find the process which could have had the handle of the file(with Process Explorer) but could not find any. Then I thought maybe the service is still alive so I typed sc query TSUfsd but the service was gone too.

I've also tried to change permissions and grant full permissions to my user but same error still occurred.

So my questions are:

  1. Is there any other ways to check which process(or anything else) can have a hold of a file?

  2. I've also noticed that, whenever I try to delete the file with Cygwin(rm TSUfsd.sys) it deletes the file without a problem. What is the difference between deleting file with cmd(del /f <filename>) and with cygwin?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
user3503143
  • 381
  • 3
  • 10
  • There was a nice utility called [`hanlde.exe`](https://learn.microsoft.com/en-us/sysinternals/downloads/handle) by Mark Russinovich. Except you want to do it yourself in code, then you can enumerate all processes and hen inspect their handles? – Mihayl Nov 27 '17 at 11:39
  • in windows 10 - system hold loaded drivers and not let delete it until driver will be unload – RbMm Nov 27 '17 at 13:23

1 Answers1

8

For this task, starting with Windows Vista special exist FileProcessIdsUsingFileInformation FILE_INFORMATION_CLASS.

So we need open file with FILE_READ_ATTRIBUTES (this is possible even if somebody open file with 0 share mode. of if we have no access to file (by it DACL) but have read access to parent directory). and call NtQueryInformationFile with FileProcessIdsUsingFileInformation. on return we got FILE_PROCESS_IDS_USING_FILE_INFORMATION structure (defined in wdm.h) where list of ProcessId which hold this file(open file handle or mapped section. say if this file is exe/dll - we got process ids where it loaded). good also with this print process name for every 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;
}
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
RbMm
  • 31,280
  • 3
  • 35
  • 56
  • Thank you very much for answer :) Since Building your code gives me `error: #error Compiler version not supported by Windows DDK #error Compiler version not supported by Windows DDK ` and I can't really update my compiler since it installed on my university computers and updating it would need permission and lot of time. @A A suggested handle.exe to solve the issue, does your code do the same thing as handle.exe? – user3503143 Nov 27 '17 at 13:03
  • @user3503143 - yes. the same - `FileProcessIdsUsingFileInformation` - what is your compiler version ? and you can simply copy-paste wdk definitions (`NtQueryInformationFile` ) to own src – RbMm Nov 27 '17 at 13:05
  • `(GCC) 4.9.2` We are using Cygwin. I've tried `handle.exe` and it gives `No Matching Handle Found`. What else could be the cause of it? User profile and file itself are not corrupted. – user3503143 Nov 27 '17 at 13:15
  • @user3503143 you definitely can compile this code by copy/paste some definition. i simply paste own ready as is. however here mandatory only `NtQueryInformationFile`, `FILE_INFORMATION_CLASS` and `FILE_PROCESS_IDS_USING_FILE_INFORMATION`. for say open file you can use `CreateFileW`, convert process id to name you can not use. – RbMm Nov 27 '17 at 13:22
  • @user3503143 - include wdm.h as is for user mode app (so with windows.h) is hard. need include it in namespace + several tricks. not try include wdm.h to project. simply copy/paste FILE_INFORMATION_CLASS and FILE_PROCESS_IDS_USING_FILE_INFORMATION and NtQueryInformationFile. file open with CreateFileW(`FILE_READ_ATTRIBUTES` and `FILE_FLAG_BACKUP_SEMANTICS`) - really task is absolute posible – RbMm Nov 27 '17 at 13:38
  • I started to copy needed resources but it turned out to be lot. At the end I managed to install VS 2017, but have some problem here too `RtlDosPathNameToNtPathName_U_WithStatus` this function is undentified and I could not even find the documentation of it. – user3503143 Nov 28 '17 at 09:53
  • @user3503143 - you really not need [RtlDosPathNameToNtPathName_U_WithStatus](http://processhacker.sourceforge.net/doc/ntrtl_8h.html#a0eef2a39be3e732e674a8f38df3a05c1) - i use it for convert *DosFileName* to *NtFileName*, because i use *NtOpenFile*. however you can use *CreateFileW* in this case you not need this api. again - begin from `FILE_INFORMATION_CLASS` , `FILE_PROCESS_IDS_USING_FILE_INFORMATION` and `NtQueryInformationFile` - copy-paste only this 3 definitions and call `NtQueryInformationFile` with `FileProcessIdsUsingFileInformation`. begin from this. – RbMm Nov 28 '17 at 10:48
  • Sorry for late response we had some electricity problem. Here is what I got https://pastebin.com/xxnaySFm The only error I'm occuring in in the pastebin link at the bottom. I could not understand the why is it occuring, hope you can help. – user3503143 Nov 29 '17 at 07:00
  • @user3503143 - this already only compiler issue. interesting that *CL* compile this expression without problem. you can use `rcb = FIELD_OFFSET(FILE_PROCESS_IDS_USING_FILE_INFORMATION, ProcessIdList) + ppiufi->NumberOfProcessIdsInList * RTL_FIELD_SIZE(FILE_PROCESS_IDS_USING_FILE_INFORMATION, ProcessIdList);` or this `rcb = FIELD_OFFSET(FILE_PROCESS_IDS_USING_FILE_INFORMATION, ProcessIdList) + ppiufi->NumberOfProcessIdsInList * sizeof(ULONG_PTR);` - all 3 the same – RbMm Nov 29 '17 at 09:00
  • @user3503143 also about *Nt* function declarations - all it must be declared in `extern "C" { .. }` block (if you use *c++*), all must have `NTSYSAPI NTSTATUS WINAPI` - look for `NtQueryInformationFile` - it not have this in your code. and finally - add `ntdll.lib` to linker input – RbMm Nov 29 '17 at 09:03
  • Thank you so much for your help :) https://pastebin.com/TQeysi0B I've added logs and it returns `ppiufi->NumberOfProcessIdsInList -> 0` which means no process has this handle right? – user3503143 Nov 29 '17 at 09:38
  • @user3503143 - yes, if `ppiufi->NumberOfProcessIdsInList == 0` after success call to `NtQueryInformationFile` mean that system not hold this file – RbMm Nov 29 '17 at 09:43
  • @user3503143 - test for example on *c:\windows\system32\ntdll.dll* for view result, than say on *c:\windows\system32\ole32.dll* and compare – RbMm Nov 29 '17 at 09:45
  • @user3503143 - however - if you run on win10 and file (*TSUfsd.sys*) is driver which is currently loaded - system hold this file (unlike previous windows version it not let let delete running drivers, like all windows not let delete running exe files), but it not show it usage - *NumberOfProcessIdsInList* will be 0. if this is your case - you need first unload driver (before delete) – RbMm Nov 29 '17 at 09:54
  • On `ntdll.dll` the process started and never returned. I tried it on some other file and it returned correctly. Thank you very very much, and one last question, where did you learn all this stuff? :) – user3503143 Nov 29 '17 at 10:19
  • @user3503143 - *On ntdll.dll the process started and never returned* - this must not be. sure that error in your code exist. you need run code under debugger for view exactly where error. why it *"never returned"*. research this just now - very important for you. *where did you learn all this stuff?* - if i have situation when something "not working" - i never drop this, but research (does not matter how many time it take) until not fix or understand why this must not work – RbMm Nov 29 '17 at 11:13