unit DeviceGuard.Logic; interface uses System.SysUtils, System.Classes, System.Generics.Collections, System.SyncObjs, System.JSON, System.IOUtils, System.StrUtils, Winapi.Windows, Winapi.Messages, Winapi.ActiveX, Winapi.Bluetooth, Tocsg.Driver, Tocsg.Path, bs1PolicyUnit, Bs1FltCtrl; const // SetupAPI & CfgMgr »ó¼ö DN_DRIVER_LOADED = $00000002; DN_STARTED = $00000008; DN_DISABLEABLE = $00002000; DN_HAS_PROBLEM = $00000400; CR_SUCCESS = $00000000; // Property IDs SPDRP_DEVICEDESC = $00000000; SPDRP_HARDWAREID = $00000001; SPDRP_COMPATIBLEIDS = $00000002; SPDRP_SERVICE = $00000004; SPDRP_CLASS = $00000007; SPDRP_CLASSGUID = $00000008; SPDRP_FRIENDLYNAME = $0000000C; SPDRP_SECURITY = $00000017; SPDRP_ENUMERATOR_NAME = $00000016; // ¿­°ÅÀÚ À̸§ (USB, PCI µî) SPDRP_REMOVAL_POLICY = $0000001F; SPDRP_INSTALL_STATE = $00000022; DICS_ENABLE = 1; DICS_DISABLE = 2; DICS_PROPCHANGE = 3; DICS_START = 4; DICS_STOP = 5; DICS_FLAG_GLOBAL = 1; DICS_FLAG_CONFIGSPECIFIC = 2; DIF_REMOVE = $00000005; DIF_PROPERTYCHANGE = $00000012; // Bluetooth Classes COD_MAJOR_AUDIO = 4; COD_MAJOR_PHONE = 2; COD_MAJOR_PERIPHERAL = 5; COD_MAJOR_WEARABLE = 7; GUID_DEVCLASS_USB = '{36fc9e60-c465-11cf-8056-444553540000}'; GUID_DEVCLASS_PORTS = '{4d36e978-e325-11ce-bfc1-08002be10318}'; GUID_DEVCLASS_MODEM = '{4d36e96d-e325-11ce-bfc1-08002be10318}'; GUID_DEVCLASS_BLUETOOTH = '{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}'; GUID_DEVCLASS_NET = '{4d36e972-e325-11ce-bfc1-08002be10318}'; GUID_DEVCLASS_INFRARED = '{6bdd1fc5-810f-11d0-bec7-08002be2092f}'; GUID_DEVCLASS_1394 = '{6bdd1fc1-810f-11d0-bec7-08002be2092f}'; GUID_DEVCLASS_PCMCIA = '{4d36e977-e325-11ce-bfc1-08002be10318}'; GUID_DEVCLASS_WPD = '{eec5ad98-8080-425f-922a-dabf3de3f69a}'; GUID_DEVCLASS_DISKDRIVE = '{4d36e967-e325-11ce-bfc1-08002be10318}'; CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL = 1; CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL = 2; CM_INSTALL_STATE_FAILED_INSTALL = 2; DICLASSPROP_INSTALLER = $00000002; type HDEVINFO = THandle; DEVINST = DWORD; // TPolicyState = ( // psAllow = 0; // psBlock, // psLog, // psLogNone // ); DEVPROPKEY = record fmtid: TGUID; pid: DWORD; end; PDEVPROPKEY = ^DEVPROPKEY; DEVPROPTYPE = DWORD; PSPClassInstallHeader = ^TSPClassInstallHeader; TSPClassInstallHeader = packed record cbSize: DWORD; InstallFunction: DWORD; end; PSPPropChangeParams = ^TSPPropChangeParams; TSPPropChangeParams = packed record ClassInstallHeader: TSPClassInstallHeader; StateChange: DWORD; Scope: DWORD; HwProfile: DWORD; end; PSPDevInfoListDetailData = ^TSPDevInfoListDetailData; TSPDevInfoListDetailData = packed record cbSize: DWORD; ClassGuid: TGUID; RemoteMachineHandle: THandle; RemoteMachineName: array [0..255] of Char; // SP_MAX_MACHINENAME_LENGTH end; TDeviceInfoDTO = record Index: Integer; DevInst: DWORD; ClassGuid: TGUID; Desc: string; HardwareID: string; Service: string; DeviceClass: string; Enumerator: string; RemovalPolicy: DWORD; IsStarted: Boolean; FriendlyName: string; ParentId: string; function GetMainHardwareID: string; end; TLogEvent = procedure(const Msg: string) of object; TGuardThread = class(TThread) private FOwnerEngine: TObject; procedure DoLog(const Msg: string); procedure ShowPopup(const Msg: string); protected procedure Execute; override; public constructor Create(AOwnerEngine: TObject); end; TDeviceGuardEngine = class private FThread: TGuardThread; FScanEvent: TEvent; FPolicyFilePath: string; FOnLog: TLogEvent; FOnPopup: TLogEvent; FUseRestart: Boolean; // ÇïÆÛ ÇÔ¼ö function GetDeviceStringProperty(DevInfoSet: HDEVINFO; var DevData: TSPDevInfoData; PropId: DWORD): string; function GetDeviceStringPropertyDWORD(DevInfoSet: HDEVINFO; var DevData: SP_DEVINFO_DATA; PropId: DWORD): DWORD; function SetDeviceState(DevInfoSet: HDEVINFO; var DevData: TSPDevInfoData; Enable: Boolean): Boolean; function GetDevicePropertyString(hDevInfo: HDEVINFO; var DevData: SP_DEVINFO_DATA; const PropKey: DEVPROPKEY): string; function GetClassPropertyString(const ClassGuid: TGUID; const PropKey: DEVPROPKEY): string; function GetDeviceInstanceId(hDevInfo: HDEVINFO; var DevData: SP_DEVINFO_DATA): string; // [ÇÙ½É] ÀåÄ¡°¡ Á¤Ã¥°ú ÀÏÄ¡ÇÏ´ÂÁö °Ë»çÇÏ´Â ÇÔ¼ö // function IsDeviceMatch(DevInfoSet: HDEVINFO; var DevData: TSPDevInfoData; Policy: TPolicyItem; out DevName: string): Boolean; procedure EnforceBluetoothPolicy(PolicyItem: TPolicyItem); procedure ApplySingleMatch(Policy: TPolicyItem; Match: TMatchCriteria); public constructor Create; destructor Destroy; override; procedure Start; procedure Stop; function IsVirtualMachineDevice(const Info: TDeviceInfoDTO): Boolean; function IsGeneralHIDDevice(const Info: TDeviceInfoDTO): Boolean; function IsSystemOrCriticalDevice(const Info: TDeviceInfoDTO): Boolean; procedure AllEnumSystemDevice; // Á¤Ã¥ °ü¸® // Ãʱâ Á¤Ã¥ µî·Ï (¸Þ¸ð¸®) //procedure AddDefaultPolicy(const Name, GuidStr: string; IsBT: Boolean = False); // [ÇÙ½É ±â´É] procedure TriggerScan; // À̺¥Æ® ½Ã±×³Î ¹ß»ý //procedure SavePolicyToFile; // ÇöÀç »óŸ¦ JSONÀ¸·Î ÀúÀå //procedure LoadPolicyFromFile; // JSON¿¡¼­ Àо »óÅ ¾÷µ¥ÀÌÆ® ÈÄ ½ºÄµ Æ®¸®°Å procedure EnforceSystemPolicy; procedure CheckAndApplyPolicy(Policy: TPolicyItem); property OnLog: TLogEvent read FOnLog write FOnLog; property OnPopup: TLogEvent read FOnPopup write FOnPopup; class function ReportCallback(Context: Pointer): DWORD; stdcall; static; procedure UsbReStart; function RestartDevice(hDevInfo: HDEVINFO; DevInfoData: SP_DEVINFO_DATA): Boolean; function ScanForHardwareChanges: Boolean; end; function CM_Get_DevNode_Status(out pulStatus: DWORD; out pulProblemNumber: DWORD; dnDevInst: DWORD; ulFlags: DWORD): DWORD; stdcall; external 'cfgmgr32.dll' name 'CM_Get_DevNode_Status'; function CM_Locate_DevNode(var pdnDevInst: DEVINST; pDeviceID: PChar; ulFlags: ULONG): CONFIGRET; stdcall; external 'cfgmgr32.dll' name 'CM_Locate_DevNodeW'; function CM_Reenumerate_DevNode(dnDevInst: DEVINST; ulFlags: ULONG): CONFIGRET; stdcall; external 'cfgmgr32.dll' name 'CM_Reenumerate_DevNode'; function SetupDiGetDeviceInfoListDetail(DeviceInfoSet: HDEVINFO; DeviceInfoListDetailData: PSPDevInfoListDetailData): BOOL; stdcall; external 'setupapi.dll' name 'SetupDiGetDeviceInfoListDetailW'; function SetupDiChangeState(DeviceInfoSet: HDEVINFO; DeviceInfoData: Pointer): BOOL; stdcall; external 'setupapi.dll' name 'SetupDiChangeState'; var gDeviceGuardEngine: TDeviceGuardEngine = nil; /////// DEVPKEY_Device_DeviceDesc: DEVPROPKEY = (fmtid: '{A45C254E-DF1C-4EFD-8020-67D146A850E0}'; pid: 2); DEVPKEY_Device_HardwareIds: DEVPROPKEY = (fmtid: '{A45C254E-DF1C-4EFD-8020-67D146A850E0}'; pid: 3); DEVPKEY_Device_Manufacturer: DEVPROPKEY = (fmtid: '{A45C254E-DF1C-4EFD-8020-67D146A850E0}'; pid: 13); DEVPKEY_Device_Parent: DEVPROPKEY = (fmtid: '{4340A6C5-93FA-4706-972C-7B648008A5A7}'; pid: 8); DEVPKEY_Device_DriverDesc: DEVPROPKEY = (fmtid: '{A8B865DD-2E3D-4094-AD97-E593A708E5D3}'; pid: 2); DEVPKEY_Device_BusReportedDeviceDesc: DEVPROPKEY = (fmtid: '{540B947E-8B40-45BC-A8A2-6A0B894CBDA2}'; pid: 4); DEVPKEY_DeviceClass_Name: DEVPROPKEY = (fmtid: '{259ABFFC-50A7-47CE-AF08-68C9A7D73366}'; pid: 2); function SetupDiGetDevicePropertyW( DeviceInfoSet: HDEVINFO; const DeviceInfoData: SP_DEVINFO_DATA; const PropertyKey: DEVPROPKEY; var PropertyType: DEVPROPTYPE; PropertyBuffer: PByte; // ¹öÆÛ´Â Æ÷ÀÎÅÍ·Î Àü´Þ PropertyBufferSize: DWORD; var RequiredSize: DWORD; Flags: DWORD ): BOOL; stdcall; external 'setupapi.dll'; function SetupDiGetClassPropertyW( const ClassGuid: TGUID; const PropertyKey: DEVPROPKEY; var PropertyType: DEVPROPTYPE; PropertyBuffer: PByte; PropertyBufferSize: DWORD; var RequiredSize: DWORD; Flags: DWORD; Reserved: PVOID ): BOOL; stdcall; external 'setupapi.dll'; function SetupDiGetDeviceInstanceId( DeviceInfoSet: HDEVINFO; const DeviceInfoData: SP_DEVINFO_DATA; DeviceInstanceId: PWideChar; DeviceInstanceIdSize: DWORD; var RequiredSize: DWORD ): BOOL; stdcall; external 'setupapi.dll' name 'SetupDiGetDeviceInstanceIdW'; implementation { TDeviceGuardEngine } class function TDeviceGuardEngine.ReportCallback(Context: Pointer): DWORD; stdcall; var logMsg: string; Instance: TDeviceGuardEngine; begin if Context = nil then Exit; LogMsg := string(PWideChar(Context)); OutputDebugString(PWideChar(Context)); TThread.Queue(nil, procedure begin if Assigned(gDeviceGuardEngine) then begin gDeviceGuardEngine.OnLog(LogMsg); end; end); Result := 0; end; constructor TDeviceGuardEngine.Create; begin gDeviceGuardEngine:= self; //FPolicies := TObjectList.Create; //FLock := TCriticalSection.Create; FScanEvent := TEvent.Create(nil, False, False, ''); FThread := nil; //FPolicyFilePath := ChangeFileExt(ParamStr(0), '.json'); end; destructor TDeviceGuardEngine.Destroy; begin Stop; FScanEvent.Free; //FPolicies.Free; //FLock.Free; inherited; end; procedure TDeviceGuardEngine.Start; var path: string; state: DWORD; begin path:= GetRunExePathDir; gBs1fltControl:= TBs1fltControl.Create; try // Ä¿³Î µå¶óÀ̹öÀÇ À§Ä¡¸¦ Á¤ÇÑ´Ù. state := gBs1fltControl.InitDriver(path, TDeviceGuardEngine.ReportCallback); if state = 0 then begin OnLog('Bs1FltCtrl ÃʱâÈ­ ¼º°ø'); gBs1fltControl.BeginControl(1); gBs1FltControl.SetDeviceProtect(1); gBs1FltControl.Debug(3); gBs1FltControl.SetHook(DWORD(BDC_USB), 1); gBs1FltControl.SetHook(DWORD(BDC_BLUETOOTH), 1); gBs1FltControl.SetHook(DWORD(BDC_MTP), 1); end else begin OnLog('Bs1FltCtrl ÃʱâÈ­ ½ÇÆÐ: ' + IntToStr(state)); end; finally end; if FThread <> nil then Exit; FThread := TGuardThread.Create(Self); FThread.Start; end; procedure TDeviceGuardEngine.Stop; begin if FThread <> nil then begin FThread.Terminate; FScanEvent.SetEvent; FThread.WaitFor; FreeAndNil(FThread); end; if gBs1fltControl <> nil then begin gBs1fltControl.BeginControl(0); gBs1fltControl.Cleanup; FreeAndNil(gBs1fltControl); end; end; procedure TDeviceGuardEngine.TriggerScan; begin if FScanEvent <> nil then FScanEvent.SetEvent; end; { // ----------------------------------------------------------------------------- // [ÇÙ½É] ÀåÄ¡ ¸ÅĪ ·ÎÁ÷ (C++ SetStateControlDevNode ·ÎÁ÷ ±¸Çö) // ----------------------------------------------------------------------------- function TDeviceGuardEngine.IsDeviceMatch(DevInfoSet: HDEVINFO; var DevData: SP_DEVINFO_DATA; Policy: TPolicyItem; out DevName: string): Boolean; var DevEnum, DevPropVal: string; begin Result := False; DevName := ''; // 1. Enumerator È®ÀÎ (¼³Á¤µÈ °æ¿ì¸¸) // ¿¹: "PCI", "USB" µî if Policy.targetEnumerator_ <> '' then begin DevEnum := GetDeviceStringProperty(DevInfoSet, DevData, SPDRP_ENUMERATOR_NAME).ToUpper; // ºÎºÐ ÀÏÄ¡°¡ ¾Æ´Ï¶ó Á¤È®È÷ ÀÏÄ¡ÇØ¾ß ÇÔ (ÇÊ¿ä ½Ã Contains·Î º¯°æ °¡´É) if DevEnum <> Policy.targetEnumerator_ then Exit; end; // 2. Property È®ÀÎ // ¿¹: SPDRP_SERVICE °ªÀÌ "PCMCIA" ÀÎÁö È®ÀÎ // ¿¹: SPDRP_CLASSGUID °ªÀÌ "..." ÀÎÁö È®ÀÎ DevPropVal := GetDeviceStringProperty(DevInfoSet, DevData, Policy.matchPropType_).ToUpper; // °ªÀÌ ºñ¾îÀÖÀ¸¸é ¸ÅĪ ½ÇÆÐ if DevPropVal = '' then Exit; // ºÎºÐ ¹®ÀÚ¿­ ¸ÅĪ (Contains) Çã¿ë -> GUID´Â Á¤È®È÷, À̸§Àº Æ÷ÇÔ µî À¯¿¬ÇÏ°Ô if Pos(Policy.matchData_, DevPropVal) > 0 then begin // ¸ÅĪ ¼º°ø! Result := True; // ·Î±×¿ë À̸§ ÃßÃâ DevName := GetDeviceStringProperty(DevInfoSet, DevData, SPDRP_FRIENDLYNAME); if DevName = '' then DevName := GetDeviceStringProperty(DevInfoSet, DevData, SPDRP_DEVICEDESC); end; end; } function TDeviceGuardEngine.ScanForHardwareChanges: Boolean; var LDevInst: DEVINST; status: CONFIGRET; begin LDevInst := 0; //CM_LOCATE_DEVNODE_NORMAL = $00000000; status := CM_Locate_DevNode(LDevInst, nil, $00000000); if status <> CR_SUCCESS then begin OnLog(Format('ScanForHardwareChanges, CM_Locate_DevNode failed: %x', [status])); Result := False; Exit; end; status := CM_Reenumerate_DevNode(LDevInst, 0); if status <> CR_SUCCESS then begin OnLog(Format('ScanForHardwareChanges, CM_Reenumerate_DevNode failed: %x', [status])); Result := False; Exit; end; Result := True; end; function TDeviceGuardEngine.RestartDevice(hDevInfo: HDEVINFO; DevInfoData: SP_DEVINFO_DATA): Boolean; var params: TSPPropChangeParams; begin FillChar(params, SizeOf(TSPPropChangeParams), 0); params.ClassInstallHeader.cbSize := SizeOf(TSPClassInstallHeader); params.ClassInstallHeader.InstallFunction := DIF_PROPERTYCHANGE; params.StateChange := DICS_PROPCHANGE; // Restart (Stop -> Start) params.Scope := DICS_FLAG_CONFIGSPECIFIC; params.HwProfile := 0; OnLog('RestartDevice.... 1'); if not SetupDiSetClassInstallParams(hDevInfo, @DevInfoData, @params.ClassInstallHeader, SizeOf(TSPPropChangeParams)) then begin OnLog(Format('restart_device, SetupDiSetClassInstallParams Fail(%d)', [GetLastError])); Result := False; Exit; end; OnLog('RestartDevice.... 2'); if not SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, @DevInfoData) then begin OnLog(Format('restart_device, SetupDiCallClassInstaller Fail(%d)', [GetLastError])); Result := False; Exit; end; OnLog('RestartDevice.... 3'); { if not SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, pDevInfoData) then begin ShowDebug(Format('restart_device, SetupDiCallClassInstaller(DIF_REMOVE) Fail(%d)', [GetLastError])); Result := False; Exit; end; } OnLog('restart_device, 4'); // ChangeState È£Ãâ SetupDiChangeState(hDevInfo, @DevInfoData); OnLog(Format('restart_device, SetupDiCallClassInstaller Succeed(%d)', [GetLastError])); Result := True; end; procedure TDeviceGuardEngine.UsbReStart; const DIGCF_PRESENT = $00000002; DIGCF_ALLCLASSES = $00000004; var hDevInfo: THandle; DeviceInfoData: SP_DEVINFO_DATA; devInfoListDetail: TSPDevInfoListDetailData; devIndex: Integer; sService: string; sHardwareID: string; sSecurity: string; begin // ÀåÄ¡ Á¤º¸ ¼¼Æ® °¡Á®¿À±â hDevInfo := SetupDiGetClassDevs(nil, nil, 0, DIGCF_PRESENT or DIGCF_ALLCLASSES); if hDevInfo = INVALID_HANDLE_VALUE then begin OnLog('SetupDiGetClassDevs Failed'); Exit; end; try DeviceInfoData.cbSize := SizeOf(TSPDevInfoData); devInfoListDetail.cbSize := SizeOf(TSPDevInfoListDetailData); // ¸®½ºÆ® »ó¼¼ Á¤º¸ (C++ Äڵ忡 ÀÖ¾úÀ¸³ª ½ÇÁ¦ ·ÎÁ÷¿¡¼± »ç¿ë ¾ÈµÊ, È£Ã⸸ À¯Áö) SetupDiGetDeviceInfoListDetail(hDevInfo, @devInfoListDetail); devIndex := 0; while SetupDiEnumDeviceInfo(hDevInfo, devIndex, DeviceInfoData) do begin // Class GUID ºñ±³ ({36FC9E60-C465-11CF-8056-444553540000}) if IsEqualGUID(DeviceInfoData.ClassGuid, StringToGUID(GUID_DEVCLASS_USB)) then begin // 1. Service ¸í È®ÀÎ (SPDRP_SERVICE) sService := GetDeviceStringProperty(hDevInfo, DeviceInfoData, SPDRP_SERVICE); OnLog(Format('SPDRP_SERVICE = [%s]', [sService])); if (SameText(sService, 'USBSTOR')) or (SameText(sService, 'USBAAPL')) then begin // 2. Hardware ID È®ÀÎ sHardwareID := GetDeviceStringProperty(hDevInfo, DeviceInfoData, SPDRP_HARDWAREID); OnLog(Format('SPDRP_HARDWAREID = [%s]', [sHardwareID])); // VID_05E3ÀÌ Æ÷ÇԵǾî ÀÖÁö ¾ÊÀ¸¸é Disable ó¸® if Pos('VID_05E3', sHardwareID) = 0 then begin OnLog('PASS~!'); // ¿øº» ·ÎÁ÷: Disable ¼öÇà SetDeviceState(hDevInfo, DeviceInfoData, True); // restart_device(hDevInfo, @DeviceInfoData); // ¿øº» ÁÖ¼® ó¸®µÊ Inc(devIndex); Continue; end; end; // 3. Apple ÀåÄ¡ È®ÀÎ (SPDRP_SECURITY_SDS -> SPDRP_SECURITY) sSecurity := GetDeviceStringProperty(hDevInfo, DeviceInfoData, SPDRP_SECURITY); OnLog(Format('SPDRP_SECURITY_SDS = [%s]', [sSecurity])); // "Apple" ¹®ÀÚ¿­ÀÌ Æ÷ÇԵǰųª °°À¸¸é Restart // C++ ¿øº»: memcmp(devID, "Apple", ...) -> Á¤È®È÷ "Apple"·Î ½ÃÀÛÇÏ´ÂÁö üũ if Pos('Apple', sSecurity) > 0 then begin RestartDevice(hDevInfo, DeviceInfoData); // SetDeviceState(hDevInfo, DeviceInfoData, True); Inc(devIndex); Continue; end; end; Inc(devIndex); end; // Çϵå¿þ¾î º¯°æ »çÇ× °Ë»ö // ScanForHardwareChanges(); finally SetupDiDestroyDeviceInfoList(hDevInfo); end; end; procedure TDeviceGuardEngine.ApplySingleMatch(Policy: TPolicyItem; Match: TMatchCriteria); var hDevInfo: THandle; DevInfoData: SP_DEVINFO_DATA; Idx: DWORD; ClassGuid: TGUID; PClassGuid: PGUID; Flags: DWORD; Enumerator: PChar; CurrentPropVal: string; DevName: string; dwStatus, dwProblem: DWORD; bIsMatch: Boolean; text: string; IsLog: Boolean; begin PClassGuid := nil; Flags := DIGCF_PRESENT; Enumerator := nil; // 1. GUID ¼³Á¤ (Á¤Ã¥ ·¹º§) if Policy.targetGUID_ <> '' then begin ClassGuid := StringToGUID(Policy.targetGUID_); PClassGuid := @ClassGuid; end else begin Flags := Flags or DIGCF_ALLCLASSES; end; // 2. Enumerator ¼³Á¤ (Match ·¹º§) if Match.Enumerator <> '' then Enumerator := PChar(Match.Enumerator); hDevInfo := SetupDiGetClassDevs(PClassGuid, Enumerator, 0, Flags); if hDevInfo = INVALID_HANDLE_VALUE then Exit; try Idx := 0; DevInfoData.cbSize := SizeOf(SP_DEVINFO_DATA); while SetupDiEnumDeviceInfo(hDevInfo, Idx, DevInfoData) do begin bIsMatch := False; // 3. MatchData ºñ±³ if Match.MatchData <> '' then begin CurrentPropVal := GetDeviceStringProperty(hDevInfo, DevInfoData, Match.PropType).ToUpper; if (CurrentPropVal <> '') and (Pos(Match.MatchData.ToUpper, CurrentPropVal) > 0) then bIsMatch := True; end else bIsMatch := True; // µ¥ÀÌÅÍ ¾øÀ¸¸é ¹«Á¶°Ç ¸ÅĪ //TGuardThread(FThread).DoLog(Format('(%s)(%d), Match.Enumerator : (%s) PropType : %x (PropVal: %s<>%s)', [Policy.name, DWORD(bIsMatch), Match.Enumerator, Match.PropType, Match.MatchData.ToUpper, CurrentPropVal])); if bIsMatch then begin //USBSTOR\DISK&VEN_VENDORCO&PROD_PRODUCTCODE&REV_2.00\8328501217610604362&0 var friendlyName := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_FRIENDLYNAME); if friendlyName = '' then friendlyName := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_DEVICEDESC); var HardwareID := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_HARDWAREID); var DeviceClass := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_CLASS); var Service := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_SERVICE); var RemovalPolicy := GetDeviceStringPropertyDWORD(hDevInfo, DevInfoData, SPDRP_REMOVAL_POLICY); var InstatllState:= GetDeviceStringPropertyDWORD(hDevInfo, DevInfoData, SPDRP_INSTALL_STATE); var ParentId := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_Parent); var DeviceDriveDesc := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_DriverDesc); var Manufacturer := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_Manufacturer); var DeviceDesc := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_DeviceDesc); var InstanceId:= GetDeviceInstanceId(hDevInfo, DevInfoData); //TGuardThread(FThread).DoLog(Format('InstanceId : %s', [InstanceId])); // »óÅ Á¦¾î (Block/Allow) if CM_Get_DevNode_Status(dwStatus, dwProblem, DevInfoData.DevInst, 0) = CR_SUCCESS then begin DevName := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_FRIENDLYNAME); if DevName = '' then DevName := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_DEVICEDESC); //TGuardThread(FThread).DoLog(Format('DevName : %s, state : %d', [DevName, DWORD(Policy.state_)])); case Policy.state_ of dsDisable: begin if (Policy.flag_ = DWORD(BDC_USB)) and gBs1Policy.IsUsbPortExcept(InstanceId) then begin TGuardThread(FThread).DoLog(text + '¿¹¿Ü' + ParentId); text:= '------------------------> [Çã¿ë] '; if ((dwStatus and DN_DISABLEABLE) <> 0) and ((dwStatus and DN_HAS_PROBLEM) <> 0) then begin if SetDeviceState(hDevInfo, DevInfoData, True) then begin IsLog:= True; if FThread <> nil then TGuardThread(FThread).DoLog(text + DevName); end; end; break; end else begin text:= '------------------------> [Â÷´Ü] '; if ((dwStatus and DN_DRIVER_LOADED) <> 0) and ((dwStatus and DN_STARTED) <> 0) then begin if SetDeviceState(hDevInfo, DevInfoData, False) then begin IsLog:= True; if FThread <> nil then begin TGuardThread(FThread).DoLog(text + DevName); TGuardThread(FThread).ShowPopup(DevName); end; end; end; end; end; dsReadOnly, dsEnable: begin text:= '------------------------> [Çã¿ë] '; if ((dwStatus and DN_DISABLEABLE) <> 0) and ((dwStatus and DN_HAS_PROBLEM) <> 0) then begin if SetDeviceState(hDevInfo, DevInfoData, True) then begin IsLog:= True; if FThread <> nil then TGuardThread(FThread).DoLog(text + DevName); end; end; end; end; if Policy.isLog_ and IsLog then begin if FThread <> nil then begin TGuardThread(FThread).DoLog(Format('[ŽÁö](%s) %s (Rule: %s)', [text, DevName, Policy.Name])); TGuardThread(FThread).DoLog(Format( 'Service:%s, DeviceClass:%s, friendlyName:%s, HardwareID:%s', [Service, DeviceClass, friendlyName, HardwareID])); TGuardThread(FThread).DoLog(Format( 'ParentId:%s, DeviceDriveDesc:%s, Manufacturer:%s, RemovalPolicy:%d, InstatllState:%d', [ParentId, DeviceDriveDesc, Manufacturer, DWORD(RemovalPolicy), DWORD(InstatllState)])); TGuardThread(FThread).DoLog(Format('InstanceId:%s', [InstanceId])); TGuardThread(FThread).DoLog('-----------------------------------------------------'); end; end; IsLog:= False; end else begin TGuardThread(FThread).DoLog(Format('CM_Get_DevNode_Status Fail : %d', [GetLastError])); end; end; Inc(Idx); end; finally SetupDiDestroyDeviceInfoList(hDevInfo); end; end; procedure TDeviceGuardEngine.CheckAndApplyPolicy(Policy: TPolicyItem); begin // Match Ç׸ñÀÌ ¾øÀ¸¸é ½ÇÇàÇÏÁö ¾ÊÀ½ (´Ü, ºí·çÅõ½º µî ¿¹¿Ü Á¦¿Ü) // ¶Ç´Â Match°¡ ¾øÀ¸¸é Â÷´Ü/Çã¿ëÀ» ¸øÇϹǷΠÆÐ½º if Policy.Matches_.Count = 0 then Exit; for var Match in Policy.Matches_ do begin TGuardThread(FThread).DoLog(Format('CheckAndApplyPolicy, Enumerator : (%s) MatchData : (%s)', [Match.Enumerator, Match.MatchData])); ApplySingleMatch(Policy, Match); end; end; procedure TDeviceGuardEngine.EnforceSystemPolicy; var Policies: TObjectList; begin Policies := TObjectList.Create(True); // µî·ÏµÈ ¸ðµç Á¤Ã¥¿¡ ´ëÇØ ¼øÈ¸ try if gBs1Policy <> nil then begin gBs1Policy.CopyPoliciesTo(Policies); end; for var Policy in Policies do begin // ºí·çÅõ½º´Â º°µµ ·ÎÁ÷ // if Policy.isBluetooth_ then // begin // EnforceBluetoothPolicy(Policy); // Continue; // end; case Policy.flag_ of DWORD(BDC_CDROM), DWORD(BDC_FLOOPY), DWORD(BDC_USB_DISK), DWORD(BDC_NETWORKDRIVEOUT), DWORD(BDC_NETWORKDRIVEIN), DWORD(BDC_EXTERNALHDD): continue; end; // ÀÏ¹Ý ÀåÄ¡ Á¦¾î (SetupDiGetClassDevs -> Enum -> Match) // gBs1fltControl.SetPolicy(Policy.flag_, DWORD(Policy.state_), DWORD(Policy.isLog_)); // if (Policy.flag_ <> DWORD(BDC_MTP)) then // begin gBs1FltControl.SetPolicy(DWORD(Policy.flag_), DWORD(Policy.state_), DWORD(Policy.isLog_)); // end; if (Policy.flag_ = DWORD(BDC_BLUETOOTH_FILE)) then begin continue; end else if (Policy.flag_ = DWORD(BDC_MTP)) then begin end; // else if (Policy.flag_ = DWORD(BDC_USB)) then // begin // if (TDeviceState(Policy.state_) = dsDisable) then // begin // if not FUseRestart then // begin // UsbReStart; // FUseRestart:= True; // end; // OnLog('UsbReStart Sucess...'); // end // else // begin // FUseRestart:= False; // end; // // continue; // end; CheckAndApplyPolicy(Policy); end; finally Policies.Free; end; end; function TDeviceGuardEngine.IsVirtualMachineDevice(const Info: TDeviceInfoDTO): Boolean; begin // Á¦°ÅÁ¤Ã¥ 2¹øÀ̸鼭 ƯÁ¤ Ŭ·¡½ºÀÎ °æ¿ì (VMware, VirtualBox µî ¹æ¾î) if Info.RemovalPolicy = CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL then begin if SameText(Info.DeviceClass, 'Net') or SameText(Info.DeviceClass, 'Monitor') or SameText(Info.DeviceClass, 'System') or SameText(Info.DeviceClass, 'HDC') or SameText(Info.DeviceClass, 'DiskDrive') or SameText(Info.DeviceClass, 'MEDIA') or SameText(Info.DeviceClass, 'Display') then begin Exit(True); // °Ç³Ê¶Ù¾î¾ß ÇÔ end; end; Result := False; end; function TDeviceGuardEngine.IsGeneralHIDDevice(const Info: TDeviceInfoDTO): Boolean; begin // Űº¸µå, ¸¶¿ì½º µîÀº Àý´ë Â÷´ÜÇÏ¸é ¾È µÊ if (Pos('KEYBOARD', UpperCase(Info.DeviceClass)) > 0) or (Pos('MOUSE', UpperCase(Info.DeviceClass)) > 0) or (SameText(Info.DeviceClass, 'HIDClass')) then begin Result := True; end else Result := False; end; function TDeviceGuardEngine.IsSystemOrCriticalDevice(const Info: TDeviceInfoDTO): Boolean; begin // °íÁ¤ ÀåÄ¡ (Removal Policy 1) if Info.RemovalPolicy = CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL then Exit(True); // ¿­°ÅÀÚ(Enumerator)¿Í ¼­ºñ½º/Ŭ·¡½º Á¶ÇÕ È®ÀÎ if (SameText(Info.Enumerator, 'SCSI') and SameText(Info.Service, 'DISK')) or (SameText(Info.Enumerator, 'PCI') and SameText(Info.Service, 'nvlddmkm')) or // NVIDIA ±×·¡ÇÈ (SameText(Info.Enumerator, 'SCSI') and SameText(Info.DeviceClass, 'DiskDrive')) or (SameText(Info.Enumerator, 'PCI') and SameText(Info.DeviceClass, 'Display')) then begin Exit(True); end; // ƯÁ¤ Ŭ·¡½º À̸§ È®ÀÎ (¼ÒÇÁÆ®¿þ¾î ÀåÄ¡, ¿Àµð¿À, ÇÁ¸°ÅÍ µî) // MatchText¸¦ ¾²¸é OR ¿¬»êÀ» ¹è¿­·Î ±ò²ûÇÏ°Ô Ã³¸®ÇÒ ¼ö ÀÖ½À´Ï´Ù. if MatchText(Info.DeviceClass, ['AudioEndpoint', 'PrintQueue', 'SoftwareDevice', 'WSDPrintDevice', 'Image']) then begin Exit(True); end; Result := False; end; function TDeviceInfoDTO.GetMainHardwareID: string; begin // HardwareID´Â ¸ÖƼ½ºÆ®¸µÀ̹ǷΠù ¹øÂ° ÁÙ¸¸ °¡Á®¿À°Å³ª ÆÄ½Ì // ¿¹: USB\VID_1234&PID_5678... Result := HardwareID.Split([#0], TStringSplitOptions.ExcludeEmpty)[0]; end; procedure TDeviceGuardEngine.AllEnumSystemDevice; var hDevInfo: THandle; DevInfoData: SP_DEVINFO_DATA; Idx: DWORD; MatchedItem: TPolicyItem; IsMatchFound: Boolean; DevName: string; Info: TDeviceInfoDTO; UniqueKey: string; dwStatus, dwProblem: DWORD; InstatllState: DWORD; begin hDevInfo := SetupDiGetClassDevs(nil, nil, 0, DIGCF_PRESENT or DIGCF_ALLCLASSES); if hDevInfo = INVALID_HANDLE_VALUE then Exit; try Idx := 0; DevInfoData.cbSize := SizeOf(SP_DEVINFO_DATA); while SetupDiEnumDeviceInfo(hDevInfo, Idx, DevInfoData) do begin try MatchedItem := nil; IsMatchFound := False; DevName := ''; Info.Index := Idx; Info.friendlyName := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_FRIENDLYNAME); if Info.friendlyName = '' then Info.friendlyName := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_DEVICEDESC); Info.HardwareID := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_HARDWAREID); Info.DeviceClass := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_CLASS); Info.Service := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_SERVICE); Info.Enumerator := GetDeviceStringProperty(hDevInfo, DevInfoData, SPDRP_ENUMERATOR_NAME); Info.RemovalPolicy := GetDeviceStringPropertyDWORD(hDevInfo, DevInfoData, SPDRP_REMOVAL_POLICY); InstatllState:= GetDeviceStringPropertyDWORD(hDevInfo, DevInfoData, SPDRP_INSTALL_STATE); Info.ParentId := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_Parent); var DeviceDriveDesc := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_DriverDesc); var Manufacturer := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_Manufacturer); var DeviceDesc := GetDevicePropertyString(hDevInfo, DevInfoData, DEVPKEY_Device_DeviceDesc); if Info.DeviceClass = '' then Continue; if InstatllState = CM_INSTALL_STATE_FAILED_INSTALL then Continue; if IsVirtualMachineDevice(Info) then Continue; // VM ¿¹¿Ü if IsSystemOrCriticalDevice(Info) then Continue; // ½Ã½ºÅÛ ÀåÄ¡ ¿¹¿Ü if IsGeneralHIDDevice(Info) then Continue; // Űº¸µå/¸¶¿ì½º ¿¹¿Ü // ÀåÄ¡ ³ëµå »óÅ ȮÀÎ (Start ¿©ºÎ) if CM_Get_DevNode_Status(dwStatus, dwProblem, DevInfoData.DevInst, 0) = CR_SUCCESS then begin Info.IsStarted := (dwStatus and DN_STARTED) <> 0; end else Continue; // »óÅ ȮÀÎ ºÒ°¡ ÀåÄ¡´Â ÆÐ½º UniqueKey := Info.GetMainHardwareID; if FThread <> nil then begin TGuardThread(FThread).DoLog(Format('[Ŭ·¡½º ¸í] : Name : %s, Class : %s', [Info.DeviceClass, DevInfoData.ClassGuid.ToString])); TGuardThread(FThread).DoLog( Format(' -> serviceName : %s || enumeratorName : %s || friendlyName: %s || HWID: %s || RemovalPolicy: %d || UniqueKey: %s', [Info.Service, Info.Enumerator, Info.friendlyName, Info.HardwareID, Info.RemovalPolicy, UniqueKey])); TGuardThread(FThread).DoLog( Format(' -> ParentId : %s || DeviceDriveDesc : %s || Manufacturer: %s || DeviceDesc: %s\n', [Info.ParentId, DeviceDriveDesc, Manufacturer, DeviceDesc])); end; sleep(10); finally Inc(Idx); end; end; finally SetupDiDestroyDeviceInfoList(hDevInfo); end; end; function TDeviceGuardEngine.GetDevicePropertyString(hDevInfo: HDEVINFO; var DevData: SP_DEVINFO_DATA; const PropKey: DEVPROPKEY): string; var PropType: DEVPROPTYPE; ReqSize: DWORD; Buffer: array of Byte; begin Result := ''; ReqSize := 0; // 1. Å©±â Á¶È¸ if not SetupDiGetDevicePropertyW(hDevInfo, DevData, PropKey, PropType, nil, 0, ReqSize, 0) then begin if GetLastError <> ERROR_INSUFFICIENT_BUFFER then Exit; end; if ReqSize = 0 then Exit; // 2. µ¥ÀÌÅÍ Á¶È¸ SetLength(Buffer, ReqSize); if SetupDiGetDevicePropertyW(hDevInfo, DevData, PropKey, PropType, @Buffer[0], ReqSize, ReqSize, 0) then begin // DEVPROP_TYPE_STRING È®ÀÎ ·ÎÁ÷ÀÌ ÇÊ¿äÇÒ ¼ö ÀÖÀ¸³ª, ¿©±â¼± ¹®ÀÚ¿­ÀÌ¶ó °¡Á¤ÇÏ°í º¯È¯ Result := TEncoding.Unicode.GetString(Buffer); Result := Result.TrimRight([#0]); // NULL Á¾·á ¹®ÀÚ Á¦°Å end; end; function TDeviceGuardEngine.GetClassPropertyString(const ClassGuid: TGUID; const PropKey: DEVPROPKEY): string; var PropType: DEVPROPTYPE; ReqSize: DWORD; Buffer: array of Byte; begin Result := ''; ReqSize := 0; // 1. Å©±â Á¶È¸ if not SetupDiGetClassPropertyW(ClassGuid, PropKey, PropType, nil, 0, ReqSize, DICLASSPROP_INSTALLER, 0) then begin if GetLastError <> ERROR_INSUFFICIENT_BUFFER then Exit; end; if ReqSize = 0 then Exit; // 2. µ¥ÀÌÅÍ Á¶È¸ SetLength(Buffer, ReqSize); if SetupDiGetClassPropertyW(ClassGuid, PropKey, PropType, @Buffer[0], ReqSize, ReqSize, DICLASSPROP_INSTALLER, 0) then begin Result := TEncoding.Unicode.GetString(Buffer); Result := Result.TrimRight([#0]); end; end; function TDeviceGuardEngine.GetDeviceStringProperty(DevInfoSet: HDEVINFO; var DevData: SP_DEVINFO_DATA; PropId: DWORD): string; var Buffer: TBytes; Size, ReqSize: DWORD; DataType: DWORD; dwPropertyRegDataType: DWORD; begin Result := ''; ReqSize := 0; SetupDiGetDeviceRegistryProperty(DevInfoSet, DevData, PropId, dwPropertyRegDataType, nil, 0, ReqSize); if (GetLastError = ERROR_INSUFFICIENT_BUFFER) and (ReqSize > 0) then begin SetLength(Buffer, ReqSize); if SetupDiGetDeviceRegistryProperty(DevInfoSet, DevData, PropId, DataType, @Buffer[0], ReqSize, Size) then if (DataType = REG_SZ) or (DataType = REG_MULTI_SZ) then Result := TEncoding.Unicode.GetString(Buffer).Trim([#0]); end; end; function TDeviceGuardEngine.GetDeviceStringPropertyDWORD(DevInfoSet: HDEVINFO; var DevData: SP_DEVINFO_DATA; PropId: DWORD): DWORD; var ReqSize, DataType: DWORD; begin Result := 0; SetupDiGetDeviceRegistryProperty(DevInfoSet, DevData, PropId, DataType, @Result, SizeOf(DWORD), ReqSize); end; function TDeviceGuardEngine.GetDeviceInstanceId(hDevInfo: HDEVINFO; var DevData: SP_DEVINFO_DATA): string; var ReqSize: DWORD; Buffer: array of Char; // µ¿Àû ¹è¿­ »ç¿ë begin Result := ''; ReqSize := 0; // 1. ÇÊ¿äÇÑ ¹öÆÛ Å©±â ±¸Çϱâ // ÇÔ¼ö°¡ False¸¦ ¸®ÅÏÇϰí GetLastError°¡ ERROR_INSUFFICIENT_BUFFERÀÏ ¶§ Á¤»ó if not SetupDiGetDeviceInstanceId(hDevInfo, DevData, nil, 0, ReqSize) then begin if GetLastError <> ERROR_INSUFFICIENT_BUFFER then Exit; end; if ReqSize = 0 then Exit; // 2. ¹öÆÛ ¸Þ¸ð¸® ÇÒ´ç SetLength(Buffer, ReqSize); // 3. ½ÇÁ¦ µ¥ÀÌÅÍ °¡Á®¿À±â if SetupDiGetDeviceInstanceId(hDevInfo, DevData, @Buffer[0], ReqSize, ReqSize) then begin // PChar·Î º¯È¯ÇÏ¿© µ¨ÆÄÀÌ ½ºÆ®¸µ¿¡ ´ã±â Result := PChar(@Buffer[0]); Result := Trim(Result); // Ȥ½Ã ¸ð¸¦ °ø¹é Á¦°Å end; end; function TDeviceGuardEngine.SetDeviceState(DevInfoSet: HDEVINFO; var DevData: SP_DEVINFO_DATA; Enable: Boolean): Boolean; var PCP: SP_PROPCHANGE_PARAMS; state : DWORD; begin FillChar(PCP, SizeOf(PCP), 0); PCP.ClassInstallHeader.cbSize := SizeOf(PCP.ClassInstallHeader); PCP.ClassInstallHeader.InstallFunction := DIF_PROPERTYCHANGE; if Enable then PCP.StateChange := DICS_ENABLE else PCP.StateChange := DICS_DISABLE; PCP.Scope := DICS_FLAG_GLOBAL; PCP.HwProfile := 0; if not SetupDiSetClassInstallParams(DevInfoSet, @DevData, @PCP, SizeOf(PCP)) then Exit(False); Result := SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfoSet, @DevData); if Result then begin state:= DWORD(SetupDiChangeState(DevInfoSet, @DevData)); if state = 0 then begin TGuardThread(FThread).DoLog('SetupDiChangeState Fail : ' + GetLastError.ToString); Result:= False; end; end else begin TGuardThread(FThread).DoLog('SetupDiCallClassInstaller Fail : ' + GetLastError.ToString); end; end; procedure TDeviceGuardEngine.EnforceBluetoothPolicy(PolicyItem: TPolicyItem); var SearchParams: BLUETOOTH_DEVICE_SEARCH_PARAMS; DeviceInfo: BLUETOOTH_DEVICE_INFO; DeviceHandle: HBLUETOOTH_DEVICE_FIND; MajorClass: Cardinal; IsTarget: Boolean; begin if PolicyItem.state_ = dsEnable then Exit; FillChar(SearchParams, SizeOf(SearchParams), 0); SearchParams.dwSize := SizeOf(SearchParams); SearchParams.fReturnAuthenticated := True; SearchParams.fReturnConnected := True; SearchParams.fReturnRemembered := True; SearchParams.fReturnUnknown := True; SearchParams.fIssueInquiry := False; FillChar(DeviceInfo, SizeOf(DeviceInfo), 0); DeviceInfo.dwSize := SizeOf(DeviceInfo); DeviceHandle := BluetoothFindFirstDevice(SearchParams, DeviceInfo); if DeviceHandle = 0 then Exit; try repeat MajorClass := (DeviceInfo.ulClassofDevice and $1F00) shr 8; IsTarget := False; // KCD_BLUETOOTH(Àüü) ¶Ç´Â KCD_BLUETOOTH_FILE(ÀϺÎ) µî¿¡ µû¶ó ·ÎÁ÷ ºÐ±â °¡´É // ¿©±â¼± ±âÁ¸ ·ÎÁ÷´ë·Î ¿Àµð¿À/Æù/¿þ¾î·¯ºí Â÷´Ü if (MajorClass = COD_MAJOR_AUDIO) or (MajorClass = COD_MAJOR_PHONE) or (MajorClass = COD_MAJOR_WEARABLE) then IsTarget := True; if MajorClass = COD_MAJOR_PERIPHERAL then IsTarget := False; if IsTarget and (PolicyItem.state_ = dsDisable) then begin if BluetoothRemoveDevice(DeviceInfo.Address) = ERROR_SUCCESS then begin if FThread <> nil then begin TGuardThread(FThread).DoLog('[BT Â÷´Ü] ' + DeviceInfo.szName); TGuardThread(FThread).ShowPopup('BT: ' + DeviceInfo.szName); end; end; end; until not BluetoothFindNextDevice(DeviceHandle, DeviceInfo); finally BluetoothFindDeviceClose(DeviceHandle); end; end; { TGuardThread } constructor TGuardThread.Create(AOwnerEngine: TObject); begin inherited Create(True); FOwnerEngine := AOwnerEngine; FreeOnTerminate := False; end; procedure TGuardThread.Execute; var Engine: TDeviceGuardEngine; WaitResult: TWaitResult; begin Engine := TDeviceGuardEngine(FOwnerEngine); while not Terminated do begin WaitResult := Engine.FScanEvent.WaitFor(INFINITE); if Terminated then Break; if WaitResult = wrSignaled then begin try Engine.EnforceSystemPolicy; except on E: Exception do DoLog('Scan Error: ' + E.Message); end; // ÀÚµ¿ ¸®¼Â À̺¥Æ®°¡ ¾Æ´Ï¸é ResetEvent È£Ãâ ÇÊ¿ä, ¿©±â¼± Create½Ã ÀÚµ¿ ¸®¼ÂÀ¸·Î ¼³Á¤ÇÔ end; end; end; procedure TGuardThread.DoLog(const Msg: string); begin if Terminated then Exit; Queue(procedure begin if Assigned(TDeviceGuardEngine(FOwnerEngine).OnLog) then TDeviceGuardEngine(FOwnerEngine).OnLog(Msg); end); end; procedure TGuardThread.ShowPopup(const Msg: string); begin if Terminated then Exit; Queue(procedure begin if Assigned(TDeviceGuardEngine(FOwnerEngine).OnPopup) then TDeviceGuardEngine(FOwnerEngine).OnPopup(Msg); end); end; end.