アプリケーションから仮想化 OS を検出しますか?
-
03-07-2019 - |
質問
アプリケーションが仮想化された OS インスタンス内で実行されているかどうかを検出する必要があります。
見つけました 記事 このトピックに関する役立つ情報が含まれています。同じ記事が複数の場所に表示されますが、元の情報源はわかりません。 ヴイエムウェア 特定の無効な x86 命令を実装して、それ自体に関する情報を返します。 仮想PC IN 命令でマジックナンバーと I/O ポートを使用します。
これは実行可能ですが、どちらの場合も文書化されていない動作のようです。VMWare または VirtualPC の将来のリリースではメカニズムが変更される可能性があると思います。もっと良い方法はありますか?どちらの製品でもサポートされているメカニズムはありますか?
同様に、検出する方法はありますか ゼン または バーチャルボックス?
私はプラットフォームが意図的に自らを隠そうとしているケースについては心配していません。たとえば、ハニーポットは仮想化を使用しますが、マルウェアが仮想化を検出するために使用するメカニズムを不明瞭にする場合があります。私のアプリがこれらのハニーポットで仮想化されていないと考えるかどうかは気にしません。私は「ベストエフォート型」のソリューションを探しているだけです。
アプリケーションはほとんどが Java ですが、この特定の機能にはネイティブ コードと JNI を使用することを想定しています。Windows XP/Vista のサポートが最も重要ですが、参照記事で説明されているメカニズムは x86 の一般的な機能であり、特定の OS 機能には依存しません。
解決
について聞いたことがありますか 青い錠剤、赤い錠剤?. 。これは、仮想マシン内で実行されているかどうかを確認するために使用される手法です。この用語の由来は、 マトリックスの映画 ここで、ネオには青または赤の錠剤が提供されます(マトリックスの中に留まるため = 青、または「現実」の世界に入る = 赤)。
以下は、「マトリックス」内で実行しているかどうかを検出するコードです。
(コードは以下から借用しました このサイト これには、当面のトピックに関するいくつかの役立つ情報も含まれています):
int swallow_redpill () {
unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3";
*((unsigned*)&rpill[3]) = (unsigned)m;
((void(*)())&rpill)();
return (m[5]>0xd0) ? 1 : 0;
}
この関数は、仮想マシン内で実行している場合は 1 を返し、それ以外の場合は 0 を返します。
他のヒント
Linux では次のコマンドを使用しました。 dmidecode (CentOSとUbuntuの両方にあります)
男性から:
dmidecodeは コンピュータのDMI(SMBIOSという説もある)テーブル の内容を、人間が読み取れるフォーマットで提供する。
そこで出力を検索したところ、おそらく Microsoft Hyper-V であることがわかりました。
Handle 0x0001, DMI type 1, 25 bytes
System Information
Manufacturer: Microsoft Corporation
Product Name: Virtual Machine
Version: 5.0
Serial Number: some-strings
UUID: some-strings
Wake-up Type: Power Switch
Handle 0x0002, DMI type 2, 8 bytes
Base Board Information
Manufacturer: Microsoft Corporation
Product Name: Virtual Machine
Version: 5.0
Serial Number: some-strings
もう 1 つの方法は、eth0 の MAC アドレスがどのメーカーに関連しているかを検索することです。 http://www.coffer.com/mac_find/
Microsoft、vmwareなどを返す場合。それはおそらく仮想サーバーです。
VMware には、 ソフトウェアが VMware 仮想マシンで実行されているかどうかを判断するメカニズム ソースコードを含むナレッジベースの記事。
Microsoft には次のページもあります 「ハイパーバイザーがインストールされているかどうかの確認」. 。MS は、ハイパーバイザーのこの要件について、「IsVM TEST」セクションで詳しく説明しています。 「サーバ仮想化検証テスト」 書類
VMware と MS のドキュメントはどちらも、CPUID 命令を使用してハイパーバイザー存在ビット (レジスタ ECX のビット 31) をチェックすることに言及しています。
RHEL バグトラッカーには、 「CPUID リーフ 0x00000001 の ISVM ビット (ECX:31) を設定する必要があります」 Xen カーネルでレジスタ ECX のビット 31 を設定します。
したがって、ベンダーの詳細には触れずに、CPUID チェックを使用して仮想的に実行しているかどうかを確認できるようです。
いいえ。これを完全な精度で検出することは不可能です。一部の仮想化システム QEMU, 、ハードウェア レジスタに至るまでマシン全体をエミュレートします。これをひっくり返してみましょう:何をしようとしているのですか?もしかしたら私たちがそれをお手伝いできるかもしれません。
今後、壊れた SIDT 仮想化のようなトリックに頼ることは、実際には役に立たないと思います。奇妙で厄介な x86 アーキテクチャが残したすべての穴をハードウェアが塞いでしまうからです。最善の方法は、VM を使用していることを通知する標準的な方法を VM プロバイダーに働きかけることです (少なくともユーザーが明示的に許可している場合には)。しかし、VM の検出を明示的に許可していると仮定すると、そこに可視マーカーを配置することもできますよね。VM 上にいることを示すファイル (たとえば、ファイル システムのルートにある小さなテキスト ファイル) を使用して、VM 上のディスクを更新することをお勧めします。または、ETH0 の MAC を検査し、それを特定の既知の文字列に設定します。
virtualbox では、VM ゲストを制御でき、dmidecode があると仮定して、次のコマンドを使用できます。
dmidecode -s bios-version
そしてそれは戻ってきます
VirtualBox
Usenix HotOS '07 に掲載された論文をお勧めしたいと思います。 互換性は透明性ではありません:VMM 検出の通説と現実, これは、アプリケーションが仮想化環境で実行されているかどうかを判断するためのいくつかの手法をまとめたものです。
たとえば、redpill と同じように sidt 命令を使用します (ただし、この命令は動的変換によって透過的にすることもできます)。または、cpuid の実行時間を他の非仮想化命令と比較します。
Linux では、/proc/cpuinfo でレポートできます。VMware 内にある場合は、通常、ベアメタル上にある場合とは異なる方法で起動しますが、常にそうとは限りません。Virtuozzo は、基盤となるハードウェアへのパススルーを示します。
新しい Ubuntu をインストールしているときに、imvirt というパッケージを発見しました。見てみてください http://micky.ibh.net/~liske/imvirt.html
この C 関数は VM ゲスト OS を検出します。
(Windows でテスト、Visual Studio でコンパイル)
#include <intrin.h>
bool isGuestOSVM()
{
unsigned int cpuInfo[4];
__cpuid((int*)cpuInfo,1);
return ((cpuInfo[2] >> 31) & 1) == 1;
}
ツールを確認する 仮想何. 。前述の dmidecode を使用して、仮想化ホスト上にいるかどうかとそのタイプを判断します。
私はこれを使います C#
ゲスト OS が仮想環境内で実行されているかどうかを検出するクラス (窓のみ):
sysInfo.cs
using System;
using System.Management;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
public class sysInfo
{
public static Boolean isVM()
{
bool foundMatch = false;
ManagementObjectSearcher search1 = new ManagementObjectSearcher("select * from Win32_BIOS");
var enu = search1.Get().GetEnumerator();
if (!enu.MoveNext()) throw new Exception("Unexpected WMI query failure");
string biosVersion = enu.Current["version"].ToString();
string biosSerialNumber = enu.Current["SerialNumber"].ToString();
try
{
foundMatch = Regex.IsMatch(biosVersion + " " + biosSerialNumber, "VMware|VIRTUAL|A M I|Xen", RegexOptions.IgnoreCase);
}
catch (ArgumentException ex)
{
// Syntax error in the regular expression
}
ManagementObjectSearcher search2 = new ManagementObjectSearcher("select * from Win32_ComputerSystem");
var enu2 = search2.Get().GetEnumerator();
if (!enu2.MoveNext()) throw new Exception("Unexpected WMI query failure");
string manufacturer = enu2.Current["manufacturer"].ToString();
string model = enu2.Current["model"].ToString();
try
{
foundMatch = Regex.IsMatch(manufacturer + " " + model, "Microsoft|VMWare|Virtual", RegexOptions.IgnoreCase);
}
catch (ArgumentException ex)
{
// Syntax error in the regular expression
}
return foundMatch;
}
}
}
使用法:
if (sysInfo.isVM()) {
Console.WriteLine("VM FOUND");
}
Linux では、systemd は、システムが仮想マシンとして実行されているかどうかを検出するコマンドを提供します。
指示:
$ systemd-detect-virt
システムが仮想化されている場合は、仮想化ソフトウェア/テクノロジーの名前が出力されます。そうでない場合は出力します none
たとえば、システムが KVM を実行している場合は、次のようになります。
$ systemd-detect-virt
kvm
sudo として実行する必要はありません。
友人が提案した別のアプローチを試してみました。VMWARE 上で実行される仮想マシンには CPU TEMPERATURE プロパティがありません。つまり、CPU の温度は表示されません。CPU温度を確認するためにCPU温度計アプリケーションを使用しています。
そこで、温度センサーを検出するための小さな C プログラムをコーディングしました。
#include "stdafx.h"
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
int main(int argc, char **argv)
{
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *)&pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t(L"SELECT * FROM Win32_TemperatureProbe"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
cout << "Query for operating system name failed."
<< " Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"SystemName", 0, &vtProp, 0, 0);
wcout << " OS Name : " << vtProp.bstrVal << endl;
VariantClear(&vtProp);
VARIANT vtProp1;
VariantInit(&vtProp1);
pclsObj->Get(L"Caption", 0, &vtProp1, 0, 0);
wcout << "Caption: " << vtProp1.bstrVal << endl;
VariantClear(&vtProp1);
pclsObj->Release();
}
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
return 0; // Program successfully completed.
}