Initial commit

This commit is contained in:
EynaExp
2026-07-11 22:35:37 +03:30
commit 59bbe353f6
4 changed files with 1480 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
# Build artifacts
*.obj
*.o
*.exe
*.dll
*.sys
*.pdb
*.ilk
*.exp
*.lib
*.ipdb
*.iobj
# Visual Studio
.vs/
*.suo
*.user
*.userosscache
*.sln.docstates
*.ncb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Build directories
Debug/
Release/
x64/
x86/
build/
out/
[Bb]in/
[Oo]bj/
# NuGet
packages/
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
# Editor
*.swp
*.swo
*~
.vscode/
.idea/
# OS
.DS_Store
# Temp
~/
+105
View File
@@ -0,0 +1,105 @@
# EDR - Endpoint Detection & Response
A Windows kernel-mode EDR agent that monitors system activity and detects malicious behavior in real-time.
## Architecture
The project consists of two components:
### Kernel Driver (`driver.cpp`)
- Registers process and thread creation callbacks via `PsSetCreateProcessNotifyRoutineEx` and `PsSetCreateThreadNotifyRoutine`
- Implements LSASS protection using `ObRegisterCallbacks` to strip `PROCESS_VM_READ` and `PROCESS_QUERY_INFORMATION` access from LSASS handles
- Manages an event queue with IRP-based communication to the user-mode agent
- Supports configurable block rules for process creation
### User-Mode Agent (`Agent.cpp`)
- Communicates with the kernel driver via IOCTLs
- Receives and displays real-time process/thread events
- Implements detection rules for:
- Credential dumping tools (mimikatz, sekurlsa)
- LSASS dump attempts (procdump, Task Manager, comsvcs.dll, rundll32, sqldumper, dumpert, nanodump)
- Suspicious command-line activity
- Can terminate malicious processes via the driver
## Detection Rules
| Rule | Description |
|------|-------------|
| Credential Dumping | Detects mimikatz, sekurlsa, kerberos::list |
| LSASS via procdump | Detects procdump targeting lsass |
| LSASS via Task Manager | Detects Task Manager dump attempts |
| LSASS via comsvcs.dll | Detects comsvcs.dll MiniDump abuse |
| LSASS via rundll32 | Detects rundll32 comsvcs MiniDump |
| LSASS via sqldumper | Detects sqldumper targeting lsass |
| LSASS via dumpert | Detects dumpert.exe |
| LSASS via nanodump | Detects nanodump |
| LSASS via MiniDumpWriteDump | Detects dbghelp/dbgcore abuse |
## Block Rules
The kernel driver supports runtime block rules that match on:
- **Image suffix** - process name match (e.g., `cmd.exe`)
- **Command-line substring** - command-line content match (e.g., `whoami`)
Default block rules:
- `cmd.exe` + `whoami`
- `cmd.exe` + `net user`
- `cmd.exe` + `net group`
- `powershell.exe` + `mimikatz`
- `powershell.exe` + `sekurlsa`
## Build Requirements
- Windows 10/11
- Visual Studio with C++ Desktop workload
- Windows Driver Kit (WDK)
- Test signing enabled (`bcdedit /set testsigning on`)
## Building
1. Open the solution in Visual Studio
2. Build the driver project (x64 Release)
3. Build the agent project (x64 Release)
## Usage
1. Load the kernel driver:
```
sc create EDRTEST type= kernel binPath= C:\path\to\driver.sys
sc start EDRTEST
```
2. Run the agent:
```
Agent.exe
```
3. The agent will display real-time events and take action on detected threats.
4. Stop and remove the driver:
```
sc stop EDRTEST
sc delete EDRTEST
```
## Project Structure
```
EDR/
├── src/
│ ├── Agent.cpp # User-mode agent
│ └── driver.cpp # Kernel-mode driver
├── docs/ # Documentation
├── .gitignore
└── README.md
```
## IOCTL Codes
| Code | Value | Description |
|------|-------|-------------|
| IOCTL_WAIT_FOR_EVENT | 0x800 | Wait for next event from driver |
| IOCTL_KILL_PROCESS | 0x801 | Terminate a process by PID |
| IOCTL_ADD_BLOCK_RULE | 0x802 | Add a block rule |
| IOCTL_CLEAR_BLOCK_RULES | 0x803 | Clear all block rules |
| IOCTL_SIGNAL_LSASS_DUMP | 0x804 | Signal LSASS dump detection |
+562
View File
@@ -0,0 +1,562 @@
#include <windows.h>
#include <stdio.h>
#include <signal.h>
// =======================================================
// IOCTLs (must match driver)
// =======================================================
#define IOCTL_WAIT_FOR_EVENT \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KILL_PROCESS \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_ADD_BLOCK_RULE \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CLEAR_BLOCK_RULES \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SIGNAL_LSASS_DUMP \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define EVENT_TYPE_PROCESS_CREATE 1
#define EVENT_TYPE_PROCESS_EXIT 2
#define EVENT_TYPE_THREAD_CREATE 3
#define EVENT_TYPE_THREAD_EXIT 4
#define EVENT_TYPE_LSASS_ACCESS 5
#pragma pack(push, 1)
typedef struct _EDR_EVENT {
ULONG EventType;
LARGE_INTEGER Timestamp;
ULONG64 ProcessId;
ULONG64 ThreadId;
ULONG64 ParentProcessId;
BOOLEAN Blocked;
WCHAR ImageFileName[260];
WCHAR CommandLine[512];
} EDR_EVENT;
typedef struct _EDR_COMMAND {
ULONG Action;
ULONG64 ProcessId;
} EDR_COMMAND;
typedef struct _BLOCK_RULE_ENTRY {
WCHAR ImageSuffix[260];
WCHAR CmdLineSubstr[512];
} BLOCK_RULE_ENTRY;
#pragma pack(pop)
// =======================================================
// Console colors
// =======================================================
#define COLOR_RESET 7
#define COLOR_RED 12
#define COLOR_GREEN 10
#define COLOR_YELLOW 14
#define COLOR_CYAN 11
#define COLOR_MAGENTA 13
void SetConsoleColor(WORD color) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
// =======================================================
// Global shutdown flag
// =======================================================
volatile BOOL g_Running = TRUE;
void SignalHandler(int signal) {
if (signal == SIGINT) {
printf("\n");
SetConsoleColor(COLOR_YELLOW);
printf("[!] Ctrl+C detected. Shutting down...\n");
SetConsoleColor(COLOR_RESET);
g_Running = FALSE;
}
}
// =======================================================
// Timestamp formatting
// =======================================================
void FormatTimestamp(LARGE_INTEGER timestamp, char* buffer, size_t bufSize) {
FILETIME ft;
SYSTEMTIME st;
ft.dwLowDateTime = timestamp.LowPart;
ft.dwHighDateTime = timestamp.HighPart;
FileTimeToSystemTime(&ft, &st);
sprintf_s(buffer, bufSize, "%02d:%02d:%02d.%03d",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
}
// =======================================================
// Event type to string
// =======================================================
const char* EventTypeToString(ULONG eventType) {
switch (eventType) {
case EVENT_TYPE_PROCESS_CREATE: return "PROCESS_CREATE";
case EVENT_TYPE_PROCESS_EXIT: return "PROCESS_EXIT";
case EVENT_TYPE_THREAD_CREATE: return "THREAD_CREATE";
case EVENT_TYPE_THREAD_EXIT: return "THREAD_EXIT";
case EVENT_TYPE_LSASS_ACCESS: return "LSASS_ACCESS";
default: return "UNKNOWN";
}
}
// =======================================================
// Kill process via driver
// =======================================================
BOOL KillProcess(HANDLE hDevice, ULONG64 processId) {
EDR_COMMAND cmd = { 0 };
cmd.Action = 1;
cmd.ProcessId = processId;
DWORD ret = 0;
BOOL ok = DeviceIoControl(
hDevice,
IOCTL_KILL_PROCESS,
&cmd, sizeof(cmd),
NULL, 0,
&ret, NULL);
if (!ok) {
SetConsoleColor(COLOR_RED);
printf("[-] Failed to kill process %llu (err=%lu)\n",
processId, GetLastError());
SetConsoleColor(COLOR_RESET);
return FALSE;
}
SetConsoleColor(COLOR_GREEN);
printf("[+] Kill command sent for PID %llu\n", processId);
SetConsoleColor(COLOR_RESET);
return TRUE;
}
// =======================================================
// Signal LSASS dump attempt to kernel
// =======================================================
BOOL SignalLsassDump(HANDLE hDevice, ULONG64 processId) {
EDR_COMMAND cmd = { 0 };
cmd.Action = 1;
cmd.ProcessId = processId;
DWORD ret = 0;
BOOL ok = DeviceIoControl(
hDevice,
IOCTL_SIGNAL_LSASS_DUMP,
&cmd, sizeof(cmd),
NULL, 0,
&ret, NULL);
if (!ok) {
SetConsoleColor(COLOR_RED);
printf("[-] Failed to signal LSASS dump for PID %llu (err=%lu)\n",
processId, GetLastError());
SetConsoleColor(COLOR_RESET);
return FALSE;
}
SetConsoleColor(COLOR_MAGENTA);
printf("[+] LSASS dump signal sent to kernel for PID %llu\n", processId);
SetConsoleColor(COLOR_RESET);
return TRUE;
}
// =======================================================
// Block rule helpers
// =======================================================
BOOL AddBlockRule(HANDLE hDevice, PCWSTR suffix, PCWSTR cmdSubstr) {
BLOCK_RULE_ENTRY rule = { 0 };
if (suffix) wcscpy_s(rule.ImageSuffix, _countof(rule.ImageSuffix), suffix);
if (cmdSubstr) wcscpy_s(rule.CmdLineSubstr, _countof(rule.CmdLineSubstr), cmdSubstr);
DWORD ret = 0;
BOOL ok = DeviceIoControl(
hDevice,
IOCTL_ADD_BLOCK_RULE,
&rule, sizeof(rule),
NULL, 0,
&ret, NULL);
if (!ok) {
SetConsoleColor(COLOR_RED);
printf("[-] Failed to add block rule (err=%lu)\n", GetLastError());
SetConsoleColor(COLOR_RESET);
return FALSE;
}
SetConsoleColor(COLOR_GREEN);
wprintf(L"[+] Block rule added: \"%s\" + \"%s\"\n",
rule.ImageSuffix, rule.CmdLineSubstr);
SetConsoleColor(COLOR_RESET);
return TRUE;
}
BOOL ClearBlockRules(HANDLE hDevice) {
DWORD ret = 0;
BOOL ok = DeviceIoControl(
hDevice,
IOCTL_CLEAR_BLOCK_RULES,
NULL, 0,
NULL, 0,
&ret, NULL);
return ok;
}
// =======================================================
// Initialize kernel block rules
// =======================================================
void InitializeBlockRules(HANDLE hDevice) {
ClearBlockRules(hDevice);
SetConsoleColor(COLOR_CYAN);
printf("[*] Initializing block rules...\n");
SetConsoleColor(COLOR_RESET);
AddBlockRule(hDevice, L"cmd.exe", L"whoami");
AddBlockRule(hDevice, L"cmd.exe", L"net user");
AddBlockRule(hDevice, L"cmd.exe", L"net group");
AddBlockRule(hDevice, L"powershell.exe", L"mimikatz");
AddBlockRule(hDevice, L"powershell.exe", L"sekurlsa");
}
// =======================================================
// Display event with formatting
// =======================================================
void DisplayEvent(EDR_EVENT* ev) {
char timeBuf[32];
FormatTimestamp(ev->Timestamp, timeBuf, sizeof(timeBuf));
WORD eventColor = COLOR_RESET;
const char* icon = "";
switch (ev->EventType) {
case EVENT_TYPE_PROCESS_CREATE:
eventColor = ev->Blocked ? COLOR_RED : COLOR_GREEN;
icon = ev->Blocked ? "[BLOCKED]" : "[CREATE]";
break;
case EVENT_TYPE_PROCESS_EXIT:
eventColor = COLOR_YELLOW;
icon = "[EXIT]";
break;
case EVENT_TYPE_THREAD_CREATE:
eventColor = COLOR_CYAN;
icon = "[THREAD+]";
break;
case EVENT_TYPE_THREAD_EXIT:
eventColor = COLOR_CYAN;
icon = "[THREAD-]";
break;
case EVENT_TYPE_LSASS_ACCESS:
eventColor = COLOR_MAGENTA;
icon = "[LSASS!]";
break;
default:
eventColor = COLOR_RESET;
icon = "[EVENT]";
break;
}
SetConsoleColor(eventColor);
printf("%s %s | PID: %6llu | Type: %s",
icon, timeBuf, ev->ProcessId, EventTypeToString(ev->EventType));
if (ev->EventType == EVENT_TYPE_LSASS_ACCESS) {
printf(" | LSASS access stripped");
}
if (wcslen(ev->ImageFileName) > 0) {
wprintf(L" | Image: %s", ev->ImageFileName);
}
if (ev->EventType == EVENT_TYPE_PROCESS_CREATE && wcslen(ev->CommandLine) > 0) {
wprintf(L"\n CmdLine: %s", ev->CommandLine);
}
if (ev->Blocked) {
SetConsoleColor(COLOR_RED);
printf(" [BLOCKED]");
}
printf("\n");
SetConsoleColor(COLOR_RESET);
}
// =======================================================
// LSASS dumping detection (user-mode rules)
// =======================================================
BOOL AnalyzeEvent(HANDLE hDevice, EDR_EVENT* ev) {
DisplayEvent(ev);
if (ev->EventType == EVENT_TYPE_PROCESS_CREATE) {
// ---- Rule 1: Kill notepad.exe ----
size_t len = wcslen(ev->ImageFileName);
if (len >= 11) {
WCHAR* end = ev->ImageFileName + len - 11;
if (_wcsicmp(end, L"notepad.exe") == 0) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] notepad.exe detected\n");
printf("[ACTION] Terminating PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return KillProcess(hDevice, ev->ProcessId);
}
}
// ---- Rule 2: Credential dumping tools ----
if (wcsstr(ev->CommandLine, L"mimikatz") ||
wcsstr(ev->CommandLine, L"sekurlsa") ||
wcsstr(ev->CommandLine, L"kerberos::list")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] Credential dumping attempt (mimikatz/sekurlsa)\n");
printf("[ACTION] Terminating PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return KillProcess(hDevice, ev->ProcessId);
}
// ---- Rule 3: LSASS dumping - procdump ----
if (wcsstr(ev->ImageFileName, L"procdump") ||
wcsstr(ev->ImageFileName, L"procdump64") ||
wcsstr(ev->ImageFileName, L"procdump.exe")) {
if (wcsstr(ev->CommandLine, L"lsass") ||
wcsstr(ev->CommandLine, L"-ma") ||
wcsstr(ev->CommandLine, L"-mm")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via procdump\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
}
// ---- Rule 4: LSASS dumping - Task Manager dump ----
if (wcsstr(ev->ImageFileName, L"taskmgr.exe") ||
wcsstr(ev->ImageFileName, L"Taskmgr.exe")) {
if (wcsstr(ev->CommandLine, L"lsass") ||
wcsstr(ev->CommandLine, L"minidump")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via Task Manager\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
}
// ---- Rule 5: LSASS dumping - comsvcs.dll ----
if (wcsstr(ev->CommandLine, L"comsvcs.dll") ||
wcsstr(ev->CommandLine, L"MiniDump") ||
wcsstr(ev->CommandLine, L"#24")) {
if (wcsstr(ev->CommandLine, L"lsass")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via comsvcs.dll\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
}
// ---- Rule 6: LSASS dumping - rundll32 comsvcs ----
if (wcsstr(ev->ImageFileName, L"rundll32.exe") &&
wcsstr(ev->CommandLine, L"comsvcs") &&
wcsstr(ev->CommandLine, L"MiniDump")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via rundll32 comsvcs\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
// ---- Rule 7: LSASS dumping - sqldumper ----
if (wcsstr(ev->ImageFileName, L"sqldumper.exe") &&
wcsstr(ev->CommandLine, L"lsass")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via sqldumper\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
// ---- Rule 8: LSASS dumping - dumpert ----
if (wcsstr(ev->ImageFileName, L"dumpert.exe") ||
wcsstr(ev->ImageFileName, L"Dumpert.exe") ||
wcsstr(ev->CommandLine, L"Outflank-Dumpert")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via dumpert\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
// ---- Rule 9: LSASS dumping - nanodump ----
if (wcsstr(ev->ImageFileName, L"nanodump") ||
wcsstr(ev->CommandLine, L"nanodump")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via nanodump\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
// ---- Rule 10: LSASS dumping - direct syscalls ----
if (wcsstr(ev->CommandLine, L"MiniDumpWriteDump") ||
wcsstr(ev->CommandLine, L"dbghelp") ||
wcsstr(ev->CommandLine, L"dbgcore")) {
if (wcsstr(ev->CommandLine, L"lsass") ||
wcsstr(ev->CommandLine, L"LSASS")) {
SetConsoleColor(COLOR_RED);
printf("[DETECTION] LSASS dump attempt via MiniDumpWriteDump\n");
printf("[ACTION] Signaling kernel to block and terminate PID %llu\n", ev->ProcessId);
SetConsoleColor(COLOR_RESET);
return SignalLsassDump(hDevice, ev->ProcessId);
}
}
}
return TRUE;
}
// =======================================================
// Main
// =======================================================
int main()
{
signal(SIGINT, SignalHandler);
SetConsoleColor(COLOR_CYAN);
printf("========================================\n");
printf(" EDR Agent - Endpoint Detection & Response\n");
printf("========================================\n\n");
SetConsoleColor(COLOR_RESET);
HANDLE hDevice = CreateFileW(
L"\\\\.\\EDRTEST",
GENERIC_READ | GENERIC_WRITE,
0, NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
SetConsoleColor(COLOR_RED);
printf("[-] Failed to open device: %lu\n", GetLastError());
printf("[-] Make sure driver is loaded\n");
SetConsoleColor(COLOR_RESET);
return 1;
}
SetConsoleColor(COLOR_GREEN);
printf("[+] Device opened successfully\n\n");
SetConsoleColor(COLOR_RESET);
InitializeBlockRules(hDevice);
printf("\n");
SetConsoleColor(COLOR_CYAN);
printf("[*] Listening for events (Ctrl+C to stop)\n");
printf("========================================\n\n");
SetConsoleColor(COLOR_RESET);
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hEvent) {
SetConsoleColor(COLOR_RED);
printf("[-] Failed to create event: %lu\n", GetLastError());
SetConsoleColor(COLOR_RESET);
CloseHandle(hDevice);
return 1;
}
while (g_Running)
{
EDR_EVENT ev = { 0 };
DWORD bytesReturned = 0;
OVERLAPPED ov;
ZeroMemory(&ov, sizeof(ov));
ov.hEvent = hEvent;
BOOL ok = DeviceIoControl(
hDevice,
IOCTL_WAIT_FOR_EVENT,
NULL, 0,
&ev, sizeof(ev),
&bytesReturned,
&ov);
if (!ok)
{
DWORD err = GetLastError();
if (err != ERROR_IO_PENDING)
{
SetConsoleColor(COLOR_RED);
printf("[-] DeviceIoControl failed: %lu\n", err);
SetConsoleColor(COLOR_RESET);
if (err == ERROR_BUSY) {
Sleep(10);
continue;
}
break;
}
if (!GetOverlappedResult(hDevice, &ov, &bytesReturned, TRUE))
{
SetConsoleColor(COLOR_RED);
printf("[-] GetOverlappedResult failed: %lu\n", GetLastError());
SetConsoleColor(COLOR_RESET);
break;
}
}
AnalyzeEvent(hDevice, &ev);
}
printf("\n");
SetConsoleColor(COLOR_YELLOW);
printf("[+] Cleaning up...\n");
SetConsoleColor(COLOR_RESET);
CancelIoEx(hDevice, NULL);
CloseHandle(hEvent);
CloseHandle(hDevice);
SetConsoleColor(COLOR_GREEN);
printf("[+] Shutdown complete\n");
SetConsoleColor(COLOR_RESET);
return 0;
}
+756
View File
@@ -0,0 +1,756 @@
#include <ntifs.h>
#include <ntddk.h>
#define IOCTL_WAIT_FOR_EVENT \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KILL_PROCESS \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_ADD_BLOCK_RULE \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CLEAR_BLOCK_RULES \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SIGNAL_LSASS_DUMP \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define EVENT_TYPE_PROCESS_CREATE 1
#define EVENT_TYPE_PROCESS_EXIT 2
#define EVENT_TYPE_THREAD_CREATE 3
#define EVENT_TYPE_THREAD_EXIT 4
#define EVENT_TYPE_LSASS_ACCESS 5
#define BLOCK_RULE_MAX_ENTRIES 64
#ifndef PROCESS_TERMINATE
#define PROCESS_TERMINATE (0x0001)
#endif
#ifndef PROCESS_QUERY_INFORMATION
#define PROCESS_QUERY_INFORMATION (0x0400)
#endif
#ifndef PROCESS_VM_READ
#define PROCESS_VM_READ (0x0010)
#endif
#ifndef STATUS_KILLED
#define STATUS_KILLED ((NTSTATUS)0x00000128L)
#endif
#pragma pack(push, 1)
typedef struct _EDR_EVENT {
ULONG EventType;
LARGE_INTEGER Timestamp;
ULONG64 ProcessId;
ULONG64 ThreadId;
ULONG64 ParentProcessId;
BOOLEAN Blocked;
WCHAR ImageFileName[260];
WCHAR CommandLine[512];
} EDR_EVENT, * PEDR_EVENT;
typedef struct _EDR_COMMAND {
ULONG Action;
ULONG64 ProcessId;
} EDR_COMMAND, * PEDR_COMMAND;
typedef struct _BLOCK_RULE_ENTRY {
WCHAR ImageSuffix[260];
WCHAR CmdLineSubstr[512];
} BLOCK_RULE_ENTRY, * PBLOCK_RULE_ENTRY;
#pragma pack(pop)
// -------------------------------------------------------
// Internal queue entry
// -------------------------------------------------------
typedef struct _EVENT_ENTRY {
LIST_ENTRY ListEntry;
EDR_EVENT Event;
} EVENT_ENTRY, * PEVENT_ENTRY;
// -------------------------------------------------------
// Driver state
// -------------------------------------------------------
typedef struct _DRIVER_STATE {
LIST_ENTRY EventQueue;
KSPIN_LOCK QueueLock;
PIRP PendingIrp;
KSPIN_LOCK IrpLock;
} DRIVER_STATE, * PDRIVER_STATE;
typedef struct _BLOCK_RULE_TABLE {
BLOCK_RULE_ENTRY Rules[BLOCK_RULE_MAX_ENTRIES];
ULONG Count;
KSPIN_LOCK Lock;
} BLOCK_RULE_TABLE;
static DRIVER_STATE g_State;
static BLOCK_RULE_TABLE g_BlockRules = { 0 };
PDEVICE_OBJECT g_DeviceObject = NULL;
PVOID g_ObRegistrationHandle = NULL;
// -------------------------------------------------------
// Forward declarations
// -------------------------------------------------------
DRIVER_UNLOAD DriverUnload;
_Dispatch_type_(IRP_MJ_CREATE)
_Dispatch_type_(IRP_MJ_CLOSE)
DRIVER_DISPATCH DispatchCreateClose;
_Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
DRIVER_DISPATCH DispatchIoControl;
OB_PREOP_CALLBACK_STATUS PreProcessHandleCallback(
PVOID RegistrationContext,
POB_PRE_OPERATION_INFORMATION OperationInformation
);
static VOID SafeCopyUnicode(
_Out_writes_z_(dstMax) PWCHAR dst,
_In_ SIZE_T dstMax,
_In_ PCUNICODE_STRING src
);
// -------------------------------------------------------
// EnqueueEvent
// -------------------------------------------------------
static VOID EnqueueEvent(_In_ PEDR_EVENT EventData)
{
KLOCK_QUEUE_HANDLE irpHandle;
KLOCK_QUEUE_HANDLE qHandle;
PIRP pendingIrp = NULL;
KeAcquireInStackQueuedSpinLock(&g_State.IrpLock, &irpHandle);
if (g_State.PendingIrp != NULL) {
PIRP irp = g_State.PendingIrp;
if (IoSetCancelRoutine(irp, NULL) != NULL) {
pendingIrp = irp;
g_State.PendingIrp = NULL;
}
}
KeReleaseInStackQueuedSpinLock(&irpHandle);
if (pendingIrp != NULL) {
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pendingIrp);
ULONG outLen = stack->Parameters.DeviceIoControl.OutputBufferLength;
if (outLen >= sizeof(EDR_EVENT)) {
RtlCopyMemory(
pendingIrp->AssociatedIrp.SystemBuffer,
EventData,
sizeof(EDR_EVENT));
pendingIrp->IoStatus.Information = sizeof(EDR_EVENT);
pendingIrp->IoStatus.Status = STATUS_SUCCESS;
}
else {
pendingIrp->IoStatus.Information = 0;
pendingIrp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
}
IoCompleteRequest(pendingIrp, IO_NO_INCREMENT);
return;
}
PEVENT_ENTRY entry = (PEVENT_ENTRY)ExAllocatePool2(
POOL_FLAG_NON_PAGED, sizeof(EVENT_ENTRY), 'rvdE');
if (entry == NULL) {
DbgPrint("[EDR] EnqueueEvent: allocation failed, dropping event\n");
return;
}
RtlCopyMemory(&entry->Event, EventData, sizeof(EDR_EVENT));
KeAcquireInStackQueuedSpinLock(&g_State.QueueLock, &qHandle);
InsertTailList(&g_State.EventQueue, &entry->ListEntry);
KeReleaseInStackQueuedSpinLock(&qHandle);
}
// -------------------------------------------------------
// IRP cancel routine
// -------------------------------------------------------
static VOID CancelPendingIrp(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
KLOCK_QUEUE_HANDLE irpHandle;
KeAcquireInStackQueuedSpinLockAtDpcLevel(&g_State.IrpLock, &irpHandle);
if (g_State.PendingIrp == Irp) {
g_State.PendingIrp = NULL;
}
KeReleaseInStackQueuedSpinLockFromDpcLevel(&irpHandle);
IoReleaseCancelSpinLock(Irp->CancelIrql);
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
// -------------------------------------------------------
// LSASS Protection Callback (ObRegisterCallbacks)
// -------------------------------------------------------
OB_PREOP_CALLBACK_STATUS PreProcessHandleCallback(
PVOID RegistrationContext,
POB_PRE_OPERATION_INFORMATION OperationInformation
)
{
UNREFERENCED_PARAMETER(RegistrationContext);
if (OperationInformation->ObjectType != *PsProcessType) {
return OB_PREOP_SUCCESS;
}
PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object;
if (!TargetProcess) return OB_PREOP_SUCCESS;
PUNICODE_STRING ProcessImageName = NULL;
NTSTATUS status = SeLocateProcessImageName(TargetProcess, &ProcessImageName);
if (NT_SUCCESS(status) && ProcessImageName && ProcessImageName->Buffer) {
if (wcsstr(ProcessImageName->Buffer, L"lsass.exe") != NULL) {
ACCESS_MASK OriginalAccess = OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess;
ACCESS_MASK NewAccess = OriginalAccess & ~(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION);
if (OriginalAccess != NewAccess) {
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = NewAccess;
DbgPrint("[EDR-LSASS] Stripped VM_READ|QUERY from lsass.exe handle. Original=0x%X New=0x%X\n",
OriginalAccess, NewAccess);
EDR_EVENT ev = {};
KeQuerySystemTime(&ev.Timestamp);
ev.EventType = EVENT_TYPE_LSASS_ACCESS;
ev.ProcessId = (ULONG64)PsGetCurrentProcessId();
ev.Blocked = TRUE;
SafeCopyUnicode(ev.ImageFileName, 260, ProcessImageName);
EnqueueEvent(&ev);
}
}
ExFreePool(ProcessImageName);
}
return OB_PREOP_SUCCESS;
}
// -------------------------------------------------------
// DispatchIoControl
// -------------------------------------------------------
_Use_decl_annotations_
NTSTATUS DispatchIoControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
ULONG ioctl = stack->Parameters.DeviceIoControl.IoControlCode;
if (ioctl == IOCTL_KILL_PROCESS) {
ULONG inLen = stack->Parameters.DeviceIoControl.InputBufferLength;
if (inLen < sizeof(EDR_COMMAND)) {
status = STATUS_BUFFER_TOO_SMALL;
}
else {
PEDR_COMMAND cmd = (PEDR_COMMAND)Irp->AssociatedIrp.SystemBuffer;
if (cmd->Action == 1) {
PEPROCESS process = NULL;
status = PsLookupProcessByProcessId((HANDLE)cmd->ProcessId, &process);
if (NT_SUCCESS(status)) {
HANDLE hProcess = NULL;
status = ObOpenObjectByPointer(
process,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_TERMINATE,
*PsProcessType,
KernelMode,
&hProcess);
if (NT_SUCCESS(status)) {
status = ZwTerminateProcess(hProcess, STATUS_KILLED);
ZwClose(hProcess);
DbgPrint("[EDR] Killed process %llu (status: 0x%08X)\n",
cmd->ProcessId, status);
}
ObDereferenceObject(process);
}
else {
DbgPrint("[EDR] Failed to find process %llu (status: 0x%08X)\n",
cmd->ProcessId, status);
}
}
else {
status = STATUS_INVALID_PARAMETER;
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
if (ioctl == IOCTL_ADD_BLOCK_RULE) {
ULONG inLen = stack->Parameters.DeviceIoControl.InputBufferLength;
if (inLen < sizeof(BLOCK_RULE_ENTRY)) {
status = STATUS_BUFFER_TOO_SMALL;
}
else {
PBLOCK_RULE_ENTRY rule = (PBLOCK_RULE_ENTRY)Irp->AssociatedIrp.SystemBuffer;
KLOCK_QUEUE_HANDLE ruleHandle;
KeAcquireInStackQueuedSpinLock(&g_BlockRules.Lock, &ruleHandle);
if (g_BlockRules.Count < BLOCK_RULE_MAX_ENTRIES) {
RtlCopyMemory(&g_BlockRules.Rules[g_BlockRules.Count],
rule, sizeof(BLOCK_RULE_ENTRY));
g_BlockRules.Count++;
status = STATUS_SUCCESS;
DbgPrint("[EDR-BLOCK] Rule added (total: %lu)\n", g_BlockRules.Count);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
KeReleaseInStackQueuedSpinLock(&ruleHandle);
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
if (ioctl == IOCTL_CLEAR_BLOCK_RULES) {
KLOCK_QUEUE_HANDLE ruleHandle;
KeAcquireInStackQueuedSpinLock(&g_BlockRules.Lock, &ruleHandle);
RtlZeroMemory(g_BlockRules.Rules,
sizeof(BLOCK_RULE_ENTRY) * g_BlockRules.Count);
g_BlockRules.Count = 0;
KeReleaseInStackQueuedSpinLock(&ruleHandle);
status = STATUS_SUCCESS;
DbgPrint("[EDR-BLOCK] All rules cleared\n");
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
if (ioctl == IOCTL_SIGNAL_LSASS_DUMP) {
ULONG inLen = stack->Parameters.DeviceIoControl.InputBufferLength;
if (inLen < sizeof(EDR_COMMAND)) {
status = STATUS_BUFFER_TOO_SMALL;
}
else {
PEDR_COMMAND cmd = (PEDR_COMMAND)Irp->AssociatedIrp.SystemBuffer;
DbgPrint("[EDR-LSASS] Signal received from usermode - LSASS dump attempt by PID %llu\n",
cmd->ProcessId);
if (cmd->Action == 1) {
PEPROCESS process = NULL;
NTSTATUS lookupStatus = PsLookupProcessByProcessId((HANDLE)cmd->ProcessId, &process);
if (NT_SUCCESS(lookupStatus)) {
HANDLE hProcess = NULL;
lookupStatus = ObOpenObjectByPointer(
process,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_TERMINATE,
*PsProcessType,
KernelMode,
&hProcess);
if (NT_SUCCESS(lookupStatus)) {
ZwTerminateProcess(hProcess, STATUS_KILLED);
ZwClose(hProcess);
DbgPrint("[EDR-LSASS] Terminated LSASS dumper PID %llu\n", cmd->ProcessId);
}
ObDereferenceObject(process);
}
}
EDR_EVENT ev = {};
KeQuerySystemTime(&ev.Timestamp);
ev.EventType = EVENT_TYPE_LSASS_ACCESS;
ev.ProcessId = cmd->ProcessId;
ev.Blocked = TRUE;
wcscpy_s(ev.ImageFileName, 260, L"LSASS_DUMP_BLOCKED");
EnqueueEvent(&ev);
status = STATUS_SUCCESS;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
if (ioctl != IOCTL_WAIT_FOR_EVENT) {
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
ULONG outLen = stack->Parameters.DeviceIoControl.OutputBufferLength;
if (outLen < sizeof(EDR_EVENT)) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
}
KLOCK_QUEUE_HANDLE qHandle;
KeAcquireInStackQueuedSpinLock(&g_State.QueueLock, &qHandle);
if (!IsListEmpty(&g_State.EventQueue)) {
PLIST_ENTRY entry_link = RemoveHeadList(&g_State.EventQueue);
PEVENT_ENTRY entry = CONTAINING_RECORD(entry_link, EVENT_ENTRY, ListEntry);
KeReleaseInStackQueuedSpinLock(&qHandle);
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &entry->Event, sizeof(EDR_EVENT));
ExFreePool(entry);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(EDR_EVENT);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
KeReleaseInStackQueuedSpinLock(&qHandle);
KLOCK_QUEUE_HANDLE irpHandle;
KeAcquireInStackQueuedSpinLock(&g_State.IrpLock, &irpHandle);
if (g_State.PendingIrp != NULL) {
KeReleaseInStackQueuedSpinLock(&irpHandle);
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_DEVICE_BUSY;
}
IoMarkIrpPending(Irp);
IoSetCancelRoutine(Irp, CancelPendingIrp);
if (Irp->Cancel) {
if (IoSetCancelRoutine(Irp, NULL) != NULL) {
KeReleaseInStackQueuedSpinLock(&irpHandle);
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_CANCELLED;
}
}
g_State.PendingIrp = Irp;
KeReleaseInStackQueuedSpinLock(&irpHandle);
return STATUS_PENDING;
}
// -------------------------------------------------------
// DispatchCreateClose
// -------------------------------------------------------
_Use_decl_annotations_
NTSTATUS DispatchCreateClose(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// -------------------------------------------------------
// Thread callback
// -------------------------------------------------------
VOID ThreadCallback(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create)
{
EDR_EVENT ev = {};
KeQuerySystemTime(&ev.Timestamp);
ev.EventType = Create ? EVENT_TYPE_THREAD_CREATE : EVENT_TYPE_THREAD_EXIT;
ev.ProcessId = (ULONG64)ProcessId;
ev.ThreadId = (ULONG64)ThreadId;
EnqueueEvent(&ev);
}
// -------------------------------------------------------
// Block rule matching helpers
// -------------------------------------------------------
static BOOLEAN WcsEndsWithInsensitive(
_In_ PWCHAR Str, _In_ ULONG StrLen, _In_ PCWSTR Suf)
{
SIZE_T sufLen = wcslen(Suf);
if (!sufLen || StrLen < sufLen) return FALSE;
SIZE_T start = StrLen - sufLen;
for (SIZE_T i = 0; i < sufLen; ++i)
if (RtlUpcaseUnicodeChar(Str[start + i]) !=
RtlUpcaseUnicodeChar(Suf[i])) return FALSE;
return TRUE;
}
static BOOLEAN WcsContainsInsensitive(
_In_ PWCHAR Str, _In_ ULONG StrLen, _In_ PCWSTR Sub)
{
SIZE_T subLen = wcslen(Sub);
if (!subLen || StrLen < subLen) return FALSE;
for (SIZE_T pos = 0; pos <= StrLen - subLen; ++pos) {
BOOLEAN match = TRUE;
for (SIZE_T i = 0; i < subLen; ++i)
if (RtlUpcaseUnicodeChar(Str[pos + i]) !=
RtlUpcaseUnicodeChar(Sub[i])) { match = FALSE; break; }
if (match) return TRUE;
}
return FALSE;
}
static BOOLEAN MatchBlockRule(
_In_ PBLOCK_RULE_ENTRY Rule,
_In_ PWCHAR ImageFileName,
_In_ ULONG ImageLen,
_In_ PWCHAR CmdLine,
_In_ ULONG CmdLineLen)
{
BOOLEAN imgOk = TRUE;
if (Rule->ImageSuffix[0] != L'\0')
imgOk = WcsEndsWithInsensitive(ImageFileName, ImageLen, Rule->ImageSuffix);
BOOLEAN cmdOk = TRUE;
if (Rule->CmdLineSubstr[0] != L'\0')
cmdOk = WcsContainsInsensitive(CmdLine, CmdLineLen, Rule->CmdLineSubstr);
return imgOk && cmdOk;
}
// -------------------------------------------------------
// Safe unicode copy
// -------------------------------------------------------
static VOID SafeCopyUnicode(
_Out_writes_z_(dstMax) PWCHAR dst,
_In_ SIZE_T dstMax,
_In_ PCUNICODE_STRING src)
{
if (!src || !src->Buffer || src->Length == 0) { dst[0] = L'\0'; return; }
SIZE_T chars = min(src->Length / sizeof(WCHAR), dstMax - 1);
RtlCopyMemory(dst, src->Buffer, chars * sizeof(WCHAR));
dst[chars] = L'\0';
}
// -------------------------------------------------------
// Process callback
// -------------------------------------------------------
VOID ProcessCallback(
PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
UNREFERENCED_PARAMETER(Process);
EDR_EVENT ev = {};
KeQuerySystemTime(&ev.Timestamp);
ev.ProcessId = (ULONG64)ProcessId;
if (CreateInfo != NULL) {
ev.EventType = EVENT_TYPE_PROCESS_CREATE;
ev.ParentProcessId = (ULONG64)CreateInfo->ParentProcessId;
if (CreateInfo->ImageFileName)
SafeCopyUnicode(ev.ImageFileName, 260, CreateInfo->ImageFileName);
if (CreateInfo->CommandLine)
SafeCopyUnicode(ev.CommandLine, 512, CreateInfo->CommandLine);
ULONG imgLen = (ULONG)wcslen(ev.ImageFileName);
ULONG cmdLen = (ULONG)wcslen(ev.CommandLine);
KLOCK_QUEUE_HANDLE ruleHandle;
KeAcquireInStackQueuedSpinLock(&g_BlockRules.Lock, &ruleHandle);
for (ULONG i = 0; i < g_BlockRules.Count; ++i) {
if (MatchBlockRule(&g_BlockRules.Rules[i],
ev.ImageFileName, imgLen, ev.CommandLine, cmdLen))
{
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
ev.Blocked = TRUE;
DbgPrint("[EDR-BLOCK] Blocked PID %llu per rule idx %lu\n",
ev.ProcessId, i);
break;
}
}
KeReleaseInStackQueuedSpinLock(&ruleHandle);
}
else {
ev.EventType = EVENT_TYPE_PROCESS_EXIT;
}
EnqueueEvent(&ev);
}
// -------------------------------------------------------
// DriverUnload
// -------------------------------------------------------
VOID DriverUnload(_In_ PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
if (g_ObRegistrationHandle) {
ObUnRegisterCallbacks(g_ObRegistrationHandle);
g_ObRegistrationHandle = NULL;
}
NTSTATUS status;
status = PsRemoveCreateThreadNotifyRoutine(ThreadCallback);
if (!NT_SUCCESS(status)) {
DbgPrint("[EDR] Failed to remove thread callback: %08X\n", status);
}
status = PsSetCreateProcessNotifyRoutineEx(ProcessCallback, TRUE);
if (!NT_SUCCESS(status)) {
DbgPrint("[EDR] Failed to remove process callback: %08X\n", status);
}
KLOCK_QUEUE_HANDLE qHandle;
KeAcquireInStackQueuedSpinLock(&g_State.QueueLock, &qHandle);
while (!IsListEmpty(&g_State.EventQueue)) {
PLIST_ENTRY link = RemoveHeadList(&g_State.EventQueue);
PEVENT_ENTRY entry = CONTAINING_RECORD(link, EVENT_ENTRY, ListEntry);
ExFreePool(entry);
}
KeReleaseInStackQueuedSpinLock(&qHandle);
KLOCK_QUEUE_HANDLE irpHandle;
KeAcquireInStackQueuedSpinLock(&g_State.IrpLock, &irpHandle);
PIRP irp = g_State.PendingIrp;
g_State.PendingIrp = NULL;
KeReleaseInStackQueuedSpinLock(&irpHandle);
if (irp != NULL) {
if (IoSetCancelRoutine(irp, NULL) != NULL) {
irp->IoStatus.Status = STATUS_CANCELLED;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
}
UNICODE_STRING symlink = RTL_CONSTANT_STRING(L"\\??\\EDRTEST");
IoDeleteSymbolicLink(&symlink);
if (g_DeviceObject) IoDeleteDevice(g_DeviceObject);
DbgPrint("[EDR] Driver unloaded\n");
}
// -------------------------------------------------------
// DriverEntry
// -------------------------------------------------------
extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
UNICODE_STRING devname = RTL_CONSTANT_STRING(L"\\Device\\EDRTEST");
UNICODE_STRING symlink = RTL_CONSTANT_STRING(L"\\??\\EDRTEST");
NTSTATUS status = IoCreateDevice(
DriverObject, 0, &devname,
FILE_DEVICE_UNKNOWN, 0, FALSE, &g_DeviceObject);
if (!NT_SUCCESS(status)) return status;
g_DeviceObject->Flags |= DO_BUFFERED_IO;
status = IoCreateSymbolicLink(&symlink, &devname);
if (!NT_SUCCESS(status)) {
IoDeleteDevice(g_DeviceObject);
return status;
}
InitializeListHead(&g_State.EventQueue);
KeInitializeSpinLock(&g_State.QueueLock);
KeInitializeSpinLock(&g_State.IrpLock);
KeInitializeSpinLock(&g_BlockRules.Lock);
g_State.PendingIrp = NULL;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoControl;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
OB_CALLBACK_REGISTRATION CallbackRegistration = { 0 };
OB_OPERATION_REGISTRATION OperationRegistration = { 0 };
OperationRegistration.ObjectType = PsProcessType;
OperationRegistration.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
OperationRegistration.PreOperation = PreProcessHandleCallback;
OperationRegistration.PostOperation = NULL;
CallbackRegistration.Version = OB_FLT_REGISTRATION_VERSION;
CallbackRegistration.OperationRegistrationCount = 1;
CallbackRegistration.RegistrationContext = NULL;
CallbackRegistration.OperationRegistration = &OperationRegistration;
DECLARE_CONST_UNICODE_STRING(Altitude, L"370000");
CallbackRegistration.Altitude = Altitude;
status = ObRegisterCallbacks(&CallbackRegistration, &g_ObRegistrationHandle);
if (!NT_SUCCESS(status)) {
DbgPrint("[EDR] ObRegisterCallbacks failed: 0x%08X\n", status);
g_ObRegistrationHandle = NULL;
}
else {
DbgPrint("[EDR] LSASS protection callback registered\n");
}
status = PsSetCreateProcessNotifyRoutineEx(ProcessCallback, FALSE);
if (!NT_SUCCESS(status)) {
if (g_ObRegistrationHandle) ObUnRegisterCallbacks(g_ObRegistrationHandle);
IoDeleteSymbolicLink(&symlink);
IoDeleteDevice(g_DeviceObject);
return status;
}
status = PsSetCreateThreadNotifyRoutine(ThreadCallback);
if (!NT_SUCCESS(status)) {
PsSetCreateProcessNotifyRoutineEx(ProcessCallback, TRUE);
if (g_ObRegistrationHandle) ObUnRegisterCallbacks(g_ObRegistrationHandle);
IoDeleteSymbolicLink(&symlink);
IoDeleteDevice(g_DeviceObject);
return status;
}
DbgPrint("[EDR] Driver loaded successfully with LSASS protection\n");
return STATUS_SUCCESS;
}