{*******************************************************} { } { ManagerCampaign } { } { Copyright (C) 2023 kku } { } {*******************************************************} unit ManagerCampaign; interface uses Tocsg.Obj, System.SysUtils, System.Classes, Winapi.Windows, System.Generics.Collections, IdHTTP, IdSSLOpenSSL, IdIOHandler, Define, superobject, CttSchDefine; type TCampnType = (ctVul, ctScan, ctScanPatial, ctForceEnc); TCampnRecursive = (crcOnce, crcWeekly, crcMonthly, crcDay, crcMonthDay{매월몇째주요일}); TCampnRespaction = (crtNone, crtDelete, crtPerDel, crtDRM, crtAIP); TCampnDelayCond = (cdnNone, cdnOnce, cdnNoChange, cdnMaxCnt); TCampnInfo = record dtStar, dtEnd, dtStd: TDateTime; sId, sName: String; CampnType: TCampnType; Recursive: TCampnRecursive; Respaction: TCampnRespaction; DelayCond: TCampnDelayCond; sExts, sCRList, sTgFolders, sExpFolders: String; nTimeoutSec, nLimitSizeMB, nNotiIfCnt, nNotiWarIfCnt, nNotiCriIfCnt, nDelayPeriod, nDelayFileCnt: Integer; bIncOldRst, bIncFName, bNoFindEnc, bDecompress, bDelayNotice, bUseFilter, bUseFilterAnd, bGetFile, bGetContents, bShowScan, bPopupResult, bNoActionVul, bScanNotice, bScanHighNotice: Boolean; bPwSafe, bFwSafe, bOsSafe, bAvSafe, bScrnSafe, bOsPatch: Boolean; CttSchLimitMB: Integer; end; PCampnEnt = ^TCampnEnt; TCampnEnt = record Info: TCampnInfo; sSchRstPath, sSchRstExpPath: String; dtLastWork, dtLastNoti: TDateTime; end; TCampnEntList = TList; TManagerCampaign = class(TTgObject) private HTTP_: TIdHTTP; SSL_: TIdSSLIOHandlerSocketOpenSSL; // UTC 시간 계산 (한국=+09:00) sUtcOffset_: String; CampnEntList_: TCampnEntList; procedure OnEntNotify(Sender: TObject; const Item: PCampnEnt; Action: TCollectionNotification); procedure AddTestEnt; public // dtTest_: TDateTime; Constructor Create; Destructor Destroy; override; procedure ClearScanDate; // 테스트를 제외하고 쓸일 없음 function GetCampnFromId(sId: String): PCampnEnt; procedure RemoveCampnFromId(sId: String); function GetWorkableCampaign(bIgrLastWrokDT: Boolean = false): PCampnEnt; function GetDelayNotiCampaign: PCampnEnt; function GetNoActionVulCampaign: PCampnEnt; procedure UpdateCampnEnts(aO: ISuperObject); procedure SendCampaignProgress(pEnt: PCampnEnt; aProg: TCttSchProg); procedure SendCampaignResultVul(sTaskId: String); procedure SendCampaignResultFile(sTaskId: String; pResult: PSchResult; nProcIdx: Integer; bCollectFile: Boolean; nLimitMB: Integer; aRespaction: TCampnRespaction); procedure SendCampaignResultSelfAction(sTaskId, sKind, sPath: String; nProcIdx: Integer); function GetCampnByType(aType: TCampnType): PCampnEnt; procedure SendTestMsg; procedure SendPrintTest; procedure Save; function SaveToFile(const sPath: String): Boolean; procedure Load; end; function GetExistPiFileCount(sCampId: String): Integer; implementation uses Tocsg.Json, Tocsg.Path, GlobalDefine, ManagerService, Tocsg.Exception, Tocsg.Safe, Tocsg.Strings, System.DateUtils, Tocsg.DateTime, Tocsg.PCRE, Tocsg.Files, Tocsg.Convert, System.Hash, IdMultipartFormData, ManagerModel, Tocsg.Trace, System.TimeSpan, Tocsg.DRM.Encrypt, Tocsg.Registry, Condition, Tocsg.FileInfo, System.Types; Constructor TManagerCampaign.Create; procedure InitHttp; begin try SSL_ := TIdSSLIOHandlerSocketOpenSSL.Create(nil); SSL_.SSLOptions.Method := sslvSSLv23; SSL_.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_1, sslvTLSv1]; HTTP_ := TIdHTTP.Create(nil); HTTP_.IOHandler := SSL_; with HTTP_ do begin // HandleRedirects := true; // Request.BasicAuthentication := true; Request.Clear; Request.UserAgent := 'Mozilla/5.0'; Request.ContentType := 'application/xml'; Request.AcceptCharSet := 'UTF-8'; //'application/json; charset=utf-8'; // Request.Connection := 'Keep-Alive'; // Request.CustomHeaders.Values['Keep-Alive'] := 'timeout=300, max=100'; Request.Connection := 'close'; HTTPOptions := HTTPOptions - [hoKeepOrigProtocol]; ConnectTimeout := 3000; ReadTimeout := 10000; end; except on E: Exception do ETgException.TraceException(Self, E, 'Create() .. InitHttp()'); end; end; begin Inherited Create; sUtcOffset_ := '+00:00'; try var TS: TTimeSpan := TTimeZone.Local.GetUtcOffset(Now); sUtcOffset_ := Format('%s%.2d:%.2d', [BooleanToStr(TS.Ticks >= 0, '+', '-'), Abs(TS.Hours), Abs(TS.Minutes)]); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetUtcOffset()'); end; // dtTest_ := 0; InitHttp; CampnEntList_ := TCampnEntList.Create; CampnEntList_.OnNotify := OnEntNotify; Load; end; Destructor TManagerCampaign.Destroy; begin FreeAndNil(CampnEntList_); FreeAndNil(HTTP_); FreeAndNil(SSL_); Inherited; end; procedure TManagerCampaign.OnEntNotify(Sender: TObject; const Item: PCampnEnt; Action: TCollectionNotification); begin if Action = cnRemoved then Dispose(Item); end; procedure TManagerCampaign.ClearScanDate; // 테스트를 제외하고 쓸일 없음 var i: Integer; begin for i := 0 to CampnEntList_.Count - 1 do CampnEntList_[i].dtLastWork := 0; end; function TManagerCampaign.GetCampnFromId(sId: String): PCampnEnt; var i: Integer; begin Result := nil; for i := 0 to CampnEntList_.Count - 1 do if CampnEntList_[i].Info.sId = sId then begin Result := CampnEntList_[i]; exit; end; end; procedure TManagerCampaign.RemoveCampnFromId(sId: String); var i: Integer; begin try for i := 0 to CampnEntList_.Count - 1 do if CampnEntList_[i].Info.sId = sId then begin try if (gMgSvc <> nil) and (gMgSvc.ProcCampn <> nil) and (gMgSvc.ProcCampn.Info.sId = sId) then begin // 동작중인 캠페인이면 중지 후 제거 24_0221 13:11:59 kku gMgSvc.StopCampaignTask; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. RemoveCampnFromId() .. StopCampaignTask()'); end; CampnEntList_.Delete(i); exit; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. RemoveCampnFromId()'); end; end; function IsWorkableCampaign(pEnt: PCampnEnt; bChkTime: Boolean; dtNow: TDateTime = 0): Boolean; function CompareDT(const A, B: TDateTime): TValueRelationship; begin if bChkTime then Result := CompareDateTime(A, B) else Result := CompareDate(A, B); end; function CompareT(const A, B: TDateTime): TValueRelationship; begin if bChkTime then Result := CompareTime(A, B) else Result := 0; end; var n1, n2, n3: Integer; begin Result := false; try if dtNow = 0 then dtNow := Now; if not ( (CompareDT(pEnt.Info.dtStar, dtNow) = -1) and (CompareDT(pEnt.Info.dtEnd, dtNow) = 1) ) then exit; case pEnt.Info.Recursive of crcOnce : // 1회 begin if IsSameDay(pEnt.Info.dtStd, dtNow) and (CompareT(pEnt.Info.dtStd, dtNow) <= 0) then Result := true; end; crcWeekly : // 매주 (요일) begin if (DayOfWeek(pEnt.Info.dtStd) = DayOfWeek(dtNow)) and (CompareT(pEnt.Info.dtStd, dtNow) <= 0) then Result := true; end; crcMonthly : // 매월 (일) begin n1 := DayOfTheMonth(pEnt.Info.dtStd); n2 := DayOfTheMonth(dtNow); n3 := DayOfTheMonth(EndOfTheMonth(dtNow)); // 같은 날짜거나, 설정 날짜가 이달의 마지막 날보다 크고 오늘이 마지막 날인 경우... if (n1 = n2) or ( (n1 >= n3) and (n2 = n3) ) then begin Result := CompareT(pEnt.Info.dtStd, dtNow) <= 0; end; end; crcDay : // 매일 begin Result := CompareT(pEnt.Info.dtStd, dtNow) <= 0; end; crcMonthDay : // 매월 (요일) begin // 몇째주 무슨요일인지 체크 if (WeekOfTheMonth(pEnt.Info.dtStd) = WeekOfTheMonth(dtNow)) and (DayOfWeek(pEnt.Info.dtStd) = DayOfWeek(dtNow)) and (CompareT(pEnt.Info.dtStd, dtNow) <= 0) then Result := true; end; end; except on E: Exception do ETgException.TraceException(E, 'Fail .. IsWorkableCampaign()'); end; end; function TManagerCampaign.GetWorkableCampaign(bIgrLastWrokDT: Boolean = false): PCampnEnt; var i, n1, n2, n3: Integer; pEnt: PCampnEnt; dtNow: TDateTime; sWorkingCpId: String; begin Result := nil; try dtNow := Now; sWorkingCpId := GetRegValueAsString(HKEY_LOCAL_MACHINE, REG_HE, 'cpid'); for i := 0 to CampnEntList_.Count - 1 do begin pEnt := CampnEntList_[i]; // if sWorkingCpId <> '' then // begin // _Trace('GetWorkableCampaign() .. WorkingCpId = %s, ID=%s', [sWorkingCpId, pEnt.Info.sId]); // end; if (sWorkingCpId <> '') and (pEnt.Info.sId = sWorkingCpId) then begin // 작업중인게 있다면 바로 이어서 하도록 기능 추가 25_0519 11:26:49 kku _Trace('GetWorkableCampaign() .. Found .. WorkingCpId = %s, ID=%s', [sWorkingCpId, pEnt.Info.sId]); Result := pEnt; exit; end; // 오늘 한번 작업한건 무시 if not bIgrLastWrokDT and IsToday(pEnt.dtLastWork) then continue; if IsWorkableCampaign(pEnt, true, dtNow) then begin Result := pEnt; exit; end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetWorkableCampaign()'); end; end; function GetExistPiFileCount(sCampId: String): Integer; var // sFld, // sFName, // sFrName, sPath: String; StrList, DataList: TStringList; i, c, nEncCnt, nExpCnt: Integer; // MgCampExpt: TManagerCampExcept; O: ISuperObject; begin Result := 0; try nEncCnt := 0; nExpCnt := 0; if LoadJsonObjFromFile(O, GetRunExePathDir + DIR_CTTSCHRST + sCampId + '.' + DAT_CTTSCHRST) then begin Result := O.I['FoundFileCount'] - O.I['DelFileCount'] - O.I['EncFileCount'] - O.I['ExpFileCount']; // nEncCnt := O.I['EncFileCount']; // nExpCnt := O.I['ExpFileCount']; // 예외 카운트 계산은 뺌 end; try // 1000개 이하일 경우에만 삭제된 파일까지 체크, // 파일이 많으면 오래걸려서 조건을 둠 24_0612 09:31:47 kku if Result <= 1000 then begin sPath := GetRunExePathDir + DIR_CTTSCHRST + sCampId + '.' + DAT_CTTSCHRSTDATA; if not FileExists(sPath) then exit; Guard(StrList, TStringList.Create); StrList.LoadFromFile(sPath, TEncoding.UTF8); Guard(DataList, TStringList.Create); // Guard(MgCampExpt, TManagerCampExcept.Create); Result := 0; for i := 0 to StrList.Count - 1 do begin SplitString(StrList[i], '', DataList, true); if DataList.Count < 4 then continue; sPath := DataList[2]; // sFld := ExtractFilePath(sPath); // sFName := ExtractFileName(sPath); // if DataList.Count >= 5 then // sFrName := DataList[4] // else // sFrName := sFName; // // c := Pos(' > ', sFName); // if c > 0 then // SetLength(sFName, c - 1); // if (sPath.StartsWith('\\') or FileExists(sPath)) and not MgCampExpt.IsExceptFile(sPath) then // if CheckSign(sPath, SIG_DRM) then // continue; if (sPath.StartsWith('\\') or FileExists(sPath)) then Inc(Result); end; Dec(Result, O.I['EncFileCount']); Dec(Result, O.I['ExpFileCount']); if Result < 0 then Result := 0; end; finally // Dec(Result, nEncCnt); // Dec(Result, nExpCnt); end; except on E: Exception do ETgException.TraceException(E, 'Fail .. GetExistsFileCount()'); end; end; function GetFoundPiFileCount(sCampId: String): Integer; var O: ISuperObject; begin Result := 0; if not LoadJsonObjFromFile(O, GetRunExePathDir + DIR_CTTSCHRST + sCampId + '.' + DAT_CTTSCHRST) then exit; Result := O.I['FoundFileCount'];// - O.I['EncFileCount']; end; function TManagerCampaign.GetDelayNotiCampaign: PCampnEnt; var i, n1, n2, n3: Integer; pEnt: PCampnEnt; dtNow: TDateTime; begin Result := nil; try // if dtTest_ = 1 then // 미조치 알림 테스트를 위함 23_1114 15:42:01 kku // begin // dtNow := EncodeDateTime(2024, 12, 31, 0, 0, 0, 0); // dtTest_ := 0; // end else dtNow := Now; for i := 0 to CampnEntList_.Count - 1 do begin pEnt := CampnEntList_[i]; if not ( (CompareDateTime(pEnt.Info.dtStar, dtNow) = -1) and (CompareDateTime(pEnt.Info.dtEnd, dtNow) = 1) ) then continue; if not pEnt.Info.bDelayNotice then continue; if pEnt.dtLastNoti = 0 then continue; // 오늘 한번 알림한거 // if IsToday(pEnt.dtLastNoti) then // continue; // 간격을 일자에서 시간 단위로 변경 24_0612 08:48:30 kku // if DaysBetween(pEnt.dtLastNoti, dtNow) <= pEnt.Info.nDelayPeriod then // continue; if HoursBetween(pEnt.dtLastNoti, dtNow) < pEnt.Info.nDelayPeriod then continue; if GetFoundPiFileCount(pEnt.Info.sId) = 0 then begin pEnt.dtLastNoti := dtNow; continue; end; case pEnt.Info.DelayCond of cdnNone : ; cdnOnce : ; // pEnt.dtLastNoti 이걸 0으로 바꿔주는걸로 처리 cdnNoChange : begin if GetFoundPiFileCount(pEnt.Info.sId) <> GetExistPiFileCount(pEnt.Info.sId) then begin pEnt.dtLastNoti := dtNow; continue; end; end; cdnMaxCnt : begin if GetExistPiFileCount(pEnt.Info.sId) < pEnt.Info.nDelayFileCnt then begin pEnt.dtLastNoti := dtNow; continue; end; end; end; Result := pEnt; exit; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetDelayNotiCampaign()'); end; end; function TManagerCampaign.GetNoActionVulCampaign: PCampnEnt; var i, n1, n2, n3: Integer; pEnt: PCampnEnt; dtNow: TDateTime; begin Result := nil; dtNow := Now; try for i := 0 to CampnEntList_.Count - 1 do begin pEnt := CampnEntList_[i]; if not ( (CompareDateTime(pEnt.Info.dtStar, dtNow) = -1) and (CompareDateTime(pEnt.Info.dtEnd, dtNow) = 1) ) then continue; if not pEnt.Info.bDelayNotice or not pEnt.Info.bNoActionVul then continue; case pEnt.Info.DelayCond of cdnNone : ; cdnOnce : ; // pEnt.dtLastNoti 이걸 0으로 바꿔주는걸로 처리 cdnNoChange : begin if GetFoundPiFileCount(pEnt.Info.sId) <> GetExistPiFileCount(pEnt.Info.sId) then continue; end; cdnMaxCnt : begin if GetExistPiFileCount(pEnt.Info.sId) < pEnt.Info.nDelayFileCnt then continue; end; end; Result := pEnt; exit; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetDelayNotiCampaign()'); end; end; procedure TManagerCampaign.Save; var O, OA: ISuperObject; i: Integer; begin try OA := TSuperObject.Create(stArray); for i := 0 to CampnEntList_.Count - 1 do OA.AsArray.Add(TTgJson.ValueToJsonObject(CampnEntList_[i]^)); O := SO; O.O['List'] := OA; SaveJsonObjToEncFile(O, GetRunExePathDir + DIR_CONF + DAT_CAMPN, PASS_STRENC); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Save()'); end; end; function TManagerCampaign.SaveToFile(const sPath: String): Boolean; var O, OA: ISuperObject; i: Integer; begin Result := false; try OA := TSuperObject.Create(stArray); for i := 0 to CampnEntList_.Count - 1 do OA.AsArray.Add(TTgJson.ValueToJsonObject(CampnEntList_[i]^)); O := SO; O.O['List'] := OA; Result := SaveJsonObjToFile(O, sPath, TEncoding.UTF8, true); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SaveToFile()'); end; end; procedure TManagerCampaign.Load; var sPath: String; O: ISuperObject; i: Integer; pEnt: PCampnEnt; begin // CampnEntList_.Clear; // AddTestEnt; // exit; sPath := GetRunExePathDir + DIR_CONF + DAT_CAMPN; if not FileExists(sPath) then exit; if not LoadJsonObjFromEncFile(O, sPath, PASS_STRENC) then exit; if (O.O['List'] = nil) or (O.O['List'].DataType <> stArray) then exit; for i := 0 to O.A['List'].Length - 1 do begin New(pEnt); pEnt^ := TTgJson.GetDataAsType(O.A['List'].O[i]); // {$IFDEF DEBUG} // TEST_KJ // pEnt.Info.dtStd := AppendTime(Now, 0, 0, 0, 0); // {$ENDIF} CampnEntList_.Add(pEnt); end; end; procedure TManagerCampaign.AddTestEnt; var pEnt: PCampnEnt; begin CampnEntList_.Clear; New(pEnt); ZeroMemory(pEnt, SizeOf(TCampnEnt)); with pEnt.Info do begin dtStar := AppendTime(StrToDate('2023-08-01'), 0, 0, 0, 0); dtEnd := AppendTime(StrToDateTime('2023-08-31'), 23, 59, 59, 0); dtStd := AppendTime(Today, 9, 0, 0, 0); sId := '4382351325320'; // 15.4 // sId := '51327596581893'; // 0.13 sName := '캠패인-TEST-1ttttt'; _Trace('DEBUG......4'); CampnType := ctScan; Recursive := crcOnce; Respaction := crtNone; DelayCond := cdnNone; sExts := 'txt;xlsx'; sCRList := ';D003;D001'; sTgFolders := 'D:\Temp'; // sExpFolders // nDelayPeriod := 0; // nDelayNotice := 0; // nDelayFileCnt, // nDelayCondition: Integer; bUseFilter := true; // bUseFilterAnd // bGetFile, // bGetContents, bShowScan := true; bPopupResult := true; bScanNotice := true; // bScanHighNotice: Boolean; // bPwSafe, // bFwSafe, // bOsSafe, // bAvSafe, // bScrnSafe, // bOsPatch: Boolean; end; CampnEntList_.Add(pEnt); end; procedure TManagerCampaign.UpdateCampnEnts(aO: ISuperObject); var sRes: String; sTemp, sTemp2, sCamId: String; i, nPos: Integer; O: ISuperObject; pEnt: PCampnEnt; CamIdList: TStringList; begin try if aO = nil then exit; if aO.DataType <> stArray then exit; _Trace('Update Campaign ..', 2); Guard(CamIdList, TStringList.Create); for i := 0 to aO.AsArray.Length - 1 do CamIdList.Add(aO.AsArray.O[i].S['campaignId']); // 할당 해제된 캠페인 처리 for i := CampnEntList_.Count - 1 downto 0 do begin if CamIdList.IndexOf(CampnEntList_[i].Info.sId) = -1 then CampnEntList_.Delete(i); end; for i := 0 to CamIdList.Count - 1 do begin sCamId := CamIdList[i]; sRes := gMgSvc.HttpPost(gMgSvc.DestIPort + 'campaignRequest.do', sCamId, '{}'); if (sRes = '') or (sRes = 'true') then begin // 캠패인 만료 RemoveCampnFromId(sCamId); continue; end; try O := SO(sRes); if O = nil then begin _Trace('UpdateCampnEnts() .. Invalid Response ... 2, ID=%s', [sCamId]); exit; end; except _Trace('UpdateCampnEnts() .. Invalid Response ... 1, ID=%s', [sCamId]); exit; end; {$IFDEF DEBUG} SaveJsonObjToFile(O, Format('c:\cs-%s.json', [sCamId])); {$ENDIF} pEnt := GetCampnFromId(sCamId); if pEnt = nil then begin New(pEnt); ZeroMemory(pEnt, SizeOf(TCampnEnt)); CampnEntList_.Add(pEnt); pEnt.Info.sId := sCamId; end; with pEnt.Info do begin dtStar := StrToDate(O.S['KEY_STARTDATE']); dtEnd := StrToDate(O.S['KEY_ENDDATE']); // sId, sName := O.S['KEY_CAMPAIGNNAME']; // 취약 0, 전체스캔 1, 증분스캔 2, 파일 암호화 3 case StrToIntDef(O.S['KEY_CAMPAIGNTYPE'], 1) of 1 : CampnType := ctScan; 2 : CampnType := ctScanPatial; 3 : CampnType := ctForceEnc; end; sTemp := O.S['KEY_RECURSIVE'].ToLower; nPos := Pos(';', sTemp); {$IFDEF DEBUG} ASSERT(nPos > 0); {$ELSE} if nPos = 0 then _Trace('Fail .. Invalid KEY_RECURSIVE Value=%s', [sTemp]); {$ENDIF} dtStd := StrToDateDef(Copy(sTemp, nPos + 1, Length(sTemp) - nPos), 0); SetLength(sTemp, nPos - 1); if sTemp = 'day' then Recursive := crcDay else if sTemp = 'once' then Recursive := crcOnce else if sTemp = 'weekly' then Recursive := crcWeekly else if sTemp = 'monthday' then Recursive := crcMonthDay else Recursive := crcMonthly; if O.S['KEY_STARTTIME'] <> '' then begin var wHour, wMin: WORD; wHour := 0; wMin := 0; sTemp := O.S['KEY_STARTTIME']; nPos := Pos(':', sTemp); if nPos > 0 then begin wHour := StrToIntDef(Copy(sTemp, 1, nPos - 1), 0); if 24 <= wHour then wHour := 0; Delete(sTemp, 1, nPos); wMin := StrToIntDef(sTemp, 0); if 60 < wMin then wMin := 0; end; if (wHour > 0) or (wMin > 0) then dtStd := AppendTime(dtStd, wHour, wMin, 0, 0); end; sTemp := O.S['KEY_RESPACTION'].ToLower; if sTemp = 'delete' then Respaction := crtDelete else if sTemp = 'erase' then Respaction := crtPerDel else if sTemp = 'drm' then Respaction := crtDRM else if sTemp = 'aip' then Respaction := crtAIP else Respaction := crtNone; sExts := StringReplace(O.S['KEY_EXTENSION'], ';', '|', [rfReplaceAll]); sCRList := O.S['KEY_CRLIST']; sTgFolders := O.S['KEY_TARGETFOLDER']; sExpFolders := O.S['KEY_EXCEPTFOLDER']; bDelayNotice := O.S['KEY_DELAYNOTICE'].ToLower = 'true'; // 미조치 다시 알림 ON/OFF nDelayPeriod := StrToIntDef(O.S['KEY_DELAYPERIOD'], 0); // 미조치 기간 nDelayFileCnt := StrToIntDef(O.S['KEY_DELAYSCANCNT'], 0); // 미조치 개수 sTemp := O.S['KEY_DELAYCONDITION'].ToLower; // 미조치 조건 (once : 1회, nochange : 변경 없을 경우, maxcnt : 개수 이상 위반 유지) if sTemp = 'once' then DelayCond := cdnOnce else if sTemp = 'nochange' then DelayCond := cdnNoChange else if sTemp = 'maxcnt' then DelayCond := cdnMaxCnt else DelayCond := cdnNone; nNotiIfCnt := StrToIntDef(O.S['KEY_POPUPRESULT'], 1); nNotiWarIfCnt := StrToIntDef(O.S['KEY_POPUPRESULTHIGH'], 0); nNotiCriIfCnt := StrToIntDef(O.S['KEY_POPUPRESULTCRITICAL'], 0); nTimeoutSec := StrToIntDef(O.S['KEY_MAXFILETIMEOUT'], 0); if nTimeoutSec = 0 then nTimeoutSec := 20; nLimitSizeMB := StrToIntDef(O.S['KEY_MAXFILESIZE'], 0); if nLimitSizeMB = 0 then nLimitSizeMB := 100; bDecompress := O.S['KEY_SCANZIP'].ToLower = 'true'; // 압축해제 여부 bUseFilter := O.S['KEY_FILTERENABLE'].ToLower = 'true'; bUseFilterAnd := O.S['KEY_FILTERAND'].ToLower = 'true'; bGetFile := O.S['KEY_GETFILE'].ToLower = 'true'; bGetContents := O.S['KEY_GETCONTENTS'].ToLower = 'true'; bShowScan := O.S['KEY_SHOWSCAN'].ToLower = 'true'; // bPopupResult := O.S['KEY_POPUPRESULT'].ToLower = 'true'; bScanNotice := O.S['KEY_SCANNOTICE'].ToLower = 'true'; bScanHighNotice := O.S['KEY_SCANHIGHNOTICE'].ToLower = 'true'; bNoFindEnc := O.S['KEY_NONDETECTEDFILEAIP'].ToLower = 'true'; bNoActionVul := O.S['KEY_VULCHECK'].ToLower = 'true'; bIncFName := O.S['key_fileNameSearch'].ToLower = 'true'; bIncOldRst := O.S['key_resultType'].ToLower = 'maintain'; bPwSafe := O.S['KEY_PWSAFE'].ToLower = 'true'; bFwSafe := O.S['KEY_FWSAFE'].ToLower = 'true'; bOsSafe := O.S['KEY_OSSAFE'].ToLower = 'true'; bAvSafe := O.S['KEY_AVSAFE'].ToLower = 'true'; bScrnSafe := O.S['KEY_SCRNSAFE'].ToLower = 'true'; bOsPatch := O.S['KEY_OSPATCH'].ToLower = 'true'; CttSchLimitMB := StrToIntDef(O.S['key_getFileSize'], 0); end; end; Save; gMgSvc.UpdateCttSchVulState(true); _Trace('Update Campaign .. OK', 2); except on E: Exception do ETgException.TraceException(E, 'Fail .. ProcessRcvCampaign()'); end; end; procedure TManagerCampaign.SendCampaignProgress(pEnt: PCampnEnt; aProg: TCttSchProg); var O: ISuperObject; dt: TDateTime; begin try O := SO; dt := Now; O.S['KEY_AGENTID'] := gMgSvc.AgentId; O.S['KEY_EMPNO'] := gMgSvc.EmpNo; O.S['KEY_HOSTNAME'] := gMgSvc.UserName; O.S['KEY_SUBMITTIME'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dt); O.S['key_submitTime'] := FormatDateTime('yyyy-MM-dd"T"hh:nn:ss.zzz', dt) + sUtcOffset_; O.S['CAMPAIGN_ID'] := pEnt.Info.sId; O.S['KEY_TASKID'] := aProg.sTaskId; O.S['CAMPAIGN_STATUS'] := aProg.sStatus; O.S['CAMPAIGN_STARTTIME'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', aProg.dtStart); O.S['campaignStartTime'] := FormatDateTime('yyyy-MM-dd"T"hh:nn:ss.zzz', aProg.dtStart) + sUtcOffset_; if aProg.sStatus = 'DONE' then // 캠페인 진행 상황 (PROGRESS, STOP, PENDING, DONE) begin O.S['CAMPAIGN_ENDTIME'] := O.S['KEY_SUBMITTIME']; O.S['campaignEndTime'] := O.S['key_submitTime']; end; // O.S['VUL_RESULT'] // 취약점 실패 항목 O.S['SCAN_CNT_COMPLETED'] := IntToStr(aProg.llProc); O.S['SCAN_CNT_TOTAL'] := IntToStr(aProg.llTotal); O.S['SCAN_CNT_FOUND'] := IntToStr(aProg.llFound); O.S['SCAN_CNT_DRM'] := IntToStr(aProg.llEncFileCnt); O.S['SCAN_CNT_ENCFAILED'] := IntToStr(aProg.llEncFailFileCnt); gMgSvc.EventLog.Push(O.AsJSon); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendCampaignProgress()'); end; end; procedure TManagerCampaign.SendCampaignResultVul(sTaskId: String); begin try // gMgSvc.EventLog.Push(O.AsJSon); // todo : 식별자 필요 except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendCampaignResultVul()'); end; end; procedure TManagerCampaign.SendCampaignResultFile(sTaskId: String; pResult: PSchResult; nProcIdx: Integer; bCollectFile: Boolean; nLimitMB: Integer; aRespaction: TCampnRespaction); var O, OA, OTemp: ISuperObject; dtNow: TDateTime; StrList, RstList: TStringList; i: Integer; begin try O := SO; dtNow := Now; O.S['TYP_MSG'] := '@(!)_CAMPN'; O.S['KEY_AGENTID'] := gMgSvc.AgentId; O.S['KEY_EMPNO'] := gMgSvc.EmpNo; O.S['KEY_HOSTNAME'] := gMgSvc.UserName; O.S['KEY_EVENTID'] := 'CM' + FormatDateTime('yymmddhhnnss', dtNow) + IntToStr(nProcIdx); O.S['KEY_TASKID'] := sTaskId; O.S['KEY_SUBMITTIME'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtNow); O.S['key_submitTime'] := FormatDateTime('yyyy-MM-dd"T"hh:nn:ss.zzz', dtNow) + sUtcOffset_; O.S['DETECTION_DATE'] := O.S['KEY_SUBMITTIME'];// 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각. O.S['detectionDate'] := O.S['key_submitTime'];// 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각. // O.S['PARENT_LA_ID'] // 상위 이벤트가 존재하는 경우 상위 이벤트 ID if pResult.sExtrTxt <> '' then begin if (gMgSvc.PrefModel.TextLimit > 0) and (Length(pResult.sExtrTxt) > gMgSvc.PrefModel.TextLimit) then SetLength(pResult.sExtrTxt, gMgSvc.PrefModel.TextLimit); var sExt: String := GetFileExt(pResult.sFName).ToUpper; if Pos(sExt, IMAGE_EXTS) > 0 then O.S['OCR_BODY'] := pResult.sExtrTxt else O.S['MESSAGE_BODY'] := pResult.sExtrTxt; end; if (pResult.sSchName <> '') and (pResult.sResultStr <> '') then begin OA := TSuperObject.Create(stArray); Guard(StrList, TStringList.Create); SplitString(pResult.sSchName, RESULT_SEPARATOR, StrList); Guard(RstList, TStringList.Create); SplitString(pResult.sResultStr, RESULT_SEPARATOR, RstList); for i := 0 to StrList.Count - 1 do begin OTemp := SO; OTemp.S['RULE_ID'] := StrList[i]; OTemp.S['TEXT'] := GetCountOverlapWords(RstList[i], ';'); OTemp.S['CNT'] := IntToStr(pResult.nHitCnt); OA.AsArray.Add(OTemp); end; O.O['RULE_VIOLATED'] := OA; end; if bCollectFile then begin if GetFileSize_path(pResult.sPath) <= (LONGLONG(nLimitMB) * 1048576) then begin O.S['COMPONENT_ID'] := gMgSvc.MakeComponentId(sTaskId + '_' + IntToStr(nProcIdx)); case aRespaction of crtDelete, crtPerDel : gMgSvc.SendFileNor(false, O.S['COMPONENT_ID'], 'quarantineLogCollect.do', pResult.sPath, 0, nLimitMB, aRespaction); else gMgSvc.SendFileNor(false, O.S['COMPONENT_ID'], 'quarantineLogCollect.do', pResult.sPath, 0, nLimitMB); // 암호화는 앞서 처리됨 end; end; end; O.S['COMPONENT_FILENAME'] := pResult.sFName; O.S['COMPONENT_PATH'] := ExtractFilePath(pResult.sPath); if FileExists(pResult.sPath) then begin var dtCreate, dtModify, dtAccess: TDateTime; GetFileDateTime_Local(pResult.sPath, dtCreate, dtModify, dtAccess); var dwAttr: DWORD := GetFileAttributes(PChar(pResult.sPath)); OTemp := SO; OTemp.S['FILESIZE'] := IntToStr(GetFileSize_path(pResult.sPath)); OTemp.S['LASTMODIFIED'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtModify); if dwAttr <> DWORD(-1) then OTemp.S['READONLY'] := BooleanToStr((dwAttr and FILE_ATTRIBUTE_READONLY) <> 0, 'true', 'false'); if dwAttr <> DWORD(-1) then OTemp.S['HIDDEN'] := BooleanToStr((dwAttr and FILE_ATTRIBUTE_HIDDEN) <> 0, 'true', 'false'); // OTemp.S['AUTHOR'] // {만든 이} // OTemp.S['LASTUSER'] // {마지막으로 저장한 사람} // OTemp.S['FILETYPE'] // {컨텐츠 타입} // OTemp.S['FILECLS'] // {컨텐츠 형식} // OTemp.S['PASSWORD'] // {암호적용여부} OTemp.S['ENCRYPTED'] := BooleanToStr(pResult.bDrm, 'true', 'false'); if pResult.bOtherDrm then OTemp.S['OTHER_ENC'] := 'true'; /// // OTemp.S['CORRUPT'] // {깨진파일/정상파일여부} // GetFileProp_all(pResult.sPath, StrList, true); // for i := 0 to StrList.Count - 1 do // OTemp.S[StrsReplace(StrList.KeyNames[i], [' '], '_')] := StrList.ValueFromIndex[i]; O.O['COMPONENT_METADATA'] := OTemp; O.S['COMPONENT_LASTACCESS'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtAccess); end; case aRespaction of // 적용된 대응 정보 (DELETE, ERASE, DRM, AIP, EXCEPT 등) crtDelete : begin O.S['RESPONSE_INFO'] := 'DELETE'; // 일단... 대응이 있으면 무조건 true 처리 O.S['RESPONSE_RESULT'] := 'true'; // 적용된 대응 적용 결과 (true / false / pending / decline) end; crtPerDel : begin O.S['RESPONSE_INFO'] := 'ERASE'; // 일단... 대응이 있으면 무조건 true 처리 O.S['RESPONSE_RESULT'] := 'true'; // 적용된 대응 적용 결과 (true / false / pending / decline) end; crtDRM : begin O.S['RESPONSE_INFO'] := 'DRM'; // 일단... 대응이 있으면 무조건 true 처리 // O.S['RESPONSE_RESULT'] := 'true'; // 적용된 대응 적용 결과 (true / false / pending / decline) O.S['RESPONSE_RESULT'] := BooleanToStr(pResult.bMakeDrm, 'true', 'false'); end; crtAIP : begin O.S['RESPONSE_INFO'] := 'AIP'; O.S['RESPONSE_RESULT'] := BooleanToStr(pResult.bMakeDrm, 'true', 'false'); end; crtNone : begin // if CUSTOMER_TYPE = CUSTOMER_SERVE1 then // begin // // 서브원 : 대응이 없어도 암호화 되어 있는 파일은 "대응 내용"에 DRM 이라고 표시되도록 수정 (4263) 25_0910 14:41:26 kku // if pResult.bDrm then // begin // if IsUseEncOnlyAIP then // O.S['RESPONSE_INFO'] := 'AIP' // else // O.S['RESPONSE_INFO'] := 'DRM'; // end; // end; end; end; // O.S['REQUEST_COMMENT'] // 사용자가 기입하는 예외 사유 //var Str: TStringList; //Guard(Str, TStringList.Create); //Str.Text := O.AsJSon; //Str.SaveToFile('c:\temp11.txt'); _Trace('SendCampaignResultFile.. Path : %s', [pResult.sPath]); gMgSvc.EventLog.Push(O.AsJSon); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendCampaignResultFile()'); end; end; procedure TManagerCampaign.SendCampaignResultSelfAction(sTaskId, sKind, sPath: String; nProcIdx: Integer); var O, OA, OTemp: ISuperObject; dtNow: TDateTime; StrList, RstList: TStringList; i: Integer; begin try O := SO; dtNow := Now; O.S['TYP_MSG'] := '@(!)_CAMPN'; O.S['KEY_AGENTID'] := gMgSvc.AgentId; O.S['KEY_EMPNO'] := gMgSvc.EmpNo; O.S['KEY_HOSTNAME'] := gMgSvc.UserName; O.S['KEY_EVENTID'] := 'CM' + FormatDateTime('yymmddhhnnss', dtNow) + IntToStr(nProcIdx); O.S['KEY_TASKID'] := sTaskId; O.S['KEY_SUBMITTIME'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtNow); O.S['key_submitTime'] := FormatDateTime('yyyy-MM-dd"T"hh:nn:ss.zzz', dtNow) + sUtcOffset_; O.S['DETECTION_DATE'] := O.S['KEY_SUBMITTIME'];// 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각. O.S['detectionDate'] := O.S['key_submitTime'];// 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각. O.S['COMPONENT_FILENAME'] := ExtractFileName(sPath); O.S['COMPONENT_PATH'] := ExtractFilePath(sPath); O.S['RESPONSE_INFO'] := sKind; O.S['RESPONSE_RESULT'] := 'true'; gMgSvc.EventLog.Push(O.AsJSon); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendCampaignResultFile()'); end; end; function TManagerCampaign.GetCampnByType(aType: TCampnType): PCampnEnt; var i: Integer; pEnt: PCampnEnt; begin Result := nil; try pEnt := nil; for i := 0 to CampnEntList_.Count - 1 do begin case CampnEntList_[i].Info.CampnType of ctScan, ctScanPatial : begin case aType of ctScan, ctScanPatial : begin pEnt := CampnEntList_[i]; break; end; end; end; else begin if CampnEntList_[i].Info.CampnType = aType then begin pEnt := CampnEntList_[i]; break; end; end; end; end; if (pEnt <> nil) and IsWorkableCampaign(pEnt, false) then Result := pEnt; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetCampnByType()'); end; end; procedure TManagerCampaign.SendTestMsg; var pEnt: PCampnEnt; Prog: TCttSchProg; sId, sComName: String; Rst: TSchResult; begin sId := '4382351325320'; // 15.4 // sId := '51327596581893'; // 0.13 pEnt := GetCampnFromId(sId); ASSERT(pEnt <> nil); ZeroMemory(@Prog, SizeOf(Prog)); Prog.dtStart := Now; Prog.sScanId := sId; // Prog.sTaskId := FormatDateTime('yyyymmddhhnnss', Prog.dtStart); sComName := gMgSvc.ComName; Prog.sTaskId := sComName + '\' + FormatDateTime('yymmddhhnnss', Prog.dtStart); if Length(Prog.sTaskId) > 64 then begin SetLength(sComName, Length(sComName) - (Length(Prog.sTaskId) - 64)); Prog.sTaskId := sComName + '\' + FormatDateTime('yymmddhhnnss', Prog.dtStart); end; Prog.llProc := 0; Prog.llTotal := 1; Prog.sStatus := 'PROGRESS'; SendCampaignProgress(pEnt, Prog); ZeroMemory(@Rst, SizeOf(Rst)); Rst.nHitCnt := 1; Rst.sFName := '1새 텍스트 문서.txt'; Rst.sPath := 'C:\Users\kku\Desktop\이전 바탕화면\'; Rst.sSchName := 'D001|!*!|D004'; Rst.sResultStr := '811108-1234567|!*!|010-1234-5678'; SendCampaignResultFile(Prog.sTaskId, @Rst, 1, false, 50, crtNone); // , 'tocsg1234'); Prog.llProc := 1; Prog.llTotal := 1; Prog.sStatus := 'DONE'; SendCampaignProgress(pEnt, Prog); end; procedure TManagerCampaign.SendPrintTest; var O: ISuperObject; sPath, sCompId, sThumIds: String; sCode, sMsg: String; dt: TDateTime; begin try sCode := LOGCODE_EVENT_PRINTER; sMsg := 'print test'; _Trace('SendPrintLog > Code=%s, Msg=%s', [LOGCODE_EVENT_PRINTER, 'print test'], 3); sThumIds := ''; sPath := 'C:\Users\kku\Desktop\새 폴더\TEST.txt - 메모장.png'; sCompId := gMgSvc.MakeComponentId(ExtractFileName(sPath)); SumString(sThumIds, sCompId, ';'); gMgSvc.SendFileNor(false, sCompId, 'printLogCollect.do', sPath); sPath := 'C:\Users\kku\Desktop\새 폴더\TEST.txt - 메모장_0002.png'; sCompId := gMgSvc.MakeComponentId(ExtractFileName(sPath)); SumString(sThumIds, sCompId, ';'); gMgSvc.SendFileNor(false, sCompId, 'printLogCollect.do', sPath); sPath := 'C:\Users\kku\Desktop\이전 바탕화면\TEST.txt'; sCompId := gMgSvc.MakeComponentId(ExtractFileName(sPath)); gMgSvc.SendFileNor(false, sCompId, 'printLogCollect.do', sPath); dt := Now; O := SO; O.S['TYP_MSG'] := '@(!)_IC_M'; O.S['KEY_AGENTID'] := gMgSvc.AgentId; O.S['KEY_EMPNO'] := gMgSvc.EmpNo; O.S['KEY_HOSTNAME'] := gMgSvc.UserName; O.S['KEY_SUBMITTIME'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dt); O.S['key_submitTime'] := FormatDateTime('yyyy-MM-dd"T"hh:nn:ss.zzz', dt) + sUtcOffset_; O.S['KEY_LOGCODE'] := sCode; O.S['DETECTION_DATE'] := O.S['KEY_SUBMITTIME']; // 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각 O.S['detectionDate'] := O.S['key_submitTime']; // 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각 O.S['KEY_SUMMARY'] := sMsg; // O.S['PARENT_LA_ID'] // 상위 이벤트 O.S['POLICY_ID'] := gMgSvc.ModePolicy.PolicyId; // O.S['POLICY_KEY'] // 정책 위반한 경우 (PREVENT, MONITOR), 위반한 정책의 정책 키 값, 수집인 경우 (DEPLOY), 수집 요청 ID // O.S['MESSAGE_BODY'] // 정책 위반한 경우 (PREVENT, MONITOR), 원문 (본문) 컨텐츠, 수집인 경우 (DEPLOY), 수집 결과 내용, 원문 수집 정책이 비활성화인경우 수집하지 않음 O.S['OCR_BODY'] := 'ocr text!!!!'; // 이미지 OCR 추출된 컨텐츠, 원문 수집 정책이 비활성화인 경우 수집하지 않음 // O.S['RULE_VIOLATED'] // 정책 위반 규칙과 위반 개수. (LIST) O.S['COMPONENT_ID'] := sCompId; // 파일 고유값. 에이전트에서 임의 생성. (이벤트간 동일 파일 판단은 서버에서 하겠습니다.) O.S['COMPONENT_FILENAME'] := ExtractFileName(sPath); // 파일명 O.S['COMPONENT_PATH'] := ExtractFilePath(sPath);// 파일 절대 경로 // O.S['COMPONENT_METADATA'] // O.S['COMPONENT_LASTACCESS'] // 파일 최종 접근 일시 O.S['COMPONENT_THUMBNAIL_ID'] := sThumIds; // 이미지 미리보기 혹은 이미지가 있는경우, 이미지 파일의 파일 고유값. (출력물의 경우 첫 3장 이미지, 캡쳐 이미지 등), 복수개 허용, ";" 구분 // O.S['SOURCE_IP'] // 이벤트 발생 시점의 에이전트 IP. 단, 외부에서 수신하는 이벤트의 경우, 발송지의 IP가 되고, DESTINATION_URL이 에이전트 IP가 된다 // O.S['SOURCE_PORT'] // 송신지 사용 포트 // O.S['EMAIL_SENDER'] // 메일 발신자 // O.S['DESTINATION_URL'] // 수신지 URL (혹은 IP) // O.S['DESTINATION_PORT'] // 수신지 포트 // O.S['EMAIL_RECIPIENT'] // 메일 수신자 ; 로 구분 // O.S['EMAIL_SUBJECT'] // 메일 제목 // O.S['APPLICATION_NAME'] // 사용 APP 이름 // O.S['APPLICATION_PATH'] // 사용 APP 절대 경로 (실행 파일 exe의 절대 경로) O.S['PRINTER_JOBNAME'] := 'TEST.txt 출력 테스트'; O.S['PRINTER_METADATA'] := '{"PAGEINFO": "2/2", "COLOR": "true", "WARTERMARK": "false"}'; // O.S['REMOVABLE_NAME'] := aExptInfo.sDevName; // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 이름 // O.S['REMOVABLE_DEVICEID'] := aExptInfo.sSerial; // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 Device ID // O.S['DEVICE_TYPE'] := IntToStr(Integer(aExptInfo.ReqDevType)); // 의미 없어서 제외 23_0822 12:40:55 kku // O.S['REMOVABLE_CLASSID'] // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 Class ID // O.S['RESPONSE_INFO'] // 적용된 대응 정보 (DELETE, DRM, POPUP, LOG, EXCEPT 등) // O.S['RESPONSE_RESULT'] // 적용된 대응 적용 결과 (true / false / pending / decline) // O.S['REQUEST_COMMENT'] := sComment; // 사용자가 기입하는 요청 코멘트 // O.S['REQUEST_DUEDATE'] := sDueDate; // 사용자가 기입하는 예외 기간 (언제 까지. 최종일) // O.S['REQUEST_TARGET'] := IntToStr(nTarget); // 사용자가 기입하는 예외 대상 (0 - 해당 pc, 1 - 해당 사용자, 2 - 해당 부서, 3-전체) // gMgSvc.EventLog.Push(O.AsString); gMgSvc.EventLog.Push(O.AsJSon); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendEventLog()'); end; end; end.