Skip to content

Commit

Permalink
在 C# 使用 Detours 劫持函数
Browse files Browse the repository at this point in the history
  • Loading branch information
wherewhere committed Jul 31, 2024
1 parent 6be6fa7 commit 8959ca4
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 225 deletions.
16 changes: 1 addition & 15 deletions CoreAppUWP.sln
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
# 17
VisualStudioVersion = 17.4.33006.217
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreAppUWP", "CoreAppUWP\CoreAppUWP.csproj", "{86F40308-5864-4E45-98A4-336A5C404BC9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreAppUWP.WinRT", "CoreAppUWP.WinRT\CoreAppUWP.WinRT.vcxproj", "{2F7E9384-91BE-4751-8322-C504B0F61AF2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Expand Down Expand Up @@ -35,18 +33,6 @@ Global
{86F40308-5864-4E45-98A4-336A5C404BC9}.Release|x86.ActiveCfg = Release|x86
{86F40308-5864-4E45-98A4-336A5C404BC9}.Release|x86.Build.0 = Release|x86
{86F40308-5864-4E45-98A4-336A5C404BC9}.Release|x86.Deploy.0 = Release|x86
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Debug|ARM64.Build.0 = Debug|ARM64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Debug|x64.ActiveCfg = Debug|x64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Debug|x64.Build.0 = Debug|x64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Debug|x86.ActiveCfg = Debug|Win32
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Debug|x86.Build.0 = Debug|Win32
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Release|ARM64.ActiveCfg = Release|ARM64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Release|ARM64.Build.0 = Release|ARM64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Release|x64.ActiveCfg = Release|x64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Release|x64.Build.0 = Release|x64
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Release|x86.ActiveCfg = Release|Win32
{2F7E9384-91BE-4751-8322-C504B0F61AF2}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
178 changes: 74 additions & 104 deletions CoreAppUWP/Common/HookRegistry.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.Foundation;
Expand All @@ -10,121 +11,96 @@ namespace CoreAppUWP.Common
{
public class HookRegistry : IDisposable
{
private unsafe delegate WIN32_ERROR RegOpenKeyEx(HKEY hKey, PCWSTR lpSubKey, uint ulOptions, REG_SAM_FLAGS samDesired, HKEY* phkResult);
private delegate WIN32_ERROR RegCloseKey(HKEY hKey);
private unsafe delegate WIN32_ERROR RegQueryValueEx(HKEY hKey, PCWSTR lpValueName, [Optional] uint* lpReserved, [Optional] REG_VALUE_TYPE* lpType, [Optional] byte* lpData, [Optional] uint* lpcbData);

[ThreadStatic]
private static HANDLE currentThread;
[ThreadStatic]
private static Dictionary<HKEY, bool> xamlKeyMap;
[ThreadStatic]
private static object locker;

[ThreadStatic]
private static unsafe FARPROC baseRegOpenKeyExW;
[ThreadStatic]
private static unsafe delegate*<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR> overrideRegOpenKeyExW;

[ThreadStatic]
private static unsafe FARPROC baseRegCloseKey;
[ThreadStatic]
private static unsafe delegate*<HKEY, WIN32_ERROR> overrideRegCloseKey;

[ThreadStatic]
private static unsafe FARPROC baseRegQueryValueExW;
[ThreadStatic]
private static unsafe delegate*<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR> overrideRegQueryValueExW;
private bool disposed;
private static int refCount;
private static readonly Dictionary<HKEY, bool> xamlKeyMap = [];
private static readonly object locker = new();

~HookRegistry()
private static unsafe delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR> RegOpenKeyExW;
private static unsafe delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR> RegCloseKey;
private static unsafe delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR> RegQueryValueExW;

public HookRegistry()
{
Dispose(disposing: true);
refCount++;
StartHook();
}

[ThreadStatic]
private static bool isHooked;
public bool IsHooked
~HookRegistry()
{
get => isHooked;
set => isHooked = value;
Dispose();
}

public unsafe void StartHook()
public static bool IsHooked { get; private set; }

private unsafe static void StartHook()
{
if (!IsHooked)
{
xamlKeyMap ??= [];
locker ??= new object();

currentThread = PInvoke.GetCurrentThread();

_ = Detours.DetourTransactionBegin();
_ = Detours.DetourUpdateThread(currentThread);

using (FreeLibrarySafeHandle library = PInvoke.LoadLibrary("ADVAPI32.dll"))
using FreeLibrarySafeHandle library = PInvoke.GetModuleHandle("ADVAPI32.dll");
if (!library.IsInvalid
&& NativeLibrary.TryGetExport(library.DangerousGetHandle(), "RegOpenKeyExW", out nint regOpenKeyExW)
&& NativeLibrary.TryGetExport(library.DangerousGetHandle(), nameof(PInvoke.RegCloseKey), out nint regCloseKey)
&& NativeLibrary.TryGetExport(library.DangerousGetHandle(), "RegQueryValueExW", out nint regQueryValueExW))
{
baseRegOpenKeyExW = PInvoke.GetProcAddress(library, "RegOpenKeyExW");
void* baseRegOpenKeyExWPointer = (void*)baseRegOpenKeyExW.Value;
overrideRegOpenKeyExW = &OverrideRegOpenKeyEx;
void* overrideRegOpenKeyExWPointer = overrideRegOpenKeyExW;
_ = Detours.DetourAttach(ref baseRegOpenKeyExWPointer, overrideRegOpenKeyExWPointer);

baseRegCloseKey = PInvoke.GetProcAddress(library, "RegCloseKey");
void* baseRegCloseKeyPointer = (void*)baseRegCloseKey.Value;
overrideRegCloseKey = &OverrideRegCloseKey;
void* overrideRegCloseKeyPointer = overrideRegCloseKey;
_ = Detours.DetourAttach(ref baseRegCloseKeyPointer, overrideRegCloseKeyPointer);

baseRegQueryValueExW = PInvoke.GetProcAddress(library, "RegQueryValueExW");
void* baseRegQueryValueExWPointer = (void*)baseRegQueryValueExW.Value;
overrideRegQueryValueExW = &OverrideRegQueryValueExW;
void* overrideRegQueryValueExWPointer = overrideRegQueryValueExW;
_ = Detours.DetourAttach(ref baseRegQueryValueExWPointer, overrideRegQueryValueExWPointer);
void* regOpenKeyExWPtr = (void*)regOpenKeyExW;
void* regCloseKeyPtr = (void*)regCloseKey;
void* regQueryValueExWPtr = (void*)regQueryValueExW;

delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR> overrideRegOpenKeyExW = &OverrideRegOpenKeyExW;
delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR> overrideRegCloseKey = &OverrideRegCloseKey;
delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR> overrideRegQueryValueExW = &OverrideRegQueryValueExW;

_ = Detours.DetourRestoreAfterWith();

_ = Detours.DetourTransactionBegin();
_ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());
_ = Detours.DetourAttach(ref regOpenKeyExWPtr, overrideRegOpenKeyExW);
_ = Detours.DetourAttach(ref regCloseKeyPtr, overrideRegCloseKey);
_ = Detours.DetourAttach(ref regQueryValueExWPtr, overrideRegQueryValueExW);
_ = Detours.DetourTransactionCommit();

RegOpenKeyExW = (delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR>)regOpenKeyExWPtr;
RegCloseKey = (delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR>)regCloseKeyPtr;
RegQueryValueExW = (delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR>)regQueryValueExWPtr;

IsHooked = true;
}

_ = Detours.DetourTransactionCommit();
IsHooked = true;
}
}

public unsafe void EndHook()
public unsafe static void EndHook()
{
if (IsHooked)
if (--refCount == 0 && IsHooked)
{
_ = Detours.DetourTransactionBegin();
_ = Detours.DetourUpdateThread(currentThread);

void* baseRegOpenKeyExWPointer = (void*)baseRegOpenKeyExW.Value;
void* overrideRegOpenKeyExWPointer = overrideRegOpenKeyExW;
_ = Detours.DetourDetach(ref baseRegOpenKeyExWPointer, overrideRegOpenKeyExWPointer);
baseRegOpenKeyExW = default;
overrideRegOpenKeyExW = default;

void* baseRegCloseKeyPointer = (void*)baseRegCloseKey.Value;
void* overrideRegCloseKeyPointer = overrideRegCloseKey;
_ = Detours.DetourDetach(ref baseRegCloseKeyPointer, overrideRegCloseKeyPointer);
baseRegCloseKey = default;
overrideRegCloseKey = default;

void* baseRegQueryValueExWPointer = (void*)baseRegQueryValueExW.Value;
void* overrideRegQueryValueExWPointer = overrideRegQueryValueExW;
_ = Detours.DetourDetach(ref baseRegQueryValueExWPointer, overrideRegQueryValueExWPointer);
baseRegQueryValueExW = default;
overrideRegOpenKeyExW = default;
void* regOpenKeyExWPtr = RegOpenKeyExW;
void* regCloseKeyPtr = RegCloseKey;
void* regQueryValueExWPtr = RegQueryValueExW;

delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR> overrideRegOpenKeyExW = &OverrideRegOpenKeyExW;
delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR> overrideRegCloseKey = &OverrideRegCloseKey;
delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR> overrideRegQueryValueExW = &OverrideRegQueryValueExW;

_ = Detours.DetourTransactionBegin();
_ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());
_ = Detours.DetourDetach(&regOpenKeyExWPtr, overrideRegOpenKeyExW);
_ = Detours.DetourDetach(&regCloseKeyPtr, overrideRegCloseKey);
_ = Detours.DetourDetach(&regQueryValueExWPtr, overrideRegQueryValueExW);
_ = Detours.DetourTransactionCommit();

locker = xamlKeyMap = null;
RegOpenKeyExW = null;
RegCloseKey = null;
RegQueryValueExW = null;

IsHooked = false;
}
}

private static unsafe WIN32_ERROR OverrideRegOpenKeyEx(HKEY hKey, PCWSTR lpSubKey, uint ulOptions, REG_SAM_FLAGS samDesired, HKEY* phkResult)
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
private static unsafe WIN32_ERROR OverrideRegOpenKeyExW(HKEY hKey, PCWSTR lpSubKey, uint ulOptions, REG_SAM_FLAGS samDesired, HKEY* phkResult)
{
if (!isHooked) { return PInvoke.RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult); }
RegOpenKeyEx RegOpenKeyEx = baseRegOpenKeyExW.CreateDelegate<RegOpenKeyEx>();
WIN32_ERROR result = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult);
if (hKey == HKEY.HKEY_LOCAL_MACHINE && lpSubKey.ToString() == @"Software\Microsoft\WinUI\Xaml")
WIN32_ERROR result = RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
if (hKey == HKEY.HKEY_LOCAL_MACHINE && lpSubKey.ToString().Equals(@"Software\Microsoft\WinUI\Xaml", StringComparison.OrdinalIgnoreCase))
{
if (result == WIN32_ERROR.ERROR_FILE_NOT_FOUND)
{
Expand All @@ -141,10 +117,9 @@ private static unsafe WIN32_ERROR OverrideRegOpenKeyEx(HKEY hKey, PCWSTR lpSubKe
return result;
}

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
private unsafe static WIN32_ERROR OverrideRegCloseKey(HKEY hKey)
{
if (!isHooked) { return PInvoke.RegCloseKey(hKey); }
static WIN32_ERROR RegCloseKey(HKEY hKey) => baseRegCloseKey.CreateDelegate<RegCloseKey>()(hKey);
bool isXamlKey;
lock (locker)
{
Expand All @@ -162,11 +137,10 @@ private unsafe static WIN32_ERROR OverrideRegCloseKey(HKEY hKey)
}
}

[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
private static unsafe WIN32_ERROR OverrideRegQueryValueExW(HKEY hKey, PCWSTR lpValueName, [Optional] uint* lpReserved, [Optional] REG_VALUE_TYPE* lpType, [Optional] byte* lpData, [Optional] uint* lpcbData)
{
if (!isHooked) { return PInvoke.RegQueryValueEx(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); }
RegQueryValueEx RegQueryValueEx = baseRegQueryValueExW.CreateDelegate<RegQueryValueEx>();
if (lpValueName.Value == default && lpValueName.ToString().Equals("EnableUWPWindow", StringComparison.OrdinalIgnoreCase))
if (lpValueName.Value != default && lpValueName.ToString().Equals("EnableUWPWindow", StringComparison.OrdinalIgnoreCase))
{
lock (locker)
{
Expand All @@ -176,7 +150,7 @@ private static unsafe WIN32_ERROR OverrideRegQueryValueExW(HKEY hKey, PCWSTR lpV
if (isRealKey)
{
// real key
result = RegQueryValueEx(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
result = RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
if (result == WIN32_ERROR.ERROR_SUCCESS && lpData != default)
{
*lpData = 1;
Expand Down Expand Up @@ -228,21 +202,17 @@ private static unsafe WIN32_ERROR OverrideRegQueryValueExW(HKEY hKey, PCWSTR lpV
}
}
}
return RegQueryValueEx(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
return RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
}

protected virtual void Dispose(bool disposing)
public void Dispose()
{
if (disposing && IsHooked)
if (!disposed && IsHooked)
{
EndHook();
}
}

public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
disposed = true;
}
}
}
Loading

0 comments on commit 8959ca4

Please sign in to comment.