
AppLocker-Based EDR Neutralization
After my article on Fairy-Law, where I used kernel mitigations to disable Endpoint Detection & Response (EDR) solutions, diversenok pointed out that IFEO exclusions (Image File Execution Options) were too invasive for third-party applications. This led to a better approach: leveraging the inherent power administrators already possess through AppLocker.
The concept was inspired by diversenok, who highlighted that administrators can legitimately control any software on their systems. From that insight, I developed a technique using AppLocker as a native Windows control mechanism. This research explores the technical implementation of AppLocker for EDR control, comparing it with WDAC and presenting a practical proof-of-concept tool.
AppLocker was introduced with Windows 7 and enhanced in Windows 8.1, 10 (Enterprise) and Windows Server 2012/R2/2016+. It is an application whitelisting framework that allows administrators to define precisely which executables, scripts, or installers may execute for specific users or groups.
AppIDSvc (Application Identity Service)
LocalService accountAppID.sys (Kernel Driver)
SeSrpAccessCheckClarification:
WhileAppID.sysperforms rule evaluation in kernel mode, DLL enforcement is not autonomous.
The kernel driver does not actively monitor DLL loads by itself. Instead, user-mode components must explicitly query the driver via IOCTL to determine whether a DLL load is permitted.
As a result, AppLocker DLL rules effectively act as a client-side protection mechanism.
AppLocker supports two primary rule categories:
Allow Rules: Explicitly permit defined applications to execute
Deny Rules: Explicitly block defined applications from executing
C:\Program Files\Security\*.exeHKLM\Software\Policies\Microsoft\Windows\SrpV2 (XML policy storage, persistent)
HKLM\SYSTEM\CurrentControlSet\Control\Srp\Gp\Exe (SDDL binary format, active enforcement)
HKLM\SYSTEM\CurrentControlSet\Control\AppID\CertStore (Certificate cache)
By default, AppLocker does not enforce rules on services or SYSTEM processes.
There is no graphical user interface option to enable this behavior.
Enforcement for services can only be enabled via the XML policy using RuleCollectionExtensions.
The following policy section is required to enforce AppLocker rules on services:
<RuleCollectionExtensions>
<ThresholdExtensions>
<Services EnforcementMode="Enabled"/>
</ThresholdExtensions>
<RedstoneExtensions>
<SystemApps Allow="Enabled"/>
</RedstoneExtensions>
</RuleCollectionExtensions>
As indicated by the extension names, these options are supported only on Windows 10+ and are not available on earlier versions. See Microsoft - AppLocker rule collection extensions
AppID.sys evaluates application attributesSTATUS_ACCESS_DISABLED_BY_POLICY_OTHER⚠️ AppLocker does NOT terminate running processes.
AppLocker enforcement only applies to new process creation events. Already-running EDR processes continue executing until system reboot. This is a fundamental architectural constraint.
Kernel Driver Telemetry Caveat:
Even after blocking EDR userland executables, kernel drivers (*.sys) remain active and operational. These drivers continue:
However, extensive testing reveals that this telemetry becomes functionally ineffective. Without userland analysis engines, correlation systems, and reporting mechanisms, the raw telemetry data cannot be processed into actionable detections. EDR solutions rely heavily on userland components for:
GhostLocker is a C++ implementation that automates AppLocker policy deployment to block EDR executables.
GhostLocker provides two implementation variants:
main.cpp – Dynamic Enumeration VersionThis version enumerates running processes and resolves their full image paths using native APIs (NtQuerySystemInformation).
The resolved absolute paths are then used to generate precise AppLocker deny rules.
The tool uses CreateToolhelp32Snapshot with TH32CS_SNAPPROCESS to enumerate all running processes. It compares process names against a predefined target list using case-insensitive matching (_wcsicmp).
Why this approach?
FindTargetsAndQueryPaths)const wchar_t* targetNames[] = {
L"MpDefenderCoreService.exe",
L"MsMpEng.exe",
L"WinDefend.exe",
L"EDR_Component_Name.exe",
};
SYSTEM_PROCESS_ID_INFORMATION spi = { 0 };
spi.ProcessId = PID;
spi.ImageName.MaximumLength = 1024;
spi.ImageName.Buffer = (PWSTR)allocBuffer;
status = NtQuerySystemInformation(
SystemProcessIdInformation,
&spi,
sizeof(spi),
0
);
Technical Details:
SystemProcessIdInformation (0x58) information class\Device\HarddiskVolume3\Windows\System32\...Path Conversion Logic:
std::wstring ForceHarddiskVolumeToC(const std::wstring& ntPath)
{
const std::wstring prefix = L"\\Device\\HarddiskVolume3\\";
if (ntPath.rfind(prefix, 0) == 0)
{
std::wstring rest = ntPath.substr(prefix.length());
return L"C:\\" + rest;
}
return ntPath;
}
Limitation: Hardcoded HarddiskVolume3 assumption. Should be improved to dynamically resolve volume numbers.
The tool embeds a complete PowerShell script that:
a) Validates Target Paths
foreach ($exe in $ExeToBlock) {
if (!(Test-Path $exe)) {
Write-Host '[!] ERROR: File does not exist:' $exe -ForegroundColor Red
exit 1
}
}