티스토리 툴바

[가상화] LDT(Local Descriptor Table)를 이용한 VMM Detection
안녕하세요, SecuRex0입니다.^^
저번 포스트(여기를 클릭하시면 보실 수 있습니다)에서는 Intel 계열의 단일 코어 CPU에서 SIDT를 이용해 VMM을 감지하는 방법에 대해서 알아보았습니다. 예고했듯이, 이번 포스트는 한차원 더 발전된 방법인 LDT를 이용한 VMM 감지입니다.

NOTE - SIDT를 이용한 VMM 감지가 멀티코어에서는 되지 않는 이유
 

싱글코어에서는 IDT의 위치가 변경되지 않기 때문에, Memory Relocated된 IDT인지 판별해서 감지를 했었습니다. 하지만 멀티코어에서는 서로 다른 코어에서 프로세스가 동작할 때 IDT가 변화될 수 있습니다. 그래서 멀티코어에서는 감지가 정확하게 되지 않습니다. 그래서 사용하는 방법이 LDT를 이용한 감지입니다. LDT를 이용해 VMM을 감지할 시 멀티코어에서도 원활히 동작합니다.


LDT(Local Descriptor Table)는 특권(privilege)의 변경을 수행할때 사용되는 테이블이며, 특권의 변경이 수행될 시 세그먼테이션을 제공합니다. Base address, access rights, 타입, 길이, 그리고 현재 세그먼트에 대한 사용 정보를 저장합니다. 이것에 대한 접근을 수행하기 위해서는 LDTR(Local Descriptor Table Register)이라는 녀석을 사용하는데요, 이 녀석은 GDT안의 LDT 디스크립터를 가리키는 셀렉터(selector)입니다. LLDT(Load LDT), SLDT(Store LDT) 명령으로 참조하고 설정할수 있습니다.

하지만 LDT는 모든 시스템에서 사용하지 않습니다. 심지어 세계 OS 시장의 대부분을 점유하고 있는 윈도우즈에서도 LDT를 사용하지 않습니다. 가상화 모드에서 동작하는 윈도우즈의 LDT는 반드시 운영 체제 위에서 존재하는 값보다 더 분리된 값(a separate value)으로 존재해야 합니다. 그래서 VM은 각각의 운영체제마다 GDT와 LDT에 대한 구현의 차이를 조금씩 둡니다. 중요한 것은 SIDT, SLDT, SGDT 어셈블리 인스트럭션은 가상화 모드를 유지하기 위해 끊임없이 가상화 되어야 합니다. 그래서 그들은 같은 값을 가질 수 없으며 VM은 그들의 값들에 대한 일련의 카피본을 제공해 줍니다.
나머지 기본적인 원리는 동일합니다. 아래는 소스 코드입니다. 

#include <stdio.h>


inline int checkIDT();

int checkGDT();

int checkLDT();


int main(int argc, char **argv)

{

        checkIDT();

        checkGDT();


        if(checkLDT())

                printf("Inside VM\n");

        else

                printf("Inside Native Machine\n");


        return 0;

}


inline int checkIDT()

{

        unsigned char m[6];


        __asm sidt m;

        return (m[5] > 0xd0) ? 1 : 0;

}


int checkGDT()

{

        unsigned char m[6];


        __asm sgdt m;

        return (m[5] > 0xd0) ? 1 : 0;

}


int checkLDT()

