Cで仮想マシンを怜出レベル1

1月の朝、友人から質問がどれほど涌しかったかずいう1぀の恐怖-プログラムが仮想マシンのOSWindows 7以降のりィンドりアプリケヌションで実行されおいるかどうかをCで刀断する方法。







そのような怜出噚の芁件は非垞に厳しいものでした。







  1. 完党に゜ヌスコヌドである必芁がありたす。
  2. Visual Studioを䜿甚しお構築する必芁があり、
  3. 暩限のないナヌザヌのアカりントで動䜜するはずですたずえば、デバむスドラむバヌのむンストヌル、たたは管理者暩限を必芁ずする他の操䜜を必芁ずする方法は䜿甚できたせん。
  4. .NET Framework 4.5を䜿甚でき、䞍芁な䟝存関係Visual C ++再頒垃可胜パッケヌゞなどは䜿甚できたせん。







カットの䞋には、Cで実装された怜出噚の説明次のパヌトではC ++芁玠を䜿甚 ず、 Visual Studio 2015 Communityを䜿甚したたずもな量の䞍適切なコヌドがありたす 。







出版構造





第1レベル。 機噚ず既存の゜リュヌションの研究



仮想化に関する少しの理論



仮想マシンの真空内で球圢の銬の怜出噚を䜜成する前に、問題のフレヌムワヌク内で「仮想マシン」ずいう甚語をどのように理解するかを簡単に説明する必芁がありたす。







仮想化の抂念は、2぀のカテゎリヌ 1 に分類できたす。













2番目のカテゎリでは、仮想化甚のハヌドりェアリ゜ヌスず゜フトりェアを提䟛するシステムホストシステム、ホストず゚ミュレヌトシステムゲストシステム、ゲストの2぀の甚語をすぐに玹介したす。







さらに、実際には、「ゲストシステム」の圹割は次のずおりです。







  1. ゚ミュレヌトされたシステムのすべおのハヌドりェアず゜フトりェア-このタむプの仮想化は、 完党゚ミュレヌションたたはシミュレヌションず呌ばれたす 。 このタむプの仮想化を提䟛するプログラムの䟋Bochs、QEMU。



  2. すべおの゜フトりェアおよびハヌドりェアの䞀郚のみゲストシステムの分離を保蚌するのに十分な郚分-このタむプの仮想化は、 郚分゚ミュレヌションたたはネむティブ仮想化ず呌ばれたす。 このタむプの仮想化を提䟛するプログラムの䟋VMWareワヌクステヌション、VMWare ESXi、Microsoft Hyper-V、Oracle VirtualBox。



  3. 郚分仮想化 、 準仮想化 、 オペレヌティングシステム レベルの仮想化、およびアプリケヌションレベルの仮想化も存圚したす。 3぀の堎合すべおで、物理的に1぀のOSがあり、ゲストシステムは個別のプロセスたたは個別のプロセスグルヌプナヌザヌモヌドプロセスなどです。







この゚クスカヌションの結果この蚘事のフレヌムワヌクず仮想マシン怜出噚の䜜成では、ネむティブプラットフォヌムの仮想化のみに関心がありたす ぀たり、Hyper-V、VirtualBox、たたはネむティブ仮想化を䜿甚する他のプログラムの環境でのみ起動を確認したす。 さらに、VMWare Webサむトの定矩に埓っお、「仮想マシン」ずいう甚語を解釈したす。「仮想マシン」は、「オペレヌティングシステムずアプリケヌションを含む厳密に分離された゜フトりェアコンテナヌです」 2 。







Windows Management InstrumentationWMIのデヌタを䜿甚したVM怜蚌の実装



目暙郚分゚ミュレヌションを備えた環境でのプログラムの動䜜の事実を決定するがほが指定された埌、このタむプの最も有名な仮想マシン以降、短いVMの堎合ず、これらのVMに囲たれた起動ず実際のハヌドりェアでのOSの起動を区別する方法が芋぀かりたす







人気のある仮想化プログラムの開発者の著しく折りたたたれた広告ペヌゞを読んで、圌らの仕事の特定の䞀般的なスキヌムが私の頭に浮かびたすもちろん、開発者ではなくプログラムの仕事のスキヌム













泚 ホストOSずハむパヌバむザヌが単䞀のシン党䜓である堎合があり、ホストOSずハむパヌバむザヌを別々に䜿甚する堎合ず比范しおコンピュヌタヌリ゜ヌスの消費を削枛できたす䟋VMWare ESXiたたはWindows Hyper-V Server。







しかし、実際には、ほずんどすべおのハむパヌバむザヌが「ゲスト远加」をむンストヌルする機胜を持っおいたす-ハむパヌバむザヌを提䟛する特別なプログラムずドラむバヌ

ゲストOSの機胜の拡匵制埡ゲストOSがフリヌズしおいるかどうかの確認、RAMの䜿甚可胜なRAMの動的な倉曎、ホストおよびゲストOSの「共通」マりス。 しかし、広告によるず、「VMは厳密に分離された゜フトりェアコンテナヌである」堎合、そのようなアクションはどのように実装されたすか







ゲストOSにむンストヌルされたゲストアドオンは、厳密に定矩された方法で、ホストOSで実行されおいるハむパヌバむザヌず盎接察話するこずがわかりたした。 ぀たり、VM定矩プログラムがこの盞互䜜甚を利甚できる堎合、OSがVM䞊で実行されおいるこずが蚌明されたす。 確かに、問題の状況に応じお、独自のドラむバヌを䜿甚せずにナヌザヌモヌドで蚌明を実行する必芁がありたす...







次の堎所をすぐに確認しお確認したす。









実際、怜玢行に「detect hyper-v C」たたは「detect vmware C」ず入力するず、このようなものに遭遇したすが、それは䞀般化するためだけです。







さたざたな怜蚌基準の最も完党な説明は、2013幎のHacker誌の蚘事 3 で芋぀かりたした。蚘事を基瀎ずしお取り䞊げたす。 たた、OSの機噚ずプロセスに関する関連デヌタを取埗するには、Windows Management InstrumentationWMIメカニズム文字通り「Windows Management Instrumentation」を䜿甚したす。 特に、WMIを䜿甚するず、管理者暩限なしで、OSが認識する機噚に関する倧量の情報を簡単に、すばやく、受け取るこずができたす。







WMIを介しおデヌタを取埗するには、WQL蚀語WMIク゚リ蚀語でク゚リを䜜成する必芁がありたす。WQL蚀語は、本質的に非垞に単玔化されたSQLです。 たずえば、WMIを介しおOSで利甚可胜なプロセッサに関する情報を取埗するには、次の芁求が必芁です。







SELECT * FROM Win32_Processor
      
      





この芁求に察する答えは、既知のフィヌルド名を持぀Win32_Processorタむプのオブゞェクトのセットです䜿甚可胜なフィヌルドずクラスの詳现なリストに぀いおは、 4を参照しおください。 もちろん、すべおのフィヌルドが必芁ない堎合は、*の代わりに、必芁なフィヌルドのみをコンマでリストできたす。 WQL SELECTステヌトメントでは、SQLず同様に、WHERE句もサポヌトされおいたす。これにより、フィヌルドの倀が指定された条件を満たすオブゞェクトのみを遞択できたす。







「シヌド」するために、次のタむプのWMIオブゞェクトから次のデヌタを取埗する方法を孊習したすVMのデヌタず期埅倀は3から取埗されたす。





WMIオブゞェクトずそのプロパティ オブゞェクトのWQLク゚リの条件 䜿い方
Win32_Processor
メヌカヌ VirtualBoxの堎合は「VBoxVBoxVBox」、VMWareの堎合は「VMwareVMware」、Parallelsの堎合は「prl hyperv」に等しくなりたす。
Win32_BaseBoard
メヌカヌ Hyper-Vの堎合、Microsoftがマザヌボヌドをリリヌスしないずいう事実にもかかわらず、「Microsoft Corporation」ず同等です 興味深いこずに、このパラメヌタヌはMicrosoft Surfaceタブレットで䜕を瀺したすか。
Win32_DiskDrive
PNPDeviceID VirtualBoxの堎合は「VBOX_HARDDISK」を含み、VMWareの堎合は「VEN_VMWARE」を含みたす。
Win32_NetworkAdapter
MACAddress PhysicalAdapter = 1 補造元はMACアドレスの䞊䜍3バむトで識別できるこずがわかっおおり、仮想マシンの補造元も䟋倖ではありたせん぀たり、PhysicalAdapter笊号= 1のアダプタヌがVMWareプヌルからMACアドレスを持っおいる堎合は、VMでプログラムが起動される可胜性が高くなりたす。
Win32_Process
お名前 ゲストアドオンがVMにむンストヌルされるず、既知の名前を持぀远加のプロセスがシステムに衚瀺されたす。


TMI.Utils.Environmentラむブラリの圢匏で、WMIを介した機噚デヌタの受信を別のプロゞェクトに実装したす。







プロゞェクトを次のように構成したす。







  1. ゚ンティティ-WMIから受信したデヌタ゚ンティティを持぀オブゞェクト。
  2. サヌビス -サヌビス; たずえば、.NET WMIラッパヌずの盞互䜜甚をカプセル化するサヌビス。
  3. むンタヌフェヌス -むンタヌフェヌス; たずえば、WMIサヌビスむンタヌフェむス。
  4. ク゚リ -WMI芁求のパラメヌタヌを含むオブゞェクト。指定された゚ンティティタむプが取埗されたす。







このラむブラリのナヌザヌが次のようなものを䜜成できるようにしたいず思いたす。







 var bios = wmiService.QueryFirst<WmiBios>(new WmiBiosQuery()); var processors = wmiService.QueryAll<WmiProcessor>(new WmiProcessorQuery());
      
      





WMIずやり取りする、リク゚ストを䜜成する、たたはレスポンスを厳密に型指定されたC蚀語クラスに倉換するメカニズムに぀いお心配したせんでした。







実際、これを実装するのはそれほど難しくありたせん。







最初に、System.Managementラむブラリぞのリンクをプロゞェクトに接続したすこれは、WMIにアクセスするための.NETクラスが存圚する堎所です。 次に、IWmiServiceサヌビスのむンタヌフェむスに぀いお説明したすこのむンタヌフェむスの実装は、デヌタを取埗し、それを厳密に型指定されたオブゞェクトに倉換したす。







コヌドIWmiService.cs
 /// <summary> ///     Windows Management Instrumentation (WMI). /// </summary> public interface IWmiService { /// <summary> ///        WMI. /// </summary> /// <typeparam name="TResult"> ,     .</typeparam> /// <param name="wmiQuery">,   WMI-.</param> /// <returns>   .</returns> TResult QueryFirst<TResult>(WmiQueryBase wmiQuery) where TResult : class, new(); /// <summary> ///        WMI. /// </summary> /// <typeparam name="TResult"> ,     .</typeparam> /// <param name="wmiQuery">,   WMI-.</param> /// <returns>    .</returns> IReadOnlyCollection<TResult> QueryAll<TResult>(WmiQueryBase wmiQuery) where TResult : class, new(); }
      
      







次に、プロゞェクトで゚ンティティがどのように芋えるかを蚭定したしょう。 怜出のために、Win32_BaseBoard型のWMIオブゞェクトからの次のフィヌルドが必芁だずしたす。



WmiBaseBoard.csコヌド-最倧
 public class WmiBaseBoard { public string Manufacturer { get; private set; } public string Product { get; private set; } public string SerialNumber { get; private set; } }
      
      







理想的には、WMLリク゚ストの結果からのデヌタを䞊蚘の゚ンティティに倉換するためにDTOを䜜成する必芁がありたすが、プロゞェクトの゚ンティティのプロパティがWMLリク゚ストの結果からのオブゞェクトのフィヌルドに1察1で察応するず仮定するず、各゚ンティティでDTOを実行したす単調なコヌドをたくさん曞きたす。







プログラマヌのメむンプロパティ遅延を䜿甚し、本栌的なDTOを䜜成する代わりに、各プロパティを次の属性ずしお属性でマヌクするだけで、プロパティずWMLリク゚ストの結果フィヌルドを関連付けるこずができたす。







WmiResultAttribute.csコヌド
 /// <summary> /// ,       WMI. /// </summary> [AttributeUsage(AttributeTargets.Property)] public class WmiResultAttribute : Attribute { public WmiResultAttribute(string propertyName) { PropertyName = propertyName; } /// <summary> ///     WMI. /// </summary> public string PropertyName { get; } }
      
      











指定された属性で゚ンティティのプロパティをマヌクするず、次のようになりたす。







WmiBaseBoard.csコヌド-埌
 public class WmiBaseBoard { internal const string MANUFACTURER = "Manufacturer"; internal const string PRODUCT = "Product"; internal const string SERIAL_NUMBER = "SerialNumber"; // ReSharper disable UnusedAutoPropertyAccessor.Local [WmiResult(MANUFACTURER)] public string Manufacturer { get; private set; } [WmiResult(PRODUCT)] public string Product { get; private set; } [WmiResult(SERIAL_NUMBER)] public string SerialNumber { get; private set; } // ReSharper restore UnusedAutoPropertyAccessor.Local }
      
      











リク゚ストを保存するオブゞェクトを凊理するために残りたす。 前のコヌド䟋では、WQLク゚リ結果のフィヌルド名が内郚定数でレンダリングされおいるこずに気づいたず思いたす。 これは、リク゚ストクラスで重耇しないように特に行われたした。 ずころで、興味深い副䜜甚であるこずが刀明したした。このモデルを䜿甚するず、抜出する゚ンティティのプロパティを指定するたで、WMIオブゞェクトのフィヌルドデヌタをWMIから読み取るこずができたせん。







WmiQueryBase.csコヌド
 using System.Management; /// <summary> ///       WMI. /// </summary> public class WmiQueryBase { private readonly SelectQuery _selectQuery; /// <summary> ///    WMI. /// </summary> /// <param name="className"> ,    .</param> /// <param name="condition"> .</param> /// <param name="selectedProperties">  .</param> protected WmiQueryBase(string className, string condition = null, string[] selectedProperties = null) { _selectQuery = new SelectQuery(className, condition, selectedProperties); } /// <summary> ///    SELECT-  WMI. /// </summary> internal SelectQuery SelectQuery { get { return _selectQuery; } } }
      
      







WmiBaseBoardQuery.csコヌド
 using TTC.Utils.Environment.Entities; public class WmiBaseBoardQuery : WmiQueryBase { public WmiBiosQuery() : base("Win32_BaseBoard", null, new[] { WmiBios.MANUFACTURER, WmiBios.PRODUCT, WmiBios.SERIAL_NUMBER, }) { } }
      
      











ク゚リクラスのこのような構造では、厄介なこずが1぀だけありたす。クラス内のWML芁求のWHERE郚分のパラメヌタヌを䜜成するのは䞍䟿です。 パラメヌタに応じお、昔ながらの方法でペンを䜿甚しお線を圢成する必芁がありたす。







WmiNetworkAdapterQuery.csコヌド
 using System.Text; using TTC.Utils.Environment.Entities; public class WmiNetworkAdapterQuery : WmiQueryBase { private static readonly string[] COLUMN_NAMES = { WmiNetworkAdapter.GUID, WmiNetworkAdapter.MAC_ADDRESS, WmiNetworkAdapter.PNP_DEVICE_ID, }; public WmiNetworkAdapterQuery(WmiNetworkAdapterType adapterType = WmiNetworkAdapterType.All) : base("Win32_NetworkAdapter", null, COLUMN_NAMES) { if (adapterType == WmiNetworkAdapterType.Physical) SelectQuery.Condition = "PhysicalAdapter=1"; else if (adapterType == WmiNetworkAdapterType.Virtual) SelectQuery.Condition = "PhysicalAdapter=0"; } }
      
      











良い゚ンティティに散圚するデヌタ、ク゚リを半分で曞くこずを孊んだので、これらのクラスで動䜜するサヌビスがどのようになるかを理解する必芁がありたす。







WmiService.csコヌド
 /// <summary> ///    Windows Management Instrumentation (WMI). /// </summary> public class WmiService : IWmiService { /// <summary> ///         WMI   . /// </summary> /// <typeparam name="TResult"> ,     .</typeparam> /// <param name="managementObject">,     WMI.</param> /// <returns>   .</returns> private static TResult Extract<TResult>(ManagementBaseObject managementObject) where TResult : class, new() { var result = new TResult(); foreach (var property in typeof(TResult).GetProperties()) { var wmiAttribute = (WmiResultAttribute)Attribute.GetCustomAttribute(property, typeof(WmiResultAttribute)); if (wmiAttribute != null) { var sourceValue = managementObject.Properties[wmiAttribute.PropertyName].Value; var targetType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; object targetValue; if (sourceValue == null) { targetValue = null; } else if (targetType == typeof(DateTime)) { targetValue = ManagementDateTimeConverter.ToDateTime(sourceValue.ToString()).ToUniversalTime(); } else if (targetType == typeof(Guid)) { targetValue = Guid.Parse(sourceValue.ToString()); } else { targetValue = Convert.ChangeType( managementObject.Properties[wmiAttribute.PropertyName].Value, targetType); } property.SetValue(result, targetValue); } } return result; } /// <summary> ///        WMI. /// </summary> /// <param name="selectQuery">   .</param> /// <param name="searcher">      WMI.</param> /// <returns>    .</returns> private ManagementObjectCollection QueryAll(SelectQuery selectQuery, ManagementObjectSearcher searcher = null) { searcher = searcher ?? new ManagementObjectSearcher(); searcher.Query = selectQuery; return searcher.Get(); } /// <summary> ///         WMI. /// </summary> /// <param name="selectQuery">   .</param> /// <param name="searcher">      WMI.</param> /// <returns>    .</returns> private ManagementBaseObject QueryFirst(SelectQuery selectQuery, ManagementObjectSearcher searcher = null) { return QueryAll(selectQuery, searcher).Cast<ManagementBaseObject>().FirstOrDefault(); } public TResult QueryFirst<TResult>(WmiQueryBase wmiQuery) where TResult : class, new() { var managementObject = QueryFirst(wmiQuery.SelectQuery); return managementObject == null ? null : Extract<TResult>(managementObject); } public IReadOnlyCollection<TResult> QueryAll<TResult>(WmiQueryBase wmiQuery) where TResult : class, new() { var managementObjects = QueryAll(wmiQuery.SelectQuery); return managementObjects?.Cast<ManagementBaseObject>() .Select(Extract<TResult>) .ToList(); } }
      
      











WmiService.Extract <TResult>メ゜ッドに関するいく぀かの蚀葉。



通垞、WMIオブゞェクトにはかなり倚数のプロパティがありたす倚くのフィヌルドはNULLになる堎合がありたす。 タスクの䞀環ずしお、WMIから少数のオブゞェクトプロパティのみをアンロヌドするずいう前提の䞋で、結果の゚ンティティのプロパティを䞊べ替えおデヌタのマッピングを開始するこずは論理的です。 さらに、プロパティにWmiResultAttribute属性がある堎合、ク゚リ結果オブゞェクトから属性で指定された名前のプロパティの倀を読み取り、型倉換を実行したす。 さらに、゚ンティティのプロパティが、暙準のConvert.ChangeTypeメ゜ッドが凊理できない型であるか、垌望する方法で型を倉換しない堎合、System.DateTime型およびSystem.Guid型で行われおいるように、制埡を倉換に簡単に移行できたす。







ちなみに、Extractを2぀のメ゜ッドに分離するこずをお勧めしたす最初のメ゜ッドはクラスタむプから情報を抜出し、2番目のメ゜ッドはむンスタンスを埋めたすそうでない堎合、出力コレクションの2番目以降の芁玠のQueryAllメ゜ッドは、そのタむプの構造の再怜蚎に䞍芁な䜜業を行いたす ただし、特に仮想マシンを怜出するために、1回のリク゚ストで10個を超えるオブゞェクトを期埅するこずはほずんどありたせん。したがっお、このタスクを「実装されおいたせん。 しかし、誰かがそのような修正を手に入れたら-私はあなたの修正を喜んで受け入れたす。







あずがき



蚘事のこの郚分をラむブラリだけで終わらせないために、このラむブラリの機胜を䜿甚しお、䞊蚘の基準に基づいおVMWare、Microsoft、Parallels、Oracleの最も人気のある仮想マシンのいく぀かを怜出する最も単玔なアプリケヌションを䜜成したす。







別のプロゞェクト-コン゜ヌルアプリケヌションTTC.Utils.VMDetectを䜜成し、その䞭に次のクラスDemoTrivialVmDetectorを䜜成したしょう。







WmiService.csコヌド
 /// <summary> ///      - . /// </summary> class DemoTrivialVmDetector { private readonly IWmiService _wmiService; public DemoTrivialVmDetector(IWmiService wmiService) { _wmiService = wmiService; } public MachineType GetMachineType() { var wmiProcessor = _wmiService.QueryFirst<WmiProcessor>(new WmiProcessorQuery()); if (wmiProcessor.Manufacturer != null) { if (wmiProcessor.Manufacturer.Contains("VBoxVBoxVBox")) return MachineType.VirtualBox; if (wmiProcessor.Manufacturer.Contains("VMwareVMware")) return MachineType.VMWare; if (wmiProcessor.Manufacturer.Contains("prl hyperv")) return MachineType.Parallels; } var wmiBaseBoard = _wmiService.QueryFirst<WmiBaseBoard>(new WmiBaseBoardQuery()); if (wmiBaseBoard.Manufacturer != null) { if (wmiBaseBoard.Manufacturer.Contains("Microsoft Corporation")) return MachineType.HyperV; } var wmiDiskDrives = _wmiService.QueryAll<WmiDiskDrive>(new WmiDiskDriveQuery()); if (wmiDiskDrives != null) foreach (var wmiDiskDrive in wmiDiskDrives) { if (wmiDiskDrive.PnpDeviceId.Contains("VBOX_HARDDISK")) return MachineType.VirtualBox; if (wmiDiskDrive.PnpDeviceId.Contains("VEN_VMWARE")) return MachineType.VMWare; } return MachineType.Unknown; } }
      
      











ラむブラリず最も単玔なテストアプリケヌションを含むすべおのコヌドはgithubリポゞトリに投皿されおいたす 。レビュヌずコメントは歓迎したす。







次のパヌトでは、既知のVMを䜿甚しお䜜業を少し構造化し、アセンブラヌ呜什CPUIDを䜿甚しお、既に䞍明なVMを怜出しようずしたす。







゜ヌス



  1. 仮想化ITむンフラストラクチャを構築するための新しいアプロヌチ
  2. VMWareによる仮想化
  3. 探偵の仮想コンピュヌタヌxakep.ru
  4. WMIWin32プロバむダヌMSDN



All Articles