Skip to content

[Feature] Unlock files by closing their handles with P/Invoke #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
BinToss opened this issue May 6, 2022 · 8 comments
Open

[Feature] Unlock files by closing their handles with P/Invoke #5

BinToss opened this issue May 6, 2022 · 8 comments
Assignees
Labels
enhancement New feature or request

Comments

@BinToss
Copy link
Collaborator

BinToss commented May 6, 2022

This option can be more dangerous compared to telling a process to end and then waiting for it to end.

Personally, I prefer not closing processes that are locking a file, especially if one of them is the File Explorer desktop shell.
Instead, I typically use ProcessHacker to close the file handle.
Closing a handle held by another process is useful when that process is not responding to attempts to terminate it.

I'll be porting portions of ProcessHacker to .NET with the intention of using some of its features for other C#/.NET projects including its ability to close handles held by other processes.

I'll submit a pull request with this feature when I have a working example.

@CodeDead
Copy link
Owner

CodeDead commented May 6, 2022

Hi @BinToss ,

Closing a handle is indeed dangerous and can cause undefined behaviour in exterior processes. The reason why this was never implemented in DeadLock is because of how dangerous this can be.

If you do decide to close a handle, you can do so using the CloseHandle API provided by Microsoft:
https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle

However, do note that this can cause serious problems and in case of elevated processes such as drivers, it can cause blue screens of death.

Killing a process is safer because you need elevated rights to kill certain processes that are essential to your system.

Closing the handle could be used as a final resort, of sorts, if killing a process was unsuccessful, but only if the holder of the handle is not absolutely required by the system.

This feature being added to the deadlock SDK is still a good idea, but if you can, it's best to separate the methods that kill the processes versus the methods that will close the handle in addition to the methods that kill handles having a code doc warning slapped on them so that the users are aware of the dangers.

Thanks for your continued contributions and innovations!

@CodeDead CodeDead added the enhancement New feature or request label May 7, 2022
@BinToss
Copy link
Collaborator Author

BinToss commented May 14, 2022

Small update: I've spent nearly every day peeling apart ProcessHacker to learn how it retrieves a list of handles from every process. I've either not found all the code necessary for this functionality or I've overlooked it.

After a few days, I'd begun to search for it from ./ProcessHacker/findobj.c.

A cursory glance tells me that a kernel-level driver (e.g. KProcessHacker) would greatly assist in handle collection.

@CodeDead
Copy link
Owner

CodeDead commented May 14, 2022

@BinToss NtQuerySystemInformation should be able to list ALL handles.

typedef struct _SYSTEM_HANDLE
{
    DWORD    dwProcessId;
    BYTE     bObjectType;
    BYTE     bFlags;
    WORD     wValue;
    PVOID    pAddress;
    DWORD    GrantedAccess;
}
SYSTEM_HANDLE;

ProcessHacker has its own implementation:
https://github.com/processhacker/processhacker/blob/master/ProcessHacker/hndlprv.c

@CodeDead
Copy link
Owner

CodeDead commented May 17, 2022

@BinToss It's an undocumented feature of NtQuerySystemInformation. I forgot to mention that, apologies. You can find the information about it here:
https://blog.katastros.com/a?ID=01600-1528e951-c96a-492d-8269-54c6a4a5646a

typedef struct _SYSTEM_HANDLE_INFORMATION_EX
{
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_INFORMATION Information[655360];//Note that the value of 655360 is defined by myself, you can define other constant values ​​yourself
}SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG ProcessId;//Process identifier 
    UCHAR ObjectTypeNumber;//The type of object opened
    UCHAR Flags;//Handle attribute flags
    USHORT Handle;//Handle value, which uniquely identifies a handle among the handles opened by the process
    PVOID Object;//This is the address of the EPROCESS corresponding to the handle
    ACCESS_MASK GrantedAccess;//Access permissions of the handle object
}SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

Hope this helps, it should be possible to get the handles with this function and close them with the CloseHandle function:
https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle

@BinToss
Copy link
Collaborator Author

BinToss commented May 18, 2022

@BinToss
Copy link
Collaborator Author

BinToss commented Aug 9, 2022

My current issue is filtering handles by their object type.
ProcessHacker wraps handle objects and passes the wrappers' pointers around which has been a major impedance.

@BinToss
Copy link
Collaborator Author

BinToss commented Aug 22, 2022

Found a way to identify the Type of a Handle's object.
scroll down to the table
https://docs.microsoft.com/en-us/windows/win32/sysinfo/kernel-objects

This is the function I'm using to check a handle's object's type
https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryobject

Now, I'm finalizing details for two wrapper classes. The File-specific of the two will be what's returned in a List.
Getting a process's command line from its mutable PEB is tedious and unreliable, but it's better than nothing.

I'd also like to make service and task host processes supply the names of their respective services and tasks, but that might be even more complicated, so I'll leave that to whoever really wants that feature.

@BinToss
Copy link
Collaborator Author

BinToss commented Oct 19, 2023

It's been mostly done since Spring. I needed a long break after discovering the limitations of what this can do without requiring all users to install a kernel-mode driver.
There's very little it can't do without the driver, but knowing that I couldn't reach my original goal was a blow to my motivation to finish it.
When complete, API users will be given warnings (and/or have access to a list of exceptions) when collected handle information is limited by a lack of token elevation (didn't run as admin) or due to a handle being owned by a protected process e.g.

Name (Service Name(s)) Protection Level (Type)
System Full (WinSystem)
Secure System Full (WinSystem)
Registry Full (WinSystem)
smss.exe Light (WinTcb)
Memory Compression Full (WinSystem)
csrss.exe Light (WinTcb)
wininit.exe Light (WinTcb)
services.exe Light (WinTcb)
svchost.exe (WaaSMedicSvc) Light (Windows)
MsMpEng.exe (WinDefend) Light (Antimalware)
NisSrv.exe (WdNisSrv) Light (Antimalware)
SecurityHealthService.exe (SecurityHealthService) Light (Windows)
svchost.exe (wscsvc) Light (Windows)
svchost.exe (DoSvc) Light (Windows)
svchost.exe (AppXSvc) Light (Windows)
lsass.exe (EFS, KeyIso, SamSs, VaultSvc) Light (Lsa)
csrss.exe Light (WinTcb)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants