Skip to content

Commit

Permalink
Cache thread Id and startAdress so they don't have to be loaded each …
Browse files Browse the repository at this point in the history
…time
  • Loading branch information
marticliment committed Feb 8, 2025
1 parent 18188e7 commit 5686e7c
Showing 1 changed file with 89 additions and 53 deletions.
142 changes: 89 additions & 53 deletions src/UniGetUI.Core.Tools/DWMThreadHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,100 +33,136 @@ private enum ThreadAccess : uint
private static bool DWM_IsSuspended;
private static bool XAML_IsSuspended;

private static int? DWMThreadId;
private static IntPtr? DWMThreadAdress;
private static int? XAMLThreadId;
private static IntPtr? XAMLThreadAdress;

public static void ChangeState_DWM(bool suspend)
{
if (DWM_IsSuspended && suspend)
{
Logger.Warn("DWM Thread was already suspended"); return;
Logger.Debug("DWM Thread was already suspended"); return;
}
else if (!DWM_IsSuspended && !suspend)
{
Logger.Warn("DWM Thread was already running"); return;
Logger.Debug("DWM Thread was already running"); return;
}

IntPtr adress = GetTargetFunctionAddress("dwmcorei.dll", 0x54F70);
if (adress == IntPtr.Zero)
DWMThreadAdress ??= GetTargetFunctionAddress("dwmcorei.dll", 0x54F70);
if (DWMThreadAdress is null)
{
Logger.Error("Failed to resolve thread start adress."); return;
}

ChangeState(suspend, adress, ref DWM_IsSuspended, "DWM");
ChangeState(suspend, (IntPtr)DWMThreadAdress, ref DWM_IsSuspended, ref DWMThreadId, "DWM");
}

public static void ChangeState_XAML(bool suspend)
{
if (XAML_IsSuspended && suspend)
{
Logger.Warn("XAML Thread was already suspended"); return;
Logger.Debug("XAML Thread was already suspended"); return;
}
else if (!XAML_IsSuspended && !suspend)
{
Logger.Warn("XAML Thread was already running"); return;
Logger.Debug("XAML Thread was already running"); return;
}

// The reported offset on ProcessExplorer seems to be missing 0x6280 somehow
// 0x54F70 + 0x6280 = 0x5B1F0
IntPtr adress = GetTargetFunctionAddress("Microsoft.UI.Xaml.dll", 0x5B1F0);
if (adress == IntPtr.Zero)
XAMLThreadAdress ??= GetTargetFunctionAddress("Microsoft.UI.Xaml.dll", 0x5B1F0);
if (XAMLThreadAdress is null)
{
Logger.Error("Failed to resolve thread start adress."); return;
}

ChangeState(suspend, adress, ref XAML_IsSuspended, "XAML");
ChangeState(suspend, (IntPtr)XAMLThreadAdress, ref XAML_IsSuspended, ref XAMLThreadId, "XAML");
}

private static void ChangeState(bool suspend, IntPtr threadAdress, ref bool IsSuspended, string loggerName)
private static IntPtr GetThreadStartAdress(int threadId)
{
foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
IntPtr hThread = OpenThread(ThreadAccess.QUERY_INFORMATION | ThreadAccess.SUSPEND_RESUME, false,
(uint)thread.Id);
if (hThread == IntPtr.Zero)
continue;

IntPtr adress = 0x00;
int status = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, ref adress, Marshal.SizeOf(typeof(IntPtr)), out _);
IntPtr hThread = OpenThread(ThreadAccess.QUERY_INFORMATION | ThreadAccess.SUSPEND_RESUME, false, (uint)threadId);
if (hThread == IntPtr.Zero) return IntPtr.Zero;

IntPtr adress = 0x00;
int status = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, ref adress, Marshal.SizeOf(typeof(IntPtr)), out _);
if(status != 0) Logger.Warn($"NtQueryInformationThread returned non-zero status code 0x{(uint)status:X}");
CloseHandle(hThread);
return adress;
}

if (status == 0 && adress == threadAdress)
private static void ChangeState(bool suspend, IntPtr threadAdress, ref bool IsSuspended, ref int? threadId,
string loggerName, bool canRetry = true)
{
if (threadId is null)
{
foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
if (suspend)
{
uint res = SuspendThread(hThread);
if (res == 0)
{
IsSuspended = true;
Logger.Warn($"{loggerName} Thread was suspended successfully");
CloseHandle(hThread);
return;
}
else
{
Logger.Error($"Could not suspend {loggerName} Thread with NTSTATUS = 0x{res:X}");
}
}
else
if (GetThreadStartAdress(thread.Id) == threadAdress)
{
int res = (int)ResumeThread(hThread);
if (res >= 0)
{
IsSuspended = false;
Logger.Warn($"{loggerName} Thread was resumed successfully");
CloseHandle(hThread);
return;
}
else
{
Logger.Error($"Could not resume {loggerName} Thread with NTSTATUS = 0x{res:X}");
}
threadId = thread.Id;
Logger.Info($"Thread with Id {threadId} was assigned as {loggerName} thread");
}
}
}

CloseHandle(hThread);
if (threadId is null)
{
Logger.Error($"No thread matching {loggerName} with start adress {threadAdress:X} was found");
return;
}
Logger.Error($"No thread matching {loggerName} was found");

IntPtr hThread = OpenThread(ThreadAccess.QUERY_INFORMATION | ThreadAccess.SUSPEND_RESUME, false, (uint)threadId);
if (hThread == IntPtr.Zero)
{ // When the thread cannot be opened
if (canRetry)
{
threadId = null; // On first try, reset argument threadId so it does get loaded again.
ChangeState(suspend, threadAdress, ref IsSuspended, ref threadId, loggerName, false);
return;
}
// The threadId was already reloaded
Logger.Warn($"Thread with id={threadId} and assigned as {loggerName} exists but could not be opened!");
return;
}

Check warning on line 129 in src/UniGetUI.Core.Tools/DWMThreadHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Avoid multiple blank lines (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide2000)

Check warning on line 129 in src/UniGetUI.Core.Tools/DWMThreadHelper.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Avoid multiple blank lines (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide2000)

if (suspend)
{
uint res = SuspendThread(hThread);
if (res == 0)
{
IsSuspended = true;
Logger.Info($"{loggerName} Thread was suspended successfully");
CloseHandle(hThread);
return;
}
else
{
Logger.Warn($"Could not suspend {loggerName} Thread with NTSTATUS = 0x{res:X}");
}
}
else
{
int res = (int)ResumeThread(hThread);
if (res >= 0)
{
IsSuspended = false;
Logger.Info($"{loggerName} Thread was resumed successfully");
CloseHandle(hThread);
return;
}
else
{
Logger.Error($"Could not resume {loggerName} Thread with NTSTATUS = 0x{res:X}");
}
}

CloseHandle(hThread);
}

private static IntPtr GetTargetFunctionAddress(string moduleName, int offset)
private static IntPtr? GetTargetFunctionAddress(string moduleName, int offset)
{
foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
{
Expand All @@ -135,6 +171,6 @@ private static IntPtr GetTargetFunctionAddress(string moduleName, int offset)
return module.BaseAddress + offset;
}
}
return IntPtr.Zero;
return null;
}
}

0 comments on commit 5686e7c

Please sign in to comment.