#include "precomp.h" enum { bluetooth_btkrnl = 0, bluetooth_rfcomm, bluetooth_btwusb, bluetooth_bthusb, bluetooth_bthenum, //Å×½ºÆ® ÇÊ¿ä.. bluetooth_maximum }; static WCHAR* s_bthname[] = { L"\\Driver\\BTKRNL", L"\\Driver\\RFCOMM", L"\\Driver\\BTWUSB", L"\\Driver\\BTHUSB", L"\\Driver\\BTHENUM", NULL }; static BOOLEAN enable_bluetoothhook = FALSE; NTSTATUS BtkrnHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS RfcommHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS BtwusbHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS BthusbHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS BthEnumHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp); static PDRIVER_DISPATCH s_ProxyDispatchers[bluetooth_maximum] = { BtkrnHookDispatch, RfcommHookDispatch, BtwusbHookDispatch, BthusbHookDispatch, BthEnumHookDispatch //Å×½ºÆ® ÇÊ¿ä }; NTSTATUS BtkrnlDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS RfcommInternalDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS BtwusbDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS BthusbDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp); NTSTATUS BthEnumPnpControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp); #define BTKRNL_COMMON_HOOK_HANDLERS \ [IRP_MJ_DEVICE_CONTROL] = { NULL, IRP_MJ_DEVICE_CONTROL, TRUE, BtkrnlDeviceIoControl }, \ #define RFCOMM_COMMON_HOOK_HANDLERS \ [IRP_MJ_INTERNAL_DEVICE_CONTROL] = { NULL, IRP_MJ_INTERNAL_DEVICE_CONTROL, TRUE, RfcommInternalDeviceIoControl }, \ #define BTWUSB_COMMON_HOOK_HANDLERS \ [IRP_MJ_DEVICE_CONTROL] = { NULL, IRP_MJ_DEVICE_CONTROL, TRUE, BtwusbDeviceIoControl }, \ #define BTHUSB_COMMON_HOOK_HANDLERS \ [IRP_MJ_DEVICE_CONTROL] = { NULL, IRP_MJ_DEVICE_CONTROL, TRUE, BthusbDeviceIoControl }, \ #define BTHENUM_COMMON_HOOK_HANDLERS \ [IRP_MJ_PNP] = { NULL, IRP_MJ_PNP, TRUE, BthEnumPnpControl }, \ static HOOK_CONTEXT g_BlueToothHookContexts[] = { { NULL, FALSE, 0, { BTKRNL_COMMON_HOOK_HANDLERS } }, { NULL, FALSE, 0, { RFCOMM_COMMON_HOOK_HANDLERS } }, { NULL, FALSE, 0, { BTWUSB_COMMON_HOOK_HANDLERS } }, { NULL, FALSE, 0, { BTHUSB_COMMON_HOOK_HANDLERS } } //{ NULL, FALSE, 0, { BTHENUM_COMMON_HOOK_HANDLERS } } }; NTSTATUS BlueToothHookDispatch_Common(ULONG ContextIndex, PDEVICE_OBJECT deviceObject, PIRP irp) { NTSTATUS NtStatus = STATUS_SUCCESS; PIO_STACK_LOCATION pCurrentIoStack = IoGetCurrentIrpStackLocation(irp); ULONG id = pCurrentIoStack->MajorFunction; PHOOK_CONTEXT hook = NULL; PDRIVER_DISPATCH pOrgHandler = NULL; if (ContextIndex >= ARRAYSIZE(g_BlueToothHookContexts)) return STATUS_UNSUCCESSFUL; InterlockedIncrement((volatile LONG*)&g_BlueToothHookContexts[ContextIndex].IrpEnterCount); hook = &g_BlueToothHookContexts[ContextIndex]; // 2. ÈÄÅ· À¯È¿¼º °Ë»ç if (hook->IsHooked && hook->HookHandlers[id].IsHook && hook->HookHandlers[id].Work) { pOrgHandler = hook->HookHandlers[id].pOrgHandler; NtStatus = hook->HookHandlers[id].Work( pOrgHandler, deviceObject, irp ); } else { NtStatus = STATUS_UNSUCCESSFUL; } InterlockedDecrement((volatile LONG*)&g_BlueToothHookContexts[ContextIndex].IrpEnterCount); return NtStatus; } NTSTATUS BthEnumHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp) { return BlueToothHookDispatch_Common(bluetooth_bthenum, deviceObject, irp); } NTSTATUS BtkrnHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp) { return BlueToothHookDispatch_Common(bluetooth_btkrnl, deviceObject, irp); } NTSTATUS RfcommHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp) { return BlueToothHookDispatch_Common(bluetooth_rfcomm, deviceObject, irp); } NTSTATUS BtwusbHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp) { return BlueToothHookDispatch_Common(bluetooth_btwusb, deviceObject, irp); } NTSTATUS BthusbHookDispatch(PDEVICE_OBJECT deviceObject, PIRP irp) { return BlueToothHookDispatch_Common(bluetooth_bthusb, deviceObject, irp); } // ¸ÅÇÎÀ» À§ÇÑ ±¸Á¶Ã¼ Á¤ÀÇ typedef struct _BTH_UUID_MAPPING { PWCHAR UuidLower; // ¼Ò¹®ÀÚ UUID PWCHAR UuidUpper; // ´ë¹®ÀÚ UUID PWCHAR ReadableName; // º¯È¯µÉ ÇѱÛ/¿µ¹® ¼³¸í } BTH_UUID_MAPPING, * PBTH_UUID_MAPPING; // ±âÁ¸ ¿Ï·á ·çƾÀÇ Á¤º¸¸¦ ¹é¾÷ÇØµÑ ±¸Á¶Ã¼ ¼±¾ð (Àü¿ª Çì´õ¿¡ ¼±¾ðÇϼ¼¿ä) typedef struct _BTH_HOOK_CONTEXT { PIO_COMPLETION_ROUTINE OldCompletionRoutine; PVOID OldContext; UCHAR OldControl; char processName[50]; } BTH_HOOK_CONTEXT, * PBTH_HOOK_CONTEXT; // ºí·çÅõ½º ÁÖ¿ä ÀåÄ¡ UUID ¸ÅÇÎ Å×À̺í (Àü¿ª ¹è¿­) BTH_UUID_MAPPING g_BthUuidTable[] = { // --- [º¸¾È À§Çù / µ¥ÀÌÅÍ À¯Ãâ (Â÷´Ü ±ÇÀå)] --- { L"1105", L"1105", L"ÆÄÀÏ Àü¼Û (OBEX Object Push)" }, { L"1101", L"1101", L"½Ã¸®¾ó Åë½Å (Serial Port)" }, { L"1115", L"1115", L"ºí·çÅõ½º Å×´õ¸µ/°øÀ¯ (PANU)" }, { L"1116", L"1116", L"ºí·çÅõ½º ÇÖ½ºÆÌ (NAP)" }, { L"1132", L"1132", L"¹®ÀÚ ¸Þ½ÃÁö Á¢±Ù (MAP)" }, // --- [¹Ìµð¾î / ¿Àµð¿À (¼±ÅÃÀû Â÷´Ü)] --- { L"110b", L"110B", L"¿Àµð¿À/Çìµå¼Â ½ºÇÇÄ¿ (A2DP Sink)" }, { L"110a", L"110A", L"¿Àµð¿À ¼Ò½º (A2DP Source)" }, { L"1108", L"1108", L"±¸Çü ¸ð³ë Çìµå¼Â (Headset)" }, { L"111e", L"111E", L"ÇÚÁîÇÁ¸® ÅëÈ­ (Handsfree)" }, { L"110c", L"110C", L"¹Ìµð¾î ¸®¸ðÄÁ (AVRCP)" }, // --- [ÀÔ·Â ÀåÄ¡ (Çã¿ë ±ÇÀå)] --- { L"1124", L"1124", L"¸¶¿ì½º/Űº¸µå (HID)" }, { L"1812", L"1812", L"ÀúÀü·Â ¸¶¿ì½º/Űº¸µå (BLE HID)" }, // ¹è¿­ÀÇ ³¡À» ¾Ë¸®´Â ³Î(NULL) °ª { NULL, NULL, L"¾Ë ¼ö ¾ø´Â ºí·çÅõ½º ÀåÄ¡" } }; /** * Çϵå¿þ¾î ID ¹®ÀÚ¿­ ³»¿¡¼­ ƯÁ¤ UUID¸¦ ã¾Æ Àб⠽¬¿î À̸§À¸·Î ¹ÝȯÇÕ´Ï´Ù. * @param HardwareIdString: °Ë»çÇÒ ¹®ÀÚ¿­ (¿¹: L"BTHENUM\\{0000110b-0000...}") * @return ¸ÅĪµÈ ÀåÄ¡ÀÇ ÇÑ±Û ¼³¸í Æ÷ÀÎÅÍ */ PWCHAR GetReadableBluetoothName(PWCHAR HardwareIdString) { int i = 0; if (HardwareIdString == NULL) { return L"À߸øµÈ ÀåÄ¡ Á¤º¸"; } // ¹è¿­À» ¼øÈ¸ÇÏ¸ç ¸ÅĪµÇ´Â UUID°¡ ÀÖ´ÂÁö °Ë»ç while (g_BthUuidTable[i].UuidLower != NULL) { // ¼Ò¹®ÀÚ ¶Ç´Â ´ë¹®ÀÚ°¡ Æ÷ÇԵǾî ÀÖ´ÂÁö È®ÀÎ (wcsstr) if (wcsstr(HardwareIdString, g_BthUuidTable[i].UuidLower) != NULL || wcsstr(HardwareIdString, g_BthUuidTable[i].UuidUpper) != NULL) { return g_BthUuidTable[i].ReadableName; } i++; } // ¸ÅĪµÇ´Â °ªÀÌ ¾øÀ¸¸é ±âº»°ª ¹Ýȯ return L"¾Ë ¼ö ¾ø´Â ºí·çÅõ½º ÀåÄ¡ (±âŸ)"; } #define TAG_LOG_STR 'goLB' NTSTATUS GetAllHardwareIdsAsString( _In_ PWCHAR MultiSzBuffer, _Out_ PUNICODE_STRING CombinedString ) { PWCHAR currentString; ULONG totalChars = 0; USHORT maxLen = 0; if (MultiSzBuffer == NULL || CombinedString == NULL) { return STATUS_INVALID_PARAMETER; } // 1. º´ÇÕ¿¡ ÇÊ¿äÇÑ Àüü ¹®ÀÚ¿­ ±æÀÌ °è»ê currentString = MultiSzBuffer; while (*currentString != L'\0') { // ÇöÀç ¹®ÀÚ¿­ ±æÀÌ + ±¸ºÐÀÚ("; ") 2±ÛÀÚ totalChars += (ULONG)wcslen(currentString) + 2; // ´ÙÀ½ ¹®ÀÚ¿­·Î Æ÷ÀÎÅÍ À̵¿ (ÇöÀç±æÀÌ + NULL) currentString += wcslen(currentString) + 1; } if (totalChars == 0) { RtlInitUnicodeString(CombinedString, L""); return STATUS_NOT_FOUND; } // UNICODE_STRINGÀÇ ÃÖ´ë Å©±â´Â 65535 ¹ÙÀÌÆ®(USHORT)·Î Á¦ÇÑµÊ if ((totalChars * sizeof(WCHAR) + sizeof(WCHAR)) > 0xFFFF) { return STATUS_BUFFER_OVERFLOW; } // 2. ¸Þ¸ð¸® ÇÒ´ç (NonPagedPool »ç¿ë - ¿Ï·á ·çƾ(DISPATCH_LEVEL)¿¡¼­ ¾ÈÀü) maxLen = (USHORT)(totalChars * sizeof(WCHAR) + sizeof(WCHAR)); // ÃֽŠWDK¿¡¼­´Â NonPagedPoolNx¸¦ ±ÇÀåÇϳª, ±¸¹öÀü ȣȯ¼ºÀ» À§ÇØ NonPagedPool »ç¿ë CombinedString->Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, maxLen, TAG_LOG_STR); if (CombinedString->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // UNICODE_STRING ±¸Á¶Ã¼ ÃʱâÈ­ CombinedString->MaximumLength = maxLen; CombinedString->Length = 0; CombinedString->Buffer[0] = L'\0'; // 3. ¹®ÀÚ¿­ °áÇÕ (Concatenation) currentString = MultiSzBuffer; while (*currentString != L'\0') { UNICODE_STRING tempStr; RtlInitUnicodeString(&tempStr, currentString); // ÇöÀç ¹®ÀÚ¿­À» ºÙÀÓ RtlAppendUnicodeStringToString(CombinedString, &tempStr); currentString += (tempStr.Length / sizeof(WCHAR)) + 1; // ´ÙÀ½ ¹®ÀÚ¿­ÀÌ Á¸ÀçÇÏ¸é ±¸ºÐÀÚ("; ") »ðÀÔ if (*currentString != L'\0') { RtlAppendUnicodeToString(CombinedString, L"; "); } } return STATUS_SUCCESS; } // ----------------------------------------------------------------------- // IRP_MN_QUERY_ID ¿Ï·á ·çƾ (¿©±â¼­ ±â±â Á¾·ù¸¦ ÆÇº°Çϰí Â÷´ÜÇÕ´Ï´Ù) // ----------------------------------------------------------------------- NTSTATUS PnpQueryIdCompletionRoutine( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ) { UNREFERENCED_PARAMETER(DeviceObject); UNREFERENCED_PARAMETER(Context); PBTH_HOOK_CONTEXT hookCtx = (PBTH_HOOK_CONTEXT)Context; ULONG state = GetPolicyState(BDC_BLUETOOTH); ULONG policyLog = IsPolicyLog(BDC_BLUETOOTH); WCHAR processName[50] = { 0, }; WCHAR notice[MAX_PATH] = { 0, }; NTSTATUS status = Irp->IoStatus.Status; BOOLEAN bBlock = FALSE; //if (Irp->PendingReturned) { // IoMarkIrpPending(Irp); //} //PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); // ÇÏÀ§ µå¶óÀ̹ö°¡ 󸮸¦ ¼º°øÇß°í, ¿ì¸®°¡ ¿øÇÏ´Â Hardware IDs ¿äûÀÎ °æ¿ì if (NT_SUCCESS(Irp->IoStatus.Status) /*&& irpSp->MinorFunction == IRP_MN_QUERY_ID && irpSp->Parameters.QueryId.IdType == BusQueryHardwareIDs*/) { if (Irp->IoStatus.Information != 0) { UNICODE_STRING allHwIdsStr; PWCHAR hwIds = (PWCHAR)Irp->IoStatus.Information; status = GetAllHardwareIdsAsString(hwIds, &allHwIdsStr); if (NT_SUCCESS(status)) { PWCHAR name = GetReadableBluetoothName(allHwIdsStr.Buffer); // ·Î±×¿¡ »ç¶÷ÀÌ ÀÐÀ» ¼ö ÀÖ´Â À̸§À¸·Î ³²±â±â À§ÇØ º¯È¯ ½Ãµµ KLogEx(DEBUG_TRACE_INFO, "btheunm connect : %S, %S", name, allHwIdsStr.Buffer); if (wcsstr(allHwIdsStr.Buffer, L"110b") != NULL || wcsstr(allHwIdsStr.Buffer, L"110B") != NULL || wcsstr(allHwIdsStr.Buffer, L"1105") != NULL) { bBlock = TRUE; } //'1124', '1812', '110B', '1108' if (policyLog) { if(hookCtx->processName) RtlStringCbPrintfW(processName, sizeof(processName), L"%S", hookCtx->processName); if (/*state == DISABLE &&*/ bBlock) RtlStringCbPrintfW(notice, sizeof(notice), L"btheunm blocked(%s)(%s)", name, allHwIdsStr.Buffer); else RtlStringCbPrintfW(notice, sizeof(notice), L"btheunm allow(%s)(%s)", name, allHwIdsStr.Buffer); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, state, 0, processName, notice); } if (/*state == DISABLE &&*/ bBlock) { KLogEx(DEBUG_TRACE_INFO, "btheunm connect STATUS_ACCESS_DENIED !!!"); ExFreePool(hwIds); // BthEnumÀÌ ¸¸µé¾îÁØ ¿ø·¡ ¹öÆÛµµ ÇØÁ¦ Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_ACCESS_DENIED; } else { } if(allHwIdsStr.Buffer) ExFreePoolWithTag(allHwIdsStr.Buffer, TAG_LOG_STR); } else { KLogEx(DEBUG_TRACE_ERROR, "GetAllHardwareIdsAsString Fail (%X)", status); } } } // »óÀ§ µå¶óÀ̹ö°¡ ¿ø·¡ µî·ÏÇØµÎ¾ú´ø ¿Ï·á ·çƾÀÌ ÀÖ´Ù¸é È£ÃâÇÏ¿© È帧 º¹¿ø if (hookCtx->OldCompletionRoutine != NULL) { BOOLEAN bInvoke = FALSE; if (NT_SUCCESS(Irp->IoStatus.Status) && (hookCtx->OldControl & SL_INVOKE_ON_SUCCESS)) bInvoke = TRUE; if (!NT_SUCCESS(Irp->IoStatus.Status) && (hookCtx->OldControl & SL_INVOKE_ON_ERROR)) bInvoke = TRUE; if (Irp->Cancel && (hookCtx->OldControl & SL_INVOKE_ON_CANCEL)) bInvoke = TRUE; if (bInvoke) { status = hookCtx->OldCompletionRoutine(DeviceObject, Irp, hookCtx->OldContext); } else if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } } else { if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } } // ÇÒ´çÇß´ø ÄÁÅØ½ºÆ® ¸Þ¸ð¸® ÇØÁ¦ (¸Þ¸ð¸® ¸¯ ¹æÁö) ExFreePoolWithTag(hookCtx, 'kooH'); return status; } NTSTATUS BthEnumPnpControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp); ULONG processId = 0; char szProcessName[20] = { 0, }; if (!g_bs1Flt.IsAttached) goto $BthEnumCleanup; if (!enable_bluetoothhook) goto $BthEnumCleanup; processId = HandleToULong(PsGetCurrentProcessId()); UGetProcessName(szProcessName); // IRP_MN_QUERY_ID (ÀåÄ¡ ½Äº°ÀÚ ¿äû) ÀÏ ¶§¸¸ ¿Ï·á ·çƾÀ» ´ä´Ï´Ù. if (irpSp->MinorFunction == IRP_MN_QUERY_ID && irpSp->Parameters.QueryId.IdType == BusQueryHardwareIDs) { PBTH_HOOK_CONTEXT hookCtx = (PBTH_HOOK_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(BTH_HOOK_CONTEXT), 'kooH'); if (hookCtx != NULL) { // ±âÁ¸ ½ºÅÃÀÇ ¿Ï·á ·çƾ ¹é¾÷ hookCtx->OldCompletionRoutine = irpSp->CompletionRoutine; hookCtx->OldContext = irpSp->Context; hookCtx->OldControl = irpSp->Control; strcpy(hookCtx->processName, szProcessName); // ¿ì¸®ÀÇ ¿Ï·á ·çƾÀ¸·Î ÇöÀç ½ºÅà À§Ä¡ µ¤¾î¾²±â (IoSetCompletionRoutine »ç¿ë ºÒ°¡) irpSp->CompletionRoutine = PnpQueryIdCompletionRoutine; irpSp->Context = hookCtx; irpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; } // ¿ø·¡ÀÇ BthEnum ÇÔ¼ö È£Ãâ return dispath(deviceObject, irp); } $BthEnumCleanup: // ±× ¿ÜÀÇ PnP ¿äûÀº ¿ø·¡ÀÇ BthEnum ÇÔ¼ö·Î ±×´ë·Î ³Ñ°Ü¹ö¸² return dispath(deviceObject, irp); } /** @brief ºí·çÅõ½º µ¿±Û ÈÄÅ·ÇÔ¼ö */ NTSTATUS BtkrnlDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(irp); ULONG buffSize = irpstack->Parameters.DeviceIoControl.InputBufferLength; ULONG controlCode = irpstack->Parameters.DeviceIoControl.IoControlCode; ULONG state = 0; WCHAR processName[50] = { 0, }; ULONG processId = 0; ULONG policyLog = 0; char szProcessName[20] = { 0, }; if (!g_bs1Flt.IsAttached) goto $BtkrnlCleanup; if (!enable_bluetoothhook) goto $BtkrnlCleanup; processId = HandleToULong(PsGetCurrentProcessId()); UGetProcessName(szProcessName); state = GetPolicyState(BDC_BLUETOOTH); policyLog = IsPolicyLog(BDC_BLUETOOTH); if (state == DISABLE) { // ¹öÆÛ Å©±â üũ µîÀº Çʿ信 µû¶ó ¼öÇà if (buffSize > 4) { WCHAR notice[MAX_PATH] = { 0, }; KLogEx(DEBUG_TRACE_INFO, "btkrnl blocked(%X)(%d)", controlCode, buffSize); if (policyLog) { RtlStringCbPrintfW(processName, sizeof(processName), L"%S", szProcessName); RtlStringCbPrintfW(notice, sizeof(notice), L"btkrnl blocked(%X)(%d)", controlCode, buffSize); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, state, 0, processName, notice); } irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; // ¶Ç´Â STATUS_ACCESS_DENIED irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } } $BtkrnlCleanup: return dispath(deviceObject, irp); } /** @brief ³»Àå ºí·çÅõ½º ÀåÄ¡ ÈÄÅ· ÇÔ¼ö (RfComm / Microsoft Stack) */ PFILE_OBJECT gFileObject = NULL; NTSTATUS RfcommInternalDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(irp); NTSTATUS status = STATUS_SUCCESS; PCHAR pBuffer = NULL; PFILE_OBJECT pFileObject = irpstack->FileObject; ULONG controlCode = irpstack->Parameters.DeviceIoControl.IoControlCode; ULONG blueToothState = 0; ULONG blueToothFileState = 0; WCHAR processName[50] = { 0, }; ULONG processId = 0; ULONG blueToothlog = 0; ULONG blueToothFilelog = 0; char szProcessName[20] = { 0, }; WCHAR notice[MAX_PATH] = { 0, }; if (!g_bs1Flt.IsAttached) return dispath(deviceObject, irp); if (!enable_bluetoothhook) return dispath(deviceObject, irp); processId = HandleToULong(PsGetCurrentProcessId()); UGetProcessName(szProcessName); blueToothState = GetPolicyState(BDC_BLUETOOTH); blueToothlog = IsPolicyLog(BDC_BLUETOOTH); if (blueToothState == DISABLE) { KLogEx(DEBUG_TRACE_INFO, " blocked(%X)", controlCode); if (blueToothlog) { RtlStringCbPrintfW(processName, sizeof(processName), L"%S", szProcessName); RtlStringCbPrintfW(notice, sizeof(notice), L"rfcom blocked(%X)", controlCode); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, blueToothState, 0, processName, notice); } irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } if (!irp->MdlAddress || !irp->MdlAddress->ByteCount ||!irpstack->FileObject) return dispath(deviceObject, irp); pBuffer = MmGetSystemAddressForMdl(irp->MdlAddress); if (!pBuffer) return dispath(deviceObject, irp); __try { blueToothFileState = GetPolicyState(BDC_BLUETOOTH_FILE); blueToothFilelog = IsPolicyLog(BDC_BLUETOOTH_FILE); RtlStringCbPrintfW(processName, sizeof(processName), L"%S", szProcessName); POBEX_COMMON_HEADER pCommon = (POBEX_COMMON_HEADER)pBuffer; KLogEx(DEBUG_TRACE_INFO, "Opcode(%X)", pCommon->Opcode); switch (pCommon->Opcode) { case OBEX_OPCODE_CONNECT: { KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_CONNECT(%p)", pFileObject); POBEX_CONNECT_PACKET pConn = (POBEX_CONNECT_PACKET)pBuffer; USHORT hostPacketLen = 0; USHORT hostMaxPktSize = 0; if (blueToothFilelog || blueToothlog) { if (irp->MdlAddress->ByteCount < sizeof(OBEX_CONNECT_PACKET)) { KLogEx(DEBUG_TRACE_INFO, "Data too short for OBEX Connect"); break; } hostPacketLen = SWAP_USHORT(pConn->PacketLength); hostMaxPktSize = SWAP_USHORT(pConn->MaxPacketSize); RtlStringCbPrintfW(notice, sizeof(notice), L"OBEX_OPCODE_CONNECT, hostPacketLen(%d), version(0x%x), flag(%d), hostMaxPktSize(%d)", hostPacketLen, pConn->Version, pConn->Flags, hostMaxPktSize); KLogEx(DEBUG_TRACE_INFO, "file blocked(%X), (%S)", controlCode, notice); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, blueToothFileState, 0, processName, notice); } gFileObject = pFileObject; if (blueToothFileState == DISABLE) { KLogEx(DEBUG_TRACE_INFO, "Blocked OBEX Connect"); irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } } break; case OBEX_OPCODE_DISCONNECT: // Disconnect { KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_DISCONNECT(%p)", pFileObject); gFileObject = NULL; if (blueToothFilelog || blueToothlog) { RtlStringCbPrintfW(notice, sizeof(notice), L"OBEX_OPCODE_DISCONNECT, ByteCount(%d)", irp->MdlAddress->ByteCount); KLogEx(DEBUG_TRACE_INFO, "(%S)", notice); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, blueToothFileState, 0, processName, notice); } } break; case OBEX_OPCODE_PUT: // Put case OBEX_OPCODE_PUT_FINAL: // Put Final { KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_PUT,OBEX_OPCODE_PUT_FINAL(%p)", pFileObject); if (gFileObject != pFileObject) { break; } ULONG currentOffset = 3; WCHAR szFileName[260] = { 0 }; ULONG packetLen = irp->MdlAddress->ByteCount; PUCHAR pData = (PUCHAR)pBuffer; if (blueToothFilelog || blueToothlog) { while (currentOffset < packetLen) { UCHAR headerId = pData[currentOffset]; if (currentOffset + 3 > packetLen) break; // OBEX Çì´õ´Â »óÀ§ 2ºñÆ®¿¡ µû¶ó ÀÎÄÚµù ¹æ½ÄÀÌ ´Ù¸§ // 00xxxxxx: Unicode String (Length Æ÷ÇÔ) -> Name Çì´õ°¡ ¿©±â ¼ÓÇÔ // 01xxxxxx: Byte Sequence (Length Æ÷ÇÔ) // 10xxxxxx: 1 Byte Value (Length ÇÊµå ¾øÀ½) // 11xxxxxx: 4 Byte Value (Length ÇÊµå ¾øÀ½) if (headerId == OBEX_OPCODE_NAME) // 0x01: ÆÄÀÏ¸í ¹ß°ß! { USHORT headerLen = 0; // Length Çʵå´Â Big Endian (2 bytes) ((PUCHAR)&headerLen)[1] = pData[currentOffset + 1]; ((PUCHAR)&headerLen)[0] = pData[currentOffset + 2]; // ½ÇÁ¦ À̸§ ±æÀÌ = Çì´õ±æÀÌ - (ID 1byte + Length 2bytes) // OBEX NameÀº Null-terminated UnicodeÀÓ (¸¶Áö¸· 2¹ÙÀÌÆ® 0x00 0x00 Æ÷ÇÔ) if (headerLen > 5 && (currentOffset + headerLen <= packetLen)) { ULONG nameByteLen = headerLen - 3; // ¼ø¼ö ¹®ÀÚ¿­ ¹ÙÀÌÆ® ¼ö (Çì´õ ¿À¹öÇìµå 3¹ÙÀÌÆ® Á¦¿Ü) if (nameByteLen >= sizeof(szFileName)) nameByteLen = sizeof(szFileName) - 2; // ÆÄÀÏ¸í º¹»ç (pData + offset + 3) RtlCopyMemory(szFileName, &pData[currentOffset + 3], nameByteLen); for (ULONG k = 0; k < nameByteLen / 2; k++) szFileName[k] = SWAP_USHORT(szFileName[k]); szFileName[nameByteLen/2] = 0; KLogEx(DEBUG_TRACE_INFO, "File Name Found: %ws", szFileName); RtlStringCbPrintfW(notice, sizeof(notice), L"File Transfer: %s, packetLen: %d", szFileName, packetLen); } break; // À̸§À» ã¾ÒÀ¸¸é ·çÇÁ Á¾·á (ÃÖÀûÈ­) } // ´ÙÀ½ Çì´õ·Î À̵¿Çϱâ À§ÇÑ ¿ÀÇÁ¼Â °è»ê if ((headerId & 0xC0) == 0x00 || (headerId & 0xC0) == 0x40) { // Length Çʵ尡 Àִ ŸÀÔ (String, Sequence) USHORT chunkLen = 0; ((PUCHAR)&chunkLen)[1] = pData[currentOffset + 1]; ((PUCHAR)&chunkLen)[0] = pData[currentOffset + 2]; if (chunkLen == 0) break; // ¹«ÇÑ·çÇÁ ¹æÁö currentOffset += chunkLen; } else if ((headerId & 0xC0) == 0x80) { // 1 Byte Value (ÃÑ 2¹ÙÀÌÆ®: ID + Value) currentOffset += 2; } else if ((headerId & 0xC0) == 0xC0) { // 4 Byte Value (ÃÑ 5¹ÙÀÌÆ®: ID + Value 4bytes) currentOffset += 5; } } if(notice[0] == 0) RtlStringCbPrintfW(notice, sizeof(notice), L"File Transfer: unknown, packetLen: %d", packetLen); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, blueToothFileState, 0, processName, notice); } if (blueToothFileState == DISABLE || blueToothFileState == READONLY) { KLogEx(DEBUG_TRACE_INFO, "Blocked File Transfer: %ws", szFileName); irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } } break; case OBEX_OPCODE_OK: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_OK"); break; case OBEX_OPCODE_CONTINUE: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_CONTINUE"); break; case OBEX_OPCODE_BODY: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_BODY"); break; case OBEX_OPCODE_END_BODY: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_END_BODY"); break; case OBEX_OPCODE_VERSION: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_VERSION"); break; case OBEX_OPCODE_CONN_FLAGS: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_CONN_FLAGS"); break; case OBEX_OPCODE_NAME: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_NAME"); break; case OBEX_OPCODE_LENGTH: KLogEx(DEBUG_TRACE_INFO, "OBEX_OPCODE_LENGTH"); break; default: KLogEx(DEBUG_TRACE_INFO, "default OBEX_OPCODE"); break; } } __except (EXCEPTION_EXECUTE_HANDLER) { NTSTATUS exceptStatus = GetExceptionCode(); KLogEx(DEBUG_TRACE_ERROR, "Exception accessing buffer: 0x%X", exceptStatus); } status = dispath(deviceObject, irp); return status; } /** @brief ºí·çÅõ½º USB ÀåÄ¡ ÈÄÅ· ÇÔ¼ö (BTWUSB) */ NTSTATUS BtwusbDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(irp); ULONG controlCode = irpstack->Parameters.DeviceIoControl.IoControlCode; NTSTATUS ntStatus = STATUS_SUCCESS; ULONG blueToothState = 0; WCHAR processName[50] = { 0, }; ULONG processId = 0; ULONG blueToothlog = 0; char szProcessName[20] = { 0, }; WCHAR notice[MAX_PATH] = { 0, }; if (!g_bs1Flt.IsAttached) return dispath(deviceObject, irp); if (!enable_bluetoothhook) return dispath(deviceObject, irp); processId = HandleToULong(PsGetCurrentProcessId()); UGetProcessName(szProcessName); blueToothState = GetPolicyState(BDC_BLUETOOTH); blueToothlog = IsPolicyLog(BDC_BLUETOOTH); if (blueToothState == DISABLE) { KLogEx(DEBUG_TRACE_INFO, " btwusb blocked(%X)", controlCode); if (blueToothlog) { RtlStringCbPrintfW(processName, sizeof(processName), L"%S", szProcessName); RtlStringCbPrintfW(notice, sizeof(notice), L"btwusb blocked(%X)", controlCode); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, blueToothState, 0, processName, notice); } irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } ntStatus = dispath(deviceObject, irp); if (blueToothlog == TRUE) { } return ntStatus; } /** @brief ºí·çÅõ½º USB ÀåÄ¡ ÈÄÅ· ÇÔ¼ö (BTHUSB) */ NTSTATUS BthusbDeviceIoControl(PDRIVER_DISPATCH dispath, PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(irp); ULONG controlCode = irpstack->Parameters.DeviceIoControl.IoControlCode; NTSTATUS ntStatus = STATUS_SUCCESS; ULONG blueToothState = 0; WCHAR processName[50] = { 0, }; ULONG processId = 0; ULONG blueToothlog = 0; char szProcessName[20] = { 0, }; WCHAR notice[MAX_PATH] = { 0, }; if (!g_bs1Flt.IsAttached) return dispath(deviceObject, irp); if (!enable_bluetoothhook) return dispath(deviceObject, irp); processId = HandleToULong(PsGetCurrentProcessId()); UGetProcessName(szProcessName); blueToothState = GetPolicyState(BDC_BLUETOOTH); blueToothlog = IsPolicyLog(BDC_BLUETOOTH); if (blueToothState == DISABLE) { if (processId == 4) return dispath(deviceObject, irp); if (IsDefalutLocalDiskExceptProcess(szProcessName)) return dispath(deviceObject, irp); if (_strnicmp(szProcessName, "SystemSettings", strlen(szProcessName)) == 0 || _strnicmp(szProcessName, "RuntimeBroker.", strlen(szProcessName)) == 0 ) return dispath(deviceObject, irp); KLogEx(DEBUG_TRACE_INFO, " bthusb blocked(%s)(%X)", szProcessName, controlCode); if (blueToothlog) { RtlStringCbPrintfW(processName, sizeof(processName), L"%S", szProcessName); RtlStringCbPrintfW(notice, sizeof(notice), L"bthusb blocked(%X)", controlCode); SetLog(NULL, NULL, LOG_POLICY, BDC_BLUETOOTH, blueToothState, 0, processName, notice); } irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } ntStatus = dispath(deviceObject, irp); if (blueToothlog == TRUE) { } return ntStatus; } NTSTATUS BlueToothIrpHookInit() { WCHAR* name = NULL; ULONG i, mi; PDRIVER_OBJECT obj = NULL; PHOOK_CONTEXT hook = NULL; for (i = 0; i < ARRAYSIZE(g_BlueToothHookContexts); i++) { hook = &g_BlueToothHookContexts[i]; if (hook->IsHooked) continue; name = s_bthname[i]; if (name == NULL) continue; KLogEx(DEBUG_TRACE_INFO, "Target driver(%S)\n", name); obj = SearchDriverObject(name); if (!obj) { KLogEx(DEBUG_TRACE_ERROR, "Not found object (%S)\n", name); continue; } hook->DriverObject = obj; for (mi = 0; mi < IRP_MJ_MAXIMUM_FUNCTION + 1; mi++) { if (!hook->HookHandlers[mi].IsHook) continue; if (obj->MajorFunction[mi] == s_ProxyDispatchers[i]) continue; hook->HookHandlers[mi].pOrgHandler = obj->MajorFunction[mi]; if (hook->HookHandlers[mi].pOrgHandler == NULL) continue; InterlockedExchangePointer((PVOID)&obj->MajorFunction[mi], (PVOID)s_ProxyDispatchers[i]); KLogEx(DEBUG_TRACE_INFO, "[Hook] %S MJ:%d Org:%p New:%p\n", name, mi, hook->HookHandlers[mi].pOrgHandler, s_ProxyDispatchers[i]); } hook->IsHooked = TRUE; enable_bluetoothhook = TRUE; } KLogEx(DEBUG_TRACE_INFO, "complete\n"); return STATUS_SUCCESS; } NTSTATUS BlueToothIrpHookCleanup() { ULONG i, mi; LARGE_INTEGER WaitTime; PHOOK_CONTEXT hook = NULL; PDRIVER_DISPATCH pCurrentHandler = NULL; enable_bluetoothhook = FALSE; KLogEx(DEBUG_TRACE_INFO, "Started...\n"); for (i = 0; i < ARRAYSIZE(g_BlueToothHookContexts); i++) { hook = &g_BlueToothHookContexts[i]; if (!hook->IsHooked || !hook->DriverObject) continue; KLogEx(DEBUG_TRACE_INFO, "Unhooking Driver Index: %d\n", i); for (mi = 0; mi <= IRP_MJ_MAXIMUM_FUNCTION; mi++) { if (!hook->HookHandlers[mi].IsHook) continue; if (hook->HookHandlers[mi].pOrgHandler == NULL) continue; pCurrentHandler = (PDRIVER_DISPATCH)InterlockedExchangePointer( (PVOID)&hook->DriverObject->MajorFunction[mi], (PVOID)hook->HookHandlers[mi].pOrgHandler ); hook->HookHandlers[mi].pOrgHandler = NULL; } WaitTime.QuadPart = -50 * 1000 * 10; // 50ms ´ÜÀ§·Î ´ë±â while (hook->IrpEnterCount > 0) { KLogEx(DEBUG_TRACE_INFO, "(%d), Waiting for active IRPs... Count: %d\n", i, hook->IrpEnterCount); KeDelayExecutionThread(KernelMode, FALSE, &WaitTime); } hook->IsHooked = FALSE; hook->DriverObject = NULL; //RtlZeroMemory(hook->HookHandlers, sizeof(hook->HookHandlers)); } WaitTime.QuadPart = -500 * 1000 * 10; KeDelayExecutionThread(KernelMode, FALSE, &WaitTime); KLogEx(DEBUG_TRACE_INFO, "Complete.\n"); return STATUS_SUCCESS; }