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

1246 lines
32 KiB
C

#include "precomp.h"
#include <ntddvol.h>
// 만약 헤더가 없어서 컴파일 에러가 난다면 아래 주석을 해제하여 사용하세요.
/*
DEFINE_DEVPROPKEY(DEVPKEY_Device_InstanceId,
0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 256);
*/
NTSTATUS
GetPhysicalDiskDeviceObject(
__in PDEVICE_OBJECT VolumeDeviceObject,
__out PDEVICE_OBJECT* PhysicalDiskDeviceObject
)
{
NTSTATUS status;
PIRP Irp;
KEVENT Event;
IO_STATUS_BLOCK Iosb;
PVOLUME_DISK_EXTENTS pDiskExtents = NULL;
// 넉넉하게 4개 정도의 Extent 공간 확보
ULONG extentsSize = sizeof(VOLUME_DISK_EXTENTS) + (sizeof(DISK_EXTENT) * 4);
UNICODE_STRING physicalDiskName;
WCHAR nameBuffer[64] = { 0 };
PFILE_OBJECT pFileObject = NULL;
PDEVICE_OBJECT pDevObj = NULL;
*PhysicalDiskDeviceObject = NULL;
// 1. 메모리 할당
pDiskExtents = (PVOLUME_DISK_EXTENTS)ExAllocatePoolWithTag(NonPagedPool, extentsSize, 'dExt');
if (!pDiskExtents) return STATUS_INSUFFICIENT_RESOURCES;
// 메모리 초기화 (중요)
RtlZeroMemory(pDiskExtents, extentsSize);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
VolumeDeviceObject,
NULL, 0,
pDiskExtents, extentsSize,
FALSE, &Event, &Iosb
);
if (!Irp) {
ExFreePoolWithTag(pDiskExtents, 'dExt');
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(VolumeDeviceObject, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
status = Iosb.Status;
}
if (!NT_SUCCESS(status)) {
ExFreePoolWithTag(pDiskExtents, 'dExt');
return status;
}
// [BSOD 방지 핵심] Extents 개수 확인!
if (pDiskExtents->NumberOfDiskExtents == 0)
{
KLogEx(DEBUG_TRACE_INFO, "No Disk Extents found.\n");
ExFreePoolWithTag(pDiskExtents, 'dExt');
return STATUS_UNSUCCESSFUL;
}
__try
{
// 2. 물리 디스크 이름 생성
ULONG diskNumber = pDiskExtents->Extents[0].DiskNumber;
// 안전한 문자열 함수 사용
if (!NT_SUCCESS(RtlStringCbPrintfW(nameBuffer, sizeof(nameBuffer), L"\\Device\\Harddisk%u\\DR0", diskNumber)))
{
status = STATUS_BUFFER_OVERFLOW;
__leave;
}
RtlInitUnicodeString(&physicalDiskName, nameBuffer);
KLogEx(DEBUG_TRACE_INFO, "Trying to open: %S\n", nameBuffer);
// 3. 디바이스 포인터 획득 (가장 위험한 구간)
// InstanceSetup에서 이 호출이 위험할 수 있으므로 실패 시 즉시 빠져나옴
status = IoGetDeviceObjectPointer(
&physicalDiskName,
FILE_READ_ATTRIBUTES, // 권한 최소화
&pFileObject,
&pDevObj
);
if (NT_SUCCESS(status))
{
ObReferenceObject(pDevObj); // 디바이스 객체 참조
ObDereferenceObject(pFileObject); // 파일 객체 닫기
*PhysicalDiskDeviceObject = pDevObj;
}
else
{
KLogEx(DEBUG_TRACE_ERROR, "IoGetDeviceObjectPointer Failed: 0x%x\n", status);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KLogEx(DEBUG_TRACE_ERROR, "Exception in GetPhysicalDiskDeviceObject\n");
status = STATUS_UNHANDLED_EXCEPTION;
}
ExFreePoolWithTag(pDiskExtents, 'dExt');
return status;
}
NTSTATUS
QueryDeviceProperty(
__in PDEVICE_OBJECT pDevice,
__in DEVICE_REGISTRY_PROPERTY DevProperty,
__out PVOID* ppBuffer,
__out PULONG pResultLength
)
{
NTSTATUS status;
PVOID pBuffer = NULL;
ULONG BufferSize = 0;
PDEVICE_OBJECT pdo = NULL;
ASSERT(ARGUMENT_PRESENT(pDevice));
// sskim - 171122 Added.
// FltGetDiskDeviceObject로 구한 pDevice는 PDO여서 Verifier에서 터짐 (FDO로 가져오게 추가)
pdo = IoGetDeviceAttachmentBaseRef(pDevice);
status = IoGetDeviceProperty(
pdo,
DevProperty,
BufferSize,
NULL,
&BufferSize
);
if (NT_SUCCESS(status))
{
ObDereferenceObject(pdo);
return STATUS_NOT_SUPPORTED;
}
while (status == STATUS_BUFFER_TOO_SMALL)
{
pBuffer = ExAllocatePoolWithTag(PagedPool, BufferSize, _ALLOC_TAG);
if (!pBuffer)
{
ObDereferenceObject(pdo);
return STATUS_NO_MEMORY;
}
status = IoGetDeviceProperty(
pdo,
DevProperty,
BufferSize,
pBuffer,
&BufferSize
);
if (NT_SUCCESS(status))
{
*ppBuffer = pBuffer;
*pResultLength = BufferSize;
ObDereferenceObject(pdo);
return status;
}
ExFreePool(pBuffer);
pBuffer = NULL;
}
ASSERT(!pBuffer);
ObDereferenceObject(pdo);
return status;
}
//#define QUERY_BUFFER_SIZE 0x2000
//NTSTATUS SafeSetVolumContextParam(PUCHAR pucDest, DWORD dwSize, PUCHAR pucSrc, DWORD dwOffset)
//{
// int size = 0;
// PCHAR pszName = NULL;
//
// if (dwOffset == 0)
// return STATUS_UNSUCCESSFUL;
//
// if (dwOffset >= dwSize)
// return STATUS_UNSUCCESSFUL;
//
// if (dwOffset >= (QUERY_BUFFER_SIZE - 10))
// return STATUS_UNSUCCESSFUL;
//
// pszName = (char*)GetPtr(pucSrc, dwOffset);
// size = (int)strlen(pszName);
//
// KLogEx(DEBUG_TRACE_INFO, "SafeSetVolumContextParam, size(%d)\n", size);
//
// if (size > VOLUME_DESCRIPTION_LENGTH)
// {
// size = VOLUME_DESCRIPTION_LENGTH - 1;
// }
//
// RtlCopyMemory(pucDest, pszName, size);
// return STATUS_SUCCESS;
//}
// 매크로 정의 (없다면 추가 필요)
#ifndef QUERY_BUFFER_SIZE
#define QUERY_BUFFER_SIZE 0x2000
#endif
// 안전한 포인터 계산 및 복사 함수 (대문자 변환 포함)
NTSTATUS SafeSetVolumContextParam(
__in PUCHAR pucDest, // (Context )
__in ULONG ulSrcTotalSize, // 원본 전체 버퍼 크기 (pDesc->Size)
__in PUCHAR pucSrcBase, // 원본 버퍼 시작 주소 (pDesc)
__in ULONG ulOffset // 문자열 오프셋
)
{
ULONG i = 0;
ULONG destIndex = 0;
PCHAR pSrcStr = NULL;
BOOLEAN bLeadingSpace = TRUE; // 앞 공백 여부 플래그
ULONG lastNonSpaceIndex = 0; // 마지막으로 공백이 아닌 문자의 인덱스
// 1. 오프셋 유효성 검사
if (ulOffset == 0 || ulOffset >= ulSrcTotalSize)
{
if (pucDest) pucDest[0] = 0;
return STATUS_SUCCESS;
}
// 2. 문자열 시작 포인터 계산
pSrcStr = (PCHAR)(pucSrcBase + ulOffset);
// 3. 복사 루프 (대문자 변환 + 앞 공백 제거)
for (i = 0; i < VOLUME_DESCRIPTION_LENGTH - 1; i++)
{
// A. 원본 버퍼 범위 체크 (Overflow 방지)
if ((ULONG_PTR)(pSrcStr + i) >= (ULONG_PTR)(pucSrcBase + ulSrcTotalSize))
break;
// B. 원본 문자열 끝(NULL) 체크
if (pSrcStr[i] == 0)
break;
// 문자 가져오기
CHAR c = pSrcStr[i];
// [Trim Start] 앞쪽 공백 제거 로직
if (bLeadingSpace)
{
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
{
continue; // 앞쪽 공백은 건너뜀
}
else
{
bLeadingSpace = FALSE; // 공백이 아닌 문자를 처음 만남 -> 이제부터 복사 시작
}
}
// [대문자 변환] 및 복사
pucDest[destIndex] = RtlUpperChar(c);
// [Trim End 준비] 공백이 아닌 문자의 위치를 기억
if (pucDest[destIndex] != ' ' && pucDest[destIndex] != '\t' &&
pucDest[destIndex] != '\r' && pucDest[destIndex] != '\n')
{
lastNonSpaceIndex = destIndex;
}
destIndex++;
}
// 4. [Trim End] 뒤쪽 공백 제거 및 NULL Termination
// 복사된 내용이 하나라도 있다면, 마지막 공백이 아닌 문자 다음을 NULL로 만듦
if (destIndex > 0 && !bLeadingSpace)
{
pucDest[lastNonSpaceIndex + 1] = 0;
}
else
{
// 공백만 있었거나 빈 문자열인 경우
pucDest[0] = 0;
}
KLogEx(DEBUG_TRACE_INFO, "Copied Length(%d), Content(%s)\n", lastNonSpaceIndex + 1, pucDest);
return STATUS_SUCCESS;
}
typedef struct _STORAGE_DEVICE_ID_DESCRIPTOR {
ULONG Version;
ULONG Size;
ULONG NumberOfIdentifiers;
UCHAR Identifiers[1]; // 가변 길이 문자열 배열
} STORAGE_DEVICE_ID_DESCRIPTOR, * PSTORAGE_DEVICE_ID_DESCRIPTOR;
NTSTATUS
GetStorageDeviceId(
__in PDEVICE_OBJECT DeviceObject,
__out PWCHAR* OutInstanceId
)
{
NTSTATUS status = STATUS_SUCCESS;
PIRP Irp;
KEVENT Event;
IO_STATUS_BLOCK Iosb;
STORAGE_PROPERTY_QUERY PropQuery = { 0 };
PSTORAGE_DEVICE_ID_DESCRIPTOR pDesc = NULL;
PVOID QueryBuffer = NULL;
ULONG QuerySize = 1024; // 넉넉하게 잡음
PDEVICE_OBJECT pTopDevice = NULL;
*OutInstanceId = NULL;
pTopDevice = IoGetAttachedDeviceReference(DeviceObject);
// 1. 메모리 할당
QueryBuffer = ExAllocatePoolWithTag(PagedPool, QuerySize, 'StoI');
if (!QueryBuffer) return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(QueryBuffer, QuerySize);
// 2. 쿼리 설정: StorageDeviceIdProperty (중요!)
PropQuery.PropertyId = StorageDeviceIdProperty; // enum 값: 2
PropQuery.QueryType = PropertyStandardQuery;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
// 3. IRP 생성 및 전송
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_QUERY_PROPERTY,
pTopDevice,
&PropQuery,
sizeof(PropQuery),
QueryBuffer,
QuerySize,
FALSE,
&Event,
&Iosb
);
if (!Irp)
{
ExFreePoolWithTag(QueryBuffer, 'StoI');
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(pTopDevice, Irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
status = Iosb.Status;
}
// 4. 결과 파싱
if (NT_SUCCESS(status) && Iosb.Information > sizeof(STORAGE_DEVICE_ID_DESCRIPTOR))
{
pDesc = (PSTORAGE_DEVICE_ID_DESCRIPTOR)QueryBuffer;
if (pDesc->NumberOfIdentifiers > 0)
{
// Identifiers는 Null로 구분된 ANSI 문자열들의 배열입니다.
// 첫 번째 문자열이 보통 가장 구체적인 PnP ID (Hardware ID)입니다.
PCHAR pAnsiId = (PCHAR)pDesc->Identifiers;
// ANSI String -> Unicode String 변환 필요
ANSI_STRING ansiStr;
UNICODE_STRING uniStr;
RtlInitAnsiString(&ansiStr, pAnsiId);
status = RtlAnsiStringToUnicodeString(&uniStr, &ansiStr, TRUE); // TRUE: Allocate Memory
if (NT_SUCCESS(status))
{
// 호출자에게 전달 (호출자가 나중에 ExFreePool 해야 함)
// RtlAnsiStringToUnicodeString이 할당한 버퍼는 PagedPool입니다.
// 여기서는 호출자가 편하게 쓰도록 새 버퍼에 복사하거나, uniStr.Buffer를 그대로 줍니다.
// 안전하게 복사본을 만들어 리턴 (NonPagedPool 권장 시)
ULONG uniSize = uniStr.Length + sizeof(WCHAR);
PWCHAR pResult = ExAllocatePoolWithTag(NonPagedPool, uniSize, 'IDst');
if (pResult)
{
RtlZeroMemory(pResult, uniSize);
RtlCopyMemory(pResult, uniStr.Buffer, uniStr.Length);
*OutInstanceId = pResult;
}
RtlFreeUnicodeString(&uniStr); // 임시 변환 버퍼 해제
}
}
}
ExFreePoolWithTag(QueryBuffer, 'StoI');
return status;
}
NTSTATUS
GetStorageProperty(
__in PDEVICE_OBJECT device_object,
__in PVOLUME_CONTEXT pVolumeContext
)
{
PIRP Irp;
KEVENT Event;
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK Iosb;
STORAGE_PROPERTY_QUERY PropQuery = { 0, };
PVOID QueryBuffer = NULL;
ULONG QuerySize = QUERY_BUFFER_SIZE;
PSTORAGE_DEVICE_DESCRIPTOR pDesc;
//PDEVICE_OBJECT pTopDevice = NULL;
//ULONG size = 0;
__try
{
//pTopDevice = IoGetAttachedDeviceReference(device_object);
QueryBuffer = ExAllocatePoolWithTag(PagedPool, QuerySize, _ALLOC_TAG);
if (!QueryBuffer)
{
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
RtlZeroMemory(&PropQuery, sizeof(PropQuery));
RtlZeroMemory(QueryBuffer, QuerySize);
PropQuery.PropertyId = StorageDeviceProperty;
PropQuery.QueryType = PropertyStandardQuery;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_QUERY_PROPERTY,
device_object,
&PropQuery,
sizeof(PropQuery),
QueryBuffer,
QuerySize,
FALSE,
&Event,
&Iosb
);
if (!Irp)
{
status = STATUS_INSUFFICIENT_RESOURCES;
__leave;
}
status = IoCallDriver(device_object, Irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(
&Event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL
);
status = Iosb.Status;
}
if (!NT_SUCCESS(status))
{
__leave;
}
if (Iosb.Information < sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
status = STATUS_UNSUCCESSFUL;
__leave;
}
pDesc = (PSTORAGE_DEVICE_DESCRIPTOR)QueryBuffer;
if (!pDesc)
__leave;
if (pDesc->Size > Iosb.Information)
{
if (pDesc->Size > QuerySize) pDesc->Size = (ULONG)Iosb.Information;
}
KLogEx(DEBUG_TRACE_INFO, "Version(%x),Size(%x),DeviceType(%x),DeviceTypeModifier(%x)\n", pDesc->Version, pDesc->Size, pDesc->DeviceType, pDesc->DeviceTypeModifier);
KLogEx(DEBUG_TRACE_INFO, "bustype(%d),Removal(%d),vidoffset(%x),pidoffset(%x)\n", pDesc->BusType, pDesc->RemovableMedia, pDesc->VendorIdOffset, pDesc->ProductIdOffset);
KLogEx(DEBUG_TRACE_INFO, "ProductRevisionOffset(%d),SerialNumberOffset(%d)\n", pDesc->ProductRevisionOffset, pDesc->SerialNumberOffset);
pVolumeContext->bustype = pDesc->BusType;
SafeSetVolumContextParam(pVolumeContext->vendorid, pDesc->Size, (PUCHAR)pDesc, pDesc->VendorIdOffset);
SafeSetVolumContextParam(pVolumeContext->productid, pDesc->Size, (PUCHAR)pDesc, pDesc->ProductIdOffset);
SafeSetVolumContextParam(pVolumeContext->productrevisionlevel, pDesc->Size, (PUCHAR)pDesc, pDesc->ProductRevisionOffset);
SafeSetVolumContextParam(pVolumeContext->vendorspecific, pDesc->Size, (PUCHAR)pDesc, pDesc->SerialNumberOffset);
}
__finally
{
if (QueryBuffer)
{
ExFreePoolWithTag(QueryBuffer, _ALLOC_TAG);
QueryBuffer = NULL;
}
}
return status;
}
typedef struct _GET_ID_WORK_CONTEXT {
PFLT_FILTER Filter;
PFLT_INSTANCE Instance; // 인스턴스 핸들 (컨텍스트 접근용)
PDEVICE_OBJECT VolumeDeviceObject; // 볼륨 디바이스
} GET_ID_WORK_CONTEXT, * PGET_ID_WORK_CONTEXT;
// [작업자 스레드 함수] 여기서 안전하게 Hardware ID를 구합니다.
VOID
Bs1FltGetHardwareIdWorker(
__in PFLT_GENERIC_WORKITEM FltWorkItem,
__in PVOID ConnectionCookie,
__in PVOID Context_
)
{
PGET_ID_WORK_CONTEXT pWorkCtx = (PGET_ID_WORK_CONTEXT)Context_;
PVOLUME_CONTEXT pVolumeCtx = NULL;
PDEVICE_OBJECT pPhysicalDisk = NULL;
PDEVICE_OBJECT pPhysicalPDO = NULL;
NTSTATUS status;
PWCHAR pHardwareId = NULL;
ULONG PropertySize = 0;
UNREFERENCED_PARAMETER(ConnectionCookie);
KLogEx(DEBUG_TRACE_INFO, "[Worker] Starting to query Hardware ID async...\n");
// 1. 인스턴스 컨텍스트 가져오기
status = FltGetInstanceContext(pWorkCtx->Instance, &pVolumeCtx);
if (NT_SUCCESS(status))
{
// 2. 물리 디스크 객체 구하기 (이제 안전함!)
// (이전에 만든 GetPhysicalDiskDeviceObject 함수 사용)
status = GetPhysicalDiskDeviceObject(pWorkCtx->VolumeDeviceObject, &pPhysicalDisk);
if (NT_SUCCESS(status) && pPhysicalDisk != NULL)
{
// 3. PDO 찾기
pPhysicalPDO = IoGetDeviceAttachmentBaseRef(pPhysicalDisk);
if (pPhysicalPDO)
{
// 4. Hardware ID 쿼리
status = QueryDeviceProperty(pPhysicalPDO, DevicePropertyHardwareID, &pHardwareId, &PropertySize);
if (NT_SUCCESS(status) && pHardwareId)
{
KLogEx(DEBUG_TRACE_INFO, "[Worker] Found Real Hardware ID: %S\n", pHardwareId);
// TODO: pVolumeCtx에 ID 저장 (필요시 파싱)
// 예: RtlStringCbCopyW(pVolumeCtx->HardwareId, ...);
ExFreePool(pHardwareId);
}
ObDereferenceObject(pPhysicalPDO);
}
ObDereferenceObject(pPhysicalDisk);
}
else
{
KLogEx(DEBUG_TRACE_INFO, "[Worker] Failed to get Physical Disk: 0x%x\n", status);
}
// 컨텍스트 해제
FltReleaseContext(pVolumeCtx);
}
// 3. 정리
ObDereferenceObject(pWorkCtx->VolumeDeviceObject);
FltObjectDereference(pWorkCtx->Instance); // 인스턴스 참조 해제
FltFreeGenericWorkItem(FltWorkItem);
ExFreePool(pWorkCtx);
}
NTSTATUS
GetVolumeProperties(
__in PCFLT_RELATED_OBJECTS FltObjects,
__in PVOLUME_CONTEXT pVolumeContext
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT pDevice = NULL;
PVOID pBuffer = NULL;
ULONG PropertySize = 0;
//PWCHAR pDeviceId = NULL;
//PDEVICE_OBJECT physicalDeviceObject = NULL;
ASSERT(ARGUMENT_PRESENT(pVolumeContext));
__try
{
status = FltGetDiskDeviceObject(FltObjects->Volume, &pDevice);
if (!NT_SUCCESS(status))
{
pDevice = NULL;
__leave;
}
//status = GetPhysicalDiskDeviceObject((PDEVICE_OBJECT)FltObjects->Volume, &pDevice);
status = GetStorageProperty(pDevice, pVolumeContext);
status = QueryDeviceProperty(pDevice, DevicePropertyRemovalPolicy, &pBuffer, &PropertySize);
if (NT_SUCCESS(status))
{
PDEVICE_REMOVAL_POLICY pRemovalPolicy = (PDEVICE_REMOVAL_POLICY)pBuffer;
pVolumeContext->removalpolicy = *pRemovalPolicy;
//pVolumeContext->device_object = pDevice;
KLogEx(DEBUG_TRACE_INFO, " REMOVAL_POLICY pDevice(%p),removalpolicy(%d)\n", pDevice, pVolumeContext->removalpolicy);
ExFreePoolWithTag(pBuffer, _ALLOC_TAG);
pBuffer = NULL;
}
else
{
KLogEx(DEBUG_TRACE_INFO, "Query DevicePropertyRemovalPolicy Failed 0x%x\n", status);
}
status = QueryDeviceProperty(pDevice, DevicePropertyFriendlyName, &pBuffer, &PropertySize);
if (NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_INFO, "DevicePropertyFriendlyName(%S)\n", (wchar_t*)pBuffer);
ExFreePoolWithTag(pBuffer, _ALLOC_TAG);
pBuffer = NULL;
}
else
{
KLogEx(DEBUG_TRACE_INFO, "Query DevicePropertyRemovalPolicy Failed 0x%x\n", status);
}
status= QueryDeviceProperty(pDevice, DevicePropertyDeviceDescription, &pBuffer, &PropertySize);
if (NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_INFO, "DevicePropertyDeviceDescription(%S)\n", (wchar_t*)pBuffer);
ExFreePoolWithTag(pBuffer, _ALLOC_TAG);
pBuffer = NULL;
}
else
{
KLogEx(DEBUG_TRACE_INFO, "Query DevicePropertyRemovalPolicy Failed 0x%x\n", status);
}
status = QueryDeviceProperty(pDevice, DevicePropertyHardwareID, &pBuffer, &PropertySize);
if (NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_INFO, "DevicePropertyHardwareID(%S)\n", (wchar_t*)pBuffer);
ExFreePoolWithTag(pBuffer, _ALLOC_TAG);
pBuffer = NULL;
}
else
{
KLogEx(DEBUG_TRACE_INFO, "Query DevicePropertyRemovalPolicy Failed 0x%x\n", status);
}
status = QueryDeviceProperty(pDevice, DevicePropertyEnumeratorName, &pBuffer, &PropertySize);
if (NT_SUCCESS(status))
{
KLogEx(DEBUG_TRACE_INFO, "DevicePropertyEnumeratorName(%S)\n", (wchar_t*)pBuffer);
ExFreePoolWithTag(pBuffer, _ALLOC_TAG);
pBuffer = NULL;
}
else
{
KLogEx(DEBUG_TRACE_INFO, "Query DevicePropertyRemovalPolicy Failed 0x%x\n", status);
}
//status = GetStorageDeviceId(pDevice, &pDeviceId);
//if (NT_SUCCESS(status) && pDeviceId != NULL)
//{
// KLogEx(DEBUG_TRACE_INFO, "Found Device ID: %S\n", pDeviceId);
// // 결과 예시: "USB\VID_346D&PID_5678&REV_0100"
// // 여기서 문자열 파싱하여 정책 적용
// ExFreePoolWithTag(pDeviceId, 'IDst');
//}
//else
//{
// KLogEx(DEBUG_TRACE_INFO, "[IOCTL] Failed 0x%x. Trying PnP Query...\n", status);
// // =================================================================
// // 시도 2: PnP 방식 (PDO에게 요청) - HardwareID / InstanceID
// // =================================================================
// // IOCTL을 처리하는 FDO의 가장 밑바닥(PDO)을 구합니다.
// physicalDeviceObject = IoGetDeviceAttachmentBaseRef(pDevice);
// if (physicalDeviceObject)
// {
// // PnP 쿼리는 PDO에게 해야 합니다.
// // DevicePropertyHardwareID (리스트) 또는 DevicePropertyInstanceID (단일) 시도
// PWCHAR pHardwareId = NULL;
// // 보통 HardwareID가 가장 확실하게 정보(VID/PID)를 줍니다.
// // HardwareID는 MULTI_SZ 형식이므로 첫 번째 문자열만 쓰면 됩니다.
// status = QueryDeviceProperty(physicalDeviceObject, DevicePropertyHardwareID, &pHardwareId, &PropertySize);
// if (NT_SUCCESS(status) && pHardwareId)
// {
// KLogEx(DEBUG_TRACE_INFO, "[PnP] Hardware ID: %S\n", pHardwareId);
// ExFreePoolWithTag(pHardwareId, _ALLOC_TAG);
// }
// else
// {
// KLogEx(DEBUG_TRACE_INFO, "[PnP] Query DevicePropertyHardwareID Failed 0x%x\n", status);
// }
// ObDereferenceObject(physicalDeviceObject);
// }
//}
status = STATUS_SUCCESS;
}
__finally
{
if (pDevice)
ObDereferenceObject(pDevice);
}
return status;
}
// -----------------------------------------------------------------------------
// Helper: PnP 속성(Registry Property) 조회 함수
// 주의: 반드시 PDO(Physical Device Object)를 넘겨줘야 정확한 값이 나옵니다.
// -----------------------------------------------------------------------------
NTSTATUS
QueryDeviceRegistryProperty(
__in PDEVICE_OBJECT pPdo,
__in DEVICE_REGISTRY_PROPERTY DeviceProperty,
__out PWCHAR pBuffer,
__in ULONG BufferLength
)
{
NTSTATUS status;
ULONG ResultLength = 0;
// 버퍼 초기화
RtlZeroMemory(pBuffer, BufferLength);
status = IoGetDeviceProperty(
pPdo,
DeviceProperty,
BufferLength,
pBuffer,
&ResultLength
);
return status;
}
// -----------------------------------------------------------------------------
// Helper: 스토리지 속성(IOCTL) 조회 함수
// 주의: IOCTL을 처리할 수 있는 스택 상단(Top Device) 혹은 FDO에 보내야 합니다.
// -----------------------------------------------------------------------------
NTSTATUS
GetStorageHardwareProperty(
__in PDEVICE_OBJECT pDeviceObject,
__inout PFULL_DISK_INFO pInfo
)
{
NTSTATUS status = STATUS_SUCCESS;
PIRP Irp;
KEVENT Event;
IO_STATUS_BLOCK Iosb;
STORAGE_PROPERTY_QUERY Query = { 0 };
PSTORAGE_DEVICE_DESCRIPTOR pDesc = NULL;
PVOID pBuffer = NULL;
ULONG BufferSize = 1024; // 넉넉하게 잡음
// 버퍼 할당
pBuffer = ExAllocatePoolWithTag(PagedPool, BufferSize, _ALLOC_TAG);
if (!pBuffer) return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(pBuffer, BufferSize);
// 쿼리 설정
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
// IOCTL 생성
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_QUERY_PROPERTY,
pDeviceObject,
&Query,
sizeof(Query),
pBuffer,
BufferSize,
FALSE,
&Event,
&Iosb
);
if (!Irp)
{
ExFreePoolWithTag(pBuffer, _ALLOC_TAG);
return STATUS_INSUFFICIENT_RESOURCES;
}
// 드라이버 호출
status = IoCallDriver(pDeviceObject, Irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
status = Iosb.Status;
}
if (NT_SUCCESS(status))
{
pDesc = (PSTORAGE_DEVICE_DESCRIPTOR)pBuffer;
pInfo->BusType = pDesc->BusType;
pInfo->RemovableMedia = pDesc->RemovableMedia;
SafeSetVolumContextParam((PUCHAR)pInfo->VendorId, pDesc->Size, (PUCHAR)pDesc, pDesc->VendorIdOffset);
SafeSetVolumContextParam((PUCHAR)pInfo->ProductId, pDesc->Size, (PUCHAR)pDesc, pDesc->ProductIdOffset);
SafeSetVolumContextParam((PUCHAR)pInfo->Productrevisionlevel, pDesc->Size, (PUCHAR)pDesc, pDesc->ProductRevisionOffset);
SafeSetVolumContextParam((PUCHAR)pInfo->SerialNumber, pDesc->Size, (PUCHAR)pDesc, pDesc->SerialNumberOffset);
}
ExFreePoolWithTag(pBuffer, _ALLOC_TAG);
return status;
}
NTSTATUS
GetDeviceInstanceId(
__in PDEVICE_OBJECT pDeviceObject,
__in const DEVPROPKEY* pKey,
__out PWCHAR pBuffer,
__in ULONG BufferLength
)
{
NTSTATUS status;
//PDEVICE_OBJECT pPdo = NULL;
ULONG returnLength = 0;
DEVPROPTYPE propType;
//// 1. PDO 구하기 (필수)
//pPdo = IoGetDeviceAttachmentBaseRef(pDeviceObject);
//if (pPdo == NULL)
//{
// return STATUS_NO_SUCH_DEVICE;
//}
// 2. IoGetDevicePropertyData 호출 (Vista 이상 지원)
// DEVPKEY_Device_InstanceId 키를 사용하여 인스턴스 경로를 요청합니다.
status = IoGetDevicePropertyData(
pDeviceObject,
pKey,
LOCALE_NEUTRAL,
0,
BufferLength,
pBuffer,
&returnLength,
&propType
);
// 3. 참조 해제
//ObDereferenceObject(pPdo);
return status;
}
NTSTATUS
GetUsbIdFromVolume(
__in PDEVICE_OBJECT pVolumeDeviceObject,
__out PWCHAR pBuffer,
__in ULONG BufferLength
)
{
NTSTATUS status;
PIRP pIrp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PVOLUME_DISK_EXTENTS pExtents = NULL;
ULONG extentsSize = sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT) * 4; // 넉넉하게 잡음
UNICODE_STRING diskName;
WCHAR nameBuffer[64];
PFILE_OBJECT pDiskFileObject = NULL;
PDEVICE_OBJECT pDiskDeviceObject = NULL;
PDEVICE_OBJECT pDiskPdo = NULL;
// ---------------------------------------------------------
// 1단계: 볼륨이 위치한 디스크 번호 알아내기 (IOCTL)
// ---------------------------------------------------------
pExtents = (PVOLUME_DISK_EXTENTS)ExAllocatePoolWithTag(NonPagedPool, extentsSize, 'Tag1');
if (!pExtents) return STATUS_INSUFFICIENT_RESOURCES;
KeInitializeEvent(&event, NotificationEvent, FALSE);
// 볼륨 드라이버 스택의 최상단 장치 구하기 (IOCTL 전송용)
PDEVICE_OBJECT pTopDevice = IoGetAttachedDeviceReference(pVolumeDeviceObject);
pIrp = IoBuildDeviceIoControlRequest(
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
pTopDevice,
NULL, 0,
pExtents, extentsSize,
FALSE, &event, &ioStatus
);
if (pIrp == NULL) {
ObDereferenceObject(pTopDevice);
ExFreePool(pExtents);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(pTopDevice, pIrp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
ObDereferenceObject(pTopDevice); // 참조 해제
if (!NT_SUCCESS(status) || pExtents->NumberOfDiskExtents == 0) {
ExFreePool(pExtents);
return status; // 디스크 정보를 얻을 수 없음
}
// ---------------------------------------------------------
// 2단계: 디스크 번호로 물리 디스크 장치 객체(Pointer) 얻기
// ---------------------------------------------------------
// 보통 첫 번째 Extent가 해당 디스크입니다.
ULONG diskNumber = pExtents->Extents[0].DiskNumber;
ExFreePool(pExtents); // 더 이상 필요 없음
// 디스크 장치 이름 생성 (예: \Device\Harddisk2\DR2)
RtlStringCchPrintfW(nameBuffer, 64, L"\\Device\\Harddisk%d\\DR%d", diskNumber, diskNumber);
RtlInitUnicodeString(&diskName, nameBuffer);
status = IoGetDeviceObjectPointer(&diskName, FILE_READ_ATTRIBUTES, &pDiskFileObject, &pDiskDeviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
// ---------------------------------------------------------
// 3단계: 물리 디스크의 PDO 구하기 (핵심)
// ---------------------------------------------------------
pDiskPdo = IoGetDeviceAttachmentBaseRef(pDiskDeviceObject);
// IoGetDeviceObjectPointer로 얻은 FileObject는 해제해야 함
ObDereferenceObject(pDiskFileObject);
if (!pDiskPdo) {
return STATUS_NO_SUCH_DEVICE;
}
// ---------------------------------------------------------
// 4단계: 드디어! USB\VID... 진짜 ID 쿼리
// ---------------------------------------------------------
// 기존에 만드신 함수나 직접 호출 사용
// 여기서는 직접 호출 예시:
ULONG returnLength = 0;
DEVPROPTYPE propType;
status = IoGetDevicePropertyData(
pDiskPdo,
&DEVPKEY_Device_InstanceId, // USB\VID_...가 들어있는 키
LOCALE_NEUTRAL,
0,
BufferLength,
pBuffer,
&returnLength,
&propType
);
// PDO 참조 해제
ObDereferenceObject(pDiskPdo);
return status;
}
NTSTATUS
GetIdFromDiskDeviceObject(
__in PDEVICE_OBJECT pDiskDeviceObject,
__out PWCHAR pBuffer,
__in ULONG BufferLength
)
{
NTSTATUS status;
PDEVICE_OBJECT pDiskPdo = NULL;
ULONG returnLength = 0;
DEVPROPTYPE propType;
// 1. 디스크 스택의 최하단 PDO (USBSTOR 등) 구하기
pDiskPdo = IoGetDeviceAttachmentBaseRef(pDiskDeviceObject);
if (!pDiskPdo) {
return STATUS_NO_SUCH_DEVICE;
}
// 2. PDO에게 Instance ID 물어보기
status = IoGetDevicePropertyData(
pDiskPdo,
&DEVPKEY_Device_InstanceId,
LOCALE_NEUTRAL,
0,
BufferLength,
pBuffer,
&returnLength,
&propType
);
// 3. 참조 해제
ObDereferenceObject(pDiskPdo);
return status;
}
VOID
SafeQueryIdRoutine(
_In_ PFLT_GENERIC_WORKITEM FltWorkItem,
_In_ PFLT_FILTER Filter,
_In_ PVOID Context
)
{
PFLT_VOLUME Volume = (PFLT_VOLUME)Context;
PDEVICE_OBJECT pVolumeDeviceObject = NULL;
NTSTATUS status;
WCHAR buffer[200];
PDEVICE_OBJECT pDiskPdo = NULL;
ULONG returnLength = 0;
DEVPROPTYPE propType;
UNREFERENCED_PARAMETER(Filter);
// 1. 볼륨의 DeviceObject 얻기
status = FltGetDiskDeviceObject(Volume, &pVolumeDeviceObject);
if (NT_SUCCESS(status)) {
pDiskPdo = IoGetDeviceAttachmentBaseRef(pVolumeDeviceObject);
if (pDiskPdo) {
status = IoGetDevicePropertyData(
pDiskPdo,
&DEVPKEY_Device_InstanceId,
LOCALE_NEUTRAL,
0, sizeof(buffer), buffer, &returnLength, &propType
);
if (NT_SUCCESS(status)) {
KLogEx(DEBUG_TRACE_INFO, "USB ID Found: %ws\n", buffer);
// TODO: 볼륨 컨텍스트에 ID 저장
}
else
{
KLogEx(DEBUG_TRACE_INFO, "DEVPKEY_Device_InstanceId fail : %x\n", status);
}
status = IoGetDevicePropertyData(
pDiskPdo,
&DEVPKEY_Device_Parent, // 부모 키 사용
LOCALE_NEUTRAL,
0, sizeof(buffer), buffer, &returnLength, &propType
);
if (NT_SUCCESS(status)) {
KLogEx(DEBUG_TRACE_INFO, "[Bs1Flt] Parent ID: %ws\n", buffer);
// 결과 예시: USB\VID_346D&PID_5678\8328501217610604362
}
else {
KLogEx(DEBUG_TRACE_INFO, "[Bs1Flt] DEVPKEY_Device_Parent Failed: 0x%08x\n", status);
}
ObDereferenceObject(pDiskPdo);
}
//status = GetUsbIdFromVolume(pVolumeDeviceObject, buffer, sizeof(buffer));
//if (NT_SUCCESS(status)) {
// KLogEx(DEBUG_TRACE_INFO, "USB ID Found: %ws\n", buffer);
// // TODO: 볼륨 컨텍스트에 ID 저장
//}
//else
//{
// KLogEx(DEBUG_TRACE_INFO, "GetUsbIdFromVolume : %x\n", status);
//}
//status = GetIdFromDiskDeviceObject(pVolumeDeviceObject, buffer, sizeof(buffer));
//if (NT_SUCCESS(status)) {
// KLogEx(DEBUG_TRACE_INFO, "[Bs1Flt] USB ID Found: %ws\n", buffer);
// // TODO: 여기서 전역 리스트나 Context에 ID를 저장하고 정책 적용
// // CheckUsbPolicy(instanceId);
//}
//else {
// KLogEx(DEBUG_TRACE_INFO, "[Bs1Flt] Failed to query ID from PDO: 0x%08x\n", status);
//}
ObDereferenceObject(pVolumeDeviceObject);
}
// 3. 정리
FltObjectDereference(Volume); // 참조 해제
FltFreeGenericWorkItem(FltWorkItem);
}
// [헬퍼 함수] 진짜 PDO를 구하는 함수 (필수 추가)
PDEVICE_OBJECT GetPdoFromDeviceObject(PDEVICE_OBJECT DeviceObject)
{
PDEVICE_OBJECT pdo = NULL;
PDEVICE_RELATIONS deviceRelations = NULL;
IO_STATUS_BLOCK ioStatus;
KEVENT event;
PIRP irp;
NTSTATUS status;
if (!DeviceObject) return NULL;
KeInitializeEvent(&event, NotificationEvent, FALSE);
// 스택의 최상단 디바이스를 구해서 IRP를 보냅니다.
PDEVICE_OBJECT targetDevice = IoGetAttachedDeviceReference(DeviceObject);
irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, targetDevice, NULL, 0, NULL, &event, &ioStatus);
if (!irp) {
ObDereferenceObject(targetDevice);
return NULL;
}
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(irp);
irpSp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
irpSp->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
status = IoCallDriver(targetDevice, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
if (NT_SUCCESS(status) && ioStatus.Information)
{
deviceRelations = (PDEVICE_RELATIONS)ioStatus.Information;
if (deviceRelations->Count > 0)
{
pdo = deviceRelations->Objects[0]; // 이것이 진짜 PDO
KLogEx(DEBUG_TRACE_ERROR, "deviceRelations->Count(% d)\n", deviceRelations->Count);
// 만약 Count가 1보다 크면 나머지는 해제 (매우 드문 케이스)
for (ULONG i = 1; i < deviceRelations->Count; i++) {
ObDereferenceObject(deviceRelations->Objects[i]);
}
}
ExFreePool(deviceRelations);
}
ObDereferenceObject(targetDevice);
return pdo;
}
// -----------------------------------------------------------------------------
// [MAIN] 디스크 정보 통합 수집 함수
// pDeviceObject: 미니필터에서 FltGetDiskDeviceObject 등으로 구한 디바이스 객체
// -----------------------------------------------------------------------------
NTSTATUS
CollectAllDiskInfo(
__in PDEVICE_OBJECT pDeviceObject,
__out PFULL_DISK_INFO pOutInfo
)
{
//NTSTATUS status;
PDEVICE_OBJECT pPdo = NULL;
PDEVICE_OBJECT pTopDevice = NULL;
RtlZeroMemory(pOutInfo, sizeof(FULL_DISK_INFO));
// PDO 구하기 (QueryDeviceProperty용)
// IoGetDeviceAttachmentBaseRef는 스택의 최하단(PDO)을 반환하며 레퍼런스 카운트를 증가시킵니다.
//pPdo = IoGetDeviceAttachmentBaseRef(pDeviceObject);
pPdo = GetPdoFromDeviceObject(pDeviceObject);
if (!pPdo)
{
return STATUS_NO_SUCH_DEVICE;
}
// Top Device 구하기 (GetStorageProperty용)
// IOCTL은 스택의 최상단으로 보내는 것이 가장 안전합니다.
pTopDevice = IoGetAttachedDeviceReference(pDeviceObject);
if (!pTopDevice)
{
ObDereferenceObject(pPdo);
return STATUS_NO_SUCH_DEVICE;
}
__try
{
// =================================================================
// PnP 레지스트리 속성 수집 (PDO 사용)
// =================================================================
if (pPdo != NULL)
{
QueryDeviceRegistryProperty(pPdo, DevicePropertyFriendlyName, pOutInfo->FriendlyName, sizeof(pOutInfo->FriendlyName));
QueryDeviceRegistryProperty(pPdo, DevicePropertyDeviceDescription, pOutInfo->Description, sizeof(pOutInfo->Description));
QueryDeviceRegistryProperty(pPdo, DevicePropertyHardwareID, pOutInfo->HardwareId, sizeof(pOutInfo->HardwareId));
QueryDeviceRegistryProperty(pPdo, DevicePropertyEnumeratorName, pOutInfo->EnumeratorName, sizeof(pOutInfo->EnumeratorName));
GetDeviceInstanceId(pPdo, &DEVPKEY_Device_InstanceId, pOutInfo->InstanceId, sizeof(pOutInfo->InstanceId));
}
else
{
// PDO를 못 구한 경우 (가상 드라이브 등) 로그 남김
KLogEx(DEBUG_TRACE_ERROR, "CollectAllDiskInfo: No valid PDO found. Skipping PnP props.\n");
}
//GetDeviceInstanceId(pPdo, &DEVPKEY_Device_Parent, pOutInfo->ParentId, sizeof(pOutInfo->ParentId));
//DEVPKEY_Device_Parent
// DEVPKEY_Device_InstanceId
// =================================================================
// 하드웨어 물리 속성 수집 (Top Device 사용)
// =================================================================
GetStorageHardwareProperty(pTopDevice, pOutInfo);
}
__finally
{
// 사용한 객체 참조 해제 (필수)
if (pPdo) ObDereferenceObject(pPdo);
if (pTopDevice) ObDereferenceObject(pTopDevice);
}
return STATUS_SUCCESS;
}