-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathwsWinEnvVar.pp
272 lines (237 loc) · 9.89 KB
/
wsWinEnvVar.pp
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
{ Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com)
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
}
{$MODE OBJFPC}
{$H+}
unit wsWinEnvVar;
interface
uses
Windows,
wsWinProcess;
const
ERROR_EXE_MACHINE_TYPE_MISMATCH = 216;
MAX_ENVVAR_NAME_LENGTH = 127;
MAX_ENVVAR_VALUE_LENGTH = 16383;
THREADPROC_ROUTINE_LENGTH = 64;
// Returns whether the environment variable exists in current process
function EnvVarExists(const Name: UnicodeString): Boolean;
// Gets the value of an environment variable as a string
function GetEnvVar(const Name: UnicodeString): UnicodeString;
// Sets (or removes) the value of an environment variable in a specified
// process (to remove a variable: Set Value parameter to empty string);
// current and other process must match "bitness" (i.e., the processes must
// both be 32-bit or they must both be 64-bit); returns zero for success, or
// non-zero for failure
function SetEnvVarInProcess(const ProcessID: DWORD; const Name, Value: UnicodeString): DWORD;
implementation
type
TSetEnvironmentVariable = function(lpName: LPCWSTR; lpValue: LPCWSTR): BOOL; stdcall;
TCreateRemoteThread = function(hProcess: HANDLE;
lpThreadAttributes: LPSECURITY_ATTRIBUTES;
dwStackSize: SIZE_T;
lpStartAddress: LPTHREAD_START_ROUTINE;
lpParameter: LPVOID; dwCreationFlags: DWORD;
lpThreadId: LPDWORD): HANDLE; stdcall;
TEnvVarNameBuffer = array[0..MAX_ENVVAR_NAME_LENGTH] of WideChar;
TEnvVarValueBuffer = array[0..MAX_ENVVAR_VALUE_LENGTH] of WideChar;
TSetEnvVarCodeBuffer = packed record
SetEnvironmentVariable: TSetEnvironmentVariable;
Name: TEnvVarNameBuffer;
Value: TEnvVarValueBuffer;
Routine: array[0..THREADPROC_ROUTINE_LENGTH - 1] of Byte;
end;
PSetEnvVarCodeBuffer = ^TSetEnvVarCodeBuffer;
{ TSetEnvVarCodeBuffer members:
* SetEnvironmentVariable: Address of SetEnvironmentVariableW function
* Name: Unicode string containing variable name
* Value: Unicode string containing variable value
* Routine: Copy of ThreadProc function
To set an environment variable in another process:
1. In code buffer:
a. Set SetEnvironmentVariable member to address of SetEnvironmentVariableW
b. Copy environment variable name to Name member
c. Copy environment variable value to Value member
d. Copy ThreadProc function to Routine member
2. Open the other process (OpenProcess)
3. Allocate memory in the other process (VirtualAllocEx)
4. Copy code buffer to the other process (WriteProcessMemory)
5. Execute the function in the other process (CreateRemoteThread)
For this to work, both processes must match "bitness" (both must be 32-bit or
both must be 64-bit).
}
function EnvVarExists(const Name: UnicodeString): Boolean;
begin
GetEnvironmentVariableW(PWideChar(Name), // LPCWSTR Name
nil, // LPWSTR lpBuffer
0); // DWORD nSize
result := GetLastError() <> ERROR_ENVVAR_NOT_FOUND;
end;
function GetEnvVar(const Name: UnicodeString): UnicodeString;
var
NumChars, BufSize: DWORD;
pBuffer: PWideChar;
begin
result := '';
NumChars := GetEnvironmentVariableW(PWideChar(Name), // LPCWSTR lpName
nil, // LPWSTR lpBuffer
0); // DWORD nSize
if NumChars > 0 then
begin
BufSize := NumChars * SizeOf(WideChar);
GetMem(pBuffer, BufSize);
if GetEnvironmentVariableW(PWideChar(Name), // LPCWSTR lpName
pBuffer, // LPWSTR lpBuffer
NumChars) > 0 then // DWORD nSize
result := pBuffer;
FreeMem(pBuffer, BufSize);
end;
end;
// Must match ThreadProc function signature
function SetEnvVarThreadProc(const pCodeBuffer: PSetEnvVarCodeBuffer): DWORD; stdcall;
begin
result := DWORD(pCodeBuffer^.SetEnvironmentVariable(pCodeBuffer^.Name, pCodeBuffer^.Value));
end;
// Must match ThreadProc function signature
function RemoveEnvVarThreadProc(const pCodeBuffer: PSetEnvVarCodeBuffer): DWORD; stdcall;
begin
result := DWORD(pCodeBuffer^.SetEnvironmentVariable(pCodeBuffer^.Name, nil));
end;
// Sets/removes an environment variable in another process
function SetEnvVarInProcess(const ProcessID: DWORD; const Name, Value: UnicodeString): DWORD;
var
CreateRemoteThread: TCreateRemoteThread;
CodeBuffer: TSetEnvVarCodeBuffer;
NameBuffer: TEnvVarNameBuffer;
ValueBuffer: TEnvVarValueBuffer;
pRoutine: Pointer;
ProcessAccess: DWORD;
hProcess: HANDLE;
pCodeBuffer: PSetEnvVarCodeBuffer;
OK: BOOL;
BytesWritten: SIZE_T;
hThread: HANDLE;
ThreadExitCode: DWORD;
begin
result := 0;
// Variable name cannot contain '=' character
if (Pos('=', Name) > 0) then
exit(ERROR_INVALID_PARAMETER);
// Range-check name and value
if (Length(Name) > MAX_ENVVAR_NAME_LENGTH) or (Length(Value) > MAX_ENVVAR_VALUE_LENGTH) then
exit(ERROR_INSUFFICIENT_BUFFER);
// Fail if "bitness" mismatch
if not CurrentProcessMatchesProcessBits(ProcessID) then
exit(ERROR_EXE_MACHINE_TYPE_MISMATCH);
// Get pointer to function
CreateRemoteThread := TCreateRemoteThread(GetProcAddress(GetModuleHandle('kernel32'),
'CreateRemoteThread'));
if not Assigned(CreateRemoteThread) then
exit(GetLastError());
// Initialize code buffer
FillChar(CodeBuffer, SizeOf(CodeBuffer), 0);
// Get address of SetEnvironmentVariableW
CodeBuffer.SetEnvironmentVariable := TSetEnvironmentVariable(GetProcAddress(GetModuleHandle('kernel32'),
'SetEnvironmentVariableW'));
if not Assigned(CodeBuffer.SetEnvironmentVariable) then
exit(GetLastError());
// Copy variable name to code buffer
NameBuffer := PWideChar(Name);
Move(NameBuffer, CodeBuffer.Name, SizeOf(NameBuffer));
// Set or clear the environment variable?
if Length(Value) > 0 then
begin
// Copy variable value to code buffer
ValueBuffer := PWideChar(Value);
Move(ValueBuffer, CodeBuffer.Value, SizeOf(ValueBuffer));
pRoutine := @SetEnvVarThreadProc;
end
else
pRoutine := @RemoveEnvVarThreadProc;
// Copy ThreadProc function to code buffer
Move(pRoutine^, CodeBuffer.Routine, SizeOf(CodeBuffer.Routine));
// Set desired access
ProcessAccess := PROCESS_CREATE_THREAD or
PROCESS_VM_OPERATION or
PROCESS_VM_READ or
PROCESS_VM_WRITE or
PROCESS_QUERY_INFORMATION or
SYNCHRONIZE;
// Open process
hProcess := OpenProcess(ProcessAccess, // DWORD dwDesiredAccess
true, // BOOL bInheritHandle
ProcessID); // DWORD dwProcessId
if hProcess = 0 then
exit(GetLastError()); // OpenProcess() failed
// Allocate memory in processs
pCodeBuffer := VirtualAllocEx(hProcess, // HANDLE hProcess
nil, // LPVOID lpAddress
SizeOf(CodeBuffer), // DWORD dwSize
MEM_COMMIT, // DWORD flAllocationType
PAGE_EXECUTE_READWRITE); // DWORD flProtect
if Assigned(pCodeBuffer) then
begin
// Copy code buffer to process
OK := WriteProcessMemory(hProcess, // HANDLE hProcess
pCodeBuffer, // LPVOID lpBaseAddress
@CodeBuffer, // LPCVOID lpBuffer
SizeOf(CodeBuffer), // SIZE_T nSize
BytesWritten); // SIZE_T lpNumberOfBytesWritten
if OK then
begin
// Execute function in process
hThread := CreateRemoteThread(hProcess, // HANDLE hProcess
nil, // LPSECURITY_ATTRIBUTES lpThreadAttributes
0, // SIZE_T dwStackSize
@pCodeBuffer^.Routine[0], // LPTHREAD_START_ROUTINE lpStartAddress
pCodeBuffer, // LPVOID lpParameter
0, // DWORD dwCreationFlags
nil); // LPDWORD lpThreadId
if hThread <> 0 then
begin
// Wait for thread to complete
if WaitForSingleObject(hThread, // HANDLE hHandle
INFINITE) <> WAIT_FAILED then // DWORD dwMilliseconds
begin
// Get exit code of thread
if GetExitCodeThread(hThread, // HANDLE hThread
ThreadExitCode) then // LPDWORD lpExitCode
begin
if ThreadExitCode <> 0 then
result := 0
else // Thread exit code <> 0
result := GetLastError();
end
else // GetExitCodeThread() failed
result := GetLastError();
end
else // WaitForSingleObject() failed
result := GetLastError();
CloseHandle(hThread);
end
else // CreateRemoteThread() failed
result := GetLastError();
end
else // WriteProcessMemory() failed
result := GetLastError();
// Free memory in process
if not VirtualFreeEx(hProcess, // HANDLE hProcess
pCodeBuffer, // LPVOID lpAddress
0, // SIZE_T dwSize
MEM_RELEASE) then // DWORD dwFreeType
result := GetLastError();
end
else // VirtualAllocEx() failed
result := GetLastError();
CloseHandle(hProcess);
end;
begin
end.