This forum uses cookies. Click X button to hide this message. What is stored? / Privacy
Total Commander Forum Index Total Commander
Forum - Public Discussion and Support
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Crash on VirtualPC
Goto page Previous  1, 2
 
Post new topic   Reply to topic    Total Commander Forum Index -> TC8.0x bug reports (English) Printable version
View previous topic :: View next topic  
Author Message
MarcinW
Power Member
Power Member


Joined: 23 Jan 2012
Posts: 841
Location: Poland

PostPosted: Tue Jan 07, 2014 8:09 am    Post subject: Reply with quote

Well, this function was created specifically for checking version of mrxvpc.sys, so all the code is required. The algorithm is:

Initialization:

1) We check for existence of GetSystemWindowsDirectoryA API function.
2) We get the system Windows folder with GetSystemDirectoryA (in most cases: C:\WINDOWS\System32).
3) We get the root Windows folder with GetSystemWindowsDirectoryA / GetWindowsDirectoryA (in most cases: C:\WINDOWS).
4) We get the drive letter of Windows partition.

We will use all this information in the main loop. Main loop enumerates all drivers that are loaded into memory and searches for mrxvpc.sys. Required APIs for driver enumeration are located in PSAPI.dll:

5) We load PSAPI.dll to ensure, that it will be loaded into address space of our process - so next calls to PSAPI functions will be successful (these calls are in fact wrappers around LoadLibrary('PSAPI.dll') and GetProcAddress).
6) We get the size of required memory buffer with EnumDeviceDrivers(nil,0,...).
7) We allocate the memory buffer with GetMem and call EnumDeviceDrivers again to get the information about all drivers.
8) The main loop ("for"): we call GetDeviceDriverBaseNameA and check with AnsiCompareFileName if it is mrxvpc.sys.

If we found mrxvpc.sys, there is only one more thing to do: check its version. We can't do this directly, by reading its memory, because it exists in kernel memory range, so it's unavailable for us. So we must use GetFileVersion API with a correct path. But we can get a driver file name only with GetDeviceDriverFileNameA, and it may return the file name in various formats. Driver file name may start with '\??\', '\SystemRoot\' or '\'. So we use information from steps 1)..4) to convert these kernel mode paths into a file system path and finally call GetFileVersion.
Back to top
View user's profile Send private message Send e-mail
ghisler(Author)
Site Admin
Site Admin


Joined: 04 Feb 2003
Posts: 35761
Location: Switzerland

PostPosted: Tue Jan 07, 2014 11:06 am    Post subject: Reply with quote

Ah, sorry, I wasn't clear - I just use this code:

Code:
  S:=SystemDirectory+'Drivers\mrxvpc.sys';
  Version:=GetFileVersion(S);
  if Version=high(Version) then
    exit;      {driver not found}
  Result:=Version < $000D0334;


Why do all this enumeration stuff instead of just checking the file directly?
_________________
Author of Total Commander
http://www.ghisler.com
Back to top
View user's profile Send private message Send e-mail Visit poster's website
MarcinW
Power Member
Power Member


Joined: 23 Jan 2012
Posts: 841
Location: Poland

PostPosted: Tue Jan 07, 2014 5:02 pm    Post subject: Reply with quote

The enumeration returns information about loaded drivers, so we check if mrxvpc.sys is currently loaded. It also maps a driver name (GetDeviceDriverBaseNameA) to its file name (GetDeviceDriverFileNameA). Without using enumeration we can check version of the driver file with GetFileVersion, but we can't determine if it is currently loaded - for example it may be not loaded when the operating system has been booted in safe mode. And enumeration also returns a path to the driver file (although in kernel-mode namespace) - so the "S:=SystemDirectory+'Drivers\mrxvpc.sys'" clause is in fact the "last resort" clause and it will never be called in practice - it would be called only when the driver information in system registry had been damaged.

And the enumeration shouldn't slow down the application in any visible manner. So the designed APIs for getting driver information should be preferred, and hardcoded paths as "S:=SystemDirectory+'Drivers\mrxvpc.sys'" should be used only for "last resort" cases.
Back to top
View user's profile Send private message Send e-mail
ghisler(Author)
Site Admin
Site Admin


Joined: 04 Feb 2003
Posts: 35761
Location: Switzerland

PostPosted: Wed Jan 08, 2014 10:22 am    Post subject: Reply with quote

My reasoning is that the file itself is only there when inside of VirtualPC. If it is, and it is the older problematic version, then I hook the functions. In the rare case where the dll is there but not loaded (safe mode), it doesn't do any harm to hook the functions too.
_________________
Author of Total Commander
http://www.ghisler.com
Back to top
View user's profile Send private message Send e-mail Visit poster's website
MarcinW
Power Member
Power Member


Joined: 23 Jan 2012
Posts: 841
Location: Poland

PostPosted: Sun Jan 12, 2014 8:13 am    Post subject: Reply with quote

The function we are talking about has name IsBuggyVirtualPCDriverLoaded, so it checks if the buggy driver is currently loaded. If you prefer just to check if the buggy driver is installed, the function will be in fact much simpler:

Code:
{$IFNDEF WIN32}
function IsBuggyVirtualPCDriverInstalled : Boolean;
begin
  Result:=False;
end;
{$ELSE}
function IsBuggyVirtualPCDriverInstalled : Boolean;

  function GetFileVersion(const FileName : AnsiString) : Cardinal;
  var
    FileNameCopy : AnsiString;
    VersionInfoSize : Cardinal;
    Temp : UINT;
    Buffer : Pointer;
    VSFixedFileInfo : PVSFixedFileInfo;
  begin
    Result:=High(Result);

    {GetFileVersionInfoA/W may modify the filename parameter,
     so we create a local, writable copy of FileName}
    FileNameCopy:=FileName;
    UniqueString(FileNameCopy);

    VersionInfoSize:=GetFileVersionInfoSizeA(PAnsiChar(FileNameCopy),Temp);
    if VersionInfoSize > 0 then
    begin
      GetMem(Buffer,VersionInfoSize);
      try
        if GetFileVersionInfoA(PAnsiChar(FileNameCopy),0,VersionInfoSize,Buffer) then
        if VerQueryValueA(Buffer,'\',Pointer(VSFixedFileInfo),Temp) then
        if VSFixedFileInfo <> nil then
          Result:=VSFixedFileInfo^.dwFileVersionMS;
      finally
        FreeMem(Buffer);
      end;
    end;
  end;

{$IFDEF VER90} {Delphi 2}
  {In all places of the IncludeTrailingBackslash usage, input string comes from
   GetSystemDirectoryA call. This function always returns path without a backslash
   at the end, unless the result points to the root directory. So we can omit
   MBCS problems by checking the string length instead of checking if the last
   char is a backslash.}
  function IncludeTrailingBackslash(const S : AnsiString) : AnsiString;
  begin
    if Length(S) > 3 then
      Result:=S+'\'
    else
      Result:=S;
  end;
{$ENDIF}

var
  Len : UINT;
  SystemDirectory : AnsiString;
begin
  Result:=False;

  if Win32Platform <> VER_PLATFORM_WIN32_NT then
    Exit;

  SetLength(SystemDirectory,GetSystemDirectoryA(nil,0));
  Len:=GetSystemDirectoryA(PAnsiChar(SystemDirectory),Length(SystemDirectory));
  if (Len = 0) or (Len > UINT(Length(SystemDirectory))) then
    Exit;
  SetLength(SystemDirectory,Len);
  SystemDirectory:=AnsiString(IncludeTrailingBackslash(string(SystemDirectory)));

  {"Drivers" subdirectory name is hardcoded in setupapi.dll (in some Windows versions)
   and in ntoskrnl.exe (in all Windows versions, including 64-bit - ntoskrnl.exe loads
   drivers during booting)}
  Result:=GetFileVersion(SystemDirectory+'Drivers\mrxvpc.sys') < $000D0334;
end;
{$ENDIF}
Back to top
View user's profile Send private message Send e-mail
MarcinW
Power Member
Power Member


Joined: 23 Jan 2012
Posts: 841
Location: Poland

PostPosted: Sat Feb 08, 2014 11:57 am    Post subject: Reply with quote

Recently, I reviewed my code - I was searching for certain kinds of bugs and also introduced some improvements (in particular, the goal was to make the code 64-bit compatible). So I also reviewed the code for VirtualPC patch and slightly updated it in my first post - below is a detailed list of changes.


1) Checking for existence of buggy driver (function IsBuggyVirtualPCDriverLoaded):
Code:
  TPointerArray = array[0..High(Integer) div SizeOf(Pointer)-1] of Pointer;
=>
  TPointerArray = packed array[0..High(Integer) div SizeOf(Pointer)-1] of Pointer;
This is to strictly follow the EnumDeviceDrivers API specification and avoid potential problems with compilers other than Delphi.

Code:
  PSAPIHandle : HMODULE;
  Buffer : array[0..MAX_COMPUTERNAME_LENGTH+MAX_PATH] of AnsiChar;
