{*******************************************************} { } { ManagerHook } { } { Copyright (C) 2022 kku } { } {*******************************************************} unit ManagerHook; interface uses Tocsg.Obj, System.SysUtils, System.Classes, Winapi.Windows, Tocsg.CommonData, Tocsg.Thread, Tocsg.Process, Winapi.Messages, GlobalDefine, Tocsg.WndUtil, AppCtrlServer, System.SyncObjs, AppCtrlDefine, System.Generics.Collections, Tocsg.Process.IPC, ManagerModel, DDispLogo; {$IFDEF DEBUG} // {$DEFINE _HOOK_TEST_} {$ENDIF} const PIPE_NAME = 'BS1@230316'; type PAppInfoEnt = ^TAppInfoEnt; TAppInfoEnt = record dwPid: DWORD; hMain: HWND; end; TDcAppInfo = TDictionary; TManagerHook = class(TTgObject) private CS_: TCriticalSection; ThdAppMon_: TThdProcessWatch; ThdWndMon_: TThdActiveWndMon; ProcList_: TProcessEntList; PidList_: TProcessIdList; TgAppList_, TgAppExList_, DrmAppList_, WebAppList_, PrintAppList_, CaptureBlockApps_: TStringList; sTgApps_, sConfDir_, sLogPath_, sHlpExe_, sDllPath_, // sDllPathPdf_, sPrtWaterSupportApp_: String; bIsWow64_: Boolean; DcApp_: TDcAppInfo; Server_: TAppCtrlServer; DefHookOpt_: TAppCtrlOpt; DrmInitInfo_: TTgFileMapping; procedure Lock; procedure Unlock; function IsTgApp(sPName: String): Boolean; procedure DelTgApp(sPName: String); function GetCtrlOpt(pEnt: PCMEnt): TAppCtrlOpt; procedure OnSendCtrlOpt(pEnt: PCMEnt); procedure OnAppNotify(aSender: TThdProcessWatch; pEnt: PPwEnt; aKind: TProcessWatchKind); procedure OnWndNotify(aSender: TObject; hActiveWnd: HWND); procedure SafeFreeClient; procedure StopHookWatch; procedure ClearHook(bMore: Boolean = false); procedure FreeMon; procedure OnAppInfoNotify(Sender: TObject; const Item: PAppInfoEnt; Action: TCollectionNotification); procedure UpdateDrmInfo; procedure SendWndCaptureBlock(hPipe: THandle; hTg: HWND; bVal: Boolean); function IsAcrobat(sPName: String): Boolean; public DrmAccessKind, DrmModifyKind: TDrmAccessKind; bPrtCollect, bPrintSecu, bPrintWater, bDrmAttachAble, bFileApproval, bIgrNetPathAB, bOpenDetect, bCaptureBlockApps: Boolean; WebbAB, OutlookAB, EtcAppAB: TAttachBlockPolicy; sEtcABApps, sWaterExcepts, sCaptureBlockApps, CaptureBlockUrls: String; CaptureBlockUrlKind: TBlockKind; MtpBlockKind: TUsbBlockKind; MtpRoExp, PrtNameH: String; PrtWaterCfg: TPrtWaterCfg; ShFileCrMon: TShFileCrMon; Constructor Create; Destructor Destroy; override; function IsChangeHookPolicy(aPO: TPrefModel): Boolean; function SetCaptureBlock(hTg: HWND; bVal: Boolean): Boolean; procedure UpdateHookTarget; procedure StartHookWatch; end; procedure RefinePrintHookAppList(aList: TStringList); implementation uses Tocsg.WinInfo, Tocsg.Path, ManagerService, Tocsg.Exception, Tocsg.Shell, Tocsg.Strings, Condition, System.IniFiles, Tocsg.Safe, Tocsg.Packet, Vcl.Forms, Tocsg.Json, Tocsg.Registry; procedure RefinePrintHookAppList(aList: TStringList); var n: Integer; begin if IsHD then begin // 아래한글 프린트 워터마크는 다른 보안프로그램으로 처리하기 때문에 예외 요청함 23_1018 17:11:05 kku n := aList.IndexOf('hwp.exe'); if n <> -1 then aList.Delete(n); end else begin case CUSTOMER_TYPE of CUSTOMER_WELFNI, CUSTOMER_WELFND : begin n := aList.IndexOf('notepad.exe'); if n <> -1 then aList.Delete(n); end; end; end; end; { TManagerHook } Constructor TManagerHook.Create; var // sTextOutDir, sTextOutApp: String; ini: TIniFile; sPath, sTemp: String; n: Integer; begin Inherited Create; // _Trace('Create() ..'); CS_ := TCriticalSection.Create; sDllPath_ := GetRunExePathDir + DIR_CONF + DLL_HOOK; if not FileExists(sDllPath_) then sDllPath_ := GetRunExePathDir + DLL_HOOK; // sDllPathPdf_ := GetRunExePathDir + DIR_CONF + DLL_HOOK_PDF; // if not FileExists(sDllPathPdf_) then // sDllPathPdf_ := GetRunExePathDir + DLL_HOOK_PDF; Server_ := nil; sLogPath_ := CutFileExt(GetRunExePath) + '.log'; sHlpExe_ := GetRunExePathDir + DIR_CONF + EXE_HP; sConfDir_ := GetRunExePathDir + DIR_CONF; sTgApps_ := ''; sCaptureBlockApps := ''; bCaptureBlockApps := false; // sTextOutDir := ''; sTextOutApp := GetRunExePathDir + DIR_CONF + EXE_OCR; if not FileExists(sTextOutApp) then sTextOutApp := ''; ZeroMemory(@PrtWaterCfg, SizeOf(PrtWaterCfg)); ZeroMemory(@ShFileCrMon, SizeOf(ShFileCrMon)); sTemp := sLogPath_[1] + ':\ProgramData\HE\'; ZeroMemory(@DefHookOpt_, SizeOf(DefHookOpt_)); DefHookOpt_.hRcvWnd := gMgSvc.RcvHwnd; DefHookOpt_.sTaskDir := sTemp + 'Desk\'; DefHookOpt_.dwCustomerType := CUSTOMER_TYPE; DefHookOpt_.sDrmPass := GetMK(false); // DefHookOpt_.sTextOutApp := sTextOutApp; // DefHookOpt_.sTextOutDir := sTextOutDir; DefHookOpt_.nFontSize := 175; DefHookOpt_.nLineCount := 4; ForceDirectories(DefHookOpt_.sTaskDir); // DefHookOpt_.sPrintWaterTxt := gMgSvc.EmpNo + '/' + gMgSvc.NicService.GetIP + '/' + FormatDateTime('yyyy-mm-dd', Now); DefHookOpt_.sAipPath := gMgSvc.GetAipPath; // if IsPrintWaterBoth and not IsPrintWaterHookForce then // DefHookOpt_.sPrintWaterImg := '*HB*|' + gMgSvc.PrefModel.PrtNameH; if IsPrintWaterHook and not IsHD then DefHookOpt_.sPrtEmfOutDir := sTemp + 'PrtCol\'; // if IsPrtSpl2Pdf then // begin // sPrtWaterSupportApp_ := '*' + EXE_HE;// + '|*' + EXE_CS; // end else begin case CUSTOMER_TYPE of CUSTOMER_GEC, CUSTOMER_HDENG : sPrtWaterSupportApp_ := RPINT_SUPPORT_APPS_HEC; else sPrtWaterSupportApp_ := PRINT_SUPPORT_APPS; end; sPath := GetProgramFilesDir + DIR_TG + INI_FORCEHE; if FileExists(sPath) then begin try Guard(ini, TIniFile.Create(sPath)); sPrtWaterSupportApp_ := sPrtWaterSupportApp_ + '|' + ini.ReadString('Force', 'PrtWaterApp', ''); // sTextOutDir := IncludeTrailingPathDelimiter(ini.ReadString('Test', 'HookOutDir', '')); DefHookOpt_.fWmTran := ini.ReadFloat('Force', 'WmTran', 0.0); except // .. end; end; end; ProcList_ := TProcessEntList.Create;; ProcList_.DetailInfo := false; PidList_ := TProcessIdList.Create; DcApp_ := TDcAppInfo.Create; DcApp_.OnValueNotify := OnAppInfoNotify; TgAppList_ := TStringList.Create; TgAppList_.CaseSensitive := false; TgAppExList_ := TStringList.Create; TgAppExList_.CaseSensitive := false; DrmAppList_ := TStringList.Create; DrmAppList_.CaseSensitive := false; {$IFNDEF _HOOK_TEST_} if not NotUseDRM then SplitString(DRM_SUPPORT_APPS, '|', DrmAppList_); {$ENDIF} WebAppList_ := TStringList.Create; WebAppList_.CaseSensitive := false; SplitString(WEB_BROWSERS, '|', WebAppList_); PrintAppList_ := TStringList.Create; PrintAppList_.CaseSensitive := false; // if IsPrintWaterHook or IsPrintWaterBoth or IsPrtSpl2Pdf then // 프린트 마스킹도 필요 24_0524 10:06:27 kku if IsPrintWaterHook then // 프린트 마스킹도 필요 24_0524 10:06:27 kku SplitString(sPrtWaterSupportApp_, '|', PrintAppList_, false, true); RefinePrintHookAppList(PrintAppList_); CaptureBlockApps_ := TStringList.Create; CaptureBlockApps_.CaseSensitive := false; ThdAppMon_ := nil; ThdWndMon_ := nil; bIsWow64_ := IsWow64; // DRM 처리 정보는 프로세스 접속 해서 정책 받기 전에 미리 받아야 한다. 그래서 앞서 별도 처리함 23_0317 13:12:50 kku DrmInitInfo_ := TTgFileMapping.Create(MAP_FILENAME_APIHOOK, SizeOf(TDrmInfo)); if DrmInitInfo_.IsAvailable then begin UpdateDrmInfo; end else begin _Trace('Fail .. Create() .. OpenFileMapping()'); end; end; Destructor TManagerHook.Destroy; begin // _Trace('Destroy() ..'); StopHookWatch; FreeAndNil(DrmInitInfo_); FreeAndNil(CaptureBlockApps_); FreeAndNil(PrintAppList_); FreeAndNil(WebAppList_); FreeAndNil(DrmAppList_); FreeAndNil(TgAppExList_); FreeAndNil(TgAppList_); FreeAndNil(DcApp_); FreeAndNil(PidList_); FreeAndNil(ProcList_); if Server_ <> nil then FreeAndNil(Server_); Inherited; FreeAndNil(CS_); end; function TManagerHook.IsChangeHookPolicy(aPO: TPrefModel): Boolean; var PPO: TPrefModel; begin PPO := gMgSvc.PrefModel; Result := (aPO.DrmAccessKind <> DrmAccessKind) or (aPO.DrmModifyKind <> DrmModifyKind) or (aPO.IsDrmAttAble <> bDrmAttachAble) or ((aPO.Print.PrintKind <> pkNone) and not bPrintSecu) or ((aPO.Print.PrintKind = pkNone) and bPrintSecu) or (IsPrintWaterHook and ((aPO.IsPrtCollectThum or IsApproveSupport) <> bPrtCollect)) or ((aPO.Print.PrintWater <> pwNone) and (aPO.Print.sWaterExcepts <> sWaterExcepts)) or // ( (IsPrintWaterHook or not FileExists(sConfDir_ + EXE_SPL) or IsPrintWaterBoth or IsPrtSpl2Pdf) and ( (IsPrintWaterHook or not FileExists(sConfDir_ + EXE_SPL)) and ( ((aPO.Print.PrintWater <> pwNone) and not bPrintWater) or ((aPO.Print.PrintWater = pwNone) and bPrintWater) ) ) or (aPO.Print.sWaterExcepts <> sWaterExcepts) or (aPO.bCaptureBlockApps_ <> bCaptureBlockApps) or (aPO.CaptureBlockApps <> sCaptureBlockApps) or (aPO.EtcAB.Kind <> EtcAppAB.Kind) or ((aPO.EtcABApps + '|' + aPO.EtcAbLogList) <> sEtcABApps) or (aPO.OutlookAB.Kind <> OutlookAB.Kind) or (aPO.WebbAB.Kind <> WebbAB.Kind) or (aPO.EtcAB.bPopup <> EtcAppAB.bPopup) or (aPO.EtcAB.bReadBlock <> EtcAppAB.bReadBlock) or (aPO.EtcAB.bWriteBlock <> EtcAppAB.bWriteBlock) or (aPO.EtcAB.bCollectTxt <> EtcAppAB.bCollectTxt) or (aPO.EtcAB.bCollectFile <> EtcAppAB.bCollectFile) or (aPO.EtcAB.ContentFilter.bActive <> EtcAppAB.ContentFilter.bActive) or (aPO.EtcAB.ContentFilter.sPatterns <> EtcAppAB.ContentFilter.sPatterns) or (aPO.EtcAB.ContentFilter.nHitLimit <> EtcAppAB.ContentFilter.nHitLimit) or (aPO.OutlookAB.bPopup <> OutlookAB.bPopup) or (aPO.OutlookAB.bReadBlock <> OutlookAB.bReadBlock) or (aPO.OutlookAB.bWriteBlock <> OutlookAB.bWriteBlock) or (aPO.OutlookAB.bCollectTxt <> OutlookAB.bCollectTxt) or (aPO.OutlookAB.bCollectFile <> OutlookAB.bCollectFile) or (aPO.OutlookAB.ContentFilter.bActive <> OutlookAB.ContentFilter.bActive) or (aPO.OutlookAB.ContentFilter.sPatterns <> OutlookAB.ContentFilter.sPatterns) or (aPO.OutlookAB.ContentFilter.nHitLimit <> OutlookAB.ContentFilter.nHitLimit) or (aPO.WebbAB.bPopup <> WebbAB.bPopup) or (aPO.WebbAB.bReadBlock <> WebbAB.bReadBlock) or (aPO.WebbAB.bWriteBlock <> WebbAB.bWriteBlock) or (aPO.WebbAB.bCollectTxt <> WebbAB.bCollectTxt) or (aPO.WebbAB.bCollectFile <> WebbAB.bCollectFile) or (aPO.WebbAB.ContentFilter.bActive <> WebbAB.ContentFilter.bActive) or (aPO.WebbAB.ContentFilter.sPatterns <> WebbAB.ContentFilter.sPatterns) or (aPO.WebbAB.ContentFilter.nHitLimit <> WebbAB.ContentFilter.nHitLimit) or ((IsApproveSupport and aPO.ExFApproval) <> bFileApproval) or (PPO.OpenDetect <> bOpenDetect) or (PPO.IgrNetPathAB <> bIgrNetPathAB) or (aPO.ShFileCrMon.nKind <> ShFileCrMon.nKind) or (aPO.ShFileCrMon.sExpLst <> ShFileCrMon.sExpLst) or (aPO.CaptureBlockUrlKind <> CaptureBlockUrlKind) or (aPO.CaptureBlockUrls <> CaptureBlockUrls) or ( (aPO.MtpBlockKind <> MtpBlockKind) and ( (aPO.MtpBlockKind = ubkReadOnly) or (MtpBlockKind = ubkReadOnly) ) ) or (MtpRoExp <> aPO.MtpExcept) or (PrtNameH <> PPO.PrtNameH) or not CompareMem(@PrtWaterCfg, @aPO.PrtWaterCfg, SizeOf(PrtWaterCfg)); end; procedure TManagerHook.SendWndCaptureBlock(hPipe: THandle; hTg: HWND; bVal: Boolean); var Send: ISendPacket; begin try Send := TTgPacket.Create(ACC_SET_CAPTURE_BLOCK); Send.Toss := hPipe; Send.I['Wnd'] := hTg; Send.B['Block'] := bVal; Server_.SendPacket(Send); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendWndCaptureBlock()'); end; end; function TManagerHook.IsAcrobat(sPName: String): Boolean; begin Result := (CompareText(sPName, 'Acrobat.exe') = 0) or (CompareText(sPName, 'AcroCEF.exe') = 0) or (CompareText(sPName, 'AcroRd32.exe') = 0) end; function TManagerHook.SetCaptureBlock(hTg: HWND; bVal: Boolean): Boolean; var dwPid: DWORD; pEnt: PCMEnt; Send: ISendPacket; begin Result := false; try dwPid := GetProcessPIDFromWndHandle(hTg); if dwPid = 0 then exit; if DcApp_.ContainsKey(dwPid) and (Server_ <> nil) and (Server_.Connected) then begin pEnt := Server_.GetCMEEntByPID(dwPid); if pEnt = nil then exit; if pEnt.bCapBlock <> bVal then begin pEnt.bCapBlock := bVal; Result := true; SendWndCaptureBlock(pEnt.hPipe, hTg, bVal); end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SetCaptureBlock()'); end; end; procedure TManagerHook.UpdateDrmInfo; var ModePolicy: TPrefModel; begin if DrmInitInfo_.IsAvailable then begin ModePolicy := gMgSvc.ModePolicy; ZeroMemory(DrmInitInfo_.Data, SizeOf(DrmInitInfo_.Data)); if not NotUseDRM then begin with DrmInitInfo_.Data^ do begin dwCustomerType := CUSTOMER_TYPE; DrmAccessKind := ModePolicy.DrmAccessKind; DrmModifyKind := ModePolicy.DrmModifyKind; StrCopy(sDrmPass, PChar(GetMK(false))); StrCopy(sEmpNo, PChar(gMgSvc.EmpNo)); if gMgSvc.DeptName <> '' then StrCopy(sDeptName, PChar(gMgSvc.DeptName)) else StrCopy(sDeptName, PChar(ModePolicy.DeptName)); StrCopy(sTaskDir, PChar(DefHookOpt_.sTaskDir)); end; end; end; end; procedure TManagerHook.Lock; begin CS_.Acquire; end; procedure TManagerHook.Unlock; begin CS_.Release; end; function TManagerHook.IsTgApp(sPName: String): Boolean; begin try Lock; try Result := TgAppList_.IndexOf(sPName) <> -1; finally Unlock; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. IsTgApp()'); end; end; procedure TManagerHook.DelTgApp(sPName: String); var n: Integer; begin try Lock; try n := TgAppList_.IndexOf(sPName); if n <> -1 then TgAppList_.Delete(n); finally Unlock; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. DelTgApp()'); end; end; function TManagerHook.GetCtrlOpt(pEnt: PCMEnt): TAppCtrlOpt; var PO: TPrefModel; StrList: TStringList; i: Integer; begin try PO := gMgSvc.ModePolicy; UpdateDrmInfo; // 최신 정보로 업데이트 되도록 위치 이동 25_1126 14:55:52 kku DefHookOpt_.sAccount := gMgSvc.Account; DefHookOpt_.sAcName := GetUserNameFromReg; DefHookOpt_.sAcSSid := gMgSvc.RecentUserSid; DefHookOpt_.sUName := gMgSvc.UName; DefHookOpt_.sEmpNo := gMgSvc.EmpNo; // 이름 변경되게 하면... 불안하다.. if gMgSvc.DeptName <> '' then DefHookOpt_.sDeptName := ExtrLastDelimiterStr(gMgSvc.DeptName, ';') else if gMgSvc.PrefModel.DeptName <> '' then DefHookOpt_.sDeptName := ExtrLastDelimiterStr(gMgSvc.PrefModel.DeptName, ';'); Result := DefHookOpt_; if DcApp_.ContainsKey(pEnt.dwPid) then Result.hMainWnd := DcApp_[pEnt.dwPid].hMain; // Result.sEmpNo := gMgSvc.EmpNo; // 이름 변경되게 하면... 불안하다.. // Result.sDeptName := ExtrLastDelimiterStr(ModePolicy.DeptName, ';'); if NotUseDRM then begin Result.DrmAccessKind := dakNone; Result.DrmModifyKind := dakNone; end else begin Result.DrmAccessKind := DrmAccessKind; Result.DrmModifyKind := DrmModifyKind; end; // if gClient <> nil then // Result.hTagWnd := gClient.GetSelfWnd // else // Result.hTagWnd := 0; Result.bDrmAttachAble := bDrmAttachAble; Result.sIpAddr := gMgSvc.NicService.GetIP; if PrintAppList_.IndexOf(pEnt.sPName) <> -1 then begin Result.bPrintSecu := bPrintSecu; Result.bPrintWater := bPrintWater; Result.bPrtCollect := bPrtCollect; // Result.bPrintWaterExp := bprin Result.bIsTest := true; // gMgSvc.IsHecDev; // todo : 정리 필요 23_1025 16:35:50 kku if Result.bPrintWater then begin if IsHD then Result.sPrintWaterExp := 'Adobe|' + sWaterExcepts // Adobe PDF Converter에서 워터마크 문제 발생 23_1220 10:34:40 kku else Result.sPrintWaterExp := sWaterExcepts; end; end; if WebAppList_.IndexOf(pEnt.sPName) <> -1 then begin // 크롬, 엣지 등에서는 백그라운드 프로세스 때문에 꼬여서 제대로 동작하지 않는다.. // ThdWebUrl.pas 에서 처리 하도록 보완 25_0324 11:08:24 kku // if Result.hMainWnd <> 0 then // begin // if ModePolicy.CaptureBlockUrlKind = bkAll then // begin // Result.hCltWnd := Result.hMainWnd; // SendWndCaptureBlock(pEnt.hPipe, Result.hMainWnd, true); // end else // SendWndCaptureBlock(pEnt.hPipe, Result.hMainWnd, false); // end else // _Trace('WB : Not found main window', 4); if (Result.hMainWnd <> 0) and (PO.CaptureBlockUrlKind = bkNone) then begin pEnt.bCapBlock := false; Result.hCltWnd := 0; SendWndCaptureBlock(pEnt.hPipe, Result.hMainWnd, false); end; if PO.WebbAB.Kind <> abkNone then begin case PO.WebbAB.Kind of abkNone : ; abkBlock : Result.FileUseBlock := fubBlock; // abkPopup, abkLog : Result.FileUseBlock := fubMonitor; abkUrlBlock, abkUrlAllow : begin Result.FileUseBlock := fubBlock; Result.bCheckUrl := true; end; end; if Result.FileUseBlock <> fubNone then begin if not gMgSvc.IsNewApi and (PO.WebbAB.Kind <> abkBlock) and not PO.WebbAB.bPopup then Result.FileUseBlock := fubMonitor; Result.bUseContentFilter := PO.WebbAB.ContentFilter.bActive; Result.bReadBlock := PO.WebbAB.bReadBlock; Result.bWriteBlock := PO.WebbAB.bWriteBlock; end; end; end; // 아웃룩 첨부 차단은 후킹으로 사용하지 않음 24_0829 16:19:48 kku // if (ModePolicy.OutlookAB.Kind <> abkNone) and (CompareText('Outlook.exe', pEnt.sPName) = 0) then // begin // case ModePolicy.OutlookAB.Kind of // abkNone : Result.FileUseBlock := fubNone; // abkBlock : Result.FileUseBlock := fubBlock; //// abkPopup, // abkLog : // begin // if IsOutlookABMonitorHook then // Result.FileUseBlock := fubMonitor // else // Result.FileUseBlock := fubNone;//Result.FileUseBlock := fubMonitor; // 플러그인에서 처리하도록 한다 24_0326 15:09:10 kku // end; // end; // // if Result.FileUseBlock <> fubNone then // begin // Result.bUseContentFilter := ModePolicy.OutlookAB.ContentFilter.bActive; // Result.bReadBlock := ModePolicy.OutlookAB.bReadBlock; // Result.bWriteBlock := ModePolicy.OutlookAB.bWriteBlock; // end; // end; if (PO.EtcAB.Kind <> abkNone) and (sEtcABApps <> '') then begin Guard(StrList, TStringList.Create); StrList.CaseSensitive := false; SplitString(sEtcABApps, '|', StrList); if (StrList.IndexOf(pEnt.sPName) <> -1) or (TgAppExList_.IndexOf(pEnt.sPName) <> -1) then begin case PO.EtcAB.Kind of abkNone : ; abkBlock : Result.FileUseBlock := fubBlock; // abkPopup, abkLog : Result.FileUseBlock := fubMonitor; end; if (Result.FileUseBlock = fubBlock) and (Pos(UpperCase(pEnt.sPName), UpperCase(PO.EtcAbLogList)) > 0) then Result.FileUseBlock := fubMonitor; if Result.FileUseBlock <> fubNone then begin Result.bUseContentFilter := PO.EtcAB.ContentFilter.bActive; Result.bReadBlock := PO.EtcAB.bReadBlock; Result.bWriteBlock := PO.EtcAB.bWriteBlock; end; end; end; // 화면 캡쳐방지 프로그램 적용 추가 23_0327 14:38:41 kku if bCaptureBlockApps and (CaptureBlockApps_.IndexOf(pEnt.sPName) <> -1) and DcApp_.ContainsKey(pEnt.dwPid) then Result.hCltWnd := DcApp_[pEnt.dwPid].hMain; // {$IFDEF DEBUG} // PrtWaterCfg.bActive := true; // PrtWaterCfg.sTopText := 'top'; // PrtWaterCfg.sBotText := 'bottom'; // PrtWaterCfg.nTopSize := 20; // PrtWaterCfg.nBotSize := 20; // PrtWaterCfg.nTopPos := 2; // PrtWaterCfg.nBotPos := 2; // {$ENDIF} Result.PrtWaterCfg := PrtWaterCfg; Result.ShFileCrMon := ShFileCrMon; // MTP 읽기만 기능 추가 24_0304 15:02:28 kku Result.bMtpWB := (PO.MtpBlockKind = ubkReadOnly) and (CompareText('explorer.exe', pEnt.sPName) = 0); Result.sMtpRoExp := MtpRoExp; Result.bFileApproval := bFileApproval; Result.bIgrNetPathAB := bIgrNetPathAB; Result.bOpenDetect := bOpenDetect; // if IsPrintWaterBoth and not IsPrintWaterHookForce then // Result.sPrintWaterImg := '*HB*|' + PrtNameH // else Result.sPrintWaterImg := '' ; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetCtrlOpt()'); end; end; procedure TManagerHook.OnSendCtrlOpt(pEnt: PCMEnt); var Send: ISendPacket; Opt: TAppCtrlOpt; bOldCapBlock: Boolean; begin try bOldCapBlock := pEnt.bCapBlock; Opt := GetCtrlOpt(pEnt); pEnt.bCapBlock := Opt.hCltWnd <> 0; if pEnt.bCapBlock <> bOldCapBlock then begin if pEnt.bCapBlock then gMgSvc.SendEventLog(URI_USER_ACTION, PREVENT_CAPTURE_WINDOW, pEnt.sPName) else gMgSvc.SendEventLog(URI_USER_ACTION, RELEASE_CAPTURE_WINDOW, pEnt.sPName); end; Send := TTgPacket.Create(ACC_SET_POLICY); Send.Toss := pEnt.hPipe; Send.O['Opt'] := TTgJson.ValueToJsonObject(Opt); Server_.SendPacket(Send); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. OnSendCtrlOpt()'); end; end; procedure TManagerHook.OnAppNotify(aSender: TThdProcessWatch; pEnt: PPwEnt; aKind: TProcessWatchKind); var nIdx: Integer; pApp: PAppInfoEnt; sPPName: String; begin case aKind of pwkUnknown : {$IFDEF DEBUG} ASSERT(false) {$ENDIF}; pwkInit, pwkExecute : begin sPPName:= GetProcessNameByPid(pEnt.dwPPid); if DrmAppList_.IndexOf(pEnt.sPName) <> -1 then begin if NotUseDRM then exit; if DrmAccessKind = dakNone then // 열람 권한 없으면 무시 23_0503 08:06:03 kku exit; if (CompareText('excel.exe', pEnt.sPName) = 0) or (CompareText('WINWORD.exe', pEnt.sPName) = 0) or (CompareText('POWERPNT.exe', pEnt.sPName) = 0) then begin // 파워포인트등 외부 객체로 불러오는 경우 무시한다 (중소기업중앙회) 24_0503 09:22:23 kku // 워드, 파워포인트에서 엑셀 차트 삽입할때도 무시 25_0723 17:37:27 kku // 객체불로오기 일경우 아래의 부모프로세스로 부터 실행 되서 Hang 걸리는 현상 발생 (에스오에스랩) 251215 mgkim if (CompareText('svchost.exe', sPPName) = 0) or (CompareText('WINWORD.EXE', sPPName) = 0) or (CompareText('POWERPNT.EXE', sPPName) = 0) or (CompareText('excel.EXE', sPPName) = 0) then begin _Trace('InjectModule(2) ..object doc PName="%s", PID=%d, sPPName="%s"', [pEnt.sPName, pEnt.dwPid, sPPName], 3); exit; end; end; end else if TgAppExList_.IndexOf(pEnt.sPName) = -1 then exit; if CompareText(pEnt.sPName, 'MSEDGEWEBVIEW2.EXE') = 0 then begin if sPPName = '' then exit; if (CompareText(sPPName, 'OLK.EXE') <> 0) and (CompareText(sPPName, 'MS-TEAMS.EXE') <> 0) then exit; if (CompareText(sPPName, 'MS-TEAMS.EXE') = 0) and not IsTgApp('MS-TEAMS.EXE') then exit; if (CompareText(sPPName, 'OLK.EXE') = 0) and not IsTgApp('OLK.EXE') then exit; end; // if IsAcrobat(pEnt.sPName) then // 이거만 따로 처리 했는데 다시 원복. 나중에 안정화 확인 시 삭제 필요 25_1215 17:30:18 kku // begin // if InjectModule(pEnt.dwPid, sDllPathPdf_, @bIsWow64_) > 0 then // begin // _Trace('InjectModule(2) .. PName="%s", PID=%d', [pEnt.sPName, pEnt.dwPid], 3); // end else begin // if FileExists(sHlpExe_) then // begin // ExecutePath(sHlpExe_, Format('-hook2 %d', [pEnt.dwPid])); // _Trace('InjectModule32(2) .. PName="%s", PID=%d', [pEnt.sPName, pEnt.dwPid], 3); // end; // end; // end else begin if InjectModule(pEnt.dwPid, sDllPath_, @bIsWow64_) > 0 then begin //부모프로세스정보도 확인할 수 있게 로그 추가251215 mgkim _Trace('InjectModule() .. PName="%s", PID=%d, sPPName="%s"', [pEnt.sPName, pEnt.dwPid, sPPName], 3); //_Trace('InjectModule() .. PName="%s", PID=%d', [pEnt.sPName, pEnt.dwPid], 3); end else begin // Sleep(500); if FileExists(sHlpExe_) then begin // ExecuteAppWaitUntilTerminate(sHlpExe, Format('-hook %d', [pEnt.dwPid]), 5000); ExecutePath(sHlpExe_, Format('-hook %d', [pEnt.dwPid])); _Trace('InjectModule32() .. PName="%s", PID=%d', [pEnt.sPName, pEnt.dwPid], 3); end; // _Trace('Fail .. InjectModule() .. PName="%s"', [pEnt.sPName], 3); end; end; PidList_.Add(pEnt.dwPid); if not DcApp_.ContainsKey(pEnt.dwPid) then begin New(pApp); pApp.dwPid := pEnt.dwPid; pApp.hMain := 0; DcApp_.Add(pEnt.dwPid, pApp); end; end; pwkTerminated : begin nIdx := PidList_.IndexOf(pEnt.dwPid); if nIdx <> -1 then PidList_.Delete(nIdx); if DcApp_.ContainsKey(pEnt.dwPid) then DcApp_.Remove(pEnt.dwPid); end; end; end; procedure TManagerHook.OnWndNotify(aSender: TObject; hActiveWnd: HWND); var dwPid: DWORD; sPName: String; pEnt: PProcessEntInfo; StrList: TStringList; pApp: PAppInfoEnt; ArchKind: TExeArchitectKind; procedure ProcessNextWork; var pCEnt: PCMEnt; begin if DcApp_.ContainsKey(pEnt.dwPid) then begin pApp := DcApp_[pEnt.dwPid]; // DRM 처리 프로세스는 여기로 옮 23_0327 15:21:22 kku if pApp.hMain = 0 then begin pApp.hMain := hActiveWnd; pCEnt := Server_.GetCMEEntByPID(pEnt.dwPid); if pCEnt <> nil then OnSendCtrlOpt(pCEnt); end else if pApp.hMain <> hActiveWnd then begin pCEnt := Server_.GetCMEEntByPID(pEnt.dwPid); if (pCEnt <> nil) and pCEnt.bCapBlock then begin // 캡처 방지. 추가된 윈도우 처리 23_0511 10:17:08 kku SendWndCaptureBlock(pCEnt.hPipe, hActiveWnd, true); end; end; end else begin New(pApp); pApp.dwPid := pEnt.dwPid; pApp.hMain := hActiveWnd; DcApp_.Add(pEnt.dwPid, pApp); end; end; begin try dwPid := GetProcessPIDFromWndHandle(hActiveWnd); if dwPid = 0 then exit; if PidList_.IndexOf(dwPid) <> -1 then begin pEnt := ProcList_.GetProcInfoByPid(dwPid); if pEnt <> nil then ProcessNextWork; exit; end; ProcList_.UpdateProcessList; pEnt := ProcList_.GetProcInfoByPid(dwPid); if pEnt <> nil then begin sPName := ExtractFileName(pEnt.sModuleFileName); if not IsTgApp(sPName) then begin // PidList_.Add(dwPid); exit; end; // if ForceDirectories(sTaskDir_) then // begin // var sPath: String := sTaskDir_ + 'vokee.$inf'; // if FileExists(sPath) then // DeleteFile(PChar(sPath)); // Guard(StrList, TStringList.Create); // StrList.Add(IntToStr(dwPid)); // StrList.Add(IntToStr(hActiveWnd)); // StrList.SaveToFile(sPath); // end; if CompareText('BizboxAMessenger.exe', sPName) = 0 then begin // 더존 메신저는 32비트 ProgramFiles에 설치되어 있고, // ArchKind 값도 eak32 확인되는데... 64bit DLL을 붙여야 동작한다 25_1015 13:58:28 kku ArchKind := eak64; end else ArchKind := GetExeFileArchitectFromePath(pEnt.sModuleFileName); case ArchKind of eakNoExe : begin // if IsAcrobat(sPName) then // 이거만 따로 처리 했는데 다시 원복. 나중에 안정화 확인 시 삭제 필요 25_1215 17:30:18 kku // begin // if InjectModule(dwPid, sDllPathPdf_, @bIsWow64_) > 0 then // begin // _Trace('InjectModule(2) .. PName="%s", PID=%d .. 1', [sPName, dwPid], 3); // PidList_.Add(dwPid); // end else begin // if FileExists(sHlpExe_) then // begin // _Trace('InjectModule32(2) .. PName="%s", PID=%d .. 1', [sPName, dwPid], 3); // ExecutePath(sHlpExe_, Format('-hook2 %d', [dwPid])); // PidList_.Add(dwPid); // end; // _Trace('Fail .. InjectModule(2) .. PName="%s", PID=%d .. 1', [sPName, dwPid], 3); // end; // end else begin if InjectModule(dwPid, sDllPath_, @bIsWow64_) > 0 then begin _Trace('InjectModule() .. PName="%s", PID=%d .. 1', [sPName, dwPid], 3); PidList_.Add(dwPid); end else begin if FileExists(sHlpExe_) then begin _Trace('InjectModule32() .. PName="%s", PID=%d .. 1', [sPName, dwPid], 3); ExecutePath(sHlpExe_, Format('-hook %d', [dwPid])); PidList_.Add(dwPid); end; _Trace('Fail .. InjectModule() .. PName="%s", PID=%d .. 1', [sPName, dwPid], 3); end; end; end; eak32 : begin if (IsUseEncOnlyAIP or gMgSvc.FirstAip) and (CompareText(sPName, 'MSIP.Viewer.exe') = 0) then begin // 얘는 왜 64bit인데 32로 잡히는지... 23_1019 15:23:07 kku if InjectModule(dwPid, sDllPath_, @bIsWow64_) > 0 then begin _Trace('InjectModule() .... PName="%s", PID=%d .. 2', [sPName, dwPid], 3); PidList_.Add(dwPid); end; end else if FileExists(sHlpExe_) then begin if IsAcrobat(sPName) then begin _Trace('InjectModule32(2) ... PName="%s", PID=%d .. 2', [sPName, dwPid], 3); ExecutePath(sHlpExe_, Format('-hook2 %d', [dwPid])); end else begin //_Trace('InjectModule32() ... PName="%s", PID=%d .. 2', [sPName, dwPid], 3); ExecutePath(sHlpExe_, Format('-hook %d', [dwPid])); end; PidList_.Add(dwPid); end; // else // _Trace('Fail .. Not found exe .. HlpExe="%s"', [sHlpExe_]); end; eak64 : begin // if IsAcrobat(sPName) then // 이거만 따로 처리 했는데 다시 원복. 나중에 안정화 확인 시 삭제 필요 25_1215 17:30:18 kku // begin // if InjectModule(dwPid, sDllPathPdf_, @bIsWow64_) >0 then // begin // _Trace('InjectModule(2) .. PName="%s", PID=%d .. 3', [sPName, dwPid], 3); // PidList_.Add(dwPid); // end; // end else begin if InjectModule(dwPid, sDllPath_, @bIsWow64_) >0 then begin _Trace('InjectModule() .. PName="%s", PID=%d .. 3', [sPName, dwPid], 3); PidList_.Add(dwPid); end; end; end; // else // _Trace('Fail .. InjectModule() ... PName="%s"', [sPName]); end; ProcessNextWork; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. OnWndNotify()'); end; end; procedure TManagerHook.FreeMon; begin try if ThdAppMon_ <> nil then begin ThdAppMon_.OnProcessWatchNotify := nil; FreeAndNil(ThdAppMon_); end; if ThdWndMon_ <> nil then begin ThdWndMon_.OnActiveWndNotify := nil; FreeAndNil(ThdWndMon_); end; PidList_.Clear; DcApp_.Clear; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. FreeMon()'); end; end; procedure TManagerHook.OnAppInfoNotify(Sender: TObject; const Item: PAppInfoEnt; Action: TCollectionNotification); begin if Action = cnRemoved then Dispose(Item); end; procedure TManagerHook.UpdateHookTarget; var PPO, ModePolicy: TPrefModel; sABApps: String; EtcABl, WebbABl, OutlookABl: TAttachBlockPolicy; enum: TEnumerator; Send: ISendPacket; begin try PPO := gMgSvc.PrefModel; ModePolicy := gMgSvc.ModePolicy; DrmAccessKind := ModePolicy.DrmAccessKind; DrmModifyKind := ModePolicy.DrmModifyKind; bPrintSecu := ModePolicy.Print.PrintKind <> pkNone; bPrtCollect := IsPrintWaterHook and (ModePolicy.IsPrtCollectThum or IsApproveSupport); // bPrintWater := (IsPrintWaterHook or not FileExists(sConfDir_ + EXE_SPL) or IsPrintWaterBoth or IsPrtSpl2Pdf ) and (ModePolicy.Print.PrintWater <> pwNone); bPrintWater := (IsPrintWaterHook or not FileExists(sConfDir_ + EXE_SPL)) and (ModePolicy.Print.PrintWater <> pwNone); sWaterExcepts := ModePolicy.Print.sWaterExcepts; bDrmAttachAble := ModePolicy.IsDrmAttAble; WebbAB := ModePolicy.WebbAB; OutlookAB := ModePolicy.OutlookAB; EtcAppAB := ModePolicy.EtcAB; sEtcABApps := ModePolicy.EtcABApps + '|' + ModePolicy.EtcAbLogList; CaptureBlockUrlKind := ModePolicy.CaptureBlockUrlKind; CaptureBlockUrls := ModePolicy.CaptureBlockUrls; MtpBlockKind := ModePolicy.MtpBlockKind; MtpRoExp := ModePolicy.MtpExcept; PrtNameH := PPO.PrtNameH; PrtWaterCfg := ModePolicy.PrtWaterCfg; ShFileCrMon := ModePolicy.ShFileCrMon; bFileApproval := IsApproveSupport and ModePolicy.ExFApproval; bIgrNetPathAB := PPO.IgrNetPathAB; bOpenDetect := PPO.OpenDetect; if DefHookOpt_.sTaskDir <> '' then ForceDirectories(DefHookOpt_.sTaskDir); // sABApps := DRM_SUPPORT_APPS + '|' + PRINT_SUPPERT_APPS; // if IsPrintWaterHook or IsPrintWaterBoth or IsPrtSpl2Pdf then if IsPrintWaterHook then sABApps := sPrtWaterSupportApp_ else sABApps := ''; EtcABl := ModePolicy.EtcAB; if (EtcABl.Kind <> abkNone) and (ModePolicy.EtcABApps <> '') then SumString(sABApps, sEtcABApps, '|'); WebbABl := ModePolicy.WebbAB; if (WebbABl.Kind <> abkNone) or (ModePolicy.CaptureBlockUrlKind <> bkNone) then SumString(sABApps, WEB_BROWSERS, '|'); OutlookABl := ModePolicy.OutlookAB; if (OutlookABl.Kind <> abkNone) and IsOutlookABMonitorHook then SumString(sABApps, 'outlook.exe', '|'); if sCaptureBlockApps <> ModePolicy.CaptureBlockApps then begin sCaptureBlockApps := ModePolicy.CaptureBlockApps; SplitString(sCaptureBlockApps, '|', CaptureBlockApps_); end; bCaptureBlockApps := ModePolicy.bCaptureBlockApps_; if bCaptureBlockApps then SumString(sABApps, sCaptureBlockApps, '|'); if (MtpBlockKind = ubkReadOnly) or (ShFileCrMon.nKind <> 0) then SumString(sABApps, 'explorer.exe', '|'); if sABApps <> sTgApps_ then begin TgAppExList_.Clear; sTgApps_ := sABApps; SplitString(sTgApps_, '|', TgAppList_, false, true); if CUSTOMER_TYPE = CUSTOMER_KBIZ then DelTgApp('excel.exe'); // iexplore.exe는 Injection 하지 않음. 25_0808 13:20:16 kku // 신한신용정보에서 iexplore.exe 을 Injection 하면 인트라넷망 로그인할때 인증 실패 현상 확인됨 DelTgApp('iexplore.exe'); DelTgApp('msedgewebview2.exe'); if (TgAppList_.IndexOf('olk.exe') <> -1) or (TgAppList_.IndexOf('ms-teams.exe') <> -1) then TgAppExList_.Add('msedgewebview2.exe'); var i: Integer; var sPName: String; for i := TgAppList_.Count - 1 downto 0 do if (TgAppList_[i].Length > 0) and (TgAppList_[i][1] = '*') then begin sPName := TgAppList_[i]; TgAppList_.Delete(i); Delete(sPName, 1, 1); if TgAppExList_.IndexOf(sPName) = -1 then TgAppExList_.Add(sPName); end; end; {$IFDEF _HOOK_TEST_} TgAppList_.Clear; TgAppList_.Add('msedge.exe'); {$ENDIF} if Server_ <> nil then begin Guard(enum, Server_.GetCMEntEnumrator); while enum.MoveNext do begin Send := TTgPacket.Create(ACC_SET_POLICY); Send.Toss := enum.Current.hPipe; Send.O['Opt'] := TTgJson.ValueToJsonObject(GetCtrlOpt(enum.Current)); Server_.SendPacket(Send); end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. UpdateHookTarget()'); end; end; procedure TManagerHook.StartHookWatch; begin try if Server_ = nil then begin ClearHook; Server_ := TAppCtrlServer.Create(gMgSvc.RcvHwnd); Server_.OnSendCtrlOpt := OnSendCtrlOpt; if Server_.ActiveNp(PIPE_NAME, true) then begin Server_.ConnectNp; end; UpdateHookTarget; ThdAppMon_ := TThdProcessWatch.Create(false); ThdAppMon_.OnProcessWatchNotify := OnAppNotify; ThdAppMon_.StartThread; ThdWndMon_ := TThdActiveWndMon.Create(false); ThdWndMon_.OnActiveWndNotify := OnWndNotify; ThdWndMon_.StartThread; // SetSharedData; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. StartHookWatch()'); end; end; procedure TManagerHook.ClearHook(bMore: Boolean = false); begin // try // if FileExists(sHlpExe_) then // ExecutePath_hide(sHlpExe_, '-clearhook'); // // EjectModuleFromPath(sDllPath_ + '|' + sDllPathPdf_); // if bMore then // begin // Sleep(1000); // EjectModuleFromPath(sDllPath_ + '|' + sDllPathPdf_); // end; // except // on E: Exception do // ETgException.TraceException(Self, E, 'Fail .. ClearHook()'); // end; end; procedure TManagerHook.SafeFreeClient; var nTO: Integer; NpClient: TTgNpClient; begin try if Server_ <> nil then begin if Server_.CountEnt = 0 then begin // 음... CreatePipeInstance() 이후에 한번도 연결되지 않고 파이프를 초기화 하면 // 프로그램이 크러쉬 되는 문제가 있다... // 그래서 최소 한번은 연결될 수 있도록 조치함...23_0412 16:56:56 kku Guard(NpClient, TTgNpClient.Create(PIPE_NAME)); if NpClient.Connect then begin // NpClient.Disconnect; end; end; // 안전 제거를 위해 종료를 미리 알림 23_0315 14:02:46 kku // Server_.SendPacket(TTgPacket.Create(ACC_SAFE_TERMINATE_CLIENT)); // // nTO := 0; // while (Server_.CountEnt > 0) and (nTO < 10) do // begin // Inc(nTO); // Sleep(500); // Application.ProcessMessages; // end; FreeAndNil(Server_); end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SafeFreeClient()'); end; end; procedure TManagerHook.StopHookWatch; begin try FreeMon; SafeFreeClient; ClearHook(true); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. StopHookWatch()'); end; end; end.