Skip to content

Instantly share code, notes, and snippets.

@CaledoniaProject
Created March 17, 2021 13:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CaledoniaProject/4a2b1d81c106e60d4901df927b11d87f to your computer and use it in GitHub Desktop.
Save CaledoniaProject/4a2b1d81c106e60d4901df927b11d87f to your computer and use it in GitHub Desktop.
system resources physical memory map VM detection trick
// 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