{*******************************************************} { } { ManagerRdpSecu } { } { Copyright (C) 2022 kku } { } {*******************************************************} unit ManagerRdpSecu; interface uses System.SysUtils, System.Classes, Winapi.Windows, Tocsg.Obj, Tocsg.Thread, Winapi.Messages; const WM_RDPSECU_LOG = WM_USER + 6483; REG_RDP_PORT = 'SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp'; NAME_RDPSVC = 'TermService'; NAME_RDPRSVC = 'UmRdpService'; type TManagerRdpSecu = class; TThdPreventRdp = class(TTgThread) protected MgRdp_: TManagerRdpSecu; procedure Execute; override; public Constructor Create(aMgRdp: TManagerRdpSecu); end; TRdpSecuState = (rssNone, rssRqPrevent, rssPrevent, rssOpenRdp, rssFail); TManagerRdpSecu = class(TTgObject) private hRcvWnd_: HWND; nRdpPort_: Integer; State_: TRdpSecuState; ThdPrevent_: TThdPreventRdp; procedure SetRdpSecuState(aState: TRdpSecuState); procedure Log(sLog: String); overload; procedure Log(sFormat: String; const Args: array of const); overload; public Constructor Create(hRcvWnd: HWND); Destructor Destroy; override; function OpenSafeRdp: Boolean; function CloseSafeRdp: Boolean; property State: TRdpSecuState read State_ write SetRdpSecuState; property RdpPort: Integer read nRdpPort_; end; function GetRdpPort: Integer; inline; function PreventRdp: Boolean; implementation uses Tocsg.Registry, System.IniFiles, Tocsg.Safe, Tocsg.Firewall, NetFwTypeLib_TLB, Tocsg.Path, Tocsg.Trace, Winapi.WinSvc, Tocsg.Service, Tocsg.Process, IdTCPClient, Tocsg.Exception, Winapi.ActiveX; function GetRandomPort(bCheck: Boolean = true): Integer; var CheckClt: TIdTCPClient; begin Randomize; Result := Random(30000) + 30000; if bCheck then begin Guard(CheckClt, TIdTCPClient.Create(nil)); CheckClt.ConnectTimeout := 1000; CheckClt.ReadTimeout := 1000; while True do begin if Result > 65000 then begin Result := 0; exit; end; try CheckClt.Connect('127.0.0.1', Result); CheckClt.Disconnect; except exit; end; Inc(Result); end; end; end; function GetRdpPort: Integer; inline; begin Result := GetRegValueAsInteger(HKEY_LOCAL_MACHINE, REG_RDP_PORT, 'PortNumber'); end; procedure RecoveryRdpSetting; var nPort, nOldPort: Integer; ini: TIniFile; dwPid: DWORD; begin try nPort := GetRdpPort; RemoveFwRule(Format('RSecu-I%d', [nPort])); Guard(ini, TIniFile.Create(CutFileExt(GetRunExePath) + '.ini')); nOldPort := ini.ReadInteger('Setting', 'RecoverPort', 0); if (nOldPort = 0) or (nPort = nOldPort) then begin ServiceStart(NAME_RDPSVC); ini.WriteInteger('Setting', 'RecoverPort', 0); TTgTrace.T('RecoveryRdpSetting() .. º¹¿ø ÇÒ Á¤º¸ ¾øÀ½'); exit; end; nPort := nOldPort; if not ServiceExists(NAME_RDPSVC) then begin TTgTrace.T('RecoveryRdpSetting() .. ¼­ºñ½º ¾øÀ½'); exit; end; dwPid := GetSerivcePid(NAME_RDPSVC); if dwPid <> 0 then begin if TerminateProcessByPid(dwPid, true) then begin ServiceStop(NAME_RDPRSVC); Sleep(500); if not ServiceStop(NAME_RDPSVC, 3) then exit; Sleep(500); end; end; SetRegValueInteger(HKEY_LOCAL_MACHINE, REG_RDP_PORT, 'PortNumber', nPort); if ServiceStart(NAME_RDPSVC) or (GetServiceStatus(NAME_RDPSVC) = SERVICE_RUNNING) then begin ini.WriteInteger('Setting', 'RecoverPort', 0); TTgTrace.T('RecoveryRdpSetting() .. º¹¿ø ¼º°ø (%d)', [nPort]); end; except on E: Exception do ETgException.TraceException(E, 'Fail .. RecoveryRdpSetting()'); end; end; function PreventRdp: Boolean; var nPort, nOldPort: Integer; ini: TIniFile; dwPid: DWORD; FwRule: TFwRule; begin Result := false; try RemoveFwRule(Format('RSecu-I%d', [GetRdpPort])); if not ServiceExists(NAME_RDPSVC) then begin TTgTrace.T('PreventRdp() .. ¼­ºñ½º ¾øÀ½'); exit; end; dwPid := GetSerivcePid(NAME_RDPSVC); if dwPid <> 0 then begin if TerminateProcessByPid(dwPid, true) then begin if not ServiceStop(NAME_RDPRSVC) then begin TTgTrace.T('PreventRdp() .. Fail .. Stop SVC2'); exit; end; Sleep(500); if not ServiceStop(NAME_RDPSVC, 3) then begin TTgTrace.T('PreventRdp() .. Fail .. Stop SVC'); exit; end; Result := true; end else TTgTrace.T('PreventRdp() .. Fail .. Terminate SVC PID=%d', [dwPid]); end else Result := true; except on E: Exception do ETgException.TraceException(E, 'Fail .. PreventRdp()'); end; end; function OpenRdpSecu: Integer; var nPort, nOldPort: Integer; ini: TIniFile; dwPid: DWORD; FwRule: TFwRule; begin Result := -1; try nPort := GetRdpPort; ASSERT(nPort <> 0); Guard(ini, TIniFile.Create(CutFileExt(GetRunExePath) + '.ini')); nOldPort := ini.ReadInteger('Setting', 'RecoverPort', 0); if (nOldPort <> 0) and (nOldPort <> nPort) then begin TTgTrace.T('OpenRdpSecu() .. ÀÌ¹Ì º¯°æ Áß'); exit; end; ini.WriteInteger('Setting', 'RecoverPort', nPort); nPort := GetRandomPort; if nPort = 0 then begin TTgTrace.T('OpenRdpSecu() .. »ç¿ë °¡´ÉÇÑ Æ÷Æ® ¾øÀ½'); exit; end; if not ServiceExists(NAME_RDPSVC) then begin TTgTrace.T('OpenRdpSecu() .. ¼­ºñ½º ¾øÀ½'); exit; end; dwPid := GetSerivcePid(NAME_RDPSVC); if dwPid = 0 then begin TTgTrace.T('OpenRdpSecu() .. ¼­ºñ½º ÇÁ·Î¼¼½º ¾øÀ½'); exit; end; if TerminateProcessByPid(dwPid, true) then begin ServiceStop(NAME_RDPRSVC); Sleep(500); if not ServiceStop(NAME_RDPSVC, 3) then exit; Sleep(500); SetRegValueInteger(HKEY_LOCAL_MACHINE, REG_RDP_PORT, 'PortNumber', nPort); if ServiceStart(NAME_RDPSVC) or (GetServiceStatus(NAME_RDPSVC) = SERVICE_RUNNING) then begin ZeroMemory(@FwRule, SizeOf(FwRule)); with FwRule do begin sName := Format('RSecu-I%d', [nPort]); sGroup := 'ToCSG'; sDesc := 'RdpSecu by ToCSG'; sLocalPorts := IntToStr(nPort); nProtocol := NET_FW_IP_PROTOCOL_TCP; nProfiles := FW_PROFILE_ANY; nAction := NET_FW_ACTION_ALLOW; nDirection := NET_FW_RULE_DIR_IN; bEnabled := true; end; AddFwRule(FwRule); TTgTrace.T('OpenRdpSecu() .. º¯°æ ¼º°ø (%d)', [nPort]); end; end; except on E: Exception do ETgException.TraceException(E, 'Fail .. OpenRdpSecu()'); end; end; { TThdPreventRdp } Constructor TThdPreventRdp.Create(aMgRdp: TManagerRdpSecu); begin Inherited Create; MgRdp_ := aMgRdp; end; procedure TThdPreventRdp.Execute; begin CoInitialize(nil); try while not Terminated and not GetWorkStop do begin if GetServiceStatus(NAME_RDPSVC) = SERVICE_RUNNING then begin if PreventRdp then MgRdp_.SetRdpSecuState(rssPrevent) else MgRdp_.SetRdpSecuState(rssFail); end else MgRdp_.SetRdpSecuState(rssPrevent); Sleep(1000); end; finally CoUninitialize; end; end; { TManagerRdpSecu } Constructor TManagerRdpSecu.Create(hRcvWnd: HWND); begin Inherited Create; hRcvWnd_ := hRcvWnd; State_ := rssNone; nRdpPort_ := GetRdpPort; ThdPrevent_ := nil; CoInitialize(nil); end; Destructor TManagerRdpSecu.Destroy; begin if ThdPrevent_ <> nil then FreeAndNil(ThdPrevent_); RecoveryRdpSetting; CoUninitialize; Inherited; end; procedure TManagerRdpSecu.Log(sLog: String); begin if hRcvWnd_ <> 0 then SendMessage(hRcvWnd_, WM_RDPSECU_LOG, 0, NativeUInt(PChar(sLog))); end; procedure TManagerRdpSecu.Log(sFormat: String; const Args: array of const); var str: String; begin FmtStr(str, sFormat, Args); Log(str); end; procedure TManagerRdpSecu.SetRdpSecuState(aState: TRdpSecuState); begin if State_ = aState then exit; State_ := aState; case aState of rssNone : begin if ThdPrevent_ <> nil then FreeAndNil(ThdPrevent_); RecoveryRdpSetting; nRdpPort_ := GetRdpPort; Log('RDPº¸¾È Àû¿ëÀÌ ÇØÁ¦ µÇ¾ú½À´Ï´Ù.'); end; rssRqPrevent : begin if ThdPrevent_ = nil then begin ThdPrevent_ := TThdPreventRdp.Create(Self); ThdPrevent_.StartThread; end; end; rssPrevent : Log('RDPº¸¾ÈÀÌ Àû¿ë µÇ¾ú½À´Ï´Ù.'); rssOpenRdp : begin nRdpPort_ := GetRdpPort; Log('RDPº¸¾È ¿¬°áÀÌ ¿äû µÇ¾ú½À´Ï´Ù.'); end; rssFail : Log('RDPº¸¾È Àû¿ëÀ» ½ÇÆÐÇß½À´Ï´Ù..'); end; end; function TManagerRdpSecu.OpenSafeRdp: Boolean; var nPort: Integer; ini: TIniFile; dwPid: DWORD; FwRule: TFwRule; PreState: TRdpSecuState; begin Result := false; try Guard(ini, TIniFile.Create(CutFileExt(GetRunExePath) + '.ini')); nPort := ini.ReadInteger('Setting', 'RecoverPort', 0); if nPort = 0 then ini.WriteInteger('Setting', 'RecoverPort', GetRdpPort); nPort := GetRandomPort; if nPort = 0 then begin TTgTrace.T('OpenRdpSecu() .. »ç¿ë °¡´ÉÇÑ Æ÷Æ® ¾øÀ½'); exit; end; if not ServiceExists(NAME_RDPSVC) then begin TTgTrace.T('OpenRdpSecu() .. ¼­ºñ½º ¾øÀ½'); exit; end; PreState := State_; if ThdPrevent_ <> nil then FreeAndNil(ThdPrevent_); dwPid := GetSerivcePid(NAME_RDPSVC); if dwPid <> 0 then begin if TerminateProcessByPid(dwPid, true) then begin ServiceStop(NAME_RDPRSVC); Sleep(500); if not ServiceStop(NAME_RDPSVC, 3) then exit; Sleep(500); end; end; SetRegValueInteger(HKEY_LOCAL_MACHINE, REG_RDP_PORT, 'PortNumber', nPort); if ServiceStart(NAME_RDPSVC) or (GetServiceStatus(NAME_RDPSVC) = SERVICE_RUNNING) then begin ZeroMemory(@FwRule, SizeOf(FwRule)); with FwRule do begin sName := Format('RSecu-I%d', [nPort]); sGroup := 'ToCSG'; sDesc := 'RdpSecu by ToCSG'; sLocalPorts := IntToStr(nPort); nProtocol := NET_FW_IP_PROTOCOL_TCP; nProfiles := FW_PROFILE_ANY; nAction := NET_FW_ACTION_ALLOW; nDirection := NET_FW_RULE_DIR_IN; bEnabled := true; end; AddFwRule(FwRule); TTgTrace.T('OpenRdpSecu() .. º¯°æ ¼º°ø (%d)', [nPort]); SetRdpSecuState(rssOpenRdp); Result := true; end; if not Result and (PreState = rssPrevent) then SetRdpSecuState(rssRqPrevent); except on E: Exception do ETgException.TraceException(E, 'Fail .. OpenRdpSecu()'); end; end; function TManagerRdpSecu.CloseSafeRdp: Boolean; begin Result := false; if State_ = rssOpenRdp then begin SetRdpSecuState(rssRqPrevent); Result := true; end; end; end.