BSOne.SFC/Tocsg.Module/Bs1Flt/MTPMon/MTPControl/Bs1ContentsFlowPolicyUnit.pas

344 lines
9.8 KiB
Plaintext

unit Bs1ContentsFlowPolicyUnit;
interface
uses
Winapi.Windows, System.SysUtils, System.Classes, System.JSON, System.IOUtils,
System.Generics.Collections, System.StrUtils,
BsoneDebug;
type
TDeviceType = ( DNONE, COMMON, REMOVALBE, EXTERNALHDD, NETWORKDRIVEOUT, CDROM, MTP, BLUETOOTH, FLOPPYDISK );
TContentsDeviceType = record
name: string;
edeviceType: TDeviceType;
end;
TDeviceControlPolicy = record
policy_: Integer;
fileSize_: Integer;
dump_: Integer;
end;
TProcessPolicy = class
private
deviceTypes_: TArray<string>;
exceptionPath_: TArray<string>;
extension_: TArray<string>;
exceptionExtension_: TArray<string>;
mode_: Integer;
function CompareExtension(const AListExtension, AInputExtension: string): Boolean;
public
constructor CreateFromJSON(AJSONObject: TJSONObject);
function IsExceptionPath(const APath: string): Boolean;
function IsExtension(const AExt: string): Boolean;
function IsExceptionExtension(const AExt: string): Boolean;
function HasDeviceType(const ADeviceType: string): Boolean;
property DeviceTypes: TArray<string> read deviceTypes_;
property Mode: Integer read mode_;
property ExceptionPath: TArray<string> read exceptionPath_;
property Extension: TArray<string> read extension_;
property ExceptionExtension: TArray<string> read exceptionExtension_;
end;
TPolicyManager = class
private
dataFlowSeq_: Integer;
globalExceptionPath_: TArray<string>;
deviceControls_: TDictionary<string, TDeviceControlPolicy>;
currentProcessPolicy_: TProcessPolicy;
isHookingTarget_: Boolean;
currentNormalizedName_: string;
policyPath_: string;
processName_: string;
cs: TRTLCriticalSection;
// 헬퍼 함수들
class function ParseStringArray(AJSONValue: TJSONValue): TArray<string>;
class function ParseStringOrArray(AJSONValue: TJSONValue): TArray<string>;
class function NormalizeProcessName(const AName: string): string;
public
constructor Create(const policyPath: string; const processName: string);
destructor Destroy; override;
procedure GetPolicy;
property IsHookingTarget: Boolean read isHookingTarget_;
property CurrentProcessPolicy: TProcessPolicy read currentProcessPolicy_;
property GlobalExceptionPath: TArray<string> read globalExceptionPath_;
property DataFlowSeq: Integer read dataFlowSeq_;
property DeviceControls: TDictionary<string, TDeviceControlPolicy> read deviceControls_;
function GetDeviceControlPolicy(const ADeviceName: string; out APolicy: TDeviceControlPolicy): Boolean;
function IsExceptionPath(const APath: string): Boolean;
end;
implementation
{ TProcessPolicy }
constructor TProcessPolicy.CreateFromJSON(AJSONObject: TJSONObject);
begin
inherited Create;
// deviceType은 문자열 하나일 수도, 배열일 수도 있음 -> 헬퍼로 통일 처리
deviceTypes_ := TPolicyManager.ParseStringOrArray(AJSONObject.GetValue('deviceType'));
mode_ := AJSONObject.GetValue<Integer>('mode');
exceptionPath_ := TPolicyManager.ParseStringArray(AJSONObject.GetValue('exceptionPath'));
extension_ := TPolicyManager.ParseStringArray(AJSONObject.GetValue('extension'));
// 오타(exceptionExtenstion) 대응
exceptionExtension_ := TPolicyManager.ParseStringArray(AJSONObject.GetValue('exceptionExtenstion'));
end;
function TProcessPolicy.CompareExtension(const AListExtension, AInputExtension: string): Boolean;
var
CleanInputExt: string;
begin
CleanInputExt := AInputExtension;
if CleanInputExt.StartsWith('.') then
CleanInputExt := CleanInputExt.Substring(1);
Result := SameText(AListExtension, CleanInputExt);
end;
function TProcessPolicy.IsExceptionPath(const APath: string): Boolean;
var
ExPath: string;
begin
Result := False;
for ExPath in exceptionPath_ do
begin
if AnsiContainsText(APath, ExPath) then
Exit(True);
end;
end;
function TProcessPolicy.IsExtension(const AExt: string): Boolean;
var
Ext: string;
begin
Result := False;
for Ext in extension_ do
begin
if CompareExtension(Ext, AExt) then
Exit(True);
end;
end;
function TProcessPolicy.IsExceptionExtension(const AExt: string): Boolean;
var
Ext: string;
begin
Result := False;
for Ext in exceptionExtension_ do
begin
if CompareExtension(Ext, AExt) then
Exit(True);
end;
end;
function TProcessPolicy.HasDeviceType(const ADeviceType: string): Boolean;
var
DType: string;
begin
Result := False;
for DType in deviceTypes_ do
begin
if SameText(DType, ADeviceType) then
Exit(True);
end;
end;
{ TPolicyManager }
constructor TPolicyManager.Create(const policyPath: string; const processName: string);
begin
inherited Create;
currentProcessPolicy_ := nil;
isHookingTarget_ := False;
policyPath_:= policyPath;
processName_:= processName;
deviceControls_ := TDictionary<string, TDeviceControlPolicy>.Create;
InitializeCriticalSection(cs);
end;
destructor TPolicyManager.Destroy;
begin
EnterCriticalSection(cs);
try
currentProcessPolicy_.Free;
deviceControls_.Free;
finally
LeaveCriticalSection(cs);
DeleteCriticalSection(cs);
end;
inherited;
end;
function TPolicyManager.IsExceptionPath(const APath: string): Boolean;
var
ExPath: string;
begin
EnterCriticalSection(cs);
try
Result := False;
for ExPath in globalExceptionPath_ do
begin
if AnsiContainsText(APath, ExPath) then
Exit(True);
end;
if (currentProcessPolicy_ <> nil) and currentProcessPolicy_.IsExceptionPath(APath) then
Exit(True);
finally
LeaveCriticalSection(cs);
end;
end;
class function TPolicyManager.NormalizeProcessName(const AName: string): string;
begin
// 1. 확장자 제거 (ChangeFileExt(AName, ''))
// 2. 대문자로 변환 (.ToUpper)
Result := ChangeFileExt(ExtractFileName(AName), '').ToUpper;
end;
procedure TPolicyManager.GetPolicy;
var
LRootValue, LValue: TJSONValue;
LJSON, LDataFlowRoot, LPolicyObj, LHookProcessObj, LDeviceControlObj, LProcessObj: TJSONObject;
LDataFlowKey, LDeviceKey: string;
LPair: TJSONPair;
LDevicePolicy: TDeviceControlPolicy;
begin
// 초기화
FreeAndNil(currentProcessPolicy_);
isHookingTarget_ := False;
SetLength(globalExceptionPath_, 0);
deviceControls_.Clear;
// 입력된 프로세스 이름을 JSON 키 형식(확장자 없음, 대문자)으로 정규화
// 예: "Fsquirt.exe" -> "FSQUIRT"
currentNormalizedName_ := NormalizeProcessName(string(processName_));
// LOG('TPolicyManager.GetPolicy, : %s', [currentNormalizedName_]);
if not TFile.Exists(policyPath_) then
begin
LOG('TPolicyManager.GetPolicy, TFile.Exists Fail : %s', [PChar(policyPath_)]);
Exit;
end;
LRootValue := TJSONObject.ParseJSONValue(TFile.ReadAllText(string(policyPath_), TEncoding.UTF8));
if LRootValue = nil then
begin
LOG('TPolicyManager.GetPolicy, TJSONObject.ParseJSONValue Fail', []);
Exit;
end;
try
if not (LRootValue is TJSONObject) then Exit;
LJSON := LRootValue as TJSONObject;
dataFlowSeq_ := LJSON.GetValue<Integer>('dataFlowSeq');
LDataFlowRoot := LJSON.GetValue('dataFlow') as TJSONObject;
if LDataFlowRoot = nil then Exit;
LDataFlowKey := dataFlowSeq_.ToString;
EnterCriticalSection(cs);
try
// 초기화
FreeAndNil(currentProcessPolicy_);
isHookingTarget_ := False;
SetLength(globalExceptionPath_, 0);
deviceControls_.Clear;
LPolicyObj := LDataFlowRoot.GetValue(LDataFlowKey) as TJSONObject;
if LPolicyObj = nil then Exit;
globalExceptionPath_ := ParseStringArray(LPolicyObj.GetValue('exceptionPath'));
LDeviceControlObj := LPolicyObj.GetValue('deviceControl') as TJSONObject;
if LDeviceControlObj <> nil then
begin
for LPair in LDeviceControlObj do
begin
if LPair.JsonValue is TJSONObject then
begin
LDeviceKey := LPair.JsonString.Value;
LDevicePolicy.policy_ := (LPair.JsonValue as TJSONObject).GetValue<Integer>('policy');
LDevicePolicy.fileSize_ := (LPair.JsonValue as TJSONObject).GetValue<Integer>('fileSize');
deviceControls_.Add(LDeviceKey, LDevicePolicy);
end;
end;
end;
// 3. HookProcess 파싱 및 현재 프로세스 정책 로드
LHookProcessObj := LPolicyObj.GetValue('hookProcess') as TJSONObject;
if LHookProcessObj <> nil then
begin
// 정규화된 이름("FSQUIRT")으로 바로 조회
if LHookProcessObj.TryGetValue(currentNormalizedName_, LValue) and (LValue is TJSONObject) then
begin
isHookingTarget_ := True;
LProcessObj := LValue as TJSONObject;
// 정책 객체 생성
currentProcessPolicy_ := TProcessPolicy.CreateFromJSON(LProcessObj);
end;
end;
finally
LeaveCriticalSection(cs);
end;
finally
LRootValue.Free;
end;
end;
class function TPolicyManager.ParseStringArray(AJSONValue: TJSONValue): TArray<string>;
var
LArray: TJSONArray;
LString: TJSONString;
I: Integer;
begin
Result := [];
if AJSONValue = nil then Exit;
if AJSONValue is TJSONArray then
begin
LArray := AJSONValue as TJSONArray;
SetLength(Result, LArray.Count);
for I := 0 to LArray.Count - 1 do
Result[I] := LArray.Items[I].Value;
end
else if (AJSONValue is TJSONString) then
begin
LString := AJSONValue as TJSONString;
if not LString.Value.IsEmpty then
Result := LString.Value.Split(['|']);
end;
end;
class function TPolicyManager.ParseStringOrArray(AJSONValue: TJSONValue): TArray<string>;
begin
Result := ParseStringArray(AJSONValue);
end;
function TPolicyManager.GetDeviceControlPolicy(const ADeviceName: string; out APolicy: TDeviceControlPolicy): Boolean;
begin
EnterCriticalSection(cs);
try
Result := deviceControls_.TryGetValue(ADeviceName, APolicy);
finally
LeaveCriticalSection(cs);
end;
end;
end.