{

        unsigned char m[6];


        __asm sldt m;

        return (m[0] != 0x00 && m[1] != 0x00) ? 1 : 0;


제 컴퓨터에서 실행한 결과입니다..^^



다음 포스트 예고: Anti VMM Detection

[가상화] SIDT 계열의 단일 인스트럭션으로 VMM 감지 - Redpill Project
 

안녕하세요, SecuRex0입니다.^^
이번 포스트에서는 ITL(Invisible Things Lab)의 Joanna Rutkowska라는 사람이 2004년 후반기에 발표한 내용인 단일 인스트럭션으로 VMM(Virtual Machine Monitor)을 감지할 수 있는 방법 및 코드에 대해서 알아보겠습니다. 물론 VMM 감지는 대부분 가상화와 관련된 기술들이 그렇듯이 오래 전부터 이슈가 되어 왔었던 주제입니다. 관련된 많은 연구가 진행되었었고, VMM 감지에 대한 기술은 현재도 공개된 정보들이 많이 있습니다. (Compatibility is Not Transparency: VMM Detection Myths and Realites라는 Paper에서 관련 자료를 더 찾아 보실수 있습니다.^^) 그런데 Joanna Rutkowska라는 사람이 만든 감지 기술은 그 기술들 중에서도 정말 재미있는 기술 중 하나에 속합니다. 

NOTE: 왜 VMM(Virtual Machine Monitor)감지가 필요한가?

왜 VMM을 감지해야 하는 걸까요, 사실 많은 사람들이 이유를 잘 모릅니다.
사실 이유는 간단합니다. 악성코드를 안전하게(Host에 영향이 없게) 분석하려면 VM 위에서 돌려야 하는데, VMM을 감지한다면 그만큼 분석하기 힘들어지겠죠..? 


그럼 한번 여행을 떠나 볼까요~!? 슈슝!

<그림 1. 여행을 떠나요~~~ㅋㅋ>
 

기술은 SIDT 계열의 단일 인스트럭션을 사용해서 VMM을 감지합니다. SIDT(Store Interrupt Descriptor Table)는 IDTR(Interrupt Descriptor Table Register)의 정보를 저장하는 녀석인데, 내부의 여러 인터럽트 관련 데이터를 저장하는 테이블입니다. 이 테이블은 인터럽트를 프로세싱 할 때 OS가 관리합니다. 그래서 디바이스(Device)들은 이벤트를 프로세싱 하기 위해 IDT를 사용합니다. 그래서 많은 루트킷들이 IDT를 공격의 대상으로 정하곤 하죠. 예를 들어 루트킷이 IDT를 파괴 또는 조작한다면 특정 인터럽트가 발생 시 원하는 코드를 실행시킬 수 있습니다. 그렇기 때문에 여러 커널 모드 키스트로크 로거(Keystroke logger) 루트킷은 IDT를 파괴해서 키로깅을 수행하기도 합니다. (조만간 관련 포스팅을 하도록 하겠습니다)
여튼, IDTR은 단 한개 밖에 존재하지 않기 때문에 Host/Guest OS가 동시에 VMM 위에서 돌아가려면 VMM이 Guest OS의 IDTR을 Host OS의 IDTR에 겹치지 않는 다른 메모리에 사상시켜 저장해둬야 합니다. 이 작업을 Memory Relocation이라고 합니다. 
그런데 Relocated된 메모리는 Native OS에서 일반적으로 IDTR가 올라가 있는 주소와 많이 다릅니다. Windows의 IDT의 주소는 0x80FFFFFF, Linux는 0xC0FFFFFF인 반면에 VMWare에서 Relocated 된 IDT의 주소는 0xFF******이고, Virtual PC의 Relocated된 IDT의 주소는 0xE8******이기 때문에, IDT의 주소를 받아온 뒤 첫째 바이트와 0xD0을 비교해 크면 VM, 아니면 VM이 아니다라고 간주합니다.
아래는 그것을 실제 구현해둔 소스 코드입니다. :-) 

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;
}


생각보다 간단하죠?
이 프로젝트의 이름을 Redpill 이라고 부른답니다..^^

레퍼런스:
SIDT(http://faydoc.tripod.com/cpu/sidt.htm
Redpill Project Page(http://invisiblethings.org/papers/redpill.html)

다음 포스트 : LDT를 이용한 VMM Detection
다다음 포스트 예고: Anti VMM Detection
다다다음 포스트 예고: TRAPKIT - Anti-Anti VMM Detection

[가상화] 가상화의 기본 개념, 2011. 7. 15 Fri.

안녕하세요, SecuRex0 입니다.
요즘 제가 HVM(Hardware-assisted Virtual Machine) 개발 프로젝트를 하게 되어 그런지 가상화(Virtualization) 분야에 관심이 많아졌어요. 저도 이제 시작한 분야라 많이 부족하지만 하나 하나 배워 나가면서 개념을 블로그에다 정리해 보려고 해요. 다들 응원해 주실거죠? ^^

보통 OS는 원론적으로 모든 하드웨어 자원을 독점해 사용하도록 구성되어 있어요. 그래서 원래 한 대의 컴퓨터에 한 OS를 설치하여 사용하도록 하는게 정석인데, 사람들이 너무 답답했던지 여러 OS를 설치할 방법을 찾기 시작해요. 그러다가 나온 아이디어가 한 대의 컴퓨터를 논리적으로 분할해 마치 여러 대의 컴퓨터 처럼 만들어서, 각각의 분할된 가상 공간에서 각각 다른 OS가 설치되어 동작할 수 있도록 만들자는 것인데, 이것이 바로 가상화의 시초 개념이에요.
왠지 어디서 많이 사용해 본 기능 같지 않나요? 네, 컴퓨터 하는 사람이라면 누구나 한번쯤 사용해 봤을 법한 프로그램인 VMWare나 VirtualBox 등이 바로 이런 기능을 소프트웨어적으로 수행해주는 역할을 해요. 그런데 소프트웨어적으로 수행하면 오버헤드도 크고 상대적으로 느리다는 문제가 있어요. 그래서 이걸 해결하기 위해 출시된 것이 바로 AMD 가상화 INTEL 가상화 예요.
AMD 가상화, 그리고 INTEL 가상화. 이 두개의 기능은 바로 각각의 제품군에 달려있는 프로세서 자체에서 가상화를 도와줘요. AMD 가상화는 AMD-V, INTEL 가상화는 INTEL VT-xVT-I라는것을 각각 출시해서 VMWare 같은 소프트웨어들이 했던 기능을 하드웨어 자체에서 지원을 해줘요.
그렇기 때문에 당연히 VMWare 같은 것으로, 즉 소프트웨어적으로 가상화를 수행하는 것보다 더욱 작은 오버헤드와 빠른 속도로 가상화를 동작시키게 되요.
이러한, 하드웨어의 도움을 받아서 가상화를 수행하는 가상 머신들을 바로 HVM(Hardware-assisted Virtual Machine) 이라고 해요. 제가 이번에 연구하는 주제이기도 하죠.

가상화에 대한 방식을 나누는 기준은 많지만 일반적으로 가상화는 전가상화(Full-Virtualization), 반가상화(Para-Virtualization)의 두 가지 경우로 나눌 수 있어요. 
전가상화는 말 그대로 하드웨어 전체를 가상화 시키는 거에요. 아래와 같은 그림을 보면 쉽게 이해하실 수 있을거에요.

 
OS에서 CPU에게 하드웨어 제어에 대한 요구를 하면 CPU는 OS와 하드웨어 사이에서 동작하는 Hypervisor라는 녀석에게 다시 그 요구를 전달해 주고, Hypervisor라는 녀석이 CPU를 직접 제어하는거죠. 즉 하드웨어 전체를 가상화 시키기 때문에 Guest OS에 아무런 수정 없이, 그리고 아무런 제약 없이 다양한 OS를 이용 할 수 있는 장점이 있어요. 

반가상화는 전가상화와는 조금 다른 개념이에요. 이 가상화 기법은 하드웨어 전체를 가상화 시키지 않아요. 아래와 같은 그림을 보면 쉽게 이해하실 수 있을 거에요.

 
하드웨어에 직접적으로 접근하지 않고 모든 접근을 Hypervisor를 통해 하죠. 그렇기 때문에 OS 커널 소스의 일부분이 수정되어야만 해요. 그래서 현재 이 방법은 오픈 소스 운영체제들에게만 적용되고 있는 방법이에요. 여기서 CPU는 가상화를 지원하지 않아도 상관 없어요.

어떤가요? 가상화라는거 별로 안 어렵죠?