This repository was archived by the owner on Sep 7, 2026. It is now read-only.
Repository navigation
Expand file tree
/
Copy pathPreventions.cpp
More file actions
327 lines (275 loc) · 11.7 KB
/
Copy pathPreventions.cpp
File metadata and controls
327 lines (275 loc) · 11.7 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
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
//By AlSch092 @github
#include "Preventions.hpp"
/*
RandomizeModuleName - Changes the program's main module name (in memory) to a random string
returns true on success
*/
bool Preventions::RandomizeModuleName()
{
bool success = false;
int moduleNameSize = (int)wcslen(_MAIN_MODULE_NAME_W);
if (moduleNameSize == 0) //this will only hit if _MAIN_MODULE_NAME_W definition is set to an empty string
{
Logger::logf(Err, "string length of definition _MAIN_MODULE_NAME_W was 0 @ Preventions::RandomizeModuleName");
return false;
}
wstring newModuleName = Utility::GenerateRandomWString(moduleNameSize); //intentionally set to -2 to trip up external programs like CE from enumerating dlls & symbols
if (Process::ChangeModuleName(_MAIN_MODULE_NAME_W, newModuleName)) //in addition to changing export function names, we can also modify the names of loaded modules/libraries.
{
success = true;
Process::SetExecutableModuleName(newModuleName);
ProcessData::MODULE_DATA* mod = Process::GetModuleInfo(newModuleName.c_str());
if (mod != nullptr)
{
this->integrityChecker->AddToWhitelist(*mod);
delete mod;
}
Logger::logfw(Info, L"Changed module name to: %s\n", newModuleName.c_str());
}
return success;
}
/*
DeployBarrier - Launches various attack prevention techniques
returns Error::OK on success
*/
Error Preventions::DeployBarrier()
{
Error retError = Error::OK;
if (!Process::ChangeNumberOfSections(_MAIN_MODULE_NAME, 1)) //change # of sections to 1
{
Logger::logf(Err, "Failed to change number of sections @ Preventions::DeployBarrier");
retError = Error::CANT_APPLY_TECHNIQUE;
}
#ifndef _DEBUG
if (!RemapProgramSections()) //anti-memory write on .text through sections remapping, thanks to changeofpace
{
Logger::logf(Err, " Couldn't remap memory @ DeployBarrier!\n");
retError = Error::CANT_STARTUP;
}
#endif
IsPreventingThreadCreation = true; //used in TLS callback to prevent thread creation (can stop shellcode + module injection)
if (RandomizeModuleName()) //randomize our main module name at runtime
{
Logger::logf(Info, " Randomized our executable's module's name!");
}
else
{
Logger::logf(Err, " Couldn't change our module's name @ Preventions::DeployBarrier");
retError = Error::CANT_APPLY_TECHNIQUE;
}
if (!StopAPCInjection()) //patch over ntdll.dll Ordinal8 unnamed function
{
Logger::logf(Err, "Couldn't apply anti-APC technique @ Preventions::DeployBarrier");
retError = Error::CANT_APPLY_TECHNIQUE;
}
#if _WIN32_WINNT >= 0x0602 //minimum windows 8 for SetMitigation routines
//third parameter (dynamic code) set to true -> prevents VirtualProtect from succeeding on .text sections of loaded/signed modules. while this can be very useful, it breaks our TLS callback protections since we patch over the first byte of new thread's execution addresses
Preventions::EnableProcessMitigations(true, true, false, true, true);
#endif
return retError;
}
/*
RemapProgramSections - remaps the current module's sections to prevent memory writing/patching
Thanks to ChangeOfPace @ Github for the RmpRemapImage routines
*/
bool Preventions::RemapProgramSections()
{
ULONG_PTR ImageBase = (ULONG_PTR)GetModuleHandle(NULL);
bool remap_succeeded = false;
if (ImageBase)
{
__try
{
if (!RmpRemapImage(ImageBase)) //re-mapping of image to stop patching, and of course we can easily detect if someone bypasses this
{
Logger::logf(Err, " RmpRemapImage failed @ RemapProgramSections");
}
else
{
Logger::logf(Info, " Successfully remapped @ RemapProgramSections");
remap_succeeded = true;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Logger::logf(Err, " Remapping image failed @ RemapProgramSections");
return false;
}
}
else
{
Logger::logf(Err, " Imagebase was NULL @ RemapAndCheckPages!");
return false;
}
return remap_succeeded;
}
/*
StopMultipleProcessInstances - Uses shared memory to prevent multiple instances of the process.
returns true on success
... Using a mutex instead may result in attackers closing the mutex handle with popular tools such as Process Hacker
*/
bool Preventions::StopMultipleProcessInstances()
{
HANDLE hSharedMemory = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(int), "UAC");
if (hSharedMemory == NULL)
{
Logger::logf(Err, "Failed to create shared memory. Error code: %lu\n", GetLastError());
return false;
}
int* pIsRunning = (int*)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(int));
if (pIsRunning == NULL)
{
Logger::logf(Err, "Failed to map view of file. Error code : % lu\n", GetLastError());
CloseHandle(hSharedMemory);
return false;
}
if (*pIsRunning == 1337) //duplicate instance found, these instructions can be obfuscated if desired
{
UnmapViewOfFile(pIsRunning);
CloseHandle(hSharedMemory);
return false;
}
*pIsRunning = 1337;
return true;
}
/*
StopAPCInjection - prevents APC injection by patching over the first byte of ntdll.Ordinal8. More information about the APC payload can be fetched through hooking
returns false on failure, true on successful patch
WARNING: if your program/game relies on usermode APC for functionality then this technique may not be suitable for you
*/
bool Preventions::StopAPCInjection()
{
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
if (!ntdll)
{
Logger::logf(Err, "Failed to fetch ntdll module @ StopAPCInjection. Error code : % lu\n", GetLastError());
return false;
}
const int Ordinal = 8;
UINT64 Oridinal8 = (UINT64)GetProcAddress(ntdll, MAKEINTRESOURCEA(Ordinal)); //TODO: make sure Ordinal8 exists on other versions of windows and is the same function
if (!Oridinal8)
{
Logger::logf(Err, "Failed to fetch ntdll.Ordinal8 address @ StopAPCInjection");
return false;
}
__try
{
DWORD dwOldProt = 0;
if (!VirtualProtect((LPVOID)Oridinal8, sizeof(byte), PAGE_EXECUTE_READWRITE, &dwOldProt))
{
Logger::logf(Warning, "Failed to call VirtualProtect on Oridinal8 address @ StopAPCInjection: %llX", Oridinal8);
return false;
}
else
{
if (Oridinal8 != 0)
*(BYTE*)Oridinal8 = 0xC3;
VirtualProtect((LPVOID)Oridinal8, sizeof(byte), dwOldProt, &dwOldProt);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Logger::logf(Err, "Failed to patch over Ordinal8 address @ StopAPCInjection");
return false;
}
return true;
}
#if _WIN32_WINNT >= 0x0602 //SetProcessMitigationPolicy starts support in Windows 8
/*
EnableProcessMitigations - enforces policies which are actioned by the system & loader to prevent dynamic code generation & execution (unsigned code will be rejected by the loader)
*/
void Preventions::EnableProcessMitigations(__in const bool useDEP, __in const bool useASLR, __in const bool useDynamicCode, __in const bool useStrictHandles, __in const bool useSystemCallDisable)
{
if (useDEP)
{
PROCESS_MITIGATION_DEP_POLICY depPolicy = { 0 }; // DEP Policy
depPolicy.Enable = 1;
depPolicy.Permanent = 1;
if (!SetProcessMitigationPolicy(ProcessDEPPolicy, &depPolicy, sizeof(depPolicy)))
{
Logger::logf(Warning, "Failed to set DEP policy @ EnableProcessMitigations: %d", GetLastError());
}
}
if (useASLR)
{
PROCESS_MITIGATION_ASLR_POLICY aslrPolicy = { 0 }; //ASLR Policy
aslrPolicy.EnableBottomUpRandomization = 1;
aslrPolicy.EnableForceRelocateImages = 1;
aslrPolicy.EnableHighEntropy = 1;
aslrPolicy.DisallowStrippedImages = 1;
if (!SetProcessMitigationPolicy(ProcessASLRPolicy, &aslrPolicy, sizeof(aslrPolicy)))
{
Logger::logf(Warning, "Failed to set ASLR policy @ EnableProcessMitigations: %d", GetLastError());
}
}
if (useDynamicCode)
{
PROCESS_MITIGATION_DYNAMIC_CODE_POLICY dynamicCodePolicy = { 0 }; //Dynamic Code Policy -> can prevent VirtualProtect calls on .text sections of loaded modules from working
dynamicCodePolicy.ProhibitDynamicCode = 1;
if (!SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &dynamicCodePolicy, sizeof(dynamicCodePolicy)))
{
Logger::logf(Warning, "Failed to set dynamic code policy @ EnableProcessMitigations: %d", GetLastError());
}
}
if (useStrictHandles)
{
PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY handlePolicy = { 0 }; // Strict Handle Check Policy
handlePolicy.RaiseExceptionOnInvalidHandleReference = 1;
handlePolicy.HandleExceptionsPermanentlyEnabled = 1;
if (!SetProcessMitigationPolicy(ProcessStrictHandleCheckPolicy, &handlePolicy, sizeof(handlePolicy)))
{
Logger::logf(Warning, "Failed to set strict handle check policy @ EnableProcessMitigations: %d", GetLastError());
}
}
if (useSystemCallDisable)
{
PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY syscallPolicy = { 0 }; // System Call Disable Policy
syscallPolicy.DisallowWin32kSystemCalls = 1;
if (!SetProcessMitigationPolicy(ProcessSystemCallDisablePolicy, &syscallPolicy, sizeof(syscallPolicy)))
{
Logger::logf(Warning, "Failed to set system call disable policy @ EnableProcessMitigations: %d", GetLastError());
}
}
}
#endif
/*
Preventions::PreventDllInjection - changes the export name of specific K32 routines such that an external attacker trying to fetch the address of these is given bad values.
*Note* : Changing export names for certain important dll routines can result in popup errors for the end-user, thus its not recommended for a live product. Alternatively, routines can have their function preambles 'ret' patched for similar effects (if you know it wont impact program functionality).
*/
bool Preventions::PreventDllInjection()
{
bool success = false;
//Anti-dll injection (creating remote thread on LoadLibrary)
string RandString1 = Utility::GenerateRandomString(12);
string RandString2 = Utility::GenerateRandomString(12);
string RandString3 = Utility::GenerateRandomString(14);
string RandString4 = Utility::GenerateRandomString(14);
//prevents DLL injection from any host process relying on calling LoadLibrary in the target process (we are the target in this case) -> can possibly be disruptive to end user
if (Exports::ChangeFunctionName("KERNEL32.DLL", "LoadLibraryA", RandString1) &&
Exports::ChangeFunctionName("KERNEL32.DLL", "LoadLibraryW", RandString2) &&
Exports::ChangeFunctionName("KERNEL32.DLL", "LoadLibraryExA", RandString3) &&
Exports::ChangeFunctionName("KERNEL32.DLL", "LoadLibraryExW", RandString4))
{
return true;
}
else
{
return false;
}
}
/*
UnloadBlacklistedDrivers - attempts to unload/stop blacklisted drivers in `driverPaths`
*/
void Preventions::UnloadBlacklistedDrivers(__in const list<wstring> driverPaths)
{
if (driverPaths.size() > 0)
{
for (auto driverPath : driverPaths)
{
if (!Services::UnloadDriver(driverPath))
{
Logger::logfw(Warning, L"Failed to unload driver %s at UnloadBlacklistedDrivers", driverPath.c_str());
}
}
}
}