=>
  PSAPIHandle : HINST;
  Buffer : packed array[0..$8000-1] of AnsiChar;
PSAPIHandle definition was changed just for consistence - to use same types for same things everywhere (HINST and HMODULE are fully compatible each other). Buffer definition was changed to be on the safe side - we use it to get paths from NT kernel, which can be up to $7FFF chars + terminating null char = $8000 chars long.

Code:
  GetSystemWindowsDirectoryA:=GetProcAddress(GetModuleHandle(kernel32),'GetSystemWindowsDirectoryA');
=>
  GetSystemWindowsDirectoryA:=GetProcAddress(GetModuleHandle(kernel32),PAnsiChar('GetSystemWindowsDirectoryA'));
and
Code:
    GetSystemWindowsDirectoryA:=GetProcAddress(GetModuleHandle(kernel32),'GetWindowsDirectoryA');
=>
    GetSystemWindowsDirectoryA:=GetProcAddress(GetModuleHandle(kernel32),PAnsiChar('GetWindowsDirectoryA'));
These are optimizations for Delphi 2009 and up, which use unicode strings by default. GetProcAddress is a very unusual function - it has only an ANSI version named just 'GetProcAddress', there are no 'GetProcAddressA' and 'GetProcAddressW' functions. But unicode Delphi uses a unicode string as an input parameter, and then it implements a wrapper around GetProcAddress to convert this unicode string to ANSI string. By using PAnsiChar('...') typecast, we force the compiler to use an ANSI string directly, so the compiler can call Windows API directly, without any wrappers and string conversions.



2) Checking if drive is being shared by VirtualPC (function IsDriveSharedOnNTByVirtualPC):
Code:
  Module : HMODULE;
=>
  Module : HINST;
Reason as above.

Code:
  NtOpenSymbolicLinkObject:=GetProcAddress(Module,'NtOpenSymbolicLinkObject');
=>
  NtOpenSymbolicLinkObject:=GetProcAddress(Module,PAnsiChar('NtOpenSymbolicLinkObject'));
and
Code:
  NtQuerySymbolicLinkObject:=GetProcAddress(Module,'NtQuerySymbolicLinkObject');
=>
  NtQuerySymbolicLinkObject:=GetProcAddress(Module,PAnsiChar('NtQuerySymbolicLinkObject'));
Reason as above.



3) PSAPI.pas:
Code:
  hPSAPI: THandle;
=>
  hPSAPI: HINST;
THandle is not a valid type for module instances.

Code:
    @_EnumDeviceDrivers := GetProcAddress(hPSAPI, 'EnumDeviceDrivers');
    @_GetDeviceDriverBaseNameA := GetProcAddress(hPSAPI, 'GetDeviceDriverBaseNameA');
    @_GetDeviceDriverFileNameA := GetProcAddress(hPSAPI, 'GetDeviceDriverFileNameA');
=>
    @_EnumDeviceDrivers := GetProcAddress(hPSAPI, PAnsiChar('EnumDeviceDrivers'));
    @_GetDeviceDriverBaseNameA := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverBaseNameA'));
    @_GetDeviceDriverFileNameA := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverFileNameA'));
Reason as above.



The optimization with GetProcAddress + PAnsiChar could also be used in other code in this thread: unit VirtualPCPatch.pas, program Loader.dpr, unit HookCreateFile.pas.

Regards
Back to top
View user's profile Send private message Send e-mail
ghisler(Author)
Site Admin
Site Admin


Joined: 04 Feb 2003
Posts: 35761
Location: Switzerland

PostPosted: Sun Feb 09, 2014 8:41 am    Post subject: Reply with quote

Thanks, but I don't use 1, and 2+3 are not relevant for Delphi 2.
_________________
Author of Total Commander
http://www.ghisler.com
Back to top
View user's profile Send private message Send e-mail Visit poster's website
MarcinW
Power Member
Power Member


Joined: 23 Jan 2012
Posts: 841
Location: Poland

PostPosted: Mon Feb 10, 2014 8:02 am    Post subject: Reply with quote

This trick with GetProcAddress + PAnsiChar is in fact not a big optimization, but I suppose it may also be useful for 64-bit Lazarus.
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    Total Commander Forum Index -> TC8.0x bug reports (English) All times are GMT - 6 Hours
Goto page Previous  1, 2
Page 2 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Imprint/Impressum: This site is maintained by Ghisler Software GmbH
Privacy Policy | Datenschutzerklärung | Politique de Confidentialité

Using phpBB © phpBB Group