mirror of
https://github.com/CloudDelphi/Virtual-File-System
synced 2025-12-19 09:53:54 +01:00
Added support for NtCreate/OpenFile, NtClose and hooks uninstalling
This commit is contained in:
parent
360fddaa46
commit
21c4b1df1c
1
Tests/Fs/Mods/FullyVirtual_2/Hobbots/mms.cfg
Normal file
1
Tests/Fs/Mods/FullyVirtual_2/Hobbots/mms.cfg
Normal file
@ -0,0 +1 @@
|
||||
It was a pleasure to override you, friend!
|
||||
@ -4,9 +4,9 @@ unit VfsIntegratedTest;
|
||||
|
||||
uses
|
||||
SysUtils, TestFramework, Windows,
|
||||
Utils, WinUtils, ConsoleApi,
|
||||
Utils, WinUtils, ConsoleApi, Files,
|
||||
VfsUtils, VfsBase, VfsDebug,
|
||||
VfsControl;
|
||||
VfsOpenFiles, VfsControl, DlgMes;
|
||||
|
||||
type
|
||||
TestIntegrated = class (TTestCase)
|
||||
@ -22,8 +22,10 @@ type
|
||||
published
|
||||
procedure TestGetFileAttributes;
|
||||
procedure TestGetFileAttributesEx;
|
||||
procedure TestFilesOpenClose;
|
||||
end;
|
||||
|
||||
|
||||
(***) implementation (***)
|
||||
|
||||
|
||||
@ -34,7 +36,7 @@ end;
|
||||
|
||||
function TestIntegrated.GetRootDir: string;
|
||||
begin
|
||||
result := SysUtils.ExtractFileDir(WinUtils.GetExePath) + '\Tests\Fs';
|
||||
result := VfsUtils.NormalizePath(SysUtils.ExtractFileDir(WinUtils.GetExePath) + '\Tests\Fs');
|
||||
end;
|
||||
|
||||
procedure TestIntegrated.SetUp;
|
||||
@ -42,22 +44,20 @@ var
|
||||
RootDir: string;
|
||||
|
||||
begin
|
||||
if not Inited then begin
|
||||
Inited := true;
|
||||
RootDir := Self.GetRootDir;
|
||||
VfsBase.ResetVfs();
|
||||
VfsBase.MapDir(RootDir, RootDir + '\Mods\FullyVirtual_2', DONT_OVERWRITE_EXISTING);
|
||||
VfsBase.MapDir(RootDir, RootDir + '\Mods\FullyVirtual', DONT_OVERWRITE_EXISTING);
|
||||
VfsBase.MapDir(RootDir, RootDir + '\Mods\B', DONT_OVERWRITE_EXISTING);
|
||||
VfsBase.MapDir(RootDir, RootDir + '\Mods\A', DONT_OVERWRITE_EXISTING);
|
||||
VfsBase.MapDir(RootDir, RootDir + '\Mods\Apache', DONT_OVERWRITE_EXISTING);
|
||||
VfsDebug.SetLoggingProc(LogSomething);
|
||||
VfsControl.RunVfs(VfsBase.SORT_FIFO);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TestIntegrated.TearDown;
|
||||
begin
|
||||
VfsBase.PauseVfs();
|
||||
VfsBase.ResetVfs();
|
||||
VfsDebug.SetLoggingProc(nil);
|
||||
end;
|
||||
|
||||
@ -112,11 +112,58 @@ var
|
||||
begin
|
||||
RootDir := Self.GetRootDir;
|
||||
CheckEquals(-1, GetFileSize(RootDir + '\non-existing.non'), '{1}');
|
||||
CheckEquals(47, GetFileSize(RootDir + '\Hobbots\mms.cfg'), '{2}');
|
||||
CheckEquals(42, GetFileSize(RootDir + '\Hobbots\mms.cfg'), '{2}');
|
||||
CheckEquals(22, GetFileSize(RootDir + '\503.html'), '{3}');
|
||||
CheckEquals(318, GetFileSize(RootDir + '\default'), '{4}');
|
||||
end; // .procedure TestIntegrated.TestGetFileAttributesEx;
|
||||
|
||||
procedure TestIntegrated.TestFilesOpenClose;
|
||||
var
|
||||
CurrDir: string;
|
||||
RootDir: string;
|
||||
FileData: string;
|
||||
hFile: integer;
|
||||
|
||||
function OpenFile (const Path: string): integer;
|
||||
begin
|
||||
result := SysUtils.FileOpen(Path, fmOpenRead or fmShareDenyNone);
|
||||
end;
|
||||
|
||||
begin
|
||||
CurrDir := SysUtils.GetCurrentDir;
|
||||
RootDir := Self.GetRootDir;
|
||||
|
||||
try
|
||||
Check(SysUtils.SetCurrentDir(RootDir), 'Setting current directory to real path must succeed');
|
||||
|
||||
Check(OpenFile(RootDir + '\non-existing.non') <= 0, 'Opening non-existing file must fail');
|
||||
|
||||
hFile := OpenFile(RootDir + '\Hobbots\mms.cfg');
|
||||
Check(hFile > 0, 'Opening fully virtual file must succeed');
|
||||
CheckEquals(RootDir + '\Hobbots\mms.cfg', VfsOpenFiles.GetOpenedFilePath(hFile), 'There must be created a corresponding TOpenedFile record for opened file handle with valid virtual path');
|
||||
SysUtils.FileClose(hFile);
|
||||
CheckEquals('', VfsOpenFiles.GetOpenedFilePath(hFile), 'TOpenedFile record must be destroyed on file handle closing {1}');
|
||||
|
||||
hFile := OpenFile('Hobbots\mms.cfg');
|
||||
Check(hFile > 0, 'Opening fully virtual file using relative path must succeed');
|
||||
CheckEquals(RootDir + '\Hobbots\mms.cfg', VfsOpenFiles.GetOpenedFilePath(hFile), 'There must be created a corresponding TOpenedFile record for opened file handle with valid virtual path when relative path was used');
|
||||
SysUtils.FileClose(hFile);
|
||||
CheckEquals('', VfsOpenFiles.GetOpenedFilePath(hFile), 'TOpenedFile record must be destroyed on file handle closing {2}');
|
||||
|
||||
Check(SysUtils.SetCurrentDir(RootDir + '\Hobbots'), 'Setting current durectory to fully virtual must succeed');
|
||||
hFile := OpenFile('mms.cfg');
|
||||
Check(hFile > 0, 'Opening fully virtual file in fully virtual directory using relative path must succeed');
|
||||
CheckEquals(RootDir + '\Hobbots\mms.cfg', VfsOpenFiles.GetOpenedFilePath(hFile), 'There must be created a corresponding TOpenedFile record for opened file handle with valid virtual path when relative path was used for fully virtual directory');
|
||||
SysUtils.FileClose(hFile);
|
||||
CheckEquals('', VfsOpenFiles.GetOpenedFilePath(hFile), 'TOpenedFile record must be destroyed on file handle closing {3}');
|
||||
|
||||
Check(Files.ReadFileContents('mms.cfg', FileData), 'File mms.cfg must be readable');
|
||||
CheckEquals('It was a pleasure to override you, friend!', FileData);
|
||||
finally
|
||||
SysUtils.SetCurrentDir(CurrDir);
|
||||
end; // .try
|
||||
end; // .procedure TestIntegrated.TestFilesOpenClose;
|
||||
|
||||
begin
|
||||
RegisterTest(TestIntegrated.Suite);
|
||||
end.
|
||||
@ -530,15 +530,10 @@ begin
|
||||
try
|
||||
DisableVfsForThread;
|
||||
result := Func(Arg);
|
||||
except
|
||||
on E: Exception do begin
|
||||
finally
|
||||
RestoreVfsForThread;
|
||||
raise E;
|
||||
end;
|
||||
end; // .try
|
||||
|
||||
RestoreVfsForThread;
|
||||
end; // .with
|
||||
end;
|
||||
end; // .function CallWithoutVfs
|
||||
|
||||
begin
|
||||
|
||||
103
VfsHooks.pas
103
VfsHooks.pas
@ -32,6 +32,13 @@ var
|
||||
NativeNtClose: WinNative.TNtClose;
|
||||
NativeNtQueryDirectoryFile: WinNative.TNtQueryDirectoryFile;
|
||||
|
||||
NtQueryAttributesFilePatch: VfsPatching.TAppliedPatch;
|
||||
NtQueryFullAttributesFilePatch: VfsPatching.TAppliedPatch;
|
||||
NtOpenFilePatch: VfsPatching.TAppliedPatch;
|
||||
NtCreateFilePatch: VfsPatching.TAppliedPatch;
|
||||
NtClosePatch: VfsPatching.TAppliedPatch;
|
||||
NtQueryDirectoryFilePatch: VfsPatching.TAppliedPatch;
|
||||
|
||||
|
||||
(* There is no 100% portable and reliable way to get file path by handle, unless file creation/opening
|
||||
was tracked. Thus we rely heavily on VfsOpenFiles.
|
||||
@ -209,11 +216,9 @@ var
|
||||
ReplacedObjAttrs: WinNative.TObjectAttributes;
|
||||
HadTrailingDelim: boolean;
|
||||
|
||||
FileInfo: Windows.TWin32FindDataW;
|
||||
|
||||
begin
|
||||
if VfsDebug.LoggingEnabled then begin
|
||||
WriteLog('NtCreateFile', ObjectAttributes.ObjectName.ToWideStr());
|
||||
WriteLog('[ENTER] NtCreateFile', Format('Access: 0x%x. CreateDisposition: 0x%x'#13#10'Path: "%s"', [Int(DesiredAccess), Int(CreateDisposition), ObjectAttributes.ObjectName.ToWideStr()]));
|
||||
end;
|
||||
|
||||
ReplacedObjAttrs := ObjectAttributes^;
|
||||
@ -222,7 +227,7 @@ begin
|
||||
RedirectedPath := '';
|
||||
|
||||
if (ExpandedPath <> '') and ((DesiredAccess and WinNative.DELETE) = 0) and (CreateDisposition = WinNative.FILE_OPEN) then begin
|
||||
RedirectedPath := VfsBase.GetVfsItemRealPath(StrLib.ExcludeTrailingDelimW(ExpandedPath, @HadTrailingDelim), @FileInfo);
|
||||
RedirectedPath := VfsBase.GetVfsItemRealPath(StrLib.ExcludeTrailingDelimW(ExpandedPath, @HadTrailingDelim));
|
||||
end;
|
||||
|
||||
if RedirectedPath = '' then begin
|
||||
@ -231,25 +236,33 @@ begin
|
||||
RedirectedPath := RedirectedPath + '\';
|
||||
end;
|
||||
|
||||
if (RedirectedPath <> '') and (RedirectedPath[1] <> '\') then begin
|
||||
if RedirectedPath <> '' then begin
|
||||
if RedirectedPath[1] <> '\' then begin
|
||||
RedirectedPath := '\??\' + RedirectedPath;
|
||||
end;
|
||||
|
||||
ReplacedObjAttrs.RootDirectory := 0;
|
||||
ReplacedObjAttrs.Attributes := ReplacedObjAttrs.Attributes or WinNative.OBJ_CASE_INSENSITIVE;
|
||||
ReplacedObjAttrs.ObjectName.AssignExistingStr(RedirectedPath);
|
||||
end;
|
||||
|
||||
with VfsOpenFiles.OpenFilesCritSection do begin
|
||||
Enter;
|
||||
|
||||
result := OrigFunc(FileHandle, DesiredAccess, @ReplacedObjAttrs, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
||||
|
||||
if (result = WinNative.STATUS_SUCCESS) and Utils.HasFlag(WinNative.FILE_SYNCHRONOUS_IO_NONALERT, CreateOptions) and Utils.HasFlag(WinNative.SYNCHRONIZE, DesiredAccess) then begin
|
||||
if (result = WinNative.STATUS_SUCCESS) and (ExpandedPath <> '') then begin
|
||||
VfsOpenFiles.SetOpenedFileInfo(FileHandle^, TOpenedFile.Create(FileHandle^, ExpandedPath));
|
||||
end;
|
||||
|
||||
Leave;
|
||||
end;
|
||||
|
||||
if VfsDebug.LoggingEnabled then begin
|
||||
if ExpandedPath <> StripNtAbsPathPrefix(RedirectedPath) then begin
|
||||
WriteLog('NtCreateFile', Format('Access: 0x%x. Handle: %x. Status: %x. Redirected "%s" => "%s"', [DesiredAccess, FileHandle^, result, StrLib.WideToAnsiSubstitute(ExpandedPath), StrLib.WideToAnsiSubstitute(StripNtAbsPathPrefix(RedirectedPath))]));
|
||||
WriteLog('[LEAVE] NtCreateFile', Format('Handle: %x. Status: %x.'#13#10'Expanded: "%s"'#13#10'Redirected: "%s"', [FileHandle^, result, ExpandedPath, StripNtAbsPathPrefix(RedirectedPath)]));
|
||||
end else begin
|
||||
WriteLog('NtCreateFile', Format('Access: 0x%x. Handle: %x. Status: %x. Path: "%s"', [DesiredAccess, FileHandle^, result, StrLib.WideToAnsiSubstitute(ExpandedPath)]));
|
||||
WriteLog('[LEAVE] NtCreateFile', Format('Handle: %x. Status: %x.'#13#10'Expanded: "%s"', [FileHandle^, result, ExpandedPath]));
|
||||
end;
|
||||
end;
|
||||
end; // .function Hook_NtCreateFile
|
||||
@ -257,11 +270,12 @@ end; // .function Hook_NtCreateFile
|
||||
function Hook_NtClose (OrigFunc: WinNative.TNtClose; hData: HANDLE): NTSTATUS; stdcall;
|
||||
begin
|
||||
if VfsDebug.LoggingEnabled then begin
|
||||
WriteLog('NtClose', Format('Handle: %x', [integer(hData)]));
|
||||
WriteLog('[ENTER] NtClose', Format('Handle: %x', [integer(hData)]));
|
||||
end;
|
||||
|
||||
with OpenFilesCritSection do begin
|
||||
with VfsOpenFiles.OpenFilesCritSection do begin
|
||||
Enter;
|
||||
|
||||
result := OrigFunc(hData);
|
||||
|
||||
if WinNative.NT_SUCCESS(result) then begin
|
||||
@ -272,7 +286,7 @@ begin
|
||||
end;
|
||||
|
||||
if VfsDebug.LoggingEnabled then begin
|
||||
WriteLog('NtClose', Format('Status: %x', [integer(result)]));
|
||||
WriteLog('[LEAVE] NtClose', Format('Status: %x', [integer(result)]));
|
||||
end;
|
||||
end; // .function Hook_NtClose
|
||||
|
||||
@ -526,36 +540,41 @@ begin
|
||||
NativeNtQueryAttributesFile := VfsPatching.SpliceWinApi
|
||||
(
|
||||
VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtQueryAttributesFile'),
|
||||
@Hook_NtQueryAttributesFile
|
||||
@Hook_NtQueryAttributesFile,
|
||||
@NtQueryAttributesFilePatch
|
||||
);
|
||||
|
||||
WriteLog('InstallHook', 'Installing NtQueryFullAttributesFile hook');
|
||||
NativeNtQueryFullAttributesFile := VfsPatching.SpliceWinApi
|
||||
(
|
||||
VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtQueryFullAttributesFile'),
|
||||
@Hook_NtQueryFullAttributesFile
|
||||
@Hook_NtQueryFullAttributesFile,
|
||||
@NtQueryFullAttributesFilePatch
|
||||
);
|
||||
|
||||
// WriteLog('InstallHook', 'Installing NtOpenFile hook');
|
||||
// NativeNtOpenFile := VfsPatching.SpliceWinApi
|
||||
// (
|
||||
// VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtOpenFile'),
|
||||
// @Hook_NtOpenFile
|
||||
// );
|
||||
WriteLog('InstallHook', 'Installing NtOpenFile hook');
|
||||
NativeNtOpenFile := VfsPatching.SpliceWinApi
|
||||
(
|
||||
VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtOpenFile'),
|
||||
@Hook_NtOpenFile,
|
||||
@NtOpenFilePatch
|
||||
);
|
||||
|
||||
// WriteLog('InstallHook', 'Installing NtCreateFile hook');
|
||||
// NativeNtCreateFile := VfsPatching.SpliceWinApi
|
||||
// (
|
||||
// VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtCreateFile'),
|
||||
// @Hook_NtCreateFile
|
||||
// );
|
||||
WriteLog('InstallHook', 'Installing NtCreateFile hook');
|
||||
NativeNtCreateFile := VfsPatching.SpliceWinApi
|
||||
(
|
||||
VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtCreateFile'),
|
||||
@Hook_NtCreateFile,
|
||||
@NtCreateFilePatch
|
||||
);
|
||||
|
||||
// WriteLog('InstallHook', 'Installing NtClose hook');
|
||||
// NativeNtClose := VfsPatching.SpliceWinApi
|
||||
// (
|
||||
// VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtClose'),
|
||||
// @Hook_NtClose
|
||||
// );
|
||||
WriteLog('InstallHook', 'Installing NtClose hook');
|
||||
NativeNtClose := VfsPatching.SpliceWinApi
|
||||
(
|
||||
VfsApiDigger.GetRealProcAddress(NtdllHandle, 'NtClose'),
|
||||
@Hook_NtClose,
|
||||
@NtClosePatch
|
||||
);
|
||||
|
||||
// WriteLog('InstallHook', 'Installing NtQueryDirectoryFile hook');
|
||||
// NativeNtQueryDirectoryFile := VfsPatching.SpliceWinApi
|
||||
@ -569,6 +588,28 @@ begin
|
||||
end; // .with
|
||||
end; // .procedure InstallHooks
|
||||
|
||||
procedure UninstallHooks;
|
||||
begin
|
||||
with HooksCritSection do begin
|
||||
Enter;
|
||||
|
||||
NtQueryAttributesFilePatch.Rollback;
|
||||
NtQueryFullAttributesFilePatch.Rollback;
|
||||
NtOpenFilePatch.Rollback;
|
||||
NtCreateFilePatch.Rollback;
|
||||
NtClosePatch.Rollback;
|
||||
|
||||
Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
HooksCritSection.Init;
|
||||
finalization
|
||||
with VfsBase.VfsCritSection do begin
|
||||
Enter;
|
||||
VfsBase.ResetVfs;
|
||||
UninstallHooks;
|
||||
Leave;
|
||||
end;
|
||||
end.
|
||||
@ -4,17 +4,24 @@ unit VfsPatching;
|
||||
All hooks are thread-safe.
|
||||
*)
|
||||
|
||||
|
||||
(***) interface (***)
|
||||
|
||||
uses
|
||||
Windows, SysUtils, Utils, PatchForge;
|
||||
|
||||
type
|
||||
PAppliedPatch = ^TAppliedPatch;
|
||||
TAppliedPatch = record
|
||||
Addr: pointer;
|
||||
Bytes: Utils.TArrayOfByte;
|
||||
|
||||
procedure Rollback;
|
||||
end;
|
||||
|
||||
(* Replaces original STDCALL function with the new one with the same prototype and one extra argument.
|
||||
The argument is callable pointer, used to execute original function. The pointer is passed as THE FIRST
|
||||
argument before other arguments. *)
|
||||
function SpliceWinApi (OrigFunc, HandlerFunc: pointer): pointer;
|
||||
function SpliceWinApi (OrigFunc, HandlerFunc: pointer; {n} AppliedPatch: PAppliedPatch = nil): pointer;
|
||||
|
||||
|
||||
(***) implementation (***)
|
||||
@ -68,7 +75,7 @@ begin
|
||||
end;
|
||||
end; // .function WritePatchAtCode
|
||||
|
||||
function SpliceWinApi (OrigFunc, HandlerFunc: pointer): pointer;
|
||||
function SpliceWinApi (OrigFunc, HandlerFunc: pointer; {n} AppliedPatch: PAppliedPatch = nil): pointer;
|
||||
const
|
||||
CODE_ADDR_ALIGNMENT = 8;
|
||||
|
||||
@ -121,9 +128,23 @@ begin
|
||||
// Create and apply hook at target function start
|
||||
p.Clear();
|
||||
p.Jump(PatchForge.JMP, SpliceBridge);
|
||||
|
||||
if AppliedPatch <> nil then begin
|
||||
AppliedPatch.Addr := OrigFunc;
|
||||
SetLength(AppliedPatch.Bytes, p.Size);
|
||||
Utils.CopyMem(p.Size, OrigFunc, @AppliedPatch.Bytes[0]);
|
||||
end;
|
||||
|
||||
WritePatchAtCode(p.PatchMaker, OrigFunc);
|
||||
// * * * * * //
|
||||
p.Release;
|
||||
end;
|
||||
|
||||
procedure TAppliedPatch.Rollback;
|
||||
begin
|
||||
if Self.Bytes <> nil then begin
|
||||
WriteAtCode(Length(Self.Bytes), @Self.Bytes[0], Self.Addr);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
@ -5,6 +5,10 @@ Move copyright to single file license?
|
||||
SetCurrentDirectoryW(GetCurrentDirectoryW)
|
||||
System.IsMultiThread for DLL and exported API
|
||||
|
||||
In order to prevent crashing on application exit we can disable logging to console
|
||||
in VFS in ExitProcess, RtlTerminateProcess or LdrShutdownProcess.
|
||||
But maybe source of crashes lies somewhere else.
|
||||
|
||||
(* Trying to turn off DEP *)
|
||||
SetProcessDEPPolicyAddr := Windows.GetProcAddress(Kernel32Handle, 'SetProcessDEPPolicy');
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user