Jump to content
Mark-

Unit uDGVMUtils and 64 bit...

Recommended Posts

Hello,

 

Has anyone converted this unit to work on 64 bit and 32 bit?

 

Thanks,

 

Mark

unit uDGVMUtils;

interface

(*******************************************************************************

  uDGVMUtils -- is an attempt to create one of the best virtual machine
    detector methods, feel free to contribute in any way you wish.

  Version 1.1, 2010-01-15

  Copyright© you are free to use it for comercial, private or both purposes

  Contributors:
    Dorin Duminica
    Chee Meng

*******************************************************************************)

type
  TVMWareVersion = (
    vvExpress = 1,
    vvESX,
    vvGSX,
    vvWorkstation,
    vvUnknown,
    vvNative);

const
  VMWARE_VERSION_STRINGS: array [TVMWareVersion] of string = (
    'Express',
    'ESX',
    'GSX',
    'Workstation',
    'Unknown',
    'Native');

type
  TVirtualMachineType = (
    vmNative,
    vmVMWare,
    vmWine,
    vmVirtualPC,
    vmVirtualBox);

const
  VIRTUALMACHINE_STRINGS: array[TVirtualMachineType] of string = (
    'Native',
    'VMWare',
    'Wine',
    'Virtual PC',
    'Virtual Box');

function IsRunningVMWare(var AVMWareVersion: TVMWareVersion): Boolean; overload;
function IsRunningVMWare: Boolean; overload;
function IsRunningWine(var AWineVersion: string): Boolean; overload;
function IsRunningWine: Boolean; overload;
function IsRunningVirtualPC: Boolean;
function IsRunningVBox: Boolean;
function IsRunningVM(var AVMVersion: string): Boolean; overload;
function IsRunningVM: Boolean; overload;
implementation

uses SysUtils,Windows,Vcl.Dialogs;

function IsRunningVMWare(var AVMWareVersion: TVMWareVersion): Boolean;
const
  CVMWARE_FLAG = $564D5868;
var
  LFlag: Cardinal;
  LVersion: Cardinal;
begin
  LFlag := 0;
  try
    asm
      push eax
      push ebx
      push ecx
      push edx

      mov eax, 'VMXh'
      mov ecx, 0Ah
      mov dx, 'VX'

      in eax, dx

      mov LFlag, ebx
      mov LVersion, ecx

      pop edx
      pop ecx
      pop ebx
      pop eax
    end;
  except
//  uncomment next two lines if you wish to see exception
//    on E: Exception do
//      ShowMessage(E.message);
  end; // trye
  if LFlag = CVMWARE_FLAG then begin
    Result := True;
    case LVersion of
      1: AVMWareVersion := vvExpress;
      2: AVMWareVersion := vvESX;
      3: AVMWareVersion := vvGSX;
      4: AVMWareVersion := vvWorkstation;
      else
        AVMWareVersion := vvUnknown;
    end
  end else begin
    Result := False;
    AVMWareVersion := vvNative;
  end; // if LFlag = CVMWARE_FLAG then begin
end;

function IsRunningVMWare: Boolean;
var
  LVMWareVersion: TVMWareVersion;
begin
  Result := IsRunningVMWare(LVMWareVersion);
end;

function IsRunningWine(var AWineVersion: string): Boolean;
type
  TWineGetVersion = function: PAnsiChar;{$IFDEF Win32}stdcall;{$ENDIF}
  TWineNTToUnixFileName = procedure (P1: Pointer; P2: Pointer);{$IFDEF Win32}stdcall;{$ENDIF}
var
  LHandle: THandle;
  LWineGetVersion: TWineGetVersion;
  LWineNTToUnixFileName: TWineNTToUnixFileName;
begin
  Result := False;
  AWineVersion := 'Unknown';
  LHandle := LoadLibrary('ntdll.dll');
  if LHandle > 32 then begin
    LWineGetVersion := GetProcAddress(LHandle, 'wine_get_version');
    LWineNTToUnixFileName := GetProcAddress(LHandle, 'wine_nt_to_unix_file_name');
    if Assigned(LWineGetVersion) or Assigned(LWineNTToUnixFileName) then begin
      Result := True;
      if Assigned(LWineGetVersion) then
        AWineVersion := ANSIString(LWineGetVersion);
