-
Notifications
You must be signed in to change notification settings - Fork 783
Expand file tree
/
Copy pathDWMThreadHelper.cs
More file actions
181 lines (155 loc) · 6.07 KB
/
DWMThreadHelper.cs
File metadata and controls
181 lines (155 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
using System.Diagnostics;
using System.Runtime.InteropServices;
using UniGetUI.Core.Logging;
using UniGetUI.Core.SettingsEngine;
namespace UniGetUI;
public class DWMThreadHelper
{
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationThread(IntPtr ThreadHandle, int ThreadInformationClass,
ref IntPtr ThreadInformation, int ThreadInformationLength, out int ReturnLength);
[DllImport("kernel32.dll")]
private static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
private static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
private static extern uint ResumeThread(IntPtr hThread);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);
[Flags]
private enum ThreadAccess : uint
{
QUERY_INFORMATION = 0x0040,
SUSPEND_RESUME = 0x0002
}
private const int ThreadQuerySetWin32StartAddress = 9;
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 (Settings.Get("DisableDMWThreadOptimizations")) return;
if (DWM_IsSuspended && suspend)
{
Logger.Debug("DWM Thread was already suspended"); return;
}
else if (!DWM_IsSuspended && !suspend)
{
Logger.Debug("DWM Thread was already running"); return;
}
DWMThreadAdress ??= GetTargetFunctionAddress("dwmcorei.dll", 0x54F70);
if (DWMThreadAdress is null)
{
Logger.Error("Failed to resolve thread start adress."); return;
}
ChangeState(suspend, (IntPtr)DWMThreadAdress, ref DWM_IsSuspended, ref DWMThreadId, "DWM");
}
public static void ChangeState_XAML(bool suspend)
{
if (Settings.Get("DisableDMWThreadOptimizations")) return;
if (XAML_IsSuspended && suspend)
{
Logger.Debug("XAML Thread was already suspended"); return;
}
else if (!XAML_IsSuspended && !suspend)
{
Logger.Debug("XAML Thread was already running"); return;
}
// The reported offset on ProcessExplorer seems to be missing 0x6280 somehow
// 0x54F70 + 0x6280 = 0x5B1F0
XAMLThreadAdress ??= GetTargetFunctionAddress("Microsoft.UI.Xaml.dll", 0x5B1F0);
if (XAMLThreadAdress is null)
{
Logger.Error("Failed to resolve thread start adress."); return;
}
ChangeState(suspend, (IntPtr)XAMLThreadAdress, ref XAML_IsSuspended, ref XAMLThreadId, "XAML");
}
private static IntPtr GetThreadStartAdress(int threadId)
{
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;
}
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 (GetThreadStartAdress(thread.Id) == threadAdress)
{
threadId = thread.Id;
Logger.Info($"Thread with Id {threadId} was assigned as {loggerName} thread");
}
}
}
if (threadId is null)
{
Logger.Error($"No thread matching {loggerName} with start adress {threadAdress:X} was found");
return;
}
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;
}
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)
{
foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
{
if (module.ModuleName.Equals(moduleName, StringComparison.OrdinalIgnoreCase))
{
return module.BaseAddress + offset;
}
}
return null;
}
}