{*******************************************************} { } { OutlookMonClient } { } { Copyright (C) 2023 kku } { } {*******************************************************} unit OutlookMonClient; interface uses Tocsg.ClientBase, System.SysUtils, System.Classes, Tocsg.Packet, Winapi.Windows, Tocsg.Win32, Tocsg.Obj, System.Generics.Collections, GlobalOutAddInDefine, ManagerModel, GlobalDefine; type TOutlookMonClient = class(TTgClientBase) private dwExecuteTick_: DWORD; bTerminated_, bAllowPopup_, bAbAllowPopup_: Boolean; OutPo_: TOutlookPolicy; OutAB_: TAttachBlockPolicy; nBlockSizeMB_: Integer; // CltMtx_: TTgMutex; protected function GetConnected: Boolean; override; procedure ConnectedEvent; override; procedure DisconnectedEvent; override; procedure ProcessRcvPacket(aRcv: IRcvPacket); override; public Constructor Create; Destructor Destroy; override; procedure UpdateOutPo(aPO: TPrefModel; bForce: Boolean = false); end; var gOutClient: TOutlookMonClient = nil; implementation uses Tocsg.Exception, Tocsg.Path, Tocsg.WndUtil, Tocsg.Strings, Tocsg.Process, Tocsg.Shell, superobject, Tocsg.Registry, Tocsg.Json, ManagerService, Tocsg.Safe, Tocsg.PCRE, CttSchDefine, ManagerRule, Tocsg.Files, AppCtrlDefine, Condition, ManagerCampaign; { TOutlookMonClient } Constructor TOutlookMonClient.Create; begin Inherited Create('', 0, 5000, 3000); ASSERT(gOutClient = nil); gOutClient := Self; bTerminated_ := false; bAllowPopup_ := false; bAbAllowPopup_ := false; dwExecuteTick_ := 0; nBlockSizeMB_ := 0; ZeroMemory(@OutPo_, SizeOf(OutPo_)); ZeroMemory(@OutAB_, SizeOf(OutAB_)); end; Destructor TOutlookMonClient.Destroy; begin gOutClient := nil; bTerminated_ := true; Inherited; end; procedure TOutlookMonClient.UpdateOutPo(aPO: TPrefModel; bForce: Boolean = false); var Send: ISendPacket; MailPo: TOutlookAddInPo; begin if IsOutlookABMonitorHook then begin if not aPO.OutPo.bActive then exit; end else begin if not aPO.OutPo.bActive and (aPO.OutlookAB.Kind = abkNone) then exit; end; if not bForce then begin if (aPO.OutPo.bActive = OutPo_.bActive) and (aPO.OutBodyAllowPopup = bAllowPopup_) and (aPO.OutPo.bBlock = OutPo_.bBlock) and (aPO.OutPo.bPopup = OutPo_.bPopup) and (aPO.OutPo.bSchSubject = OutPo_.bSchSubject) and (aPO.OutPo.bSchBody = OutPo_.bSchBody) and (aPO.OutPo.bUseMasking = OutPo_.bUseMasking) and (aPO.OutPo.bCollect = OutPo_.bCollect) and (aPO.OutPo.bAttachLog = OutPo_.bAttachLog) and (aPO.OutFileBlockMB = nBlockSizeMB_) and (aPO.OutPo.ContentFilter.bActive = OutPo_.ContentFilter.bActive) and (aPO.OutPo.ContentFilter.sPatterns = OutPo_.ContentFilter.sPatterns) and (aPO.OutPo.ContentFilter.nHitLimit = OutPo_.ContentFilter.nHitLimit) and ( IsOutlookABMonitorHook or ( (aPO.OutlookAB.Kind = OutAB_.Kind) and (aPO.OutlookAB.ContentFilter.bActive = OutAB_.ContentFilter.bActive) and (aPO.OutlookAB.ContentFilter.sPatterns = OutAB_.ContentFilter.sPatterns) and (aPO.OutlookAB.ContentFilter.nHitLimit = OutAB_.ContentFilter.nHitLimit) and (aPO.OutlookAB.bPopup = OutAB_.bPopup) and (aPO.OutFileAllowPopup = bAbAllowPopup_) and (aPO.OutlookAB.bCollectTxt = OutAB_.bCollectTxt) and (aPO.OutlookAB.bCollectFile = OutAB_.bCollectFile) ) ) then exit; end; OutPo_ := aPO.OutPo; OutAB_ := aPO.OutlookAB; bAllowPopup_ := aPO.OutBodyAllowPopup; bAbAllowPopup_ := aPO.OutFileAllowPopup; nBlockSizeMB_ := aPO.OutFileBlockMB; if Connected then begin ZeroMemory(@MailPo, SizeOf(MailPo)); MailPo.bFastHash := IsUseFastHash; MailPo.bCollectSendMail := true; MailPo.sRunDir := GetRunExePathDir; MailPo.sCttSchPtrnPath := MailPo.sRunDir + DAT_PTNSCH; if OutPo_.ContentFilter.bActive and (OutPo_.ContentFilter.sPatterns <> '') and (OutPo_.bSchSubject or OutPo_.bSchBody) then begin MailPo.bMailCttSch := true; MailPo.sPatterns := gMgSvc.MgRule.GetRuleSearchStrFromIdsN(OutPo_.ContentFilter.sPatterns); MailPo.nHitLimit := OutPo_.ContentFilter.nHitLimit; if OutPo_.bUseMasking then MailPo.MailCttSchProc := mcspMask else if OutPo_.bBlock then MailPo.MailCttSchProc := mcspClear; end else if OutPo_.bBlock then MailPo.MailCttSchProc := mcspClear; if not IsOutlookABMonitorHook and (OutAB_.Kind <> abkNone) then begin MailPo.AttachAB := OutAB_; MailPo.AttachAB.ContentFilter.sPatterns := gMgSvc.MgRule.GetRuleSearchStrFromIdsN(OutAB_.ContentFilter.sPatterns); MailPo.sCollectAttachPath := 'C:\ProgramData\HE\ETask\'; DeleteDir(MailPo.sCollectAttachPath); MailPo.nBlockSizeMB := nBlockSizeMB_; end; if OutPo_.bSchSubject and OutPo_.bSchBody then MailPo.MailCttSchPos := mcsoBoth else if OutPo_.bSchSubject then MailPo.MailCttSchPos := mcsoSubject else if OutPo_.bSchBody then MailPo.MailCttSchPos := mcsoBody else MailPo.bMailCttSch := false; Send := TTgPacket.Create(OAI_MAILSECU_POLICY); Send.O['PO'] := TTgJson.ValueToJsonObject(MailPo); Send.I['RcvHwnd'] := gMgSvc.RcvHwnd; SendPacket(Send); end; end; function TOutlookMonClient.GetConnected: Boolean; procedure TryConnection; var hFind, hIpc: HWND; sSid: String; begin // hIpc := StrToInt64Def(GetRegValueAsString(HKEY_CURRENT_USER, 'Software\BS1Addin', 'Outlook'), 0); sSid := GetRegRecentUserSid; if sSid <> '' then begin hIpc := StrToInt64Def(GetRegValueAsString(HKEY_USERS, sSid + '\Software\BS1Addin', 'Outlook'), 0); if hIpc <> 0 then ConnectWnd(hIpc); end; end; var sParam: String; begin Result := Inherited; if not Result and not bTerminated_ and (W2W_ <> nil) then begin if (GetTickCount - dwExecuteTick_) > 10000 then // 최소 10초에 한번만 실행 되도록함 begin dwExecuteTick_ := GetTickCount; TryConnection; end; end; end; procedure TOutlookMonClient.ConnectedEvent; begin try Inherited; SetSendPauseState(false); _Trace('Connected.'); UpdateOutPo(gMgSvc.ModePolicy, true); // 이거 여기에 안두면 재실행 시 정책을 못받아감 25_0808 10:27:14 kku except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. ConnectedEvent()'); end; end; procedure TOutlookMonClient.DisconnectedEvent; var sSid: String; begin try Inherited; QSendPacket_.Clear; ZeroMemory(@OutPo_, SizeOf(OutPo_)); // sSid := GetRegRecentUserSid; // if sSid <> '' then // DelRegValue(HKEY_USERS, sSid + '\Software\BS1Addin', 'Outlook'); _Trace('Disconnected'); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. DisconnectedEvent()'); end; end; procedure TOutlookMonClient.ProcessRcvPacket(aRcv: IRcvPacket); // function GetVioInfo: ISuperObject; // var // EntList, InfoList: TStringList; // i: Integer; // begin // Result := nil; // try // if gMgSvc.RecentFoundCode <> '' then // begin // Guard(EntList, TStringList.Create); // SplitString(gMgSvc.RecentFoundCode, RESULT_SEPARATOR, EntList); // gMgSvc.RecentFoundCode := ''; // // Result := TSuperObject.Create(stArray); // var O: ISuperObject; // Guard(InfoList, TStringList.Create); // for i := 0 to EntList.Count - 1 do // begin // SplitString(EntList[i], '|', InfoList); // if InfoList.Count > 2 then // begin // O := SO; // O.S['RULE_ID'] := InfoList[0]; // O.S['TEXT'] := RemoveOverlapWords(InfoList[1]); // O.S['CNT'] := InfoList[2]; // Result.AsArray.Add(O); // end; // end; // end; // except // // .. // end; // end; var sMsg, sCC, sFounds, sCode: String; bPrevent: Boolean; PO: TPrefModel; procedure ProcessMailMessage; begin if not PO.OutPo.bActive then exit; sMsg := ''; if aRcv.S['Founds'] <> '' then SumString(sMsg, 'Found : ' + aRcv.S['Founds'], ', '); // SumString(sMsg, 'To : ' + aRcv.S['To'], ', '); // if aRcv.S['CC'] <> '' then // SumString(sMsg, 'CC : ' + aRcv.S['CC'], ', '); SumString(sMsg, 'Subject : ' + aRcv.S['Subject'], ', '); bPrevent := true; if aRcv.B['Block'] then sCode := PREVENT_OUTLOOK_BODY else if aRcv.B['Mask'] then sCode := PREVENT_OUTLOOK_MASKING else begin sCode := MONITOR_OUTLOOK_BODY; bPrevent := false; end; if gMgSvc.IsNewApi then begin var LogInfo: TLogInfo; ZeroMemory(@LogInfo, SizeOf(LogInfo)); LogInfo.sCode := sCode; LogInfo.sDevName := 'OUTLOOK.EXE'; LogInfo.sSummary := sMsg; if PO.OutPo.bCollect and (not PO.OutPo.ContentFilter.bActive or (aRcv.S['Founds'] <> '')) then begin LogInfo.sSubject := aRcv.S['Subject']; LogInfo.sBody := aRcv.S['Body']; LogInfo.sSender := aRcv.S['Sender']; LogInfo.sRecipient := aRcv.S['Rcvs']; if aRcv.S['FoundsEx'] <> '' then begin try var EntList: TStringList; var InfoList: TStringList; Guard(EntList, TStringList.Create); SplitString(aRcv.S['FoundsEx'], RESULT_SEPARATOR, EntList); var i: Integer := 0; var OA := TSuperObject.Create(stArray); var O: ISuperObject; var pEnt: PRuleEnt; Guard(InfoList, TStringList.Create); for i := 0 to EntList.Count - 1 do begin SplitString(EntList[i], '|', InfoList); if InfoList.Count > 2 then begin O := SO; pEnt := gMgSvc.MgRule.GetRuleFromRName(InfoList[0]); if pEnt <> nil then begin O.S['RULE_ID'] := pEnt.sId; O.S['TEXT'] := RemoveOverlapWords(InfoList[1]); O.S['CNT'] := InfoList[2]; OA.AsArray.Add(O); end; end; end; if OA.AsArray.Length > 0 then LogInfo.OVio := OA; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. ExtractFoundEx..'); end; end; // LogInfo.sRecipient := aRcv.S['To']; // if aRcv.S['CC'] <> '' then // SumString(LogInfo.sRecipient, 'CC:' + aRcv.S['CC'] + ';'); // if aRcv.S['BCC'] <> '' then // SumString(LogInfo.sRecipient, 'BCC:' + aRcv.S['BCC'] + ';'); gMgSvc.SendEventLogEx(@LogInfo, bPrevent); if IsDivPopup then begin if (bPrevent and OutPo_.bPopup) or (not bPrevent and bAllowPopup_) then begin sMsg := sCode + '|' + aRcv.S['Founds'] + '|' + aRcv.S['To'] + '|' + aRcv.S['CC'] + '|' + aRcv.S['Subject']; gMgSvc.PopupMessage(TYPE_MSG_OUTLOOK_CATCH, sMsg); end; end else begin if OutPo_.bPopup then begin sMsg := sCode + '|' + aRcv.S['Founds'] + '|' + aRcv.S['To'] + '|' + aRcv.S['CC'] + '|' + aRcv.S['Subject']; gMgSvc.PopupMessage(TYPE_MSG_OUTLOOK_CATCH, sMsg); end; end; end else begin // 컨텐츠 필터 조건이 있고, 검출된거 없다면 무시 24_0516 10:11:32 kku if OutPo_.ContentFilter.bActive and (OutPo_.ContentFilter.sPatterns <> '') and (OutPo_.bSchSubject or OutPo_.bSchBody) and (aRcv.S['Founds'] = '') then exit; if PO.OutPo.bActive then begin // 원문 수집 아닌 일반 발송 수집 gMgSvc.SendEventLogEx(@LogInfo, bPrevent); if OutPo_.bPopup then begin sMsg := sCode + '|' + aRcv.S['Founds'] + '|' + aRcv.S['To'] + '|' + aRcv.S['CC'] + '|' + aRcv.S['Subject']; gMgSvc.PopupMessage(TYPE_MSG_OUTLOOK_CATCH, sMsg); end; end; end; end else gMgSvc.SendEventLog(URI_USER_ACTION, sCode, sMsg, bPrevent); end; procedure ProcessMailAttach; var i: Integer; LogInfo: TLogInfo; sOutDir, sPath: String; O, ONoti: ISuperObject; procedure DeleteAttFile(sPath: String); begin if bPrevent then begin // 차단 예외 결재 요청 시 필요할 수 있으므로 5분 후 삭제 gMgSvc.ThdReact.AddEnt(crtDelete, sPath, 300); end else DeleteFile(PChar(sPath)); end; begin if aRcv.O['AFile'] = nil then exit; if PO.OutlookAB.Kind = abkNone then exit; // aRcv.O['AFile'].S['AttList'] // 첨부파일 목록 sOutDir := aRcv.O['AFile'].S['AttatchDir']; if PO.OutlookAB.ContentFilter.bActive and (PO.OutlookAB.ContentFilter.sPatterns <> '') then begin if (aRcv.O['AFile'].O['AttFounds'] = nil) or (aRcv.O['AFile'].O['AttFounds'].DataType <> stArray) or (aRcv.O['AFile'].A['AttFounds'].Length = 0) then exit; for i := 0 to aRcv.O['AFile'].A['AttFounds'].Length - 1 do begin O := aRcv.O['AFile'].A['AttFounds'].O[i]; sMsg := Format('Process : Outlook.exe, Path : %s', [O.S['FName']]); Finalize(LogInfo); ZeroMemory(@LogInfo, SizeOf(LogInfo)); LogInfo.sSummary := sMsg; LogInfo.sAppName := 'Outlook.exe'; LogInfo.sPath := O.S['FName']; LogInfo.sSubject := aRcv.S['Subject']; // LogInfo.sBody := aRcv.S['Body']; LogInfo.sSender := aRcv.S['Sender']; LogInfo.sRecipient := aRcv.S['Rcvs']; if PO.OutlookAB.bCollectTxt then begin LogInfo.sBody := O.S['Data']; end; if O.S['FoundEx'] <> '' then begin try var EntList: TStringList; var InfoList: TStringList; Guard(EntList, TStringList.Create); SplitString(O.S['FoundEx'], RESULT_SEPARATOR, EntList); var v: Integer := 0; var OA := TSuperObject.Create(stArray); var OEnt: ISuperObject; var pEnt: PRuleEnt; Guard(InfoList, TStringList.Create); for v := 0 to EntList.Count - 1 do begin SplitString(EntList[v], '|', InfoList); if InfoList.Count > 2 then begin OEnt := SO; pEnt := gMgSvc.MgRule.GetRuleFromRName(InfoList[0]); if pEnt <> nil then begin OEnt.S['RULE_ID'] := pEnt.sId; OEnt.S['TEXT'] := RemoveOverlapWords(InfoList[1]); OEnt.S['CNT'] := InfoList[2]; OA.AsArray.Add(OEnt); end; end; end; if OA.AsArray.Length > 0 then LogInfo.OVio := OA; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. ExtractFoundEx..'); end; end; bPrevent := O.B['Block']; if sOutDir <> '' then begin sPath := sOutDir + O.S['FName']; if FileExists(sPath) then begin if PO.OutlookAB.bCollectFile and (GetFileSize_path(sPath) <= (LONGLONG(PO.OutABLimitMB) * 1048576)) then begin LogInfo.sFileCompId := gMgSvc.MakeComponentId(sPath); // 차단된거면 결재 요청 시 업로드 할 수 있으므로 삭제 딜레이 필요 var nDelay: Integer := 0; if bPrevent then nDelay := 300; gMgSvc.SendFile(LogInfo, 'quarantineLogCollect.do', sPath, PO.OutABMinMB, PO.OutABLimitMB, crtDelete, nDelay); end else DeleteAttFile(sPath); end else continue; end; ONoti := SO; ONoti.S['PName'] := 'Outlook.exe'; ONoti.S['Path'] := O.S['FName']; ONoti.S['Hash'] := O.S['Hash']; if bPrevent then begin sCode := PREVENT_OUTLOOK_ATTACH; ONoti.S['OutDir'] := sOutDir; end else begin var sFName: String := O.S['FName']; var sExt: String := GetFileExt(sFName).ToUpper; // 서명등 본문에 들어가는 이미지를 식별해서 예외 25_1203 10:49:17 kku if sFName.ToLower.StartsWith('image0') and ( (sExt = 'PNG') or (sExt = 'JPG') or (sExt = 'JPEG') or (sExt = 'BMP')) then continue; sCode := MONITOR_OUTLOOK_ATTACH; ONoti.B['M'] := true; end; LogInfo.sCode := sCode; gMgSvc.SendEventLogEx(@LogInfo, bPrevent); if IsDivPopup then begin if (bPrevent and PO.OutlookAB.bPopup) or (not bPrevent and PO.OutFileAllowPopup) then gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon); end else begin if PO.OutlookAB.bPopup then gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon); end; end; end else begin // 조건 없는 차단, 로그 if (aRcv.O['AFile'].O['AttFounds'] = nil) or (aRcv.O['AFile'].O['AttFounds'].DataType <> stArray) or (aRcv.O['AFile'].A['AttFounds'].Length = 0) then exit; for i := 0 to aRcv.O['AFile'].A['AttFounds'].Length - 1 do begin O := aRcv.O['AFile'].A['AttFounds'].O[i]; sMsg := Format('Process : Outlook.exe, Path : %s', [O.S['FName']]); Finalize(LogInfo); ZeroMemory(@LogInfo, SizeOf(LogInfo)); LogInfo.sSummary := sMsg; LogInfo.sAppName := 'Outlook.exe'; LogInfo.sPath := O.S['FName']; LogInfo.sSubject := aRcv.S['Subject']; // LogInfo.sBody := aRcv.S['Body']; LogInfo.sSender := aRcv.S['Sender']; LogInfo.sRecipient := aRcv.S['Rcvs']; if PO.OutlookAB.bCollectTxt then begin LogInfo.sBody := O.S['Data']; end; bPrevent := O.B['Block']; if sOutDir <> '' then begin sPath := sOutDir + O.S['FName']; if FileExists(sPath) then begin if PO.OutlookAB.bCollectFile and (GetFileSize_path(sPath) <= (LONGLONG(PO.OutABLimitMB) * 1048576)) then begin LogInfo.sFileCompId := gMgSvc.MakeComponentId(sPath); // 차단된거면 결재 요청 시 업로드 할 수 있으므로 삭제 딜레이 필요 var nDelay: Integer := 0; if bPrevent then nDelay := 300; gMgSvc.SendFile(LogInfo, 'quarantineLogCollect.do', sPath, PO.OutABMinMB, PO.OutABLimitMB, crtDelete, nDelay); end else DeleteAttFile(sPath); end else continue; end; ONoti := SO; ONoti.S['PName'] := 'Outlook.exe'; ONoti.S['Path'] := O.S['FName']; ONoti.S['Hash'] := O.S['Hash']; if bPrevent then begin sCode := PREVENT_OUTLOOK_ATTACH; ONoti.S['OutDir'] := sOutDir; end else begin var sFName: String := O.S['FName']; var sExt: String := GetFileExt(sFName).ToUpper; // 서명등 본문에 들어가는 이미지를 식별해서 예외 25_1203 10:49:17 kku if sFName.ToLower.StartsWith('image0') and ( (sExt = 'PNG') or (sExt = 'JPG') or (sExt = 'JPEG') or (sExt = 'BMP')) then continue; sCode := MONITOR_OUTLOOK_ATTACH; ONoti.B['M'] := true; end; LogInfo.sCode := sCode; gMgSvc.SendEventLogEx(@LogInfo, bPrevent); if IsDivPopup then begin if (bPrevent and PO.OutlookAB.bPopup) or (not bPrevent and PO.OutFileAllowPopup) then gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon); end else begin if PO.OutlookAB.bPopup then gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon); end; end; end; end; begin try case aRcv.Command of 0 : ; OAI_MAILITEM_RCV_DATA, OAI_MAILITEM_SEND_DATA : begin PO := gMgSvc.ModePolicy; ProcessMailMessage; ProcessMailAttach; end; else Inherited; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. ProcessRcvPacket()'); end; end; end.