{*******************************************************} { } { ScrSecuHook } { } { Copyright (C) 2022 kku } { } {*******************************************************} unit ScrSecuHook; interface uses System.SysUtils, System.Classes, Tocsg.DllEntry, Tocsg.CommonData, GlobalDefine, Winapi.Windows, Winapi.Messages, Winapi.ShellAPI, Tocsg.Trace, WinAPI.Foundation.Collections, System.Generics.Collections; type TScrSecuHook = class(TTgDllEntry) private SharedData_: TTgFileMapping; hShellHook_: HHook; Trace_: TTgTrace; dtCreate_: TDateTime; sLogPath_, sTaskDir_: String; hRecentWnd_: HWND; bInitOk_: Boolean; DcVfHandle_: TDictionary; DcVfPath_: TDictionary; ProcList_: TList; procedure Log(sLog: String); function InstallHook: Integer; function UnInstallHook: Integer; procedure AddInterceptAPI(var aProcDest: Pointer; aProcSrc, aProcHook: Pointer; sProcName: String); procedure DoInterceptCreate; procedure DoInterceptRemove; procedure OnBeforeLog(aSender: TObject); procedure OnAfterLog(aSender: TObject); function GetActive: Boolean; function GetRcvWnd: LONGLONG; public Constructor Create; Destructor Destroy; override; property Active: Boolean read GetActive; property RcvWnd: LONGLONG read GetRcvWnd; end; THandleStreamEx = class(THandleStream) protected llSize_: LONGLONG; function GetSize: Int64; override; public Constructor Create(aHandle: THandle; llSize: LONGLONG); end; TFun_CreateFileA = function(lpFileName: PAnsiChar; dwDesiredAccess, dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; stdcall; TFun_CreateFileW = function(lpFileName: PWideChar; dwDesiredAccess, dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; stdcall; TFun_CloseHandle = function(hFile: THandle): BOOL; stdcall; TFun_ReadFile = function(hFile: THandle; var Buffer; nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; stdcall; TFun_WriteFile = function(hFile: THandle; const Buffer; nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; stdcall; TFun_WriteFileEx = function(hFile: THandle; lpBuffer: Pointer; nNumberOfBytesToWrite: DWORD; const lpOverlapped: TOverlapped; lpCompletionRoutine: FARPROC): BOOL; stdcall; function InstallDrmHook: Integer; export; stdcall; function UnInstallDrmHook: Integer; export; stdcall; implementation uses BoxedAppSDK_Static, // BoxedAppSDK_DLL, DDetours, Vcl.Graphics, Vcl.Imaging.pngimage, Vcl.Imaging.jpeg, Tocsg.Safe, Tocsg.Path, Tocsg.Strings, Tocsg.Process, Winapi.DwmApi, Vcl.Forms, Tocsg.WndUtil, Tocsg.Exception, Tocsg.Encrypt, Tocsg.Shell, Tocsg.Files, superobject; var _ScrSecuHook: TScrSecuHook = nil; _bLogProcessing: Boolean = false; _bInternalOpen: Boolean = false; ozCreateFileA: TFun_CreateFileA = nil; ozCreateFileW: TFun_CreateFileW = nil; ozCloseHandle: TFun_CloseHandle = nil; ozReadFile: TFun_ReadFile = nil; ozWriteFile: TFun_WriteFile = nil; ozWriteFileEx: TFun_WriteFileEx = nil; const IMAGE_EXTS = 'jpg|png|jpe|jpeg|bmp|ico|gif|tiff'; { THandleStreamEx } Constructor THandleStreamEx.Create(aHandle: THandle; llSize: LONGLONG); begin Inherited Create(aHandle); llSize_ := llSize; end; function THandleStreamEx.GetSize: Int64; begin Result := llSize_; end; function ProcessCreateFile(sPath: String; dwDesiredAccess, dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; inline; var sExt, sVfDir, sVfPath: String; bVf: Boolean; dwFlags: DWORD; Label LB_ProcOrg; begin Result := 0; bVf := false; try // case dwDesiredAccess of // 0, $80 : goto LB_ProcOrg; // end; sExt := GetFileExt(sPath).ToLower; if Pos(sExt, IMAGE_EXTS) = 0 then goto LB_ProcOrg; // dwFlags := GetFileAttributes(PChar(sPath)); // if (dwFlags and FILE_ATTRIBUTE_DIRECTORY) <> 0 then // goto LB_ProcOrg; // _ScrSecuHook.Log(Format('ProcessCreateFile() ... dwCreationDisposition=%d', [dwCreationDisposition])); if ( (dwCreationDisposition = CREATE_NEW) or (dwCreationDisposition = CREATE_ALWAYS) ) and not _bInternalOpen then begin _ScrSecuHook.Log(Format('ProcessCreateFile() .. 1, dwDesiredAccess=%d, Path="%s"', [dwDesiredAccess, sPath])); if not _ScrSecuHook.DcVfPath_.ContainsKey(sPath) then begin { sVfDir := Format('%s:\VF\', [sPath[1]]); BoxedAppSDK_CreateVirtualDirectory(PChar(sVfDir), nil); sVfPath := sVfDir + ExtractFileName(sPath); // sVfPath := ExtractFilePath(sPath) + 'V@' + ExtractFileName(sPath); _ScrSecuHook.DcVfPath_.Add(sPath, sVfPath); } _ScrSecuHook.DcVfPath_.Add(sPath, sPath); end; // else // sVfPath := _ScrSecuHook.DcVfPath_[sPath]; // _ScrSecuHook.Log(Format('ProcessCreateFile() ... 2, VfPath="%s"', [sVfPath])); // try // Result := BoxedAppSDK_CreateVirtualFile(PChar(sVfPath), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, // dwFlagsAndAttributes, hTemplateFile); // except // _ScrSecuHook.Log('Fail .. BoxedAppSDK_CreateVirtualFile()'); // Result := 0; // exit; // end; Result := ozCreateFileW(PChar(sPath), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); if (Result <> 0) and (Result <> INVALID_HANDLE_VALUE) then _ScrSecuHook.DcVfHandle_.Add(Result, sPath); end else begin if _ScrSecuHook.DcVfPath_.ContainsKey(sPath) then begin sPath := _ScrSecuHook.DcVfPath_[sPath]; bVf := true; _ScrSecuHook.Log(Format('ProcessCreateFile() .. 2, VfPath="%s"', [sPath])); end; LB_ProcOrg : Result := ozCreateFileW(PChar(sPath), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); // if BoxedAppSDK_IsVirtualFile(PChar(sPath)) then // À̰ŠüũÇϸé "SnippingTool.exe"¿¡¼­ ¿Àµ¿ÀÛÇÑ´Ù.. 22_1228 14:02:39 kku if bVf then begin _ScrSecuHook.DcVfHandle_.Add(Result, sPath); // _ScrSecuHook.Log('ProcessCreateFile() .. 2, _ScrSecuHook.DcVfHandle_.Add(Result, sPath)'); end; end; except on E: Exception do ETgException.TraceException(E, 'Fail .. ProcessCreateFile()'); end; end; function CreateFileAHook(lpFileName: PAnsiChar; dwDesiredAccess, dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; stdcall; begin Result := ProcessCreateFile(StrPas(lpFileName), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); // if Result = 0 then // begin // Result := ozCreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, // dwFlagsAndAttributes, hTemplateFile); // end; end; function CreateFileWHook(lpFileName: PWideChar; dwDesiredAccess, dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; stdcall; begin Result := ProcessCreateFile(StrPas(lpFileName), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); // if Result = 0 then // begin // Result := ozCreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, // dwFlagsAndAttributes, hTemplateFile); // end; end; function CloseHandleHook(hFile: THandle): BOOL; stdcall; var sPath, sVfPath: String; bExe: Boolean; llSize: LONGLONG; begin try bExe := false; if _ScrSecuHook.DcVfHandle_.ContainsKey(hFile) then begin sPath := _ScrSecuHook.DcVfHandle_[hFile]; // if _ScrSecuHook.DcVfPath_.ContainsKey(sPath) then // sVfPath := _ScrSecuHook.DcVfPath_[sPath]; _ScrSecuHook.DcVfHandle_.Remove(hFile); if _ScrSecuHook.SharedData_.IsAvailable and (_ScrSecuHook.SharedData_.Data.llRcvWnd <> 0) then begin if GetFileSizeEx(hFile, llSize) and (FileSeek(hFile, 0, 1) = llSize) then begin // ZeroMemory(@_ScrSecuHook.SharedData_.Data.sImgPath, 1024); // StrCopy(_ScrSecuHook.SharedData_.Data.sImgPath, PChar(sPath)); // PostMessage(_ScrSecuHook.SharedData_.Data.llRcvWnd, WM_NOTI_CREATE_IMAGE_FILE, 0, 0); bExe := true; end; end; // _ScrSecuHook.Log(Format('CloseHandleHook() .. hFile=%d, VfPath="%s"', [hFile, sPath])); end; Result := ozCloseHandle(hFile); if bExe then begin // if sPath <> sVfPath then // MoveFile_wait(sPath, sVfPath); _bInternalOpen := true; try if FileExists(sPath) and (GetFileSize_path(sPath) > 0) then begin var O: ISuperObject; O := SO; O.S['Path'] := sPath; // O.S['VfPath'] := sVfPath; O.I['PID'] := GetCurrentProcessId; O.I['RH'] := _ScrSecuHook.SharedData_.Data.llRcvWnd; if SaveJsonObjToFile(O, _ScrSecuHook.sTaskDir_ + REQ_PREVIEW) then begin // var ProcInfo := ExecuteApp(_ScrSecuHook.sTaskDir_ + EXE_MAIN, // Format('/img "%s"', [_ScrSecuHook.sTaskDir_ + REQ_PREVIEW]), SW_SHOWNORMAL); // // if ProcInfo.dwProcessId <> 0 then // begin // Sleep(1000); // _ScrSecuHook.Log('Success .. ExecuteApp()'); // // if BoxedAppSDK_DetachFromProcess(ProcInfo.hProcess) then // _ScrSecuHook.Log('Success .. BoxedAppSDK_DetachFromProcess()'); // end; ExecutePath(_ScrSecuHook.sTaskDir_ + EXE_MAIN, Format('/img "%s"', [_ScrSecuHook.sTaskDir_ + REQ_PREVIEW])); end; end; finally _bInternalOpen := false; end; end; except on E: Exception do ETgException.TraceException(E, 'Fail .. CloseHandleHook()'); end; end; function ReadFileHook(hFile: THandle; var Buffer; nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; stdcall; begin Result := ozReadFile(hFile, Buffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped); end; function WriteFileHook(hFile: THandle; const Buffer; nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; stdcall; begin Result := ozWriteFile(hFile, Buffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); end; function WriteFileExHook(hFile: THandle; lpBuffer: Pointer; nNumberOfBytesToWrite: DWORD; const lpOverlapped: TOverlapped; lpCompletionRoutine: FARPROC): BOOL; stdcall; begin Result := ozWriteFileEx(hFile, lpBuffer, nNumberOfBytesToWrite, lpOverlapped, lpCompletionRoutine); end; function process_WH_SHELL(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; begin if Assigned(_ScrSecuHook) then begin try try if nCode >= HC_ACTION then case nCode of // HSHELL_ACCESSIBILITYSTATE : ; // Windows 2000/XP: accessibility »óŰ¡ º¯°æµÇ¾úÀ½ // HSHELL_APPCOMMAND : ; // Windows 2000/XP: »ç¿ëÀÚÀÇ ÀÔ·Â À̺¥Æ®¿¡ ÀÇÇØ¼­ WM_APPCOMMAND °¡ ¹ß»ýÇÑ °æ¿ì´Ù // HSHELL_ACTIVATESHELLWINDOW : ; // ½© ¸ÞÀÎ À©µµ¿ì°¡ Ȱ¼ºÈ­ µÇ¾î¾ß ÇÏ´Â °æ¿ì´Ù // HSHELL_GETMINRECT : ; // À©µµ¿ì°¡ ÃÖ¼ÒÈ­/ÃÖ´ëÈ­µÈ °æ¿ì´Ù // HSHELL_LANGUAGE : ; // Űº¸µå ¾ð¾î°¡ º¯°æµÇ°Å³ª »õ·Î¿î Űº¸µå ·¹À̾ƿôÀÌ ·ÎµåµÈ °æ¿ì´Ù // HSHELL_TASKMAN : ; // »ç¿ëÀÚ°¡ ŽºÅ© ¸®½ºÆ®¸¦ ¼±ÅÃÇÑ °æ¿ì´Ù. Å×½ºÆ®ÇØ º» °á°ú Ctrl + Esc ۸¦ ´©¸¥ °æ¿ì ÈÅ ÇÁ·Î½ÃÀú°¡ È£ÃâµÇ¾ú´Ù. ½ÃÀÛ ¹öưÀ» ´­·¶À» ¶§¿£ È£ÃâµÇÁö ¾Ê´Â´Ù HSHELL_WINDOWACTIVATED, // : ; // ž ·¹º§ À©µµ¿ìÀÇ È°¼ºÈ­ »óŰ¡ º¯°æµÈ °æ¿ì´Ù HSHELL_WINDOWCREATED, // : ; // ž ·¹º§ À©µµ¿ì°¡ »ý¼ºµÈ °æ¿ì´Ù. ÀÌ ÈÅÀÌ È£ÃâµÇ´Â ½ÃÁ¡¿¡ À©µµ¿ì´Â »ý¼ºµÈ »óÅ´٠HSHELL_REDRAW : // ŽºÅ©¹Ù¿¡ ÀÖ´Â À©µµ¿ì ŸÀÌÆ²ÀÌ »õ·Î ±×·ÁÁö´Â °æ¿ì´Ù begin // if _ScrSecuHook.hRecentWnd_ <> wParam then // begin // _ScrSecuHook.hRecentWnd_ := wParam; // if SetWindowDisplayAffinity(_ScrSecuHook.hRecentWnd_, 1) then // _ScrSecuHook.Log(Format('Success .. SetWindowDisplayAffinity(), Title="%s"', [GetWindowCaption(_ScrSecuHook.hRecentWnd_)])) // end; end; HSHELL_WINDOWDESTROYED : ; // ž ·¹º§ À©µµ¿ì°¡ ÆÄ±«µÈ °æ¿ì´Ù. ÀÌ ÈÅÀÌ È£ÃâµÇ´Â ½ÃÁ¡¿¡ À©µµ¿ì´Â Á¸ÀçÇÑ´Ù HSHELL_WINDOWREPLACED : ; // Windows XP: ž ·¹º§ À©µµ¿ì°¡ ´ëü(replaced)µÈ °æ¿ì´Ù end; except exit; end; finally Result := CallNextHookEx(_ScrSecuHook.hShellHook_, nCode, wParam, lParam); end; end else Result := 0; end; { TScrSecuHook } Constructor TScrSecuHook.Create; procedure GetStartTime; var ftCreate, ftExit, ftKernel, ftUser: TFileTime; nDosTime: Integer; begin dtCreate_ := 0; GetProcessTimes(GetCurrentProcess, ftCreate, ftExit, ftKernel, ftUser); if FileTimeToLocalFileTime(ftCreate, ftCreate) then if FileTimeToDosDateTime(ftCreate, LongRec(nDosTime).Hi, LongRec(nDosTime).Lo) then dtCreate_ := FileDateToDateTime(nDosTime); end; begin Inherited Create; ASSERT(_ScrSecuHook = nil); _ScrSecuHook := Self; bInitOk_ := false; GetStartTime; hShellHook_ := 0; DcVfHandle_ := TDictionary.Create; DcVfPath_ := TDictionary.Create; ProcList_ := TList.Create; SharedData_ := TTgFileMapping.Create(MAP_FILENAME_APIHOOK, SizeOf(TSharedData)); if SharedData_.IsAvailable then begin sLogPath_ := SharedData_.Data.sLogPath; sTaskDir_ := SharedData_.Data.sTaskDir; end; {$IFDEF DEBUG} Trace_ := TTgTrace.Create(ExtractFilePath(sLogPath_), ExtractFileName(sLogPath_)); Trace_.OnBeforeLog := OnBeforeLog; Trace_.OnAfterLog := OnAfterLog; {$ELSE} Trace_ := nil; {$ENDIF} DoInterceptCreate; end; Destructor TScrSecuHook.Destroy; begin DoInterceptRemove; _ScrSecuHook := nil; FreeAndNil(SharedData_); if Trace_ <> nil then FreeAndNil(Trace_); FreeAndNil(DcVfPath_); FreeAndNil(ProcList_); Inherited; FreeAndNil(DcVfHandle_); end; procedure TScrSecuHook.OnBeforeLog(aSender: TObject); begin _bLogProcessing := true; end; procedure TScrSecuHook.OnAfterLog(aSender: TObject); begin _bLogProcessing := false; end; function TScrSecuHook.GetActive: Boolean; begin try if SharedData_.IsAvailable then Result := SharedData_.Data.bActive else Result := false; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetActive()'); end; end; function TScrSecuHook.GetRcvWnd: LONGLONG; begin try if SharedData_.IsAvailable then Result := SharedData_.Data.llRcvWnd else Result := 0; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. GetRcvWnd()'); end; end; procedure TScrSecuHook.AddInterceptAPI(var aProcDest: Pointer; aProcSrc, aProcHook: Pointer; sProcName: String); begin if aProcDest = nil then begin aProcDest := InterceptCreate(aProcSrc, aProcHook); if aProcDest <> nil then begin Log(Format('>>> InterceptCreate() - %s <<<', [sProcName])); ProcList_.Add(@aProcDest); end else Log(Format('>>> Fail .. InterceptCreate() - %s <<<', [sProcName])); end; end; procedure TScrSecuHook.DoInterceptCreate; var bLoadBx: Boolean; // nTryCnt: Integer; Label LB_LoadBx; begin Log('DoInterceptCreate()'); DoInterceptRemove; // Log('DoInterceptCreate() .. 0'); // nTryCnt := 0; bLoadBx := false; LB_LoadBx : try // Init BoxedApp SDK BoxedAppSDK_SetContext('21f2f010-06e4-465f-af8f-cde6a3752c39'); BoxedAppSDK_Init; BoxedAppSDK_EnableOption(DEF_BOXEDAPPSDK_OPTION__ALL_CHANGES_ARE_VIRTUAL, TRUE); BoxedAppSDK_EnableOption(DEF_BOXEDAPPSDK_OPTION__EMBED_BOXEDAPP_IN_CHILD_PROCESSES, TRUE); // BoxedAppSDK_EnableOption(DEF_BOXEDAPPSDK_OPTION__INHERIT_OPTIONS, FALSE); bLoadBx := true; except Log('Fail .. BoxedAppSDK'); end; if not bLoadBx then begin Sleep(10); goto LB_LoadBx; end; AddInterceptAPI(@ozCreateFileA, @CreateFileA, @CreateFileAHook, 'CreateFileA'); AddInterceptAPI(@ozCreateFileW, @CreateFileW, @CreateFileWHook, 'CreateFileW'); AddInterceptAPI(@ozCloseHandle, @CloseHandle, @CloseHandleHook, 'CloseHandle'); // AddInterceptAPI(@ozReadFile, @ReadFile, @ReadFileHook, 'ReadFile'); // AddInterceptAPI(@ozWriteFile, @WriteFile, @WriteFileHook, 'WriteFile'); // AddInterceptAPI(@ozWriteFileEx, @WriteFileEx, @WriteFileExHook, 'WriteFileEx'); bInitOk_ := true; end; procedure TScrSecuHook.DoInterceptRemove; procedure RemoveAPI(var aProc: Pointer); inline; begin if aProc <> nil then begin InterceptRemove(aProc); aProc := nil; end; end; var i: Integer; begin if bInitOk_ then begin Log('DoInterceptRemove()'); for i := 0 to ProcList_.Count - 1 do RemoveAPI(ProcList_[i]^); ProcList_.Clear; // BoxedAppSDK_Exit; bInitOk_ := false; end; end; procedure TScrSecuHook.Log(sLog: String); begin {$IFDEF DEBUG} if Trace_ <> nil then Trace_.T('(%s) %s', [ModuleName, sLog]); {$ENDIF} end; function TScrSecuHook.InstallHook: Integer; begin Log('InstallHook'); try hShellHook_ := SetWindowsHookEx(WH_SHELL, process_WH_SHELL, HInstance, 0); if hShellHook_ = 0 then begin Log('SetWindowsHookEx(WH_SHELL) fail!!'); Result := 2; exit; end; except exit; end; // if (hCbtHook_ <> 0) and (hShellHook_ <> 0) then Result := 0; // else // Result := -2; end; function TScrSecuHook.UnInstallHook: Integer; begin Log('UnInstallHook'); try if hShellHook_ <> 0 then begin Log('UnhookWindowsHookEx(ShellHook)'); UnhookWindowsHookEx(hShellHook_); hShellHook_ := 0; end; except hShellHook_ := 0; end; Result := 0; end; function InstallDrmHook: Integer; begin Result := -1; if Assigned(_ScrSecuHook) then Result := _ScrSecuHook.InstallHook; end; function UnInstallDrmHook: Integer; begin Result := -1; if Assigned(_ScrSecuHook) then Result := _ScrSecuHook.UnInstallHook; end; exports InstallDrmHook, UnInstallDrmHook; end.