-
Notifications
You must be signed in to change notification settings - Fork 35
/
NtUtils.Transactions.Remote.pas
192 lines (154 loc) · 5.26 KB
/
NtUtils.Transactions.Remote.pas
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
182
183
184
185
186
187
188
189
190
191
192
unit NtUtils.Transactions.Remote;
{
This module allows querying and setting current transaction for threads in
other processes.
}
interface
uses
Ntapi.ntpsapi, Ntapi.ntseapi, NtUtils;
const
PROCESS_GET_THREAD_TRANSACTION = PROCESS_VM_READ;
PROCESS_SET_THREAD_TRANSACTION = PROCESS_VM_WRITE
{$IFDEF Win64}or PROCESS_VM_READ{$ENDIF};
PROCESS_SET_PROCESS_TRANSACTION = PROCESS_QUERY_INFORMATION or
PROCESS_SUSPEND_RESUME or PROCESS_SET_THREAD_TRANSACTION;
THREAD_GET_TRANSACTION = THREAD_QUERY_LIMITED_INFORMATION;
THREAD_SET_TRANSACTION = THREAD_QUERY_LIMITED_INFORMATION;
// Get a handle value of the current transaction on a remote thread
function RtlxGetTransactionThread(
[Access(PROCESS_GET_THREAD_TRANSACTION)] const hxProcess: IHandle;
[Access(THREAD_GET_TRANSACTION)] const hxThread: IHandle;
out HandleValue: THandle
): TNtxStatus;
// Set a handle value of the current transaction on a remote thread
function RtlxSetTransactionThread(
[Access(PROCESS_SET_THREAD_TRANSACTION)] const hxProcess: IHandle;
[Access(THREAD_SET_TRANSACTION)] const hxThread: IHandle;
[opt] HandleValue: THandle
): TNtxStatus;
// Set a handle value as a current transaction on all threads in a process
[RequiredPrivilege(SE_DEBUG_PRIVILEGE, rpForBypassingChecks)]
function RtlxSetTransactionProcess(
[Access(PROCESS_SET_PROCESS_TRANSACTION)] const hxProcess: IHandle;
[opt] HandleValue: THandle
): TNtxStatus;
implementation
uses
Ntapi.ntwow64, Ntapi.ntstatus, NtUtils.Threads, NtUtils.Processes,
NtUtils.Memory, NtUtils.Processes.Info;
{$BOOLEVAL OFF}
{$IFOPT R+}{$DEFINE R+}{$ENDIF}
{$IFOPT Q+}{$DEFINE Q+}{$ENDIF}
function RtlxGetTransactionThread;
var
ThreadInfo: TThreadBasicInformation;
begin
{$IFDEF Win32}
// Although under WoW64 we can work with other WoW64 processes we won't
// since we still need to update 64-bit TEB, so it gets complicated.
if RtlxAssertNotWoW64(Result) then
Exit;
{$ENDIF}
// Query TEB location for the thread
Result := NtxThread.Query(hxThread, ThreadBasicInformation, ThreadInfo);
if not Result.IsSuccess then
Exit;
// Make sure the thread is alive
if not Assigned(ThreadInfo.TebBaseAddress) then
begin
Result.Location := 'RtlxGetTransactionThread';
Result.Status := STATUS_THREAD_IS_TERMINATING;
Exit;
end;
// Read the handle value from thread's TEB.
// In case of a WoW64 target it has two TEBs, and both of them should
// store the same handle value. However, 64-bit TEB has precedence, so
// the following code also works for WoW64 processes.
Result := NtxMemory.Read(
hxProcess,
@ThreadInfo.TebBaseAddress.CurrentTransactionHandle,
HandleValue
);
end;
function RtlxSetTransactionThread;
var
ThreadInfo: TThreadBasicInformation;
{$IFDEF Win64}
IsWow64Target: Boolean;
Teb32Offset: Integer;
Teb32: PTeb32;
HandleValue32: Cardinal;
{$ENDIF}
begin
{$IFDEF Win32}
// Although under WoW64 we can work with other WoW64 processes we won't
// since we still need to update 64-bit TEB, so it gets complicated.
if RtlxAssertNotWoW64(Result) then
Exit;
{$ENDIF}
if not Result.IsSuccess then
Exit;
// Query TEB location for the thread
Result := NtxThread.Query(hxThread, ThreadBasicInformation, ThreadInfo);
if not Result.IsSuccess then
Exit;
// Make sure the thread is alive
if not Assigned(ThreadInfo.TebBaseAddress) then
begin
Result.Location := 'RtlxGetTransactionThread';
Result.Status := STATUS_THREAD_IS_TERMINATING;
Exit;
end;
// Write the handle value to thread's TEB
Result := NtxMemory.Write(hxProcess,
@ThreadInfo.TebBaseAddress.CurrentTransactionHandle,
HandleValue
);
if not Result.IsSuccess then
Exit;
// Threads in WoW64 processes have two TEBs, so we should update both of them.
// However, this operation is optional since 64-bit TEB has precedence,
// therefore we ignore errors in the following code.
{$IFDEF Win64}
if NtxQueryIsWoW64Process(hxProcess, IsWow64Target).IsSuccess and
IsWow64Target then
begin
// 64-bit TEB stores an offset to a 32-bit TEB, read it
if not NtxMemory.Read(hxProcess, @ThreadInfo.TebBaseAddress.WowTebOffset,
Teb32Offset).IsSuccess then
Exit;
if Teb32Offset = 0 then
Exit;
HandleValue32 := Cardinal(HandleValue);
Teb32 := PTeb32(NativeInt(ThreadInfo.TebBaseAddress) + Teb32Offset);
// Write the handle to the 32-bit TEB
NtxMemory.Write(hxProcess, @Teb32.CurrentTransactionHandle, HandleValue32);
end;
{$ENDIF}
end;
function RtlxSetTransactionProcess;
var
hxThread: IHandle;
IsTerminated: LongBool;
DelayedResumer: IAutoReleasable;
begin
// Suspend the process to avoid race conditions
Result := NtxSuspendProcess(hxProcess);
if not Result.IsSuccess then
Exit;
// Resume automatically when we are done
DelayedResumer := NtxDelayedResumeProcess(hxProcess);
hxThread := nil;
while NtxGetNextThread(hxProcess, hxThread,
THREAD_QUERY_LIMITED_INFORMATION).HasEntry(Result) do
begin
// Skip terminated threads
Result := NtxThread.Query(hxThread, ThreadIsTerminated, IsTerminated);
// Update current transaction
if Result.IsSuccess and not IsTerminated then
Result := RtlxSetTransactionThread(hxProcess, hxThread, HandleValue);
if not Result.IsSuccess then
Exit;
end;
end;
end.