//        AWineVersion := StrPas(LWineGetVersion);
    end; // if Assigned(LWineGetVersion) or ...
    FreeLibrary(LHandle);
  end; // if LHandle > 32 then begin
end;

function IsRunningWine: Boolean;
var
  LWineVersion: string;
begin
  Result := IsRunningWine(LWineVersion);
end;

function IsRunningVirtualPC: Boolean;
 asm
  push ebp;
  mov ebp, esp;

  mov ecx, offset @exception_handler;

  push ebx;
  push ecx;

  push dword ptr fs:[0];
  mov dword ptr fs:[0], esp;

  mov ebx, 0; // Flag
  mov eax, 1; // VPC function number

//   call VPC
  db $0F, $3F, $07, $0B

  mov eax, dword ptr ss:[esp];
  mov dword ptr fs:[0], eax;

  add esp, 8;

  test ebx, ebx;

  setz al;

  lea esp, dword ptr ss:[ebp-4];
  mov ebx, dword ptr ss:[esp];
  mov ebp, dword ptr ss:[esp+4];

  add esp, 8;

  jmp @ret1;

  @exception_handler:
  mov ecx, [esp+0Ch];
  mov dword ptr [ecx+0A4h], -1; // EBX = -1 ->; not running, ebx = 0 -> running
  add dword ptr [ecx+0B8h], 4; // ->; skip past the call to VPC
  xor eax, eax; // exception is handled

  @ret1:
end;

function IsRunningVBox: Boolean;

  function Test1: Boolean;
  var
    LHandle: Cardinal;
  begin
    Result := False;
    try
      LHandle := LoadLibrary('VBoxHook.dll');
      Result := (LHandle <> 0);
      if Result then
        FreeLibrary(LHandle);
    except
    end; // trye
  end; // function Test1: Boolean;

  function Test2: Boolean;
  var
    LHandle: Cardinal;
  begin
    Result := False;
    try
      LHandle := CreateFile(
        '\\\\.\\\VBoxMiniRdrDN',
        GENERIC_READ,
        FILE_SHARE_READ,
        NIL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0);
      Result := (LHandle <> INVALID_HANDLE_VALUE);
      if Result then
        CloseHandle(LHandle);
    except
    end; // trye
  end; // function Test2: Boolean;

begin
  Result := Test1 or Test2;
end;

function IsRunningVM(var AVMVersion: string): Boolean;
begin
  AVMVersion := VIRTUALMACHINE_STRINGS[vmNative];
  Result := True;
  if IsRunningWine then
    AVMVersion := VIRTUALMACHINE_STRINGS[vmWine]
  else
    if IsRunningVMWare then
      AVMVersion := VIRTUALMACHINE_STRINGS[vmVMWare]
    else
      if IsRunningVirtualPC then
        AVMVersion := VIRTUALMACHINE_STRINGS[vmWine]
      else
        if IsRunningVBox then
          AVMVersion := VIRTUALMACHINE_STRINGS[vmVirtualBox]
        else begin
          AVMVersion := VIRTUALMACHINE_STRINGS[vmNative];
          Result := False;
        end;
end;

function IsRunningVM: Boolean;
var
  LVMVersion: string;
begin
  Result := IsRunningVM(LVMVersion);
end;
end.

 

Share this post


Link to post

Thanks for the reply.

 

The issue with 64 bit support is the "asm" portions for two reasons.

64 bit assembly uses different opcodes from 32 bit and different register notation.

Delphi does not allow the mixing of asm and Pascal in 64 bit.

 

Share this post


Link to post

Why not get the BIOS description string? 

The kind of Virtual machine is clearly available there.

 

Just read in the registry:  HKEY_LOCAL_MACHINE\Hardware\Description\System\BIOS 

