BSOne.SFC/Tocsg.Module/Bs1Flt/bs1dc_Delphi/bs1DeviceControl.pas

894 lines
25 KiB
Plaintext

unit bs1DeviceControl;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, ShellAPI, Vcl.Graphics, System.StrUtils,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, SuperObject,
System.Generics.Collections, DefineHelper, GlobalDefine, Bs1FltCtrl, bs1PolicyUnit, DataFlowSettingForm, Bs1MadHookInject,
MessageBoxFrom, DeviceGuard.Logic;
const
// 윈도우 장치 변경 메시지 상수
DBT_DEVNODES_CHANGED = $0007;
WM_DEVICECHANGE = $0219;
// 편의상 GUID 상수를 여기에 정의 (혹은 Logic 유닛 상수를 써도 됨)
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}';
WM_REFRESH_VIEW = WM_USER + 3951;
WM_POPUP_MSG2 = WM_REFRESH_VIEW + 1;
type
TScrollablePanel = class(TPanel)
public
property OnMouseWheel;
end;
TUIItem = class
public
Panel: TPanel;
RgControl: TRadioGroup;
RgLog: TRadioGroup;
Flag: DWORD;
constructor Create(APanel: TPanel; ARgControl, ARgLog: TRadioGroup; AFlag: DWORD);
end;
TForm1 = class(TForm)
PanelTop: TPanel;
MemoLog: TMemo;
ScrollBoxDevices: TScrollBox;
Splitter1: TSplitter;
ButtonAllEnumDevice: TButton;
ButtonRunDeviceManager: TButton;
btnDataFlowConfig: TButton;
btnOpenRegedit: TButton;
grpUsbPortExcept: TGroupBox;
lblVendor: TLabel;
lblProduct: TLabel;
lblSerial: TLabel;
edtVendor: TEdit;
edtProduct: TEdit;
edtSerial: TEdit;
btnUsbPortExceptAdd: TButton;
btnPortExceptDel: TButton;
btnLogClear: TButton;
btnDataFlowStart: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure BtnApplyClick(Sender: TObject);
procedure ButtonAllEnumDeviceClick(Sender: TObject);
procedure PanelMouseWheelHandler(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
procedure ButtonRunDeviceManagerClick(Sender: TObject);
procedure btnDriveControlClick(Sender: TObject);
procedure btnDataFlowConfigClick(Sender: TObject);
procedure btnOpenRegeditClick(Sender: TObject);
procedure btnUsbPortExceptAddClick(Sender: TObject);
procedure btnPortExceptDelClick(Sender: TObject);
procedure btnLogClearClick(Sender: TObject);
procedure OnDataFlowStartClick(Sender: TObject);
private
FEngine: TDeviceGuardEngine;
FUIList: TObjectList<TUIItem>;
Fbs1MadHookInject_: TBs1MadHookInject;
// 윈도우 메시지 훅
procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
procedure CreateDefaultPolicies;
procedure BuildUIFromEngine;
procedure AddDeviceUI(const Name: string; Flag: DWORD; state : TDeviceState; log: Boolean; IsBT: Boolean = False);
procedure OnEngineLog(const Msg: string);
procedure OnEnginePopup(const Msg: string);
procedure OnUIChange(Sender: TObject);
procedure ModelessClose(Sender: TObject; var Action: TCloseAction);
procedure ShowMessageModeless(const Msg: string);
procedure process_WM_COPYDATA(var msg: TMessage); Message WM_COPYDATA;
procedure process_WM_POPUP_MSG2(var msg: TMessage); Message WM_POPUP_MSG2;
protected
// [중요] 윈도우 생성 파라미터를 조작하여 클래스명을 변경하는 메서드
procedure CreateParams(var Params: TCreateParams); override;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
DriveControlForm;
{ TUIItem }
constructor TUIItem.Create(APanel: TPanel; ARgControl, ARgLog: TRadioGroup; AFlag: DWORD);
begin
Panel := APanel;
RgControl := ARgControl;
RgLog := ARgLog;
Flag := AFlag;
end;
{ TForm1 }
procedure TForm1.ShowMessageModeless(const Msg: string);
var
Dlg: TForm;
i: Integer;
begin
// 1. 메시지 다이얼로그 폼 생성 (화면에 띄우지는 않음)
Dlg := CreateMessageDialog(Msg, mtInformation, [mbOK]);
Dlg.FormStyle := fsStayOnTop;
Dlg.Color := clBlue;
for i := 0 to Dlg.ComponentCount - 1 do
begin
// 메시지 텍스트(TLabel) 찾기
if Dlg.Components[i] is TLabel then
TLabel(Dlg.Components[i]).Font.Color := clWhite;
end;
Dlg.OnClose := ModelessClose;
Dlg.Show;
end;
procedure TForm1.ModelessClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree; // 폼이 닫힐 때 메모리 해제
end;
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
// 델파이 기본 클래스명(TfrmReceiver) 대신 내가 원하는 이름으로 변경
// StrCopy를 사용하여 PChar 배열에 복사해야 합니다.
StrCopy(Params.WinClassName, PChar(BS1DC_CLASS_NAME));
end;
procedure TForm1.FormCreate(Sender: TObject);
//var
// LMessageBoxForm: TMessageBoxForm;
begin
Caption := 'Advanced Device Guard (Event & Flag Based)';
//ScrollBoxDevices.Align := alLeft; // 왼쪽 정렬 (창 늘려도 너비 유지)
// ScrollBoxDevices.Width := 600; // 원하는 너비로 고정 (예: 600px)
// 앵커 설정: 위/아래로 폼 크기 변경 시 높이는 따라가되, 너비는 고정akBottom
ScrollBoxDevices.Anchors := [akLeft, akTop];
btnDataFlowStart.Caption := '파일 유출 제어 시작';
btnDataFlowStart.Tag := 1;
FEngine := TDeviceGuardEngine.Create;
FEngine.OnLog := OnEngineLog;
FEngine.OnPopup := OnEnginePopup;
FUIList := TObjectList<TUIItem>.Create;
gBs1Policy:= TBs1Policy.Create;
gBs1Policy.LoadDataControlConfig;
FEngine.TriggerScan;
if gBs1Policy.Policies.Count = 0 then
begin
ShowMessage('저장된 정책이 없어 기본 정책을 생성합니다.');
CreateDefaultPolicies;
end;
ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD);
BuildUIFromEngine;
FEngine.Start;
Fbs1MadHookInject_:= TBs1MadHookInject.Create;
Fbs1MadHookInject_.OnLog := OnEngineLog;
Fbs1MadHookInject_.StartInject();
OnEngineLog('감시 엔진이 시작되었습니다 (Event 대기 중).');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
OutputDebugStringW('[Bs1dc] Destory!!!++++++++++++++');
if Assigned(Fbs1MadHookInject_) then
begin
Fbs1MadHookInject_.StopInject();
FreeAndNil(Fbs1MadHookInject_);
end;
FEngine.Stop;
FEngine.Free;
FUIList.Free;
gBs1Policy.Free;
OutputDebugStringW('[Bs1dc] Destory!!!--------------');
ShowMessage('종료 되었습니다.');
end;
procedure TForm1.CreateDefaultPolicies;
var
P: TPolicyItem;
begin
//드라이브 제어
P := gBs1Policy.CreatePolicy('BDC_CDROM', BDC_CDROM);
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('BDC_FLOOPY', BDC_FLOOPY);
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('BDC_USB_DISK', BDC_USB_DISK);
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('BDC_NETWORKDRIVEOUT', BDC_NETWORKDRIVEOUT);
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('BDC_NETWORKDRIVEIN', BDC_NETWORKDRIVEIN);
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('BDC_EXTERNALHDD', BDC_EXTERNALHDD);
gBs1Policy.AddPolicyObject(P);
///
///
//
// P := gBs1Policy.CreatePolicy('PCMCIA', BDC_PCMCIA);
// P.AddMatch(SPDRP_SERVICE, 'PCMCIA', 'PCI');
// gBs1Policy.AddPolicyObject(P);
//
// P := gBs1Policy.CreatePolicy('SERIAL', BDC_SERIAL);
// P.AddMatch(SPDRP_SERVICE, 'Serial', 'ACPI');
// gBs1Policy.AddPolicyObject(P);
//
// P := gBs1Policy.CreatePolicy('1394', BDC_1394);
// P.AddMatch(SPDRP_SERVICE, '1394', '1394');
// gBs1Policy.AddPolicyObject(P);
//
// P := gBs1Policy.CreatePolicy('IRDA', BDC_IRDA);
// P.AddMatch(SPDRP_SERVICE, 'IRDA', 'IRDA');
// gBs1Policy.AddPolicyObject(P);
//
// P := gBs1Policy.CreatePolicy('WIBRO', BDC_WIBRO);
// P.AddMatch(SPDRP_SERVICE, 'WIBRO', 'WIBRO');
// gBs1Policy.AddPolicyObject(P);
//
// P := gBs1Policy.CreatePolicy('PARALLEL', BDC_PARALLEL);
// P.AddMatch(SPDRP_SERVICE, 'parport', 'ACPI');
// gBs1Policy.AddPolicyObject(P);
//
// P := gBs1Policy.CreatePolicy('MODEM', BDC_MODEM);
// P.AddMatch(SPDRP_SERVICE, 'MODEM', 'MODEM');
// gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('USB', BDC_USB);
P.AddMatch(SPDRP_SERVICE, 'USBSTOR', 'USB');
P.AddMatch(SPDRP_SERVICE, 'USBAAPL', 'USB');
P.AddMatch(SPDRP_SECURITY, 'Apple', 'USB');
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('MTP', BDC_MTP, GUID_DEVCLASS_WPD);
P.AddMatch(SPDRP_HARDWAREID, '_mtp', 'USB');
P.AddMatch(SPDRP_COMPATIBLEIDS, '_mtp', 'USB');
P.AddMatch(SPDRP_DEVICEDESC, 'apple', 'USB');
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('TETHERING', BDC_TETHERING);
P.AddMatch(SPDRP_SERVICE, 'usb_rndisx', 'USB');
P.AddMatch(SPDRP_SERVICE, 'usb_rndisk', 'USB');
P.AddMatch(SPDRP_SERVICE, 'Netaapl', 'USB');
P.AddMatch(SPDRP_SERVICE, 'andnetndis', 'USB');
P.AddMatch(SPDRP_SERVICE, 'lgwqmindis', 'USB');
P.AddMatch(SPDRP_SERVICE, 'usbrndis6', 'USB');
P.AddMatch(SPDRP_SERVICE, 'BthPan', 'BTH');
P.AddMatch(SPDRP_SERVICE, 'RFCOMM', 'BTH');
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('SDCARD', BDC_SDCARD);
P.AddMatch(SPDRP_SERVICE, 'risdxc', 'PCI');
P.AddMatch(SPDRP_SERVICE, 'disk', 'RISD');
P.AddMatch(SPDRP_SERVICE, 'sffdisk', 'SD');
P.AddMatch(SPDRP_SERVICE, 'sdbus', 'PCI');
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('WIRELESS', BDC_WIRELESS);
P.AddMatch(SPDRP_SERVICE, 'mtkwlex', 'PCI');
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('WEBCAM', BDC_WEBCAM);
P.AddMatch(SPDRP_SERVICE, 'usbvideo', 'USB');
gBs1Policy.AddPolicyObject(P);
P := gBs1Policy.CreatePolicy('Bluetooth', BDC_BLUETOOTH, '');
gBs1Policy.AddPolicyObject(P);
// P := gBs1Policy.CreatePolicy('Bluetooth File', BDC_BLUETOOTH_FILE, '');
// P.AddMatch(SPDRP_SERVICE, 'BTHUSB', 'USB');
// P.AddMatch(SPDRP_SERVICE, 'BTWUSB', 'USB');
// P.AddMatch(SPDRP_SERVICE, 'BTMUSB', 'USB');
gBs1Policy.AddPolicyObject(P);
end;
procedure TForm1.BuildUIFromEngine;
var
i: Integer;
begin
// 1. FUIList 초기화 (TUIItem 객체들 해제)
FUIList.Clear;
// 2. ScrollBox 내의 기존 컨트롤(패널들) 제거
// 역순으로 지워야 인덱스 오류가 안 납니다.
if ScrollBoxDevices <> nil then
begin
// ScrollBoxDevices의 스크롤 위치 초기화 (UI 깨짐 방지)
ScrollBoxDevices.VertScrollBar.Position := 0;
for i := ScrollBoxDevices.ControlCount - 1 downto 0 do
begin
// TScrollablePanel만 골라서 제거
if ScrollBoxDevices.Controls[i] is TPanel then
ScrollBoxDevices.Controls[i].Free;
end;
end;
// 3. 정책 개수 확인 (디버깅용)
// if FEngine.Policies.Count = 0 then
// ShowMessage('표시할 정책(장치)이 없습니다.');
// 4. UI 생성 루프
for var Policy in gBs1Policy.Policies do
begin
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;
AddDeviceUI(Policy.Name, Policy.flag_, Policy.state_, Policy.isLog_, Policy.isBluetooth_);
// 방금 추가된 UI 아이템 가져오기
// if FUIList.Count > 0 then
// begin
// var LastUI := FUIList.Last;
//
// // 제어 상태 반영 (ItemIndex: 0=사용, 1=차단)
// if Policy.state_ = dsDisable then
// LastUI.RgControl.ItemIndex := 1
// else
// LastUI.RgControl.ItemIndex := 0;
//
// // 로그 상태 반영 (ItemIndex: 0=미사용, 1=사용)
// if Policy.isLog_ then
// LastUI.RgLog.ItemIndex := 1
// else
// LastUI.RgLog.ItemIndex := 0;
OnEngineLog(Format('BuildUIFromEngine, state_(%d), isLog_(%d)', [DWORD(Policy.state_), DWORD(Policy.isLog_)]));
// end;
end;
end;
procedure TForm1.WMDeviceChange(var Msg: TMessage);
begin
inherited;
if Msg.WParam = DBT_DEVNODES_CHANGED then
begin
OnEngineLog('[System] 하드웨어 변경 감지 -> 재검사 요청');
FEngine.TriggerScan;
end;
end;
procedure TForm1.process_WM_POPUP_MSG2(var msg: TMessage);
begin
TMessageBoxForm.Create(nil, string(msg.LParam)).Show;
end;
procedure TForm1.process_WM_COPYDATA(var msg: TMessage);
var
sReceivedText: string;
dwData: DWORD;
pCpData: PCopyDataStruct;
O: ISuperObject;
begin
msg.Result := 0;
pCpData := PCopyDataStruct(msg.LParam);
OnEngineLog('process_WM_COPYDATA');
try
dwData := pCpData.dwData;
OnEngineLog('process_WM_COPYDATA : ' + dwData.ToString);
case dwData of
HPCMD_FILE_OPERATION_NOTI:
begin
O := SO(Copy(PChar(pCpData.lpData), 1, pCpData.cbData));
if O.B['N'] then
begin
var Code: DWORD;
var Summary: string;
var sDstPath: string;
var sSrcPath: string;
var deviceName: string;
var evt: Integer;
OnEngineLog('process_WM_COPYDATA : HPCMD_FILE_OPERATION_NOTI');
evt:= O.I['E'];
sSrcPath:= O.S['S'];
sDstPath:= O.S['D'];
deviceName:= O.S['T'];
Summary:= '[Folder]' + ExtractFileName(O.S['D']);
OnEngineLog(Format('[FILE_OPER] (%s)(%s), (%d), (%s)->(%s)', [deviceName, Summary, DWORD(evt), sSrcPath, sDstPath]));
if O.B['B'] then
begin
SendMessage(Handle, WM_POPUP_MSG2, 0, NativeInt(O.AsString));
end;
// begin
// Code:= PREVENT_NETFOLDER_FILE;
// Summary:= 'Block : ';
//
// if MgSvc_.ModePolicy.ShFileCrMon.bBlkNoti then
// MgSvc_.PopupMessage(TYPE_MSG_PREVENT_FILEOPER, O.AsJSon);
// end else begin
// Code:= MONITOR_NETFOLDER_FILE;
// Summary:= 'Monitor : ';
//
// if MgSvc_.ModePolicy.ShFileCrMon.bMonNoti then
// MgSvc_.PopupMessage(TYPE_MSG_MONITOR_FILEOPER, O.AsJSon);
// end;
// LogInfo.sSummary := LogInfo.sSummary + ExtractFileName(O.S['D']);
// LogInfo.sAppName := 'explorer.exe';
// LogInfo.sPath := O.S['D'];
// MgSvc_.SendEventLogEx(@LogInfo, O.B['B']);
end;
msg.Result := 1;
end;
end;
except
on E: Exception do
OnEngineLog('process_WM_COPYDATA except');
// ETgException.TraceException(Self, E, 'Fail .. process_WM_CTTSCH_REQUEST()');
end;
//
//
// //case dwData of
// // 1. 전달받은 데이터가 있는지 확인
// if Msg.CopyDataStruct.lpData <> nil then
// begin
// // 2. lpData 포인터에서 문자열을 가져옴
// // (보내는 쪽에서 Null-Terminated String으로 보냈다고 가정)
// sReceivedText := PChar(Msg.CopyDataStruct.lpData);
//
// // 3. 화면에 표시
// OnEngineLog(Format('[%s] %s', [FormatDateTime('hh:mm:ss', Now), sReceivedText]));
//
// // 4. 처리 결과 반환 (일반적으로 처리가 잘 되었으면 1 또는 True 반환)
// Msg.Result := 1;
// end
// else
// begin
// Msg.Result := 0;
// end;
end;
// 정책 저장 버튼
procedure TForm1.BtnApplyClick(Sender: TObject);
begin
gBs1Policy.SavePolicyToFile;
OnEngineLog('정책 저장 완료.');
FEngine.TriggerScan;
end;
procedure TForm1.ButtonAllEnumDeviceClick(Sender: TObject);
begin
FEngine.AllEnumSystemDevice;
end;
procedure TForm1.btnOpenRegeditClick(Sender: TObject);
begin
ShellExecute( 0, 'open', 'regedit.exe', nil, nil, SW_SHOWNORMAL );
end;
function StrToHexOrInt(const StrValue: string): Integer;
var
S: string;
begin
S := Trim(StrValue); // 앞뒤 공백 제거
if TryStrToInt(S, Result) then Exit;
if StartsText('0x', S) then
begin
S := StringReplace(S, '0x', '$', [rfIgnoreCase]);
if TryStrToInt(S, Result) then Exit;
end;
if TryStrToInt('$' + S, Result) then Exit;
Result := 0;
end;
procedure TForm1.btnPortExceptDelClick(Sender: TObject);
var
InputStrVid: string;
InputStrPid: string;
LsVid: string;
LsPid: string;
LSerial: string;
LProdoct: string;
state: DWORD;
LVid: DWORD;
LPid: DWORD;
begin
InputStrVid:= edtVendor.Text;
InputStrPid:= edtProduct.Text;
LSerial:= edtSerial.Text;
if StartsText('0x', InputStrVid) then
begin
LsVid := Copy(InputStrVid, 3, Length(InputStrVid));
LVid := StrToInt('$' + LsVid);
end
else
begin
LsVid := InputStrVid;
LVid := StrToInt(LsVid);
end;
if StartsText('0x', InputStrPid) then
begin
LsPid := Copy(InputStrPid, 3, Length(InputStrPid));
LPid := StrToInt('$' + LsPid);
end
else
begin
LsPid := InputStrPid;
LPid := StrToInt(LsPid);
end;
gBs1Policy.RemoveUsbPortExcept(LsVid, LsPid, LSerial);
state:= gBs1fltControl.DelUsbPortException(LVid, LPid, 0, PWideChar(LSerial));
if state = 0 then
begin
ShowMessage('제거 되었습니다');
end
else
ShowMessage('제거 실패 하였습니다.');
end;
procedure TForm1.btnUsbPortExceptAddClick(Sender: TObject);
var
InputStrVid: string;
InputStrPid: string;
LsVid: string;
LsPid: string;
LSerial: string;
LProdoct: string;
state: DWORD;
LVid: DWORD;
LPid: DWORD;
begin
InputStrVid:= edtVendor.Text;
InputStrPid:= edtProduct.Text;
LSerial:= edtSerial.Text;
if StartsText('0x', InputStrVid) then
begin
LsVid := Copy(InputStrVid, 3, Length(InputStrVid));
LVid := StrToInt('$' + LsVid);
end
else
begin
LsVid := InputStrVid;
LVid := StrToInt(LsVid);
end;
if StartsText('0x', InputStrPid) then
begin
LsPid := Copy(InputStrPid, 3, Length(InputStrPid));
LPid := StrToInt('$' + LsPid);
end
else
begin
LsPid := InputStrPid;
LPid := StrToInt(LsPid);
end;
// LVid:= StrToHexOrInt(LVendor);
// LPid:= StrToHexOrInt(LProdoct);
gBs1Policy.AddUsbPortExcept(LsVid, LsPid, LSerial);
state:= gBs1fltControl.SetUsbPortException(LVid, LPid, 0, PWideChar(LSerial));
if state = 0 then
begin
ShowMessage('추가 되었습니다');
end
else
ShowMessage('추가 실패 하였습니다.');
end;
procedure TForm1.ButtonRunDeviceManagerClick(Sender: TObject);
begin
ShellExecute( 0, 'open', 'devmgmt.msc', nil, nil, SW_SHOWNORMAL );
end;
procedure TForm1.OnUIChange(Sender: TObject);
var
Rg: TRadioGroup;
NewState: TDeviceState;
NewLogState: Boolean;
begin
Rg := Sender as TRadioGroup;
for var UI in FUIList do
begin
if (UI.RgControl = Rg) or (UI.RgLog = Rg) then
begin
// 현재 UI의 상태 읽기
if UI.RgControl.ItemIndex = 1 then
NewState := dsDisable
else
NewState := dsEnable;
if UI.RgLog.ItemIndex = 1 then
NewLogState := True
else
NewLogState := False;
// 엔진 업데이트 호출 (UpdatePolicyState 시그니처 수정 필요할 수 있음)
gBs1Policy.UpdatePolicyState(UI.Flag, NewState, NewLogState);
Break;
end;
end;
end;
procedure TForm1.AddDeviceUI(const Name: string; Flag: DWORD; state : TDeviceState; log: Boolean; IsBT: Boolean);
var
Pnl: TPanel; // TScrollablePanel 대신 TPanel 사용 권장 (가볍음)
RightPnl: TPanel;
RgCtrl, RgLog: TRadioGroup;
Lbl: TLabel;
// 배율 적용을 위한 변수
ScaleF: Single;
RadioWidth: Integer;
RightPanelWidth: Integer;
PanelHeight: Integer;
begin
if ScrollBoxDevices = nil then Exit;
// 1. [핵심] 현재 화면 배율 계산 (기본 96 DPI 기준)
// 델파이 최신 버전(10.x 이상)에서는 ScaleFactor 속성을 지원합니다.
// 구버전이라면 GetDeviceCaps(Canvas.Handle, LOGPIXELSX) / 96.0; 을 쓰세요.
ScaleF := Self.ScaleFactor;
// 2. [핵심] 고정값에 배율 곱하기
RadioWidth := Round(120 * ScaleF); // 120 -> 240 (200%일 때)
RightPanelWidth := (RadioWidth * 2) + Round(20 * ScaleF);
PanelHeight := Round(55 * ScaleF);
// 3. 메인 행 패널 생성
Pnl := TPanel.Create(ScrollBoxDevices);
Pnl.Parent := ScrollBoxDevices;
Pnl.Align := alTop;
Pnl.Height := PanelHeight; // 높이도 배율 적용
Pnl.BevelOuter := bvNone;
Pnl.BevelKind := bkTile;
Pnl.BevelEdges := [beBottom];
Pnl.AlignWithMargins := True;
Pnl.Margins.SetBounds(2, 2, 2, 0);
Pnl.ParentBackground := False;
Pnl.Color := clWhite;
// Panel은 캡션이 가운데 뜨므로 지워줌
Pnl.Caption := '';
Pnl.Visible := True;
// 4. 오른쪽 버튼 컨테이너 패널
RightPnl := TPanel.Create(Pnl);
RightPnl.Parent := Pnl;
RightPnl.Align := alRight;
RightPnl.Width := RightPanelWidth; // 너비도 배율 적용
RightPnl.BevelOuter := bvNone;
RightPnl.ParentBackground := False;
RightPnl.Color := clWhite;
RightPnl.Caption := '';
RightPnl.Margins.Right := Round(10 * ScaleF);
RightPnl.AlignWithMargins := True;
// 5. 로그 라디오 그룹 (오른쪽)
RgLog := TRadioGroup.Create(RightPnl);
RgLog.Parent := RightPnl;
RgLog.Caption := '로그';
RgLog.Align := alRight;
RgLog.Width := RadioWidth; // 너비 배율 적용
RgLog.Columns := 2;
RgLog.Items.Add('OFF');
RgLog.Items.Add('ON');
// 로그 상태 반영 (ItemIndex: 0=미사용, 1=사용)
if log then
RgLog.ItemIndex := 1
else
RgLog.ItemIndex := 0;
RgLog.OnClick := OnUIChange; // 이벤트 연결 필요하면 주석 해제
RgLog.Margins.SetBounds(Round(5 * ScaleF), Round(2 * ScaleF), 0, Round(2 * ScaleF));
RgLog.AlignWithMargins := True;
// 6. 제어 라디오 그룹 (왼쪽 채우기)
RgCtrl := TRadioGroup.Create(RightPnl);
RgCtrl.Parent := RightPnl;
RgCtrl.Caption := '제어';
RgCtrl.Align := alClient;
RgCtrl.Columns := 2;
RgCtrl.Items.Add('허용');
RgCtrl.Items.Add('차단');
if state = dsDisable then
RgCtrl.ItemIndex := 1
else
RgCtrl.ItemIndex := 0;
// RgCtrl.ItemIndex := 0;
RgCtrl.OnClick := OnUIChange; // 이벤트 연결 필요하면 주석 해제
RgCtrl.Margins.SetBounds(0, Round(2 * ScaleF), Round(5 * ScaleF), Round(2 * ScaleF));
RgCtrl.AlignWithMargins := True;
// 7. 장치 이름 라벨 (왼쪽)
Lbl := TLabel.Create(Pnl);
Lbl.Parent := Pnl;
Lbl.Caption := Name;
Lbl.AutoSize := False;
Lbl.Align := alClient;
Lbl.Layout := tlCenter;
Lbl.Font.Size := 9; // 폰트 크기는 OS가 알아서 배율 적용함 (보통 안 건드려도 됨)
Lbl.Font.Style := [fsBold];
Lbl.Margins.Left := Round(15 * ScaleF);
Lbl.AlignWithMargins := True;
Lbl.Transparent := True;
// 리스트에 추가
FUIList.Add(TUIItem.Create(Pnl, RgCtrl, RgLog, Flag));
end;
procedure TForm1.OnDataFlowStartClick(Sender: TObject);
begin
//
if btnDataFlowStart.Tag = 0 then
begin
btnDataFlowStart.Caption := '파일 유출 제어 시작';
btnDataFlowStart.Tag := 1;
Fbs1MadHookInject_.StopInject();
end
else
begin
btnDataFlowStart.Caption := '파일 유출 제어 중지';
btnDataFlowStart.Tag := 0;
Fbs1MadHookInject_.StartInject();
end;
end;
procedure TForm1.OnEngineLog(const Msg: string);
begin
MemoLog.Lines.BeginUpdate;
try
MemoLog.Lines.Add(Format('[%s] %s', [FormatDateTime('hh:nn:ss', Now), Msg]));
// 스크롤도 여기서 처리해야 안전함
SendMessage(MemoLog.Handle, WM_VSCROLL, SB_BOTTOM, 0);
finally
MemoLog.Lines.EndUpdate;
end;
// TThread.Queue(nil, procedure
// var
// LForm: TMessageBoxForm;
// begin
// // 1. 메모 로그 출력 (이제 안전함)
// MemoLog.Lines.BeginUpdate;
// try
// MemoLog.Lines.Add(Format('[%s] %s', [FormatDateTime('hh:nn:ss', Now), Msg]));
//
// // 스크롤도 여기서 처리해야 안전함
// SendMessage(MemoLog.Handle, WM_VSCROLL, SB_BOTTOM, 0);
// finally
// MemoLog.Lines.EndUpdate;
// end;
//
// // 2. 특정 조건 시 메시지 창 띄우기
// if ContainsText(Msg, 'MATCHING!!!!!BLOCK!!!!!') then
// begin
// LForm := TMessageBoxForm.Create(Application);
// try
// LForm.lblMessage.Caption := Msg;
// LForm.Show;
// except
// LForm.Free;
// end;
// end;
// end);
end;
procedure TForm1.OnEnginePopup(const Msg: string);
begin
self.Caption := '경고: ' + Msg;
end;
procedure TForm1.PanelMouseWheelHandler(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
ScrollCode: Integer;
begin
if WheelDelta > 0 then
ScrollCode := SB_LINEUP
else
ScrollCode := SB_LINEDOWN;
PostMessage(ScrollBoxDevices.Handle, WM_VSCROLL, ScrollCode, 0);
Handled := True;
end;
procedure TForm1.btnDataFlowConfigClick(Sender: TObject);
var
dlg: TDataFlowSettingForm;
begin
dlg := TDataFlowSettingForm.Create(Self);
try
dlg.SetEngine(gBs1Policy);
if dlg.ShowModal = mrOk then
begin
OnEngineLog('DataFlow 설정이 변경되었습니다.');
end;
finally
dlg.Free;
end;
end;
procedure TForm1.btnDriveControlClick(Sender: TObject);
var
DriveForm: TDriveControlForm;
begin
DriveForm := TDriveControlForm.Create(Self);
try
// 엔진 참조 전달 (필요 시)
DriveForm.Engine := FEngine;
DriveForm.OnLog := OnEngineLog;
DriveForm.LoadSettings;
DriveForm.ShowModal;
finally
DriveForm.Free;
end;
end;
procedure TForm1.btnLogClearClick(Sender: TObject);
begin
MemoLog.Lines.Clear;
end;
end.