Hypervisor Abyss, part 2

mai 11th, 2008 at 07:56 admin

Suite de notre voyage dans le monde des hyperviseurs, dans la 1ère partie j’ai montré comment initialiser le support VMX sur le CPU, cette fois-ci, on passe aux choses sérieuses avec la création de la VMCS (Virtual Machine Control data Structure), cette zone mémoire va contenir toutes les infos pour contrôler le comportement du Guest en mode non-root. Lors de certains événements clés, le jeu d’instructions VMX va utiliser la VMCS pour sortir du Guest et restaurer le contexte de l’Host. Une fois que l’hyperviseur rend la main, la VMCS permet de rétablir le contexte du Guest sur le CPU. Dans notre cas, du fait que nous virtualisons notre OS à la volée la création de la VMCS est moins compliquée, elle n’en est pas moins fastidieuse …

La création de la VMCS demande les même propriétés que la VMXONRegion, une mémoire non cachée créée avec MmAllocateNonCachedMemory, la taille est la même que la VMXONRegion, c’est à dire celle qu’on lit dans le champ VMRegionSize du MSR IA32_VMX_BASIC (0×480), les 4 premiers bytes de la VMCS sont composés aussi de la valeur du champ RevId du IA32_VMX_BASIC, jusqu’ici c’est exactement pareil que pour la VMXONRegion.

Une fois que notre VMCS est alloué, nous devons la « clear » avec l’instruction VMCLEAR qui prend comme argument l’adresse physique de notre VMCS (obtenue avec MmGetPhysicalAddress), cette étape sert juste à mettre la VMCS dans un état neutre, je pense aussi que même si on pouvait le faire à la main, l’instruction VMCLEAR (comme les autres fournis par Intel) garantie le fait que notre mémoire n’aille pas dans le cache. On doit vérifier que VMCLEAR s’est bien déroulée en regardant si les flags CF et ZF de l’EFlags sont à 0. En application cela donne le code :

;VOID _VmxClear(PHYSICAL_ADDRESS Addr)
_VmClear PROC StdCall LowPart, HighPart
	mov ebp, esp
	sub esp, 8
	
	push HighPart
	push LowPart
	vmclear qword ptr [esp]
	
	leave
	retn 8
_VmClear ENDP

Maintenant il faut dire au core actif que nous allons utiliser cette zone mémoire en tant que VMCS, pour cela l’instruction VMPTRLD prend l’adresse physique de notre VMCS et la définie comme « active » sur le core courant. On a donc le bout de code :

;VOID _VmPtrLd(PHYSICAL_ADDRESS Addr)
_VmPtrLd PROC LowPart, HighPart
	mov ebp, esp
	sub esp, 8
	
	push HighPart
	push LowPart
	vmptrld qword ptr [esp]
	
	leave
	retn 8
_VmPtrLd ENDP

Les 32 bits suivant le RevId au début de la VMCS sont nomé par « VMX-abort indicator », un VM Abort apparait au moment d’un VM Exit qui pose problème, VMX va mettre à jour ce champ pour dire ce qu’il s’est passé, vous pouvez retrouver ces valeurs à la section 23.7 du « Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3B: System Programming Guide, Part 2« .

La partie suivante de la VMCS nous incombe, nous devons tout remplir nous même. La VMCS se compose de 6 zones :

  1. Guest-state area : L’état du core est chargé depuis cette zone lors d’un VM Entry et sauvegardé dedans lors d’un VM Exit.
  2. Host-state area : Le contexte du core est récupéré ici lors d’un VM Exit pour relancer l’Host.
  3. VM-execution control fields : Ces variables contrôlent le comportement du Guest en mode non-root et détermine les VM Exit.
  4. VM-exit control fields : Ces champs déterminent le comportement de l’hyperviseur lors de certains VM Exit.
  5. VM-entry control fields : Ces champs déterminent le comportement du CPU lors des VM Entry.
  6. VM-exit information fields : Donne des informations sur la cause et la nature du VM Exit.

On a de la chance, la zone « M-exit information fields » est en lecture seule, c’est le VMX qui la met à jour lors d’un VM Exit, c’est cool, ça nous fait moins de boulot pour init la VMCS :]

Alors, grosse particularité de la VMCS, on y écrit et lit pas comme on veut dedans. En effet pour éviter les problèmes de cache et les problèmes de padding du au futurs changement et extensions sur cette structure Intel demande d’accéder au VMCS à travers les instructions VMREAD et VMWRITE. Chacune d’elles prend 2 arguments, le premier est un registre ou un pointeur qui va recevoir la valeur lu ou donnera la valeur à écrire, le second argument est un registre contenant l’encoding du champ auquel on souhait accéder. A partir de là on peut concevoir les wrappers suivants :

; ULONG32 _VmRead(ULONG32 Encoding)
_VmRead PROC StdCall Encoding
	push ebp
	mov ebp, esp
	sub esp, 4
	
	vmread Encoding, esp
	mov eax, dword ptr [esp]
	
	leave
	retn 8
_VmRead ENDP


; VOID _VmWrite(ULONG32 Encoding, ULONG32 Value)
_VmWrite PROC StdCall Encoding, Value
	push eax
	mov eax, Encoding
	vmwrite eax, Value
	pop eax
	retn 8
_VmWrite ENDP

L’encoding des champs de la VMCS possède cette forme :

Vmcs encoding
Heureusement Intel fournit dans son manuel, l’encoding de tout les champs :

/* VMCS Encodings */
enum
{
	// 16 bits Guest State Fields
	GUEST_ES_SELECTOR = 0x00000800,
	GUEST_CS_SELECTOR = 0x00000802,
	GUEST_SS_SELECTOR = 0x00000804,
	GUEST_DS_SELECTOR = 0x00000806,
	GUEST_FS_SELECTOR = 0x00000808,
	GUEST_GS_SELECTOR = 0x0000080a,
	GUEST_LDTR_SELECTOR = 0x0000080c,
	GUEST_TR_SELECTOR = 0x0000080e,

	// 16 bits Host State Fields
	HOST_ES_SELECTOR = 0x00000c00,
	HOST_CS_SELECTOR = 0x00000c02,
	HOST_SS_SELECTOR = 0x00000c04,
	HOST_DS_SELECTOR = 0x00000c06,
	HOST_FS_SELECTOR = 0x00000c08,
	HOST_GS_SELECTOR = 0x00000c0a,
	HOST_TR_SELECTOR = 0x00000c0c,

	// 64 bits Control Fields
	IO_BITMAP_A = 0x00002000,
	IO_BITMAP_A_HIGH = 0x00002001,
	IO_BITMAP_B = 0x00002002,
	IO_BITMAP_B_HIGH = 0x00002003,
	MSR_BITMAP = 0x00002004,
	MSR_BITMAP_HIGH = 0x00002005,
	VM_EXIT_MSR_STORE_ADDR = 0x00002006,
	VM_EXIT_MSR_STORE_ADDR_HIGH = 0x00002007,
	VM_EXIT_MSR_LOAD_ADDR = 0x00002008,
	VM_EXIT_MSR_LOAD_ADDR_HIGH = 0x00002009,
	VM_ENTRY_MSR_LOAD_ADDR = 0x0000200a,
	VM_ENTRY_MSR_LOAD_ADDR_HIGH = 0x0000200b,
	TSC_OFFSET = 0x00002010,
	TSC_OFFSET_HIGH = 0x00002011,
	VIRTUAL_APIC_PAGE_ADDR = 0x00002012,
	VIRTUAL_APIC_PAGE_ADDR_HIGH = 0x00002013,

	// 64 bits Guest State Fields
	VMCS_LINK_POINTER = 0x00002800,
	VMCS_LINK_POINTER_HIGH = 0x00002801,
	GUEST_IA32_DEBUGCTL = 0x00002802,
	GUEST_IA32_DEBUGCTL_HIGH = 0x00002803,

	// 64 bits Host-State Field
	HOST_IA32_PERF_GLOBAL_CTRL = 0x00002C04,
	HOST_IA32_PERF_GLOBAL_CTRL_HIG = 0x00002C05,

	// 32 bits Control Fields
	PIN_BASED_VM_EXEC_CONTROL = 0x00004000,
	PRIMARY_CPU_BASED_VM_EXEC_CONTROL = 0x00004002,
	EXCEPTION_BITMAP = 0x00004004,
	PAGE_FAULT_ERROR_CODE_MASK = 0x00004006,
	PAGE_FAULT_ERROR_CODE_MATCH = 0x00004008,
	CR3_TARGET_COUNT = 0x0000400a,
	VM_EXIT_CONTROLS = 0x0000400c,
	VM_EXIT_MSR_STORE_COUNT = 0x0000400e,
	VM_EXIT_MSR_LOAD_COUNT = 0x00004010,
	VM_ENTRY_CONTROLS = 0x00004012,
	VM_ENTRY_MSR_LOAD_COUNT = 0x00004014,
	VM_ENTRY_INTR_INFO_FIELD = 0x00004016,
	VM_ENTRY_EXCEPTION_ERROR_CODE = 0x00004018,
	VM_ENTRY_INSTRUCTION_LEN = 0x0000401a,
	TPR_THRESHOLD = 0x0000401c,
	SECONDARY_CPU_BASED_VM_EXEC_CONTROL = 0x000401e,

	// 32 bits Read Only Data Fields
	VM_INSTRUCTION_ERROR = 0x00004400,
	VM_EXIT_REASON = 0x00004402,
	VM_EXIT_INTR_INFO = 0x00004404,
	VM_EXIT_INTR_ERROR_CODE = 0x00004406,
	IDT_VECTORING_INFO_FIELD = 0x00004408,
	IDT_VECTORING_ERROR_CODE = 0x0000440a,
	VM_EXIT_INSTRUCTION_LEN = 0x0000440c,
	VMX_INSTRUCTION_INFO = 0x0000440e,
	
	// 32 bits Guest State Fields
	GUEST_ES_LIMIT = 0x00004800,
	GUEST_CS_LIMIT = 0x00004802,
	GUEST_SS_LIMIT = 0x00004804,
	GUEST_DS_LIMIT = 0x00004806,
	GUEST_FS_LIMIT = 0x00004808,
	GUEST_GS_LIMIT = 0x0000480a,
	GUEST_LDTR_LIMIT = 0x0000480c,
	GUEST_TR_LIMIT = 0x0000480e,
	GUEST_GDTR_LIMIT = 0x00004810,
	GUEST_IDTR_LIMIT = 0x00004812,
	GUEST_ES_AR_BYTES = 0x00004814,
	GUEST_CS_AR_BYTES = 0x00004816,
	GUEST_SS_AR_BYTES = 0x00004818,
	GUEST_DS_AR_BYTES = 0x0000481a,
	GUEST_FS_AR_BYTES = 0x0000481c,
	GUEST_GS_AR_BYTES = 0x0000481e,
	GUEST_LDTR_AR_BYTES = 0x00004820,
	GUEST_TR_AR_BYTES = 0x00004822,
	GUEST_INTERRUPTIBILITY_INFO = 0x00004824,
	GUEST_ACTIVITY_STATE = 0x00004826,
	GUEST_SM_BASE = 0x00004828,
	GUEST_SYSENTER_CS = 0x0000482A,
	
	// 32 bits Host State Field
	HOST_IA32_SYSENTER_CS = 0x00004c00,

	// Natural width Control Fields
	CR0_GUEST_HOST_MASK = 0x00006000,
	CR4_GUEST_HOST_MASK = 0x00006002,
	CR0_READ_SHADOW = 0x00006004,
	CR4_READ_SHADOW = 0x00006006,
	CR3_TARGET_VALUE0 = 0x00006008,
	CR3_TARGET_VALUE1 = 0x0000600a,
	CR3_TARGET_VALUE2 = 0x0000600c,
	CR3_TARGET_VALUE3 = 0x0000600e,

	// Natural Width Read Only Data Fields
	EXIT_QUALIFICATION = 0x00006400,
	GUEST_LINEAR_ADDRESS = 0x0000640a,

	// Natural Witdh Guest State Fields
	GUEST_CR0 = 0x00006800,
	GUEST_CR3 = 0x00006802,
	GUEST_CR4 = 0x00006804,
	GUEST_ES_BASE = 0x00006806,
	GUEST_CS_BASE = 0x00006808,
	GUEST_SS_BASE = 0x0000680a,
	GUEST_DS_BASE = 0x0000680c,
	GUEST_FS_BASE = 0x0000680e,
	GUEST_GS_BASE = 0x00006810,
	GUEST_LDTR_BASE = 0x00006812,
	GUEST_TR_BASE = 0x00006814,
	GUEST_GDTR_BASE = 0x00006816,
	GUEST_IDTR_BASE = 0x00006818,
	GUEST_DR7 = 0x0000681a,
	GUEST_ESP = 0x0000681c,
	GUEST_EIP = 0x0000681e,
	GUEST_EFLAGS = 0x00006820,
	GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822,
	GUEST_SYSENTER_ESP = 0x00006824,
	GUEST_SYSENTER_EIP = 0x00006826,

	// Natural Width Host State Fields
	HOST_CR0 = 0x00006c00,
	HOST_CR3 = 0x00006c02,
	HOST_CR4 = 0x00006c04,
	HOST_FS_BASE = 0x00006c06,
	HOST_GS_BASE = 0x00006c08,
	HOST_TR_BASE = 0x00006c0a,
	HOST_GDTR_BASE = 0x00006c0c,
	HOST_IDTR_BASE = 0x00006c0e,
	HOST_IA32_SYSENTER_ESP = 0x00006c10,
	HOST_IA32_SYSENTER_EIP = 0x00006c12,
	HOST_ESP = 0x00006c14,
	HOST_EIP = 0x00006c16,
};

Les champs 64 sont découpés en 2 parties, les 32 bits de poids fort sont dans le champ finissant par _HIGH.

A ce moment nous sommes prêts pour remplir la VMCS. On va commencer simplement avec les Guest-state et Host-state areas. Comme je l’ai dit au début, du fait que nous virtualisons notre OS « on the fly » (ho yeah) cette partie sera simple à compléter, il suffira de mettre les mêmes valeurs pour le Guest-state et Host-State. Concrètement nous avons en commun pour l’Host et le Guest les segments, la GDT, l’IDT, certaines valeurs de MSR, les registres de contrôles CR0 et CR4. Pour toutes ces valeurs il nous suffit de prendre les valeurs de l’Host et de le mettre dans les champs correspondants des Host-state et Guest-state, pratique le bluepill :=)

Pour être clair, prenons les encoding qui concernent le segment CS, nous avons donc GUEST_GS_SELECTOR, GUEST_CS_BASE, GUEST_CS_LIMIT, GUEST_CS_AR_BYTES et HOST_CS_SELECTOR. GUEST_GS_SELECTOR et HOST_CS_SELECTOR sont égaux, GUEST_CS_BASE et GUEST_CS_LIMIT s’obtiennent en lisant la GDT de l’Host avec les fonctions suivantes :

ULONG GetSegmentDescriptorBase(PVOID GdtBase, SEGMENT_SELECTOR SegSelector)
{
	ULONG SegDescBase=0;
	PSEGMENT_DESCRIPTOR SegDescriptor;
	
	SegDescriptor=(PSEGMENT_DESCRIPTOR)((PUCHAR)GdtBase+SegSelector.Index*8);
	
	SegDescBase=SegDescriptor->BaseHigh;
	SegDescBase<<=8;

	SegDescBase|=SegDescriptor->BaseMid;
	SegDescBase<<=16;

	SegDescBase|=SegDescriptor->BaseLow;

	return SegDescBase;
}

ULONG GetSegmentDescriptorLimit(PVOID GdtBase, SEGMENT_SELECTOR SegSelector)
{
	ULONG Limit=0;
	PSEGMENT_DESCRIPTOR	SegDescriptor;
	
	SegDescriptor=(PSEGMENT_DESCRIPTOR)((PUCHAR)GdtBase+SegSelector.Index*8);

	Limit=SegDescriptor->LimitHigh;
	Limit<<=16;
	Limit|=SegDescriptor->LimitLow;

	//
	// Check granularity
	//
	if(SegDescriptor->Gran)
	{
		Limit*=0x1000;
		Limit+=0xFFF;
	}
	return Limit;
}

Le GUEST_CS_AR_BYTES est un peu particulier, il s’agit d’une version modifiée des segments descriptor de la GDT, normalement ceux-ci on la forme suivante :

La structure SEGMENT_ACCESS_RIGHTS reprend celle d’un SEGMENT_DESCRIPTOR en enlevant les champs LimitLow, BaseLow, BaseMid, LimitHigh et BaseHigh. Elle est donc définie par :

typedef struct _SEGMENT_ACCESS_RIGHTS
{
	union
	{
		struct
		{
			unsigned Type			:4;
			unsigned System			:1; // Segment type (0=system, 1=code or data)
			unsigned DPL			:2;
			unsigned Present		:1; // Segment Present
			unsigned Reserved1		:4;
			unsigned Avl			:1;
			unsigned Reserved2		:1;
			unsigned DB				:1;
			unsigned Gran			:1;
			unsigned UnUsable		:1;
			unsigned Reserved3		:15;
		};
		ULONG Access;
	};
}SEGMENT_ACCESS_RIGHTS;

On pourrait se dire qu’il est contraignant de remplir une structure SEGMENT_ACCESS_RIGHTS à partir d’un SEGMENT_DESCRIPTOR mais nan en fait, il suffit de faire l’opération suivante : SegRights=(*(PULONG)((PUCHAR)SegDescriptor+5)) & 0x0000F0FF. En SegDescriptor+5 nous sommes au niveau du champ Type du SEGMENT_DESCRIPTOR, on récupère 4 bytes qu’on masque avec la valeur 0xF0FF pour récupérer les 8 premiers bits et les 4 derniers.

On effectue les mêmes opérations sur les segments ES, DS, SS, GS, FS et les registres TR (Task Register) et LDTR (LDT Register). Ouf, nous avons finit d’initialiser les Guest et Host state …

Passons maintenant aux VM-execution control fields. Les champs PRIMARY_CPU_BASED_VM_EXEC_CONTROL et SECONDARY_CPU_BASED_VM_EXEC_CONTROL sont les plus importants, ce sont des ensembles de bits qui servent à activer des nouveaux evenement sur lesquels le Guest devra effectuer un VM Exit. Par exemple, le bit 12 du PRIMARY_CPU_BASED_VM_EXEC_CONTROL, « RDTSC exiting » définit si l’instruction RDTSC provoque un VM Exit ou non. L’utilisation du PRIMARY_CPU_BASED_VM_EXEC_CONTROL demande de prendre en compte les bits reservés de ce bitmap, c’est pourquoi Intel demande de lire le MSR IA32_VMX_PROCBASED_CTLS pour savoir comment définir ces bits. Comme dit dans la doc, les 32 premiers bits du MSR définissent les bits qui doivent être à 0 alors que les 32 bits suivant définissent les bits qui doivent être à 1. Il faut faire pareil avec le SECONDARY_CPU_BASED_VM_EXEC_CONTROL. J’avoue c’est un peu lourd à supporter, au final, l’opération minimaliste consiste à faire :

//
// The IA32_VMX_PROCBASED_CTLS MSR (index 482H) reports on the allowed 
// settings of the primary processor-based VM-execution controls (see Section 20.6.2):
// 
// - Bits 31:0 indicate the allowed 0-settings of these controls. VM entry fails if bit X 
//  in the primary processor-based VM-execution controls is 0 and bit X is 1 in this 
//  MSR.
// - Bits 63:32 indicate the allowed 1-settings of these controls. VM entry fails if bit X 
//  in the primary processor-based VM-execution controls is 1 and bit 32+X is 0 in 
//  this MSR.
//
ReadMsr(IA32_VMX_PROCBASED_CTLS, &Msr);

Tmp=0;
Tmp|=Msr.Low;
Tmp&=Msr.High;

WriteVMCS(PRIMARY_CPU_BASED_VM_EXEC_CONTROL, Tmp);

Dans l’exemple au dessus, je ne m’occupe pas des bits utiles, je les laisse tous à 0, après rien ne vous empêche de les seter à 1, faites attention de bien lire la doc, car certains comme le bit 28 (Use MSR bitmaps) font appel à des champs du VMCS comme MSR_BITMAP et MSR_BITMAP_HIGH.

Un mot sur l’EXCEPTION_BITMAP, chaque bit de ce champ contrôle un VM Exit sur un numéro d’exception, concernant les page faults (bit 14) comme nous voulons qu’il soit gérer par le Guest il faut faire attention, l’implémentation étant particulière. En fait, au moment d’un page fault, le CPU consulte le page-fault error-code [PFEG] puis 2 masques, le page-fault error-code mask [PFEC_MASK] et le page-fault error code match [PFEC_MATH]. Le CPU effectue l’opération suivante : PFEC & PFEC_MASK = PFEC_MATCH, si il y égalité alors le CPU regarde la valeur du bit 14 et effectue ou non un VM Exit. S’il n’y a pas égalité, alors le sens du bit 14 est inversé. DONC !@#! Si on veut qu’il n’y ait pas de VM Exit lors d’un page fault, il suffit de mettre le PFEC_MASK à 0, le PFEC_MATCH à 0xFFFFFFFF et le bit 14 à 1. Comme ça, il n’y aura jamais égalité et donc pas de VM Exit !

Chose marrante aussi, les CR0_GUEST_HOST_MASK et CR0_READ_SHADOW (la même chose existe pour le CR4), dans le cas ou un des bits du CR0_GUEST_HOST_MASK est un 1, une tentative de modifier le bit correspondant par le Guest produira un VM Exit. Truc cool, une tentative de lecture d’un des bits à 1 du MASK se verra retourner celui du CR0_READ_SHADOW. Cela veut dire qu’il est possible de faire croire que le bit 13 du CR4, celui qui indique si le jeu d’instruction VMX est dispo sur le CPU, est à 0 alors qu’il est à 1 pour l’Host sans provoquer de VM Exit.

Bon je ne vais pas m’amuser à décrire toutes les fonctionnalités des VM-execution control fields, déjà que je suis loin de les maîtriser, si vous voulez les connaitre mieux allez lire la doc ca sera plus simple :p

Il nous reste à régler les VM-exit control fields et VM-entry control fields. Pour les VM Exit, ces champs permettent de contrôler les MSR à charger pour l’Host à travers un tableau de Msr Entry, il faut utiliser les champs VM_EXIT_MSR_LOAD_ADDR et VM_EXIT_MSR_LOAD_ADDR_HIGH pour spécifier l’adresse physique du tableau, le champ VM_EXIT_MSR_STORE_COUNT contenant le nombre d’entrés. Il existe la même chose pour les MSR ayant besoin d’être stockés lors du VM Exit avec un tableau de VM_EXIT_MSR_STORE_COUNT situé en VM_EXIT_MSR_STORE_ADDR et VM_EXIT_MSR_STORE_ADDR_HIGH.

En parallèle on retrouve la même chose pour le VM Entry avec les champs VM_ENTRY_MSR_LOAD_ADDR et VM_ENTRY_MSR_LOAD_ADDR_HIGH qui pointent sur un tableau de VM_ENTRY_MSR_LOAD_COUNT MSR Entry qui seront chargés lors du Vm Entry.

Ouf ! Nous avons finit avec l’initialisation de la VMCS, c’était long (et bon !) mais c’est nécessaire pour lancer notre hyperviseur. Je n’ai pas voulu tout détaillé dans ce post ca m’aurait prit trop de places et puis il faut bien que vous bossiez un peu vous aussi, j’ai essayé de dégrossir et de montrer les fonctionnalités de base, à vous d’adapter en fonction de vos besoins et pour cela votre meilleur atout sera de lire la doc, je sais c’est long et chiant mais c’est comme ça et au final les docs Intel ne sont pas si mal faites, il suffit de prendre son temps :]

La prochaine fois nous verrons enfin comment implémenter la routine qui devra gérer les VM Exit obligatoire, la manière de lancer notre HVM et comment l’arrêter avec en cadeau une petite feature pour Abyss :) En attendant je retourne bosser dessus.

Et n’oubliez pas, il s’appel Robert Paulson …

Entry Filed under: RE

6 Comments

  • 1. Dora  |  mai 11th, 2008 at 21:59

    Il s’appel Robert Paulson …


  • 2. Tyler  |  mai 12th, 2008 at 22:31

    Il s’appel Robert Paulson …


  • 3. Cindy.S  |  mai 18th, 2008 at 12:46

    Il s’appel Robert Paulson …


  • 4. robert paulson  |  mai 24th, 2008 at 13:53

    il s’appel moi :/


  • 5. Ivanlef0u’s Blog &r&hellip  |  mai 26th, 2008 at 17:51

    [...] et dernière partie de notre périple à travers les hyperviseurs, la dernière fois j’ai montré comment créer la structure de contrôle de notre VM Monitor, la VMCS. Cette fois, [...]


  • 6. Malware :: Hiding Virtual&hellip  |  novembre 27th, 2012 at 00:22

    [...] Blog – Hypervisor Abyss (in French) :: Part 1 :: Part 2 :: Part [...]


Trackback this post


Calendar

décembre 2019
L Ma Me J V S D
« fév    
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

Most Recent Posts