This is how we do in our Open Source mORMot framework:

  with TRegistry.Create do
  try
    RootKey := HKEY_LOCAL_MACHINE;
    if OpenKeyReadOnly('\Hardware\Description\System\CentralProcessor\0') then begin
      cpu := ReadString('ProcessorNameString');
      if cpu='' then
        cpu := ReadString('Identifier');
    end;
    if OpenKeyReadOnly('\Hardware\Description\System\BIOS') then begin
      manuf := SysUtils.Trim(ReadString('SystemManufacturer'));
      if manuf<>'' then
        manuf := manuf+' ';
      prod := SysUtils.Trim(ReadString('SystemProductName'));
      prodver := SysUtils.Trim(ReadString('SystemVersion'));
      if prodver='' then
        prodver := SysUtils.Trim(ReadString('BIOSVersion'));
      if OpenKeyReadOnly('\Hardware\Description\System') then begin
        if prod='' then
          prod := SysUtils.Trim(ReadString('SystemBiosVersion'));
        if prodver='' then begin
          prodver := SysUtils.Trim(ReadString('VideoBiosVersion'));
          i := Pos(#13,prodver);
          if i>0 then // e.g. multilines 'Oracle VM VirtualBox Version 5.2.33'
            SetLength(prodver,i-1);
        end;
      end;
      if prodver<>'' then
        FormatUTF8('%% %',[manuf,prod,prodver],BiosInfoText) else
        FormatUTF8('%%',[manuf,prod],BiosInfoText);
    end;
  finally
    Free;
  end;

See https://synopse.info/fossil/finfo?name=SynCommons.pas

Edited by Arnaud Bouchez

Share this post


Link to post
7 minutes ago, Arnaud Bouchez said:

Why not get the BIOS description string? 

The kind of Virtual machine is clearly available there.

 

Thanks for the response.

Why do you think/know those registry values cannot be changed without causing a failure of the the guest OS or host program?

Share this post


Link to post

They could be changed, of course.

 

To be honnest, if you expect to defeat hackers you most probably will loose your time - especially if you don't know how to convert i386 asm to x64.

Even the asm trick used in this code could be disabled - see https://www.gta.ufrj.br/ensino/CPE758/artigos-basicos/carpenter07.pdf and http://www.trapkit.de/tools/scoopyng/SecureVMX.txt just after a quick Google Search.

 

And BTW Wine is not a virtual machine, I don't understand why it is part of this detection.

Edited by Arnaud Bouchez
  • Like 2

Share this post


Link to post

Code can be made compatible with 64-bit, i have posted probable solution here:

https://stackoverflow.com/a/61874765/3225668

 

NOTE: in FastcodeCPUID.pas there is an error with position of registers when returning value. Registers in code goes like Move...EBX...EDX...ECX... Proper way should be EBX...ECX...EDX...!

Share this post


Link to post
On 12/6/2019 at 6:44 PM, Arnaud Bouchez said:

To be honnest, if you expect to defeat hackers you most probably will loose your time - especially if you don't know how to convert i386 asm to x64.

Even the asm trick used in this code could be disabled - see https://www.gta.ufrj.br/ensino/CPE758/artigos-basicos/carpenter07.pdf and http://www.trapkit.de/tools/scoopyng/SecureVMX.txt just after a quick Google Search.

Exactly, and i want to add this, if detecting VM running your application is critical for you, then you would better use exe packer/protector to have better chance to handle this, after all this is like never ending process, better solution to detect VM's always appear then followed by better way to hide the VM's.

 

QEMU is the one which is the hardest to detect, yet there is papers and articles on how to detect it, like this one https://publik.tuwien.ac.at/files/pub-inf_5317.pdf , but if you are asking on how to convert 32bit assembly to 64bit, then that paper is not your cup of tea, right ?

 

That lead to this simple logic, how much is it important for you to detect VM ? if it is critical then go with specialized software ( protectors ) because the team that work on such software do have the resources and experince needed for such task, this will save you time and fraustration, on other hand if this is not so much critical then you can use half baked library like the one you posted up, which is way easier than you think to defeat, eg. with Cheat Engine ( simple memory search and edit)

 

And by half baked, i meant it is clearly outdated (2010 or much earlier) methods, you yourself should test it on each VM system (current and modern), as no point to trust software depends on undocumented code to sill working right after all those years.

  • Sad 1

Share this post


Link to post
1 hour ago, davornik said:

Code can be made compatible with 64-bit, i have posted probable solution here:

https://stackoverflow.com/a/61874765/3225668

 

NOTE: in FastcodeCPUID.pas there is an error with position of registers when returning value. Registers in code goes like Move...EBX...EDX...ECX... Proper way should be EBX...ECX...EDX...!

 

Thanks

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×