Added support for NtCreate/OpenFile, NtClose and hooks uninstalling

This commit is contained in:
Berserker 2019-05-05 19:44:18 +03:00
parent 360fddaa46
commit 21c4b1df1c
6 changed files with 176 additions and 67 deletions

View File

@ -0,0 +1 @@
It was a pleasure to override you, friend!

View File

@ -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', 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;
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;
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.

View File

@ -530,15 +530,10 @@ begin
try
DisableVfsForThread;
result := Func(Arg);
except
on E: Exception do begin
RestoreVfsForThread;
raise E;
end;
end; // .try
RestoreVfsForThread;
end; // .with
finally
RestoreVfsForThread;
end;
end;
end; // .function CallWithoutVfs
begin

View File

@ -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
RedirectedPath := '\??\' + RedirectedPath;
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;
ReplacedObjAttrs.RootDirectory := 0;
ReplacedObjAttrs.Attributes := ReplacedObjAttrs.Attributes or WinNative.OBJ_CASE_INSENSITIVE;
ReplacedObjAttrs.ObjectName.AssignExistingStr(RedirectedPath);
with VfsOpenFiles.OpenFilesCritSection do begin
Enter;
result := OrigFunc(FileHandle, DesiredAccess, @ReplacedObjAttrs, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
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
VfsOpenFiles.SetOpenedFileInfo(FileHandle^, TOpenedFile.Create(FileHandle^, ExpandedPath));
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.

View File

@ -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.

View File

@ -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');