BSOne.SFC/Tocsg.Module/Bs1Flt/bs1flt/bs1flt_create.c

1337 lines
40 KiB
C

#include "precomp.h"
NTSTATUS
Bs1FltFileInUserMode(
__in PCFLT_RELATED_OBJECTS FltObjects,
__in PUNICODE_STRING FilePath,
__out PBOOLEAN SafeToOpen
)
{
NTSTATUS status = STATUS_SUCCESS;
//PVOID buffer = NULL;
//ULONG bytesRead;
PFILE_OBJECT_DESC notification = NULL;
//FLT_VOLUME_PROPERTIES volumeProps;
//LARGE_INTEGER offset;
ULONG replyLength;
//ULONG length;
//PFLT_VOLUME volume = NULL;
UNREFERENCED_PARAMETER(FltObjects);
*SafeToOpen = TRUE;
if (g_bs1Flt.ClientPort == NULL)
{
return STATUS_SUCCESS;
}
try {
notification = ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_OBJECT_DESC), 'pbsn');
if (notification == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
RtlZeroMemory(notification, sizeof(FILE_OBJECT_DESC));
notification->pid = HandleToULong(PsGetCurrentProcessId());
notification->size = min(FilePath->Length, PATH_SIZE - 2);
RtlCopyMemory(&notification->path, FilePath->Buffer, notification->size);
/// 유저 클라이언트로 정보 전달 & Wait
KLogEx(DEBUG_TRACE_INFO, "notify path (%d)(%S)\n", notification->pid, FilePath->Buffer);
replyLength = sizeof(BS1FLT_REPLY);
status = FltSendMessage(g_bs1Flt.Filter,
&g_bs1Flt.ClientPort,
notification,
sizeof(FILE_OBJECT_DESC),
notification,
&replyLength,
NULL);
if (status == STATUS_SUCCESS)
{
*SafeToOpen = ((PBS1FLT_REPLY)notification)->SafeToOpen;
KLogEx(DEBUG_TRACE_INFO, "FltSendMessage, Success %d size = %d\n", *SafeToOpen, sizeof(FILE_OBJECT_DESC));
}
else
{
KLogEx(DEBUG_TRACE_INFO, "couldn't send message to user-mode to, status 0x%X\n", status);
}
}
finally
{
if (NULL != notification)
{
ExFreePoolWithTag(notification, 'pbsn');
}
}
return status;
}
FLT_PREOP_CALLBACK_STATUS
Bs1FltNofity(PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PUNICODE_STRING pUniFilePath,
ULONG ulPidType
)
{
BOOLEAN safeToOpen = FALSE;
FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
ULONG create_option = 0;
ULONG disposition = 0;
DWORD desiredaccess = 0;
USHORT fileattribute = 0;
create_option = Data->Iopb->Parameters.Create.Options;
disposition = (create_option >> 24) & 0xFF;
desiredaccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
fileattribute = Data->Iopb->Parameters.Create.FileAttributes;
if (disposition != FILE_OPEN)
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (!(fileattribute & FILE_ATTRIBUTE_NORMAL))
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
/// 디렉토리면 예외
if (create_option & FILE_DIRECTORY_FILE)
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (!(create_option & FILE_NON_DIRECTORY_FILE))
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
//cretaeoption = 1000140, disposition = 1, desiredaccess = a1 예외 처리 정보
if (create_option & FILE_COMPLETE_IF_OPLOCKED)
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (ulPidType == PG_PID_WHITE)
{
Bs1FltFileInUserMode(FltObjects, pUniFilePath, &safeToOpen);
if (!safeToOpen)
{
KLogEx(DEBUG_TRACE_INFO, "foul language detected in postcreate !!!\n");
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
returnStatus = FLT_PREOP_COMPLETE;;
}
//SetFileObjectList(0, FltObjects->FileObject, pUniFilePath->Buffer, pUniFilePath->Length);
}
return returnStatus;
}
BOOLEAN
Bs1FltIsFileName(PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PUNICODE_STRING pUniFilePath,
ULONG ulPidType,
ULONG ulFileType
)
{
//WCHAR buf[260] = { 0, };
ULONG create_option = 0;
WCHAR* ptr = NULL;
UNICODE_STRING uniFileName = { 0, };
UCHAR major = Data->Iopb->MajorFunction;
UNREFERENCED_PARAMETER(ulPidType);
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Data);
if (major == IRP_MJ_CREATE)
{
create_option = Data->Iopb->Parameters.Create.Options;
//KLogEx("PBShareLockIsFileName, CreateOption = %x\n", create_option);
if (create_option & FILE_DIRECTORY_FILE)
{
return FALSE;
}
}
// if(!(create_option & FILE_NON_DIRECTORY_FILE))
// {
// return TRUE;
// }
ptr = wcsrchr(pUniFilePath->Buffer, '\\');
if (ptr)
{
++ptr;
RtlInitUnicodeString(&uniFileName, ptr);
/// 2015.3.20 정책 파일 접근에 대한 문제로 인해 모든 프로세스에 읽기 전용으로 변경
if (IsFileName(ulFileType, uniFileName.Buffer, uniFileName.Length)/* && ulPidType == PG_PID_GREEN*/)
{
if (major == IRP_MJ_CREATE)
{
DWORD desiredaccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
KLogEx(DEBUG_TRACE_ERROR, "desiredaccess(%x) name(%S) len(%d)\n", desiredaccess, uniFileName.Buffer, uniFileName.Length);
if (desiredaccess & ((WRITE_MODE | DELETE)))
return FALSE;
}
else if (major == IRP_MJ_SET_INFORMATION)
{
FILE_INFORMATION_CLASS fileinfoclass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
/// 이름 변경 & 삭제의 경우 차단
if (fileinfoclass == FileRenameInformation || fileinfoclass == FileDispositionInformation
|| fileinfoclass == FileRenameInformationEx || fileinfoclass == FileDispositionInformationEx)
return FALSE;
}
return TRUE;
}
}
return FALSE;
}
/*
BOOLEAN IsPolicyState(ULONG type, ULONG state, PFLT_CALLBACK_DATA data, PCFLT_RELATED_OBJECTS fltobject)
{
ULONG disposition;
ULONG desired_access = 0;
ULONG create_option = 0;
// 쓰기 및 변경과 관련된 모든 권한 마스크 정의
ULONG WriteAccessMask = FILE_WRITE_DATA | FILE_APPEND_DATA |
FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES |
FILE_DELETE_CHILD | DELETE |
WRITE_DAC | WRITE_OWNER |
MAXIMUM_ALLOWED |
GENERIC_WRITE | GENERIC_ALL;
UNREFERENCED_PARAMETER(type);
if (data->Iopb == NULL) return FALSE; // 방어 코드
desired_access = data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
create_option = data->Iopb->Parameters.Create.Options;
// Disposition 추출 (상위 8비트)
disposition = (data->Iopb->Parameters.Create.Options >> 24) & 0xFF;
if (state == DISABLE) return TRUE;
if (state == ENABLE) return FALSE;
// READONLY 정책 처리
if (state == READONLY)
{
KLogEx(DEBUG_TRACE_ERROR, "READONLY, disposition[%x],create_option(%x), desired_access(%x)\n", disposition, create_option, desired_access);
// 1. [핵심] Disposition 별 처리
switch (disposition)
{
// [CASE A] 무조건 파일 내용을 변경하거나 새로 만드는 경우 -> 즉시 차단
case FILE_CREATE: // 없으면 생성, 있으면 실패 -> 생성 시도이므로 차단
case FILE_SUPERSEDE: // 있으면 덮어쓰기(삭제 후 생성) -> 차단
case FILE_OVERWRITE: // 있으면 덮어쓰기 -> 차단
case FILE_OVERWRITE_IF: // 없으면 생성, 있으면 덮어쓰기 -> 둘 다 변경이므로 차단
KLogEx(DEBUG_TRACE_ERROR, "Block(Destructive Action) disposition[%x]\n", disposition);
return TRUE;
// [CASE B] 파일이 있으면 열고, 없으면 만드는 경우
case FILE_OPEN_IF:
{
HANDLE hFileHandle = 0;
OBJECT_ATTRIBUTES objectAttributes; // 초기화 주의
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS ntStatus = 0;
InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
// 파일 존재 여부 확인 (읽기 권한으로 시도)
ntStatus = FltCreateFile(
fltobject->Filter,
fltobject->Instance,
&hFileHandle,
FILE_READ_ATTRIBUTES, // 최소 권한으로 확인
&objectAttributes,
&IoStatusBlock,
0,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE, // 옵션 주의
NULL,
0,
0
);
if (NT_SUCCESS(ntStatus))
{
// 파일이 존재함 -> "파일 열기" 시도
FltClose(hFileHandle);
// [중요 수정] 파일이 있어도 '쓰기 권한'을 요청했다면 차단해야 함
if (desired_access & WriteAccessMask)
{
KLogEx(DEBUG_TRACE_ERROR, "Block(OPEN_IF/Exist but Write) access(%x)\n", desired_access);
return TRUE;
}
// 쓰기 권한 없으면 허용 (읽기 전용 오픈)
}
else
{
// 파일이 없음 -> "파일 생성" 시도 -> 차단
KLogEx(DEBUG_TRACE_ERROR, "Block(OPEN_IF/Not Exist) -> Try Create\n");
return TRUE;
}
break;
}
// [CASE C] 파일이 있을 때만 여는 경우 (기존 코드에서 가장 취약했던 부분)
case FILE_OPEN:
{
// [중요 수정] DELETE 뿐만 아니라 Write Data, Append Data 등도 체크해야 함
if (desired_access & WriteAccessMask)
{
KLogEx(DEBUG_TRACE_ERROR, "Block(FILE_OPEN with Write) access(%x)\n", desired_access);
return TRUE;
}
break;
}
default:
break;
}
}
return FALSE;
}
*/
BOOLEAN IsPolicyState(ULONG type, ULONG state, PFLT_CALLBACK_DATA data, PCFLT_RELATED_OBJECTS fltobject)
{
ULONG disposition;
ULONG desired_access = 0;
ULONG create_option = 0;
USHORT create_fileattribute = 0;
//USHORT share_access = 0;
//ULONG devicetype = 0;
UNREFERENCED_PARAMETER(type);
desired_access = data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
create_option = data->Iopb->Parameters.Create.Options;
disposition = (create_option >> 24) & 0xFF;
create_fileattribute = data->Iopb->Parameters.Create.FileAttributes;
if (state == DISABLE)
return TRUE;
if (state == ENABLE)
{
return FALSE;
}
else if (state == READONLY)
{
KLogEx(DEBUG_TRACE_ERROR, "READONLY, disposition[%x],create_option(%x), desired_access(%x)\n", disposition, create_option, desired_access);
switch (disposition)
{
case FILE_CREATE:
case FILE_SUPERSEDE:
{
/// 폴더 생성 차단
if (create_option & FILE_DIRECTORY_FILE)
{
if (desired_access & (FILE_WRITE_EA))
{
KLogEx(DEBUG_TRACE_ERROR, "block(1) create_option(%x), desired_access(%x)\n", create_option, desired_access);
return TRUE;
}
// sskim - 170207 Added.
if (desired_access & (FILE_LIST_DIRECTORY))
{
KLogEx(DEBUG_TRACE_ERROR, "block(2) create_option(%x), desired_access(%x)\n", create_option, desired_access);
return TRUE;
}
}
if (desired_access & (FILE_WRITE_DATA))
{
KLogEx(DEBUG_TRACE_ERROR, "block(3) disposition(%x), desired_access(%x)\n", disposition, desired_access);
return TRUE;
}
// 삭제 차단
if (desired_access & (DELETE))
{
KLogEx(DEBUG_TRACE_ERROR, "block(4) disposition(%x), desired_access(%x)\n", disposition, desired_access);
return TRUE;
}
break;
}
case FILE_OVERWRITE_IF:
case FILE_OPEN_IF:
{
KLogEx(DEBUG_TRACE_ERROR, "FILE_OVERWRITE_IF , FILE_OPEN_IF disposition[%x]create_option(%x), desired_access(%x)\n", disposition, create_option, desired_access);
}
{
HANDLE hFileHandle = 0;
OBJECT_ATTRIBUTES objectAttributes = { 0, };
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS ntStatus = 0;
ntStatus = FltCreateFile(
fltobject->Filter, fltobject->Instance, &hFileHandle,
GENERIC_READ,
&objectAttributes,
&IoStatusBlock,
0,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0,
0
);
if (NT_SUCCESS(ntStatus))
{
// 이미 파일이 있는 경우
FltClose(hFileHandle);
// 허용
KLogEx(DEBUG_TRACE_ERROR, "PERMIT(1) disposition(%x), desired_access(%x),create_option(%x)\n", disposition, desired_access, create_option);
}
else
{
//차단
KLogEx(DEBUG_TRACE_ERROR, "block(5) disposition(%x), desired_access(%x)\n", disposition, desired_access);
return TRUE;
}
}
if (desired_access & (DELETE))
{
KLogEx(DEBUG_TRACE_ERROR, "block(6) disposition(%x), desired_access(%x)\n", disposition, desired_access);
return TRUE;
}
{
if(desired_access & (FILE_WRITE_DATA))
{
KLogEx(DEBUG_TRACE_ERROR, "block(3) disposition(%x), desired_access(%x)\n", disposition, desired_access);
return TRUE;
}
// 삭제 차단
if(desired_access & (DELETE))
{
KLogEx(DEBUG_TRACE_ERROR, "block(4) disposition(%x), desired_access(%x)\n", disposition, desired_access);
return TRUE;
}
}
break;
case FILE_OPEN:
{
if (desired_access & (DELETE))
{
KLogEx(DEBUG_TRACE_ERROR, "block(7) disposition(%x), desired_access(%x)\n", disposition, desired_access);
return TRUE;
}
}
break;
case FILE_OVERWRITE:
{
KLogEx(DEBUG_TRACE_ERROR, "PERMIT(2) disposition(%x), desired_access(%x),create_option(%x)\n", disposition, desired_access, create_option);
//return TRUE;
}
break;
default:
return FALSE;
}
}
return FALSE;
}
BOOLEAN IsNetworkDrive(LPWSTR path)
{
static LPCWSTR s_networkSignatures[] =
{
/// 네트워크 드라이브 사용
/// etc
/// win 7. \Device\Mup\;LanmanRedirector\;Z:00000000001cdd14\KMG-PC\share\allowpl - 복사본 (2).ini
/// xp sp3 \Device\LanmanRedirector\;Z:00000000000133ab\kmg-pc\share\
L"\\;LanmanRedirector\\;",
L"\\;lanmanredirector\\;",
L"\\LanmanRedirector\\;",
NULL
};
LPCWSTR* ep = s_networkSignatures;
while (*ep)
{
if (wcsstr(path, *ep) != NULL)
return TRUE;
++ep;
}
//드라이브 레터 패턴 확인(; Z: 또는; z: 형태)
LPCWSTR p = path;
while ((p = wcschr(p, L';')) != NULL)
{
// p[0]은 ';' 입니다.
// p[1]이 NULL이 아니고(문자열 끝 아님), p[2]가 ':' 인지 확인
if (p[1] != L'\0' && p[2] == L':')
{
WCHAR driveLetter = p[1];
// 영문자(A-Z, a-z)인지 확인
if ((driveLetter >= L'A' && driveLetter <= L'Z') ||
(driveLetter >= L'a' && driveLetter <= L'z'))
{
return TRUE;
}
}
p++;
}
return FALSE;
}
static BOOLEAN once = FALSE;
BOOLEAN IsUsbException(PCFLT_RELATED_OBJECTS FltObjects, ULONG device_type)
{
PVOLUME_CONTEXT vctx = NULL;
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN state = FALSE;
UNREFERENCED_PARAMETER(device_type);
status = FltGetVolumeContext(FltObjects->Filter, FltObjects->Volume, &vctx);
if (!NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_ERROR, "FltGetVolumeContext Failed (%x)\n", status);
return FALSE;
}
//KLogEx(DEBUG_TRACE_INFO, "Vendor ID(%s),productid(%s)(%s)\n", vctx->vendorid, vctx->productid, vctx->vendorspecific);
if (IsUsbDiskExceptionList((PCHAR)vctx->vendorid, (PCHAR)vctx->productid, (PCHAR)vctx->productrevisionlevel, (PCHAR)vctx->vendorspecific))
{
KLogEx(DEBUG_TRACE_INFO, "Vendor ID(%s),productid(%s)(%s)\n", vctx->vendorid, vctx->productid, vctx->vendorspecific);
state = TRUE;
}
FltReleaseContext(vctx);
return state;
}
// 기본 필터링 조건을 확인하여 패스 여부를 결정
BOOLEAN IsSkipCondition(PFLT_CALLBACK_DATA Data)
{
if (!g_bs1Flt.IsAttached)
return TRUE;
if (IoThreadToProcess(Data->Thread) == g_bs1Flt.UserProcess)
return TRUE;
if (!NT_SUCCESS(Data->IoStatus.Status) || (STATUS_REPARSE == Data->IoStatus.Status))
return TRUE;
if (Data->Iopb->MajorFunction == IRP_MJ_CLEANUP)
return TRUE;
// 관심있는 Major Function만 처리
if (Data->Iopb->MajorFunction != IRP_MJ_SET_INFORMATION &&
Data->Iopb->MajorFunction != IRP_MJ_CREATE &&
Data->Iopb->MajorFunction != IRP_MJ_READ &&
Data->Iopb->MajorFunction != IRP_MJ_WRITE) // READ/WRITE 추가 (코드 로직상 필요)
return TRUE;
if (KeGetCurrentIrql() > PASSIVE_LEVEL)
return TRUE;
return FALSE;
}
// 로컬 디스크 정책 처리
FLT_PREOP_CALLBACK_STATUS HandleLocalDiskPolicy(
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PUNICODE_STRING pUniFileName,
PUNICODE_STRING pProcessPath,
ULONG pid,
ULONG pidType
)
{
ULONG pathType = 0;
ULONG majorFunction = Data->Iopb->MajorFunction;
ULONG createOption = 0;
ULONG disposition = 0;
ULONG desiredAccess = 0;
UNREFERENCED_PARAMETER(pProcessPath);
if (!g_bs1Flt.IsFolderProtect)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
// 허용된 프로세스인 경우, Rename/Delete 차단 옵션 확인
if (pidType & PG_PID_ALLOW)
{
if ((pidType & PG_PID_BLOCK_RENAME) && (majorFunction == IRP_MJ_SET_INFORMATION))
{
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
if (infoClass != FileRenameInformation && infoClass != FileRenameInformationEx)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
// Rename이면 아래 로직 계속 진행 (차단 검사)
}
else
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
}
pathType = IsProtectPath(PG_PATH_ALL, pUniFileName->Buffer, pUniFileName->Length);
if (pathType == PG_PATH_UNDEFINED)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (pathType == PG_PATH_GRAY) // GRAY: 읽기 허용, 쓰기/삭제/이름변경 차단
{
if (majorFunction == IRP_MJ_CREATE)
{
createOption = Data->Iopb->Parameters.Create.Options;
disposition = (createOption >> 24) & 0xFF;
desiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
if (disposition == FILE_CREATE || (desiredAccess & (WRITE_MODE | DELETE)))
{
KLogEx(DEBUG_TRACE_INFO, "PG_PATH_GRAY BLOCK: CREATE/WRITE/DELETE Attempt. Path(%S)\n", pUniFileName->Buffer);
}
else
{
// 읽기 전용 접근 허용
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
}
else if (majorFunction == IRP_MJ_SET_INFORMATION)
{
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
if (infoClass != FileRenameInformation && infoClass != FileDispositionInformation &&
infoClass != FileRenameInformationEx && infoClass != FileDispositionInformationEx)
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
else
{
KLogEx(DEBUG_TRACE_INFO, "PG_PATH_GRAY BLOCK: RENAME Attempt. Path(%S)\n", pUniFileName->Buffer);
}
}
}
if (pathType == PG_PATH_BLACK) // BLACK: 모든 접근 차단(단, 예외 파일은 허용)
{
if (Bs1FltIsFileName(Data, FltObjects, pUniFileName, pidType, PG_FILE_ALLOW))
{
KLogEx(DEBUG_TRACE_INFO, "PG_PATH_BLACK PERMIT FILENAME: Path(%S)\n", pUniFileName->Buffer);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
KLogEx(DEBUG_TRACE_INFO, "ACCESS DENIED (Local Disk): Path(%S), Pid(%d)\n", pUniFileName->Buffer, pid);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
return FLT_PREOP_COMPLETE;
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
FLT_PREOP_CALLBACK_STATUS HandleNetWorkInDevicePolicy(
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
ULONG deviceType,
ULONG policyState,
ULONG policyLog
)
{
char szProcessName[20] = { 0, };
WCHAR processName[50] = { 0, };
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
NTSTATUS status;
ULONG pid = HandleToULong(PsGetCurrentProcessId());
try
{
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_ERROR, "FltGetFileNameInformation Failed [%x]\n", status);
leave;
}
else
{
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_ERROR, "FltParseFileNameInformation Failed [%x]\n", status);
leave;
}
}
KLogEx(DEBUG_TRACE_INFO, "Network Drive In processName(%S), Path(%S)\n", processName, nameInfo->Name.Buffer);
if (policyLog)
{
UGetProcessName(szProcessName);
RtlStringCbPrintfW(processName, sizeof(processName), L"%S", szProcessName);
SetLog(Data, FltObjects, LOG_POLICY, deviceType, policyState, 0, processName, nameInfo->Name.Buffer);
}
if (policyState != ENABLE)
{
KLogEx(DEBUG_TRACE_INFO, "ACCESS DENIED Pid(%d)\n", pid);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
return FLT_PREOP_COMPLETE;
}
}
finally
{
if (nameInfo != NULL)
FltReleaseFileNameInformation(nameInfo);
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// USB, 네트워크 및 기타 장치(CD-ROM 등) 정책 처리
FLT_PREOP_CALLBACK_STATUS HandleExternalDevicePolicy(
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PUNICODE_STRING pUniFileName,
PUNICODE_STRING pProcessPath,
ULONG pid,
ULONG deviceType,
ULONG policyState,
ULONG policyLog
)
{
BOOLEAN isBlock = FALSE;
ULONG majorFunction = Data->Iopb->MajorFunction;
UNREFERENCED_PARAMETER(policyLog);
if (!g_bs1Flt.IsDeviceProtect)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
try
{
if (deviceType == BDC_USB_DISK || deviceType == BDC_EXTERNALHDD)
{
if (IsUsbException(FltObjects, deviceType))
leave;
}
// 네트워크 드라이브/공유 경로 재확인 및 정책 갱신
if (deviceType == BDC_NETWORKDRIVEOUT)
{
if (!IsNetworkDrive(pUniFileName->Buffer))
{
deviceType = BDC_NETWORKSHAREOUT;
policyState = GetPolicyState(deviceType);
}
}
KLogEx(DEBUG_TRACE_INFO, "Check External Policy: Pid(%d), Dev(%d), Path(%S)\n", pid, deviceType, pUniFileName->Buffer);
// Operation 별 차단 여부 결정
switch (majorFunction)
{
case IRP_MJ_CREATE:
if (IsPolicyState(deviceType, policyState, Data, FltObjects))
isBlock = TRUE;
break;
case IRP_MJ_SET_INFORMATION:
{
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
if (policyState == ENABLE)
{
// ENABLE 상태일 때 로그 처리 로직 (주석되어 있었음)
}
else
{
if (infoClass == FileRenameInformation || infoClass == FileDispositionInformation)
isBlock = TRUE;
}
}
break;
case IRP_MJ_READ:
if (deviceType == BDC_CDROM)
{
// Windows Burning 관련 임시 파일 차단
if (wcsstr(pUniFileName->Buffer, L"\\AppData\\Local\\Microsoft\\Windows\\Burn\\Burn\\") != NULL)
isBlock = TRUE;
}
break;
case IRP_MJ_WRITE:
if (policyState != ENABLE)
{
KLogEx(DEBUG_TRACE_INFO, "Check External Policy: Write !!!! Pid(%d), Dev(%d), Path(%S)\n", pid, deviceType, pUniFileName->Buffer);
isBlock = TRUE;
}
break;
}
// 네트워크 호스트 IP 허용 예외 처리
if ((deviceType == BDC_NETWORKDRIVEOUT || deviceType == BDC_NETWORKSHAREOUT) && isBlock)
{
if (IsAllowHostIp(pUniFileName->Buffer, pUniFileName->Length))
{
isBlock = FALSE; // IP 허용 목록에 있으면 차단 해제
}
}
// CD-ROM 예외 처리
if (deviceType == BDC_CDROM && isBlock)
{
PWCHAR pwszTemp = wcsstr(pUniFileName->Buffer, L"\\CdRom");
if (pwszTemp)
{
size_t len = wcslen(pwszTemp);
if (len == 7 || len == 8)
isBlock = FALSE;
}
}
}
finally
{
// 로그 기록
if (policyLog)
{
if (policyState == READONLY && !isBlock)
{
}
else
{
SetLog(Data, FltObjects, LOG_POLICY, deviceType, policyState, 0, pProcessPath->Buffer, pUniFileName->Buffer);
}
}
// 최종 차단 처리
if (policyState != ENABLE && isBlock == TRUE)
{
KLogEx(DEBUG_TRACE_INFO, "ACCESS DENIED Pid(%d), Path(%S)\n", pid, pUniFileName->Buffer);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
return FLT_PREOP_COMPLETE;
}
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
/*************************************************************************
MiniFilter callback routines.
*************************************************************************/
FLT_PREOP_CALLBACK_STATUS
Bs1FltPreOperation(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Flt_CompletionContext_Outptr_ PVOID* CompletionContext
)
/*++
Routine Description:
This routine is a pre-operation dispatch routine for this miniFilter.
This is non-pageable because it could be called on the paging path
Arguments:
Data - Pointer to the filter callbackData that is passed to us.
FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
opaque handles to this filter, instance, its associated volume and
file object.
CompletionContext - The context for the completion routine for this
operation.
Return Value:
The return value is the status of the operation.
--*/
{
FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
UNICODE_STRING filePath = { 0, };
UNICODE_STRING processPath = { 0, };
NTSTATUS status;
ULONG pid = 0;
ULONG pidType = 0;
ULONG processType = 0;
ULONG deviceType = 0;
ULONG policyState = 0;
ULONG policyLog = 0;
char szProcessName[20] = { 0, };
UNREFERENCED_PARAMETER(CompletionContext);
PAGED_CODE();
if (IsSkipCondition(Data))
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
deviceType = GetDeviceType(Data, FltObjects);
if (deviceType == BDC_UNKNOWN_DEV)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Data->RequestorMode == KernelMode)
{
if (deviceType != BDC_NETWORKDRIVEIN)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// 정책 상태 확인
policyState = GetPolicyState(deviceType);
policyLog = IsPolicyLog(deviceType);
if (deviceType != BDC_LOCAL_DISK)
{
if (policyState == ENABLE && policyLog == FALSE)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
if (deviceType == BDC_NETWORKDRIVEIN)
{
return HandleNetWorkInDevicePolicy(
Data,
FltObjects,
deviceType,
policyState,
policyLog
);
}
// 프로세스 ID 및 이름 확인
pid = HandleToULong(PsGetCurrentProcessId());
pidType = PgGetPidState(pid);
if ((pidType & PG_PID_ALLOW))
return FLT_PREOP_SUCCESS_NO_CALLBACK;
UGetProcessName(szProcessName);
if (deviceType == BDC_LOCAL_DISK)
{
if (IsDefalutLocalDiskExceptProcess(szProcessName))
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
else
{
if (IsDefalutExternalDiskExceptProcess(szProcessName))
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
// 파일 이름 정보 획득
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_ERROR, "FltGetFileNameInformation Failed (%x)\n", status);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
try
{
FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(UStrNew(&filePath, nameInfo->Name.MaximumLength + 10)))
{
KLogEx(DEBUG_TRACE_ERROR, "FltParseFileNameInformation Failed (%S)\n", nameInfo->Name.Buffer);
leave;
}
UStrCopy(&filePath, &nameInfo->Name);
status = UGetCurrentStackProcessImageName(pid, &processPath);
if (!NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_ERROR, "Get Process Name Info Failed. szProcessName(%s), pid(%d), status(%x)\n", szProcessName, pid, status);
leave;
}
// 정의되지 않은 프로세스 정책 업데이트
if (pidType == PG_PID_UNDEFINED)
{
if (!IsAllowProcessName(processPath.Buffer, processPath.Length, &processType))
{
pidType = PG_PID_BLACK;
}
else
{
KLogEx(DEBUG_TRACE_INFO, "Update Allow Process, Path(%S)(%d), Type(%d)\n", processPath.Buffer, pid, processType);
pidType = processType;
PgAddPid(pid, pidType);
}
}
// 디바이스 타입별 로직 분기
if (deviceType == BDC_LOCAL_DISK)
{
returnStatus = HandleLocalDiskPolicy(Data, FltObjects, &filePath, &processPath, pid, pidType);
}
else
{
if (pidType & PG_PID_ALLOW)
leave;
returnStatus = HandleExternalDevicePolicy(Data, FltObjects, &filePath, &processPath, pid, deviceType, policyState, policyLog);
}
}
finally
{
UStrFree(&processPath);
UStrFree(&filePath);
if (nameInfo != NULL)
FltReleaseFileNameInformation(nameInfo);
}
return returnStatus;
}
FLT_PREOP_CALLBACK_STATUS
Bs1FltPreRead(
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID* CompletionContext
)
{
UNREFERENCED_PARAMETER(CompletionContext);
NTSTATUS status = FLT_PREOP_SUCCESS_NO_CALLBACK;
if (!g_bs1Flt.IsAttached)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (g_bs1Flt.UserProcess == PsGetCurrentProcess())
return FLT_PREOP_SUCCESS_NO_CALLBACK;
if(!g_bs1Flt.IsDeviceProtect)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Data == NULL)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Data->RequestorMode == KernelMode)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
// CDROM 정책 확인
if (GetPolicyState(BDC_CDROM) != ENABLE)
{
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
HANDLE processid = PsGetCurrentProcessId();
BOOLEAN isblock = FALSE;
UNICODE_STRING filepath = { 0, };
PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb;
ULONG readLen = iopb->Parameters.Read.Length;
ULONG uByteOffset = iopb->Parameters.Read.ByteOffset.LowPart;
try
{
if (readLen == 0 || (!FLT_IS_IRP_OPERATION(Data)) || uByteOffset != 0) {
leave;
}
/// 파일 경로 얻음
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status))
{
//KLogEx(DEBUG_TRACE_ERROR, "kdcmPreRead FltGetFileNameInformation fail(%x)\n", status);
leave;
}
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status))
{
//KLogEx(DEBUG_TRACE_ERROR, "kdcmPreRead FltParseFileNameInformation fail(%x)\n", status);
leave;
}
status = UStrNew(&filepath, nameInfo->Name.MaximumLength + 10);
if (!NT_SUCCESS(status))
leave;
status = UStrCopy(&filepath, &nameInfo->Name);
if (!NT_SUCCESS(status))
leave;
if (wcsrchr(filepath.Buffer, '\\') == NULL)
leave;
_wcslwr(filepath.Buffer);
if (wcsstr(filepath.Buffer, L"\\appdata\\local\\microsoft\\windows\\burn\\burn") != NULL
&& wcsstr(filepath.Buffer, L"\\desktop.ini") == NULL)
{
UNICODE_STRING processpath = { 0, };
ULONG state = 0;
status = UGetCurrentStackProcessImageName(HandleToULong(processid), &processpath);
state = GetPolicyState(BDC_CDROM);
isblock = TRUE;
SetLog(Data, FltObjects, LOG_POLICY, BDC_CDROM, state, 0, processpath.Buffer, filepath.Buffer);
KLogEx(DEBUG_TRACE_INFO, "READ BLOCK!!!!!!!!!!!!! pid(%d),processpath(%S)", processid, processpath.Buffer);
}
}
finally
{
UStrFree(&filepath);
if (nameInfo != NULL)
FltReleaseFileNameInformation(nameInfo);
}
if (isblock == TRUE)
{
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
return FLT_PREOP_COMPLETE;
}
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
VOID
Bs1FltOperationStatusCallback(
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_ PFLT_IO_PARAMETER_BLOCK ParameterSnapshot,
_In_ NTSTATUS OperationStatus,
_In_ PVOID RequesterContext
)
/*++
Routine Description:
This routine is called when the given operation returns from the call
to IoCallDriver. This is useful for operations where STATUS_PENDING
means the operation was successfully queued. This is useful for OpLocks
and directory change notification operations.
This callback is called in the context of the originating thread and will
never be called at DPC level. The file object has been correctly
referenced so that you can access it. It will be automatically
dereferenced upon return.
This is non-pageable because it could be called on the paging path
Arguments:
FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
opaque handles to this filter, instance, its associated volume and
file object.
RequesterContext - The context for the completion routine for this
operation.
OperationStatus -
Return Value:
The return value is the status of the operation.
--*/
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(RequesterContext);
UNREFERENCED_PARAMETER(OperationStatus);
UNREFERENCED_PARAMETER(ParameterSnapshot);
//KLogEx(DEBUG_TRACE_INFO,
// ("cdsflt!cdsfltOperationStatusCallback: Entered\n"));
//KLogEx(DEBUG_TRACE_INFO,
// ("cdsflt!cdsfltOperationStatusCallback: Status=%08x ctx=%p IrpMj=%02x.%02x \"%s\"\n",
// OperationStatus,
// RequesterContext,
// ParameterSnapshot->MajorFunction,
// ParameterSnapshot->MinorFunction,
// FltGetIrpName(ParameterSnapshot->MajorFunction)));
}
FLT_POSTOP_CALLBACK_STATUS
Bs1FltPostOperation(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
/*++
Routine Description:
This routine is the post-operation completion routine for this
miniFilter.
This is non-pageable because it may be called at DPC level.
Arguments:
Data - Pointer to the filter callbackData that is passed to us.
FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
opaque handles to this filter, instance, its associated volume and
file object.
CompletionContext - The completion context set in the pre-operation routine.
Flags - Denotes whether the completion is successful or is being drained.
Return Value:
The return value is the status of the operation.
--*/
{
UNREFERENCED_PARAMETER(Data);
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
UNREFERENCED_PARAMETER(Flags);
//PT_DBG_PRINT(PTDBG_TRACE_ROUTINES,
// ("cdsflt!cdsfltPostOperation: Entered\n"));
return FLT_POSTOP_FINISHED_PROCESSING;
}
FLT_PREOP_CALLBACK_STATUS
cdsfltPreOperationNoPostOperation(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Flt_CompletionContext_Outptr_ PVOID* CompletionContext
)
/*++
Routine Description:
This routine is a pre-operation dispatch routine for this miniFilter.
This is non-pageable because it could be called on the paging path
Arguments:
Data - Pointer to the filter callbackData that is passed to us.
FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
opaque handles to this filter, instance, its associated volume and
file object.
CompletionContext - The context for the completion routine for this
operation.
Return Value:
The return value is the status of the operation.
--*/
{
UNREFERENCED_PARAMETER(Data);
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
//PT_DBG_PRINT(PTDBG_TRACE_ROUTINES,
// ("cdsflt!cdsfltPreOperationNoPostOperation: Entered\n"));
// This template code does not do anything with the callbackData, but
// rather returns FLT_PREOP_SUCCESS_NO_CALLBACK.
// This passes the request down to the next miniFilter in the chain.
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
BOOLEAN
Bs1FltDoRequestOperationStatus(
_In_ PFLT_CALLBACK_DATA Data
)
/*++
Routine Description:
This identifies those operations we want the operation status for. These
are typically operations that return STATUS_PENDING as a normal completion
status.
Arguments:
Return Value:
TRUE - If we want the operation status
FALSE - If we don't
--*/
{
PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb;
//
// return boolean state based on which operations we are interested in
//
return (BOOLEAN)
//
// Check for oplock operations
//
(((iopb->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
((iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) ||
(iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) ||
(iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_1) ||
(iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2)))
||
//
// Check for directy change notification
//
((iopb->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) &&
(iopb->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY))
);
}