프로젝트 개요
문서 중앙화 DLP 시스템의 Windows 커널 레벨 파일 필터 드라이버입니다. Windows Filter Manager 프레임워크(Minifilter)를 사용해 파일 I/O를 커널에서 가로채어 USB 쓰기 차단, 네트워크 드라이브 복사 차단, CDR(Content Disarm & Reconstruction) 정책을 적용합니다.
GitHub: https://github.com/LEntropy/Document_Management
전체 DLP 구조
[커널 레벨]
DlpMiniFilter.sys (Minifilter 드라이버)
├─ IRP_MJ_CREATE → 파일 열기 시 CDR 후보 마킹
├─ IRP_MJ_WRITE → USB/네트워크 쓰기 차단
├─ IRP_MJ_SET_INFORMATION → Rename/Move 차단
└─ Named Pipe 포트 → 유저레이어 에이전트와 통신
[유저 레벨]
DlpAgent.Service (C# Windows 서비스)
├─ NativeFltLib.cs → FilterConnectCommunicationPort, FilterGetMessage
├─ PolicyEngine.cs → DLP 정책 의사결정
└─ PolicyModel.cs → 정책 JSON 구조 (USB/CDR/프로세스 차단)
1. Minifilter 드라이버 등록 (DlpMiniFilter.c)
// Filter 등록 콜백 목록
const FLT_OPERATION_REGISTRATION Callbacks[] =
{
{
IRP_MJ_CREATE,
0,
DlpPreCreate, // 파일 열기 전 처리 (CDR 후보 마킹)
DlpPostCreate // 파일 열기 후 처리
},
{
IRP_MJ_WRITE,
0,
DlpPreWrite, // 쓰기 전: USB/네트워크 차단
NULL
},
{
IRP_MJ_SET_INFORMATION,
0,
DlpPreSetInfo, // Rename/Move 차단
NULL
},
{ IRP_MJ_OPERATION_END }
};
// DriverEntry: Filter Manager에 드라이버 등록
NTSTATUS DriverEntry(PDRIVER_OBJECT driverObject, PUNICODE_STRING registryPath)
{
FLT_REGISTRATION reg = { ... };
NTSTATUS st = FltRegisterFilter(driverObject, ®, &gFilter);
if (!NT_SUCCESS(st)) return st;
return FltStartFiltering(gFilter);
}
2. 파일 위치 분류 - 볼륨 타입 판별
파일이 로컬 디스크인지, USB(이동식 미디어)인지, 네트워크 드라이브인지를 커널에서 판별합니다.
typedef enum _DLP_LOCATION_TYPE {
DlpLocUnknown = 0,
DlpLocLocal, // 내부 하드디스크
DlpLocRemovable, // USB 등 이동식 미디어
DlpLocNetwork // SMB/NFS 네트워크 드라이브
} DLP_LOCATION_TYPE;
static DLP_LOCATION_TYPE DlpClassifyInstanceVolume(PFLT_INSTANCE Instance)
{
PFLT_VOLUME_PROPERTIES props = ...;
FltGetVolumeProperties(vol, props, size, &size);
if (props->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM ||
props->DeviceType == FILE_DEVICE_NETWORK)
return DlpLocNetwork; // 네트워크 드라이브
if (props->Characteristics & FILE_REMOVABLE_MEDIA)
return DlpLocRemovable; // USB/SD카드
return DlpLocLocal; // 내장 디스크
}
3. CDR 후보 파일 마킹 (PreCreate)
파일이 열릴 때(.docx, .xlsx, .pptx, .hwp 등) CDR 후보로 마킹합니다. Word 임시파일(~$, .tmp, .asd)은 제외합니다.
static BOOLEAN DlpIsCdrCandidatePath(PCWSTR Path)
{
if (DlpIsInAgentWorkingDir(Path)) return FALSE; // 에이전트 작업 폴더 제외
if (DlpIsOfficeTempLikePath(Path)) return FALSE; // Office 임시파일 제외
const WCHAR* exts[] = { L".docx", L".xlsx", L".pptx", L".txt",
L".hwp", L".hwpx", L".enc" };
for (UINT32 i = 0; i < RTL_NUMBER_OF(exts); i++)
if (DlpEndsWithExt(&name, exts[i])) return TRUE;
return FALSE;
}
// Office 임시파일 패턴 탐지
static BOOLEAN DlpIsOfficeTempLikePath(PCWSTR Path)
{
if (name[0] == L'~' && name[1] == L'$') return TRUE; // Word: ~$document.docx
if (name[0] == L'.' && name[1] == L'~') return TRUE; // LibreOffice lock 파일
if (DlpEndsWithExt(&u, L".tmp")) return TRUE;
if (DlpEndsWithExt(&u, L".asd")) return TRUE; // Word 자동 저장
if (DlpEndsWithExt(&u, L".wbk")) return TRUE; // Word 백업
return FALSE;
}
// StreamHandle Context에 CDR 후보 여부 저장
typedef struct _DLP_HANDLE_CONTEXT {
BOOLEAN IsProtected;
BOOLEAN IsCdrCandidate; // CDR 처리 대상 여부
BOOLEAN SnapshotValid;
LARGE_INTEGER InitialEof;
LARGE_INTEGER InitialLastWriteTime;
} DLP_HANDLE_CONTEXT;
4. USB 쓰기 차단 (PreWrite)
FLT_PREOP_CALLBACK_STATUS DlpPreWrite(
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID* CompletionContext)
{
DLP_LOCATION_TYPE loc = DlpClassifyInstanceVolume(FltObjects->Instance);
if (loc == DlpLocRemovable && gPolicy.BlockRemovableWrite)
{
// USB 쓰기 차단: STATUS_ACCESS_DENIED 반환
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
// 유저 에이전트에 이벤트 전송 (Named Pipe)
DlpSendEventToAgent(Data, DlpEventUsbWriteBlocked);
return FLT_PREOP_COMPLETE; // IRP 차단 (하위 드라이버로 전달 안함)
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
5. Named Pipe 통신 (드라이버 ↔ C# 서비스)
드라이버가 차단 이벤트나 CDR 요청을 유저레이어 서비스로 전달하고, 서비스의 응답에 따라 허용/차단을 결정합니다.
// 드라이버 쪽: 포트 생성
FltCreateCommunicationPort(
gFilter, &gServerPort,
&portName, // L"\\DlpPort"
NULL, NULL,
DlpConnectNotify, // 클라이언트(서비스) 연결 시
DlpDisconnectNotify,
DlpMessageNotify, // 메시지 수신 시
1 // 최대 연결 수
);
// C# 서비스 쪽: fltlib.dll P/Invoke
[DllImport("fltlib.dll", CharSet = CharSet.Unicode)]
public static extern int FilterConnectCommunicationPort(
string lpPortName, // "\\.\\DlpPort"
uint dwOptions,
IntPtr lpContext,
ushort wSizeOfContext,
IntPtr lpSecurityAttributes,
out SafeFileHandle hPort
);
// 드라이버 메시지 수신
[DllImport("fltlib.dll")]
public static extern int FilterGetMessage(
SafeFileHandle hPort,
IntPtr lpMessageBuffer,
uint dwMessageBufferSize,
IntPtr lpOverlapped
);
// 드라이버에 응답 (허용/차단)
[DllImport("fltlib.dll")]
public static extern int FilterReplyMessage(
SafeFileHandle hPort,
IntPtr lpReplyBuffer,
uint dwReplyBufferSize
);
6. DLP 정책 모델 - PolicyModel.cs
public sealed class DlpPolicy
{
public bool BlockNetworkRename { get; set; } = false; // 네트워크 드라이브 Rename 차단
public bool BlockRemovableWrite { get; set; } = false; // USB 쓰기 차단
public bool BlockDelete { get; set; } = false; // 파일 삭제 차단
public List<string> BlockProcesses { get; set; } // 차단할 프로세스 목록
public List<string> AllowProcesses { get; set; } // 허용할 프로세스 목록
public CdrPolicy CDR { get; set; } = new CdrPolicy();
}
public sealed class CdrPolicy
{
public bool Enabled { get; set; } = true;
public List<string> FileTypes { get; set; } =
new List<string> { "docx","xlsx","pptx","txt","hwp","hwpx" };
public bool BlockUntilSanitized { get; set; } = false; // 정화 전 열람 차단
public CdrRedirectPolicy Redirect { get; set; };
public CdrLocalPaths Local { get; set; };
}
public sealed class CdrLocalPaths
{
public string QuarantineDir { get; set; } = @"C:\ProgramData\DlpAgent\Quarantine";
public string SanitizedDir { get; set; } = @"C:\ProgramData\DlpAgent\Sanitized";
public string ReportsDir { get; set; } = @"C:\ProgramData\DlpAgent\CdrReports";
public int MaxFileMB { get; set; } = 50;
}
7. 드라이버 배포 방법
-- deploy/ 폴더 구조 --
payload/
driver/
DlpMiniFilter.sys ← 드라이버 파일
DlpMiniFilter.inf ← 설치 정보 파일
DlpMiniFilter.cat ← 코드 서명 카탈로그
service/
DlpAgent.Service.exe
DlpAgent.Common.dll
-- 설치 스크립트 (install_pnputil.bat) --
@echo off
pnputil /add-driver payload\driver\DlpMiniFilter.inf /install
-- 수동 설치 (install_manual_driver.bat) --
sc create DlpMiniFilter type= kernel start= demand binPath= "%~dp0payload\driver\DlpMiniFilter.sys"
sc start DlpMiniFilter
-- 제거 (uninstall_all.bat) --
sc stop DlpMiniFilter
sc delete DlpMiniFilter
8. NT 경로 ↔ DOS 경로 변환 (QueryDosDevice)
커널 드라이버는 NT 경로(\Device\HarddiskVolume3\...)를 사용하지만, 유저 레이어는 DOS 경로(C:\...)를 씁니다. 이 변환이 필요합니다.
// C# 서비스에서 NT 경로를 DOS 경로로 변환
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern uint QueryDosDevice(
string lpDeviceName,
StringBuilder lpTargetPath,
int ucchMax);
public static string QueryDosDevice(string deviceName)
{
string dn = deviceName.Trim().TrimEnd('\\');
if (dn.Length >= 2 && dn[1] == ':' && dn.Length > 2)
dn = dn.Substring(0, 2);
var sb = new StringBuilder(1024);
QueryDosDevice(dn, sb, sb.Capacity);
return sb.ToString();
// "C:" → "\Device\HarddiskVolume3"
}'勉強 > C,C++' 카테고리의 다른 글
| 문서 중앙화 프로젝트 - Linux inotify + OpenSSL AES-128 파일 자동 암호화 구현 (0) | 2026.06.02 |
|---|---|
| 연산자와 기초예제 (1) | 2024.01.09 |
| 사칙연산과 기초예제 (2) | 2024.01.05 |