뭘 이런걸..

Posted
Filed under Tech/프로그래밍
현재 접속해 있는 host가 vm guest 인지 아닌지 여부를 확인 하는 방법은 보통 다음과 같다.

1. dmidecode 에서 Xen 또는 VMware 가 있는지 찾기
2. Xen guest의 CentOS 6 이하는 dmidecode를 제대로 지원하지 않는 경우가 많은데, 이 경우에는 ps 명령에서 xenbus 를 찾는 방법이 있다.

여기서 논하는 방법은 CPU ID를 확인하는 방법인데, VMWare 문서에 의하면 다음과 같이 소개하고 있다.

Intel 및 AMD의 CPU는 CPUID 리프 0x1의 ECX의 비트 31을 하이퍼 바이저 존재 비트로 예약하고 있습니다. 이 비트에 의해 하이퍼 바이저는 게스트 OS에 그 존재를 나타낼 수 있습니다. 하이퍼 바이저는이 비트를설정하여 실제 CPU (모든 기존 CPU와 장래의 CPU)는이 비트를 0으로 설정합니다. 게스트 OS는 31 비트를테스트하여 그 하이퍼 바이저가 가상 머신 내에서 실행되고 있는지를 확인할 수 있습니다.

Intel 및 AMD는 소프트웨어에서 사용할 수 있도록 CPUID 리프 0x40000000 ~ 0x400000FF도 예약하고 있습니다. 하이퍼 바이저는 이러한 리프를 사용하여 하이퍼 바이저에서 가상 머신 내에서 실행되는 게스트 OS에 정보를 전달하기위한 인터페이스를 제공 할 수 있습니다. 하이퍼 바이저 비트는 하이퍼 바이저가 존재하는 것을 의미하며 이러한 추가 소프트웨어 리프를 테스트하는 것이 안전하다는 것을 보여줍니다. VMware는 0x40000000 리프를 하이퍼 바이저 CPUID 정보 리프로 정의합니다. VMware 하이퍼 바이저 코드를 실행함으로써 하이퍼바이저 서명이 있는지 CPUID 정보 리프를 테스트 할 수 있습니다. VMware는 CPUID 리프 0x40000000의 EBX, ECX, EDX에 "VMwareVMware"라는 문자열을 저장합니다.

이 내용을 C code 로 만들면 다음과 같다.


#include <stdio.h>
#include <string.h>

// https://stackoverflow.com/questions/6491566/getting-the-machine-serial-number-and-cpu-id-using-c-c-in-linux
static inline void cpuid (unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
{
/* ecx is often an input as well as an output. */
asm volatile (
"cpuid"
: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "0" (*eax), "2" (*ecx)
);
}

// https://kb.vmware.com/s/article/1009458
int cpuid_check (void) {
unsigned int eax, ebx, ecx, edx;
char hyper_vendor_id[13];
//cpuid(0x1, &eax, &ebx, &ecx, &edx);
eax = 0x1;
cpuid (&eax, &ebx, &ecx, &edx);

// bit 31 of ecx is set
// 0x8000000 10000 0000 0000 0000 0000 0000 0000 0000
if ( ecx >= 0x80000000 ) {
//cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
eax = 0x40000000;
cpuid (&eax, &ebx, &ecx, &edx);
memcpy (hyper_vendor_id + 0, &ebx, 4);
memcpy (hyper_vendor_id + 4, &ecx, 4);
memcpy (hyper_vendor_id + 8, &edx, 4);
hyper_vendor_id[12] = '\0';

//printf ("This is GuesVM %s\n");
//printf ("##################### %s\n", hyper_vendor_id);

// VMwareVMware
if ( ! strncmp (hyper_vendor_id, "VMware", 6) )
return 0; // Success - running under VMware
// XenVMMXenVMM
else if ( ! strncmp (hyper_vendor_id, "XenVM", 5) )
return 0; // Success - running under Xen
}

return 1;
}

int main (void) {
// if guest vm return 0 and case others, return 1
return cpuid_check ();
}


이 코드는 VMWare EX 5.1 이상에서 정상 동작 함을 확인 했고, Xen server의 경우 7 에서 정상 동작하며, Xen server 6.5의 경우 CentOS 6.x 이하에서는 CPU  id 에서 해당 영역값을 읽어오지 못한다. 즉, legacy system에서는 동작하지 않을 수 있다.
2019/01/30 13:30 2019/01/30 13:30