-
-
Save mahmoudimus/dea72a7aab8ec4aac9f8ee50b5c529c3 to your computer and use it in GitHub Desktop.
system resources physical memory map VM detection trick
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// system resources physical memory map VM detection trick | |
// written by Graham Sutherland (@gsuberland) for Nettitude | |
// based on prior work done as part of the al-khaser project | |
// https://github.com/LordNoteworthy/al-khaser/ | |
// ref: https://blog.xpnsec.com/total-meltdown-cve-2018-1038/ | |
// ref: https://gist.github.com/xpn/3792ec34d712425a5c47caf5677de5fe | |
// compile: | |
// cl.exe -DUNICODE -D_UNICODE vm_resource_check.c kernel32.lib advapi32.lib /W4 /link /out:vm_resource_check.exe | |
#include <stdio.h> | |
#include <Windows.h> | |
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; | |
#pragma pack(push,4) | |
typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR { | |
UCHAR Type; | |
UCHAR ShareDisposition; | |
USHORT Flags; | |
union { | |
struct { | |
PHYSICAL_ADDRESS Start; | |
ULONG Length; | |
} Generic; | |
struct { | |
PHYSICAL_ADDRESS Start; | |
ULONG Length; | |
} Port; | |
struct { | |
#if defined(NT_PROCESSOR_GROUPS) | |
USHORT Level; | |
USHORT Group; | |
#else | |
ULONG Level; | |
#endif | |
ULONG Vector; | |
KAFFINITY Affinity; | |
} Interrupt; | |
struct { | |
union { | |
struct { | |
#if defined(NT_PROCESSOR_GROUPS) | |
USHORT Group; | |
#else | |
USHORT Reserved; | |
#endif | |
USHORT MessageCount; | |
ULONG Vector; | |
KAFFINITY Affinity; | |
} Raw; | |
struct { | |
#if defined(NT_PROCESSOR_GROUPS) | |
USHORT Level; | |
USHORT Group; | |
#else | |
ULONG Level; | |
#endif | |
ULONG Vector; | |
KAFFINITY Affinity; | |
} Translated; | |
} DUMMYUNIONNAME; | |
} MessageInterrupt; | |
struct { | |
PHYSICAL_ADDRESS Start; | |
ULONG Length; | |
} Memory; | |
struct { | |
ULONG Channel; | |
ULONG Port; | |
ULONG Reserved1; | |
} Dma; | |
struct { | |
ULONG Channel; | |
ULONG RequestLine; | |
UCHAR TransferWidth; | |
UCHAR Reserved1; | |
UCHAR Reserved2; | |
UCHAR Reserved3; | |
} DmaV3; | |
struct { | |
ULONG Data[3]; | |
} DevicePrivate; | |
struct { | |
ULONG Start; | |
ULONG Length; | |
ULONG Reserved; | |
} BusNumber; | |
struct { | |
ULONG DataSize; | |
ULONG Reserved1; | |
ULONG Reserved2; | |
} DeviceSpecificData; | |
struct { | |
PHYSICAL_ADDRESS Start; | |
ULONG Length40; | |
} Memory40; | |
struct { | |
PHYSICAL_ADDRESS Start; | |
ULONG Length48; | |
} Memory48; | |
struct { | |
PHYSICAL_ADDRESS Start; | |
ULONG Length64; | |
} Memory64; | |
struct { | |
UCHAR Class; | |
UCHAR Type; | |
UCHAR Reserved1; | |
UCHAR Reserved2; | |
ULONG IdLowPart; | |
ULONG IdHighPart; | |
} Connection; | |
} u; | |
} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR; | |
#pragma pack(pop,4) | |
typedef enum _INTERFACE_TYPE { | |
InterfaceTypeUndefined, | |
Internal, | |
Isa, | |
Eisa, | |
MicroChannel, | |
TurboChannel, | |
PCIBus, | |
VMEBus, | |
NuBus, | |
PCMCIABus, | |
CBus, | |
MPIBus, | |
MPSABus, | |
ProcessorInternal, | |
InternalPowerBus, | |
PNPISABus, | |
PNPBus, | |
Vmcs, | |
ACPIBus, | |
MaximumInterfaceType | |
} INTERFACE_TYPE, *PINTERFACE_TYPE; | |
typedef struct _CM_PARTIAL_RESOURCE_LIST { | |
USHORT Version; | |
USHORT Revision; | |
ULONG Count; | |
CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1]; | |
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST; | |
typedef struct _CM_FULL_RESOURCE_DESCRIPTOR { | |
INTERFACE_TYPE InterfaceType; | |
ULONG BusNumber; | |
CM_PARTIAL_RESOURCE_LIST PartialResourceList; | |
} *PCM_FULL_RESOURCE_DESCRIPTOR, CM_FULL_RESOURCE_DESCRIPTOR; | |
typedef struct _CM_RESOURCE_LIST { | |
ULONG Count; | |
CM_FULL_RESOURCE_DESCRIPTOR List[1]; | |
} *PCM_RESOURCE_LIST, CM_RESOURCE_LIST; | |
struct memory_region { | |
ULONG64 size; | |
ULONG64 address; | |
}; | |
struct map_key { | |
LPTSTR KeyPath; | |
LPTSTR ValueName; | |
}; | |
/* registry keys for resource maps */ | |
#define VM_RESOURCE_CHECK_REGKEY_PHYSICAL 0 | |
#define VM_RESOURCE_CHECK_REGKEY_RESERVED 1 | |
#define VM_RESOURCE_CHECK_REGKEY_LOADER_RESERVED 2 | |
#define ResourceRegistryKeysLength 3 | |
const struct map_key ResourceRegistryKeys[ResourceRegistryKeysLength] = { | |
{ | |
L"Hardware\\ResourceMap\\System Resources\\Physical Memory", | |
L".Translated" | |
}, | |
{ | |
L"Hardware\\ResourceMap\\System Resources\\Reserved", | |
L".Translated" | |
}, | |
{ | |
L"Hardware\\ResourceMap\\System Resources\\Loader Reserved", | |
L".Raw" | |
} | |
}; | |
/* parse a REG_RESOURCE_LIST value for memory descriptors */ | |
DWORD parse_memory_map(struct memory_region *regions, struct map_key key) | |
{ | |
HKEY hKey = NULL; | |
LPTSTR pszSubKey = key.KeyPath; | |
LPTSTR pszValueName = key.ValueName; | |
LPBYTE lpData = NULL; | |
DWORD dwLength = 0, count = 0, type = 0;; | |
DWORD result; | |
if ((result = RegOpenKeyW(HKEY_LOCAL_MACHINE, pszSubKey, &hKey)) != ERROR_SUCCESS) | |
{ | |
printf("[X] Could not get reg key: %d / %d\n", result, GetLastError()); | |
return 0; | |
} | |
if ((result = RegQueryValueExW(hKey, pszValueName, 0, &type, NULL, &dwLength)) != ERROR_SUCCESS) | |
{ | |
printf("[X] Could not query hardware key: %d / %d\n", result, GetLastError()); | |
return 0; | |
} | |
lpData = (LPBYTE)malloc(dwLength); | |
RegQueryValueEx(hKey, pszValueName, 0, &type, lpData, &dwLength); | |
CM_RESOURCE_LIST *resource_list = (CM_RESOURCE_LIST *)lpData; | |
for (DWORD i = 0; i < resource_list->Count; i++) | |
{ | |
for (DWORD j = 0; j < resource_list->List[0].PartialResourceList.Count; j++) | |
{ | |
if (resource_list->List[i].PartialResourceList.PartialDescriptors[j].Type == 3) | |
{ | |
if (regions != NULL) | |
{ | |
regions->address = resource_list->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Start.QuadPart; | |
regions->size = resource_list->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Length; | |
regions++; | |
} | |
count++; | |
} | |
} | |
} | |
return count; | |
} | |
#define VM_RESOURCE_CHECK_ERROR -1 | |
#define VM_RESOURCE_CHECK_NO_VM 0 | |
#define VM_RESOURCE_CHECK_HYPERV 1 | |
#define VM_RESOURCE_CHECK_VBOX 2 | |
#define VM_RESOURCE_CHECK_UNKNOWN_PLATFORM 99 | |
int vm_resource_check( | |
struct memory_region *phys, int phys_count, | |
struct memory_region *reserved, int reserved_count, | |
struct memory_region *loader_reserved, int loader_reserved_count) | |
{ | |
const ULONG64 VBOX_PHYS_LO = 0x0000000000001000ULL; | |
const ULONG64 VBOX_PHYS_HI = 0x000000000009f000ULL; | |
const ULONG64 HYPERV_PHYS_LO = 0x0000000000001000ULL; | |
const ULONG64 HYPERV_PHYS_HI = 0x00000000000a0000ULL; | |
const ULONG64 RESERVED_ADDR_LOW = 0x0000000000001000ULL; | |
const ULONG64 LOADER_RESERVED_ADDR_LOW = 0x0000000000000000ULL; | |
if (phys_count <= 0 || reserved_count <= 0 || loader_reserved_count <= 0) | |
{ | |
return VM_RESOURCE_CHECK_ERROR; | |
} | |
if (phys == NULL || reserved == NULL || loader_reserved == NULL) | |
{ | |
return VM_RESOURCE_CHECK_ERROR; | |
} | |
/* find the reserved address range starting | |
RESERVED_ADDR_LOW, and record its end address */ | |
ULONG64 lowestReservedAddrRangeEnd = 0; | |
for (int i = 0; i < reserved_count; i++) | |
{ | |
if (reserved[i].address == RESERVED_ADDR_LOW) | |
{ | |
lowestReservedAddrRangeEnd = reserved[i].address + reserved[i].size; | |
break; | |
} | |
} | |
if (lowestReservedAddrRangeEnd == 0) | |
{ | |
/* every system tested had a range starting at RESERVED_ADDR_LOW */ | |
/* this is an outlier. error. */ | |
return VM_RESOURCE_CHECK_ERROR; | |
} | |
/* find the loader reserved address range starting | |
LOADER_RESERVED_ADDR_LOW, and record its end address */ | |
ULONG64 lowestLoaderReservedAddrRangeEnd = 0; | |
for (int i = 0; i < loader_reserved_count; i++) | |
{ | |
if (loader_reserved[i].address == LOADER_RESERVED_ADDR_LOW) | |
{ | |
lowestLoaderReservedAddrRangeEnd = loader_reserved[i].address + loader_reserved[i].size; | |
break; | |
} | |
} | |
if (lowestLoaderReservedAddrRangeEnd == 0) | |
{ | |
/* every system tested had a range starting at LOADER_RESERVED_ADDR_LOW */ | |
/* this is an outlier. error. */ | |
return VM_RESOURCE_CHECK_ERROR; | |
} | |
/* check if the end addresses are equal. if not, we haven't detected a VM */ | |
if (lowestReservedAddrRangeEnd != lowestLoaderReservedAddrRangeEnd) | |
{ | |
return VM_RESOURCE_CHECK_NO_VM; | |
} | |
/* now find the type of VM by its known physical memory range */ | |
for (int i = 0; i < phys_count; i++) | |
{ | |
if (phys[i].address == HYPERV_PHYS_LO && (phys[i].address + phys[i].size) == HYPERV_PHYS_HI) | |
{ | |
/* hyper-v */ | |
return VM_RESOURCE_CHECK_HYPERV; | |
} | |
if (phys[i].address == VBOX_PHYS_LO && (phys[i].address + phys[i].size) == VBOX_PHYS_HI) | |
{ | |
/* vbox */ | |
return VM_RESOURCE_CHECK_VBOX; | |
} | |
} | |
/* pretty sure it's a VM, but we don't know what type */ | |
return VM_RESOURCE_CHECK_UNKNOWN_PLATFORM; | |
} | |
int main() | |
{ | |
DWORD count; | |
printf("[*] Getting physical memory regions from registry\n"); | |
struct memory_region *regions[ResourceRegistryKeysLength]; | |
int region_counts[ResourceRegistryKeysLength]; | |
for (int i = 0; i < ResourceRegistryKeysLength; i++) | |
{ | |
printf("[*] Reading data from %ws\\%ws\n", | |
ResourceRegistryKeys[i].KeyPath, ResourceRegistryKeys[i].ValueName); | |
count = parse_memory_map(NULL, ResourceRegistryKeys[i]); | |
if (count == 0) | |
{ | |
printf("[X] Could not find memory region, exiting.\n"); | |
return -1; | |
} | |
regions[i] = (struct memory_region *)malloc(sizeof(struct memory_region) * count); | |
count = parse_memory_map(regions[i], ResourceRegistryKeys[i]); | |
region_counts[i] = count; | |
for (DWORD r = 0; r < count; r++) | |
{ | |
printf("[*] --> Memory region found: %.16llx - %.16llx\n", | |
regions[i][r].address, regions[i][r].address + regions[i][r].size); | |
} | |
} | |
int check_result = vm_resource_check( | |
regions[VM_RESOURCE_CHECK_REGKEY_PHYSICAL], | |
region_counts[VM_RESOURCE_CHECK_REGKEY_PHYSICAL], | |
regions[VM_RESOURCE_CHECK_REGKEY_RESERVED], | |
region_counts[VM_RESOURCE_CHECK_REGKEY_RESERVED], | |
regions[VM_RESOURCE_CHECK_REGKEY_LOADER_RESERVED], | |
region_counts[VM_RESOURCE_CHECK_REGKEY_LOADER_RESERVED] | |
); | |
switch (check_result) | |
{ | |
case VM_RESOURCE_CHECK_ERROR: | |
printf("[X] Error occurred during VM check.\n"); | |
break; | |
case VM_RESOURCE_CHECK_NO_VM: | |
printf("[-] No VM detected.\n"); | |
break; | |
case VM_RESOURCE_CHECK_HYPERV: | |
printf("[+] Detected Hyper-V.\n"); | |
break; | |
case VM_RESOURCE_CHECK_VBOX: | |
printf("[+] Detected VirtualBox.\n"); | |
break; | |
case VM_RESOURCE_CHECK_UNKNOWN_PLATFORM: | |
printf("[+] Likely VM detected, but cannot identify platform. \n"); | |
break; | |
default: | |
printf("[X] VM check returned unexpected value.\n"); | |
break; | |
} | |
printf("\nDone.\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment