HyperDbg Debugger
Loading...
Searching...
No Matches
SyscallFootprints.c File Reference

Try to hide SYSCALL methods from anti-debugging and anti-hypervisor. More...

#include "pch.h"

Functions

VOID TransparentHandleSystemCallHook (GUEST_REGS *Regs)
 Handle The triggered hook on KiSystemCall64 system call handler when the Transparency mode is enabled.
VOID TransparentHandleNtQuerySystemInformationSyscall (GUEST_REGS *Regs)
 Handle The NtQuerySystemInformation system call when the Transparent mode is enabled.
PVOID TransparentGetObjectNameFromAttributesVirtualPointer (UINT64 virtPtr)
 Obtain a copy of the PWCHAR wide character string from a OBJECT_ATTRIBUTES structure at a guest virtual address.
VOID TransparentHandleNtQueryAttributesFileSyscall (GUEST_REGS *Regs)
 Handle The NtQueryAttributesFile system call when the Transparent mode is enabled.
VOID TransparentHandleNtOpenDirectoryObjectSyscall (GUEST_REGS *Regs)
 Handle The NtOpenDirectoryObject system call when the Transparent mode is enabled.
VOID TransparentHandleNtSystemDebugControlSyscall (GUEST_REGS *Regs)
 Handle The NtSystemDebugControl system call when the Transparent mode is enabled.
VOID TransparentHandleNtQueryInformationProcessSyscall (GUEST_REGS *Regs)
 Handle The NtQueryInformationProcess system call when the Transparent mode is enabled.
VOID TransparentHandleNtOpenFileSyscall (GUEST_REGS *Regs)
 Handle The NtOpenFile system call when the Transparent mode is enabled.
VOID TransparentHandleNtOpenKeySyscall (GUEST_REGS *Regs)
 Handle The NtOpenKey system call when the Transparent mode is enabled.
VOID TransparentHandleNtQueryValueKeySyscall (GUEST_REGS *Regs)
 Handle The NtQueryValueKey system call when the Transparent mode is enabled.
VOID TransparentHandleNtEnumerateKeySyscall (GUEST_REGS *Regs)
 Handle The NtEnumerateKey system call when the Transparent mode is enabled.
BOOLEAN TransparentHandleModuleInformationQuery (PVOID Ptr, UINT64 VirtualAddress, UINT32 BufferSize)
 Handle the request for SystemModuleInformation.
BOOLEAN TransparentHandleProcessInformationQuery (SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
 Handle the request for SystemProcessInformation.
UINT64 TransparentHandleFirmwareInformationQuery (UINT64 Ptr, UINT32 BufMaxSize, UINT64 BufSizePtr)
 Handle the request for SystemFirmwareTableInformation.
UINT64 TransparentReplaceVendorStringFromBufferWChar (SYSCALL_CALLBACK_CONTEXT_PARAMS *Params, ULONG DataOffset, ULONG DataLenOffset)
 Replace occurrences of a hypervisor specific strings with legitimate vendor strings in a provided buffer.
UINT64 TransparentCallbackHandleAfterNtQueryValueKeySyscall (SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
 Callback function to handle the returns from the NtQueryValueKey syscall.
UINT64 TransparentCallbackHandleAfterNtEnumerateKeySyscall (SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
 Callback function to handle the returns from the NtEnumerateKey syscall.
VOID TransparentCallbackHandleAfterNtQuerySystemInformationSyscall (GUEST_REGS *Regs, SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
 Callback function to handle the returns from the NtQuerySystemInformation syscall.
VOID TransparentCallbackHandleAfterSyscall (GUEST_REGS *Regs, UINT32 ProcessId, UINT32 ThreadId, UINT64 Context, SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
 Callback function to handle returns from the syscall.

Detailed Description

Try to hide SYSCALL methods from anti-debugging and anti-hypervisor.

Author
Sina Karvandi (sina@.nosp@m.hype.nosp@m.rdbg..nosp@m.org)
Version
0.14
Date
2025-06-08

Function Documentation

◆ TransparentCallbackHandleAfterNtEnumerateKeySyscall()

UINT64 TransparentCallbackHandleAfterNtEnumerateKeySyscall ( SYSCALL_CALLBACK_CONTEXT_PARAMS * Params)

Callback function to handle the returns from the NtEnumerateKey syscall.

Parameters
ParamsThe set transparent callback params that contain: in OptionalParam1 the KEY_VALUE_INFORMATION_CLASS enum value in OptionalParam2 a pointer to a valid read/writable memory buffer that contains both, a WCHAR string and its length in bytes in OptionalParam3 max size in bytes of the allocated buffer in OptionalParam4 a pointer to a ULONG containing current size of the buffer
Returns
UINT64
1555{
1556 ULONG LenOffset = 0;
1557 ULONG BufOffset = 0;
1558
1559 //
1560 // Based on the KEY_INFORMATION_CLASS given, set the struct offsets for the data buffer and its length fields
1561 //
1562 switch (Params->OptionalParam1)
1563 {
1564 //
1565 // KeyBasicInformation
1566 //
1567 case 0x0:
1568 {
1569 LenOffset = sizeof(LARGE_INTEGER) + sizeof(ULONG);
1570 BufOffset = sizeof(LARGE_INTEGER) + (sizeof(ULONG) * 2);
1571
1572 break;
1573 }
1574
1575 //
1576 // KeyNodeInformation
1577 //
1578 case 0x1:
1579 {
1580 return 0;
1581 }
1582
1583 //
1584 // KeyNameInformation
1585 //
1586 case 0x3:
1587 {
1588 LenOffset = 0;
1589 BufOffset = sizeof(ULONG);
1590
1591 break;
1592 }
1593 default:
1594 {
1595 LogInfo("NtEnumerateKey was called with KeyInformationClass 0x%x, a handler for which has not been implemented", Params->OptionalParam1);
1596 return 0;
1597 }
1598 }
1599
1600 //
1601 // Given the user buffer, read and exchange any Hypervisor vendor strings in the registry key names
1602 //
1603 return TransparentReplaceVendorStringFromBufferWChar(Params, BufOffset, LenOffset);
1604}
UINT64 TransparentReplaceVendorStringFromBufferWChar(SYSCALL_CALLBACK_CONTEXT_PARAMS *Params, ULONG DataOffset, ULONG DataLenOffset)
Replace occurrences of a hypervisor specific strings with legitimate vendor strings in a provided buf...
Definition SyscallFootprints.c:1252
unsigned long ULONG
Definition BasicTypes.h:31
#define LogInfo(format,...)
Define log variables.
Definition HyperDbgHyperLogIntrinsics.h:71
UINT64 OptionalParam1
Definition DataTypes.h:343

◆ TransparentCallbackHandleAfterNtQuerySystemInformationSyscall()

VOID TransparentCallbackHandleAfterNtQuerySystemInformationSyscall ( GUEST_REGS * Regs,
SYSCALL_CALLBACK_CONTEXT_PARAMS * Params )

Callback function to handle the returns from the NtQuerySystemInformation syscall.

Parameters
RegsThe virtual processor's state of registers
ParamsThe (optional) parameters of the caller
Returns
VOID
1616{
1617 //
1618 // Handle each defined SYSTEM_INFORMATION_CLASS
1619 //
1620 switch (Params->OptionalParam1)
1621 {
1623 {
1624 //
1625 // Check if the obtained buffer pointer is valid
1626 //
1627 if (g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, (UINT32)Params->OptionalParam3))
1628 {
1630
1631 //
1632 // Read data from the saved pointer of the now filled information buffer
1633 //
1634 g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam2, &Temp, Params->OptionalParam3);
1635
1636 //
1637 // Modify the data and write it back to the information buffer to be passed to user mode
1638 //
1639 Temp.CodeIntegrityOptions = 0x01;
1640 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam2, &Temp, Params->OptionalParam3);
1641 }
1642 else
1643 {
1644 LogInfo("A call for the NtQuerySystemInformation system call requesting SystemCodeIntegrityInformation structure was made, but the usermode buffer was not captured");
1645 }
1646
1647 break;
1648 }
1651 {
1652 //
1653 // Check if the obtained buffer pointer is valid
1654 //
1655 if (Params->OptionalParam2 != 0x0 &&
1656 Params->OptionalParam3 != 0x0 &&
1657 g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, (UINT32)Params->OptionalParam3))
1658 {
1660 {
1661 //
1662 // Some internal Windows calls to these system calls use different offsetting/entry structure layout and causes errors
1663 //
1664
1665 // LogInfo("Error while modifying the buffer for data query 0x02x", Params->OptionalParam1);
1666 }
1667 }
1668 break;
1669 }
1670
1672 {
1673 //
1674 // Check if the obtained buffer pointer is valid
1675 //
1676 if (Params->OptionalParam2 != 0x0 &&
1677 Params->OptionalParam3 != 0x0 &&
1678 g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, (UINT32)Params->OptionalParam3))
1679 {
1680 //
1681 // Allocate a buffer to copy user buffer data to for modification
1682 //
1684 if (Buf == NULL)
1685 {
1686 LogError("Err, insufficient memory");
1687 break;
1688 }
1689
1690 //
1691 // Copy over the data and perform the modifications
1692 //
1693 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam2, Buf, Params->OptionalParam3))
1694 {
1695 LogInfo("Error reading memory buffer given by the usermode call");
1696 }
1697 else
1698 {
1700 {
1701 //
1702 // Some internal Windows calls to these system calls use different offsetting/entry structure layout and causes errors
1703 //
1704
1705 // LogInfo("Error while modifying the buffer for data query 0x02x", Params->OptionalParam1);
1706 }
1707 }
1708
1710 }
1711 break;
1712 }
1714 {
1715 //
1716 // Check if the obtained buffer pointer is valid
1717 //
1718 if (g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, (UINT32)Params->OptionalParam3))
1719 {
1720 //
1721 // Write to the output buffer 0x0001 for "Debugger not present"
1722 //
1723 WORD Temp = 0x0100;
1724 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam2, &Temp, 2);
1725 }
1726 }
1728 {
1729 if (g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, (UINT32)Params->OptionalParam3))
1730 {
1732 if (RetVal == 0x0)
1733 {
1734 LogInfo("A query for SystemFirmwareTableInformation was made, but the transparent mitigation failed");
1735 }
1736 else if (RetVal != 0x1)
1737 {
1738 LogInfo("Changing to %llx", RetVal);
1739 Regs->rax = RetVal;
1740 }
1741 }
1742 else
1743 {
1744 LogInfo("A query for SystemFirmwareTableInformation was made, but the user-mode buffer was not captured");
1745 }
1746 break;
1747 }
1748
1749 default:
1750 {
1751 break;
1752 }
1753 }
1754}
PVOID PlatformMemAllocateZeroedNonPagedPool(SIZE_T NumberOfBytes)
Allocates zeroed non-paged pool memory.
Definition PlatformMem.c:248
PVOID PlatformMemFreePool(PVOID BufferAddress)
Frees a memory pool.
Definition PlatformMem.c:269
BOOLEAN TransparentHandleProcessInformationQuery(SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
Handle the request for SystemProcessInformation.
Definition SyscallFootprints.c:889
UINT64 TransparentHandleFirmwareInformationQuery(UINT64 Ptr, UINT32 BufMaxSize, UINT64 BufSizePtr)
Handle the request for SystemFirmwareTableInformation.
Definition SyscallFootprints.c:1068
BOOLEAN TransparentHandleModuleInformationQuery(PVOID Ptr, UINT64 VirtualAddress, UINT32 BufferSize)
Handle the request for SystemModuleInformation.
Definition SyscallFootprints.c:835
struct _SYSTEM_CODEINTEGRITY_INFORMATION SYSTEM_CODEINTEGRITY_INFORMATION
System Information for Code Integrity.
@ SystemKernelDebuggerInformation
Definition SyscallFootprints.h:28
@ SystemFirmwareTableInformation
Definition SyscallFootprints.h:30
@ SystemModuleInformation
Definition SyscallFootprints.h:27
@ SystemCodeIntegrityInformation
Definition SyscallFootprints.h:29
@ SystemProcessInformation
Definition SyscallFootprints.h:24
@ SystemExtendedProcessInformation
Definition SyscallFootprints.h:25
unsigned short WORD
Definition BasicTypes.h:42
void * PVOID
Definition BasicTypes.h:56
unsigned int UINT32
Definition BasicTypes.h:54
#define LogError(format,...)
Log in the case of error.
Definition HyperDbgHyperLogIntrinsics.h:113
HYPEREVADE_CALLBACKS g_Callbacks
List of callbacks.
Definition Transparency.h:23
UINT64 OptionalParam4
Definition DataTypes.h:346
UINT64 OptionalParam2
Definition DataTypes.h:344
UINT64 OptionalParam3
Definition DataTypes.h:345
ULONG CodeIntegrityOptions
Definition SyscallFootprints.h:45
UINT64 rax
Definition BasicTypes.h:141

◆ TransparentCallbackHandleAfterNtQueryValueKeySyscall()

UINT64 TransparentCallbackHandleAfterNtQueryValueKeySyscall ( SYSCALL_CALLBACK_CONTEXT_PARAMS * Params)

Callback function to handle the returns from the NtQueryValueKey syscall.

Parameters
ParamsThe set transparent callback params that contain: in OptionalParam1 the KEY_VALUE_INFORMATION_CLASS enum value in OptionalParam2 a pointer to a valid read/writable memory buffer that contains both, a WCHAR string and its length in bytes in OptionalParam3 max size in bytes of the allocated buffer in OptionalParam4 a pointer to a ULONG containing current size of the buffer
Returns
UINT64
1473{
1474 ULONG LenOffset = 0;
1475 ULONG BufOffset = 0;
1476
1477 //
1478 // Based on the KEY_VALUE_INFORMATION_CLASS given, set the struct offsets for the data buffer and its length fields
1479 //
1480 switch (Params->OptionalParam1)
1481 {
1482 //
1483 // KeyValueBasicInformation and queries for a key that has a hypervisor specific name
1484 //
1485 case 0x0:
1486 {
1487 if (Params->OptionalParam2 != 0)
1488 return 0;
1489
1490 //
1491 // If the key query was for a registry key that has a hypervisor specific name, return an error code
1492 //
1493 return (UINT64)(UINT32)STATUS_OBJECT_NAME_NOT_FOUND;
1494 }
1495
1496 //
1497 // KeyValuePartialInformation
1498 //
1499 case 0x2:
1500 {
1501 LenOffset = sizeof(ULONG) * 2;
1502 BufOffset = sizeof(ULONG) * 3;
1503
1504 break;
1505 }
1506
1507 //
1508 // KeyValueFullInformation
1509 //
1510 case 0x3:
1511 case 0x1:
1512 {
1513 LenOffset = sizeof(ULONG) * 3;
1514 BufOffset = sizeof(ULONG) * 4; // Name offset, Data is after it
1515
1516 break;
1517 }
1518
1519 //
1520 // KeyValuePartialInformationAlign64
1521 //
1522 case 0x4:
1523 {
1524 LenOffset = sizeof(ULONG) * 1;
1525 BufOffset = sizeof(ULONG) * 2;
1526
1527 break;
1528 }
1529 default:
1530 {
1531 LogInfo("NtQueryValueKey was called with KeyValueInformationClass 0x%x, a handler for which has not been implemented", Params->OptionalParam1);
1532 return 0;
1533 }
1534 }
1535
1536 //
1537 // Given the user buffer, read and exchange any Hypervisor vendor strings in the registry key data
1538 //
1539 return TransparentReplaceVendorStringFromBufferWChar(Params, BufOffset, LenOffset);
1540}

◆ TransparentCallbackHandleAfterSyscall()

VOID TransparentCallbackHandleAfterSyscall ( GUEST_REGS * Regs,
UINT32 ProcessId,
UINT32 ThreadId,
UINT64 Context,
SYSCALL_CALLBACK_CONTEXT_PARAMS * Params )

Callback function to handle returns from the syscall.

Parameters
RegsThe virtual processor's state of registers
ProcessIdThe process id of the thread
ThreadIdThe thread id of the thread
ContextThe context of the caller
ParamsThe (optional) parameters of the caller
Returns
VOID
1773{
1774 //
1775 // Handle each defined system call separately, after the kernel execution has finished(at the SYSRET instruction)
1776 //
1777
1778 //
1779 // Handle the memory buffer and return code modification after NtQuerySystemInformation system call
1780 //
1781 if (Context == g_SystemCallNumbersInformation.SysNtQuerySystemInformation)
1782 {
1784 }
1785 //
1786 // Handle the memory buffer and return code modification after NtQueryAttributesFile system call
1787 //
1788 else if (Context == g_SystemCallNumbersInformation.SysNtQueryAttributesFile)
1789 {
1790 //
1791 // Check if the obtained buffer pointer is valid
1792 //
1793 if (g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam1, sizeof(FILE_BASIC_INFORMATION)))
1794 {
1795 FILE_BASIC_INFORMATION Buf = {0};
1796 //
1797 // Copy over the data from the output buffer pointer
1798 //
1799 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam1, &Buf, sizeof(FILE_BASIC_INFORMATION)))
1800 {
1801 LogError("Err, Virtual memory read failed");
1802 }
1803 else
1804 {
1805 //
1806 // Modify the file attribute to INVALID_FILE_ATTRIBUTES and write it back to the pointer
1807 //
1808 Buf.FileAttributes = ((DWORD)-1);
1809
1810 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam1, &Buf, sizeof(FILE_BASIC_INFORMATION)))
1811 {
1812 LogError("Err, Virtual memory write failed");
1813 }
1814 }
1815 }
1816 else
1817 {
1818 LogInfo("A call for the NtQueryAttributeFile system call for a marked file was made, but the output buffer was not captured");
1819 }
1820 }
1821
1822 //
1823 // Handle the memory buffer and return code modification after NtOpenDirectoryObject system call.
1824 //
1825 // NOTE: No transparent mitigations of this call have been implemented
1826 //
1827 else if (Context == g_SystemCallNumbersInformation.SysNtOpenDirectoryObject)
1828 {
1829 LogInfo("A NtOpenDirectoryObject system call was made for a known directory that reveals hypervisor presence. process: %x, thread: %x\n",
1830 ProcessId,
1831 ThreadId);
1832 LogInfo("No action to mitigate this was made as a handler for NtOpenDirectoryObject has not been implemented");
1833 }
1834 //
1835 // Handle the memory buffer modification after NtQueryInformationProcess system call
1836 //
1837 else if (Context == g_SystemCallNumbersInformation.SysNtQueryInformationProcess)
1838 {
1839 switch (Params->OptionalParam1)
1840 {
1841 case 0x07:
1842 {
1843 if (g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, sizeof(DWORD_PTR)))
1844 {
1845 //
1846 // Zero out the return buffer to user-mode
1847 //
1848 DWORD_PTR NoDebugPort = 0x0;
1849
1850 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam2, &NoDebugPort, sizeof(DWORD_PTR));
1851 }
1852 break;
1853 }
1854 case 0x1f:
1855 {
1856 //
1857 // Zero out the return buffer to user-mode
1858 //
1859 ULONG notDebugged = 0x0;
1860 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam2, &notDebugged, sizeof(ULONG));
1861 break;
1862 }
1863 case 0x1e:
1864 {
1865 if (g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, (UINT32)Params->OptionalParam3))
1866 {
1867 LogInfo("Process %llx called the NtQueryInformationProcess system call with the ProcessDebugObject class, no transparent mitigations were performed", ProcessId);
1868 }
1869 break;
1870 }
1871 default:
1872 {
1873 break;
1874 }
1875 }
1876 }
1877 //
1878 // Handle the return code modification after NtSystemDebugControl system call
1879 //
1880 else if (Context == g_SystemCallNumbersInformation.SysNtSystemDebugControl)
1881 {
1882 //
1883 // In the entry handler, the Syscall number was changed to corrupt this call, after the SYSRET, change the return code to STATUS_DEBUGGER_INACTIVE
1884 //
1885 Regs->rax = (UINT64)(UINT32)STATUS_DEBUGGER_INACTIVE;
1886 }
1887
1888 //
1889 // Handle the return code modification after SysNtOpenFile system call
1890 //
1891 else if (Context == g_SystemCallNumbersInformation.SysNtOpenFile)
1892 {
1893 //
1894 // In the entry handler, the Syscall number was changed to corrupt this call if the request was for a known hypervisor file
1895 // after the SYSRET, change the return code to STATUS_OBJECT_NAME_NOT_FOUND
1896 //
1897 Regs->rax = (UINT64)(UINT32)STATUS_OBJECT_NAME_NOT_FOUND;
1898 }
1899
1900 //
1901 // Handle the return code modification after NtNtQueryValueKey system call
1902 //
1903 // NOTE: The transparent mitigation will replace all occurrences of a hypervisor vendor string in the registry
1904 // key data to a randomized real hardware vendor string, no matter the meaning of the key,
1905 // This can cause some keys to produce illogical data, for example,
1906 // a disk drive ID having a vendor string of ASUS even though (as far as I know) ASUS doesn't produce storage devices.
1907 //
1908 else if (Context == g_SystemCallNumbersInformation.SysNtQueryValueKey)
1909 {
1910 UINT64 RetVal;
1911
1912 //
1913 // Call the handler of NtQueryValueKey syscall callback
1914 //
1916
1917 //
1918 // If a custom(Specific to transparency) error code should be returned,
1919 // set it to %RAX
1920 // Else leave it to what the kernel already set it to
1921 //
1922 if (RetVal != 0)
1923 {
1924 Regs->rax = RetVal;
1925 }
1926 }
1927 //
1928 // Handle the memory buffer modification after NtOpenKey system call and its derivatives
1929 //
1930 else if (Context == g_SystemCallNumbersInformation.SysNtOpenKey || Context == g_SystemCallNumbersInformation.SysNtOpenKeyEx)
1931 {
1932 //
1933 // In the entry handler, the Syscall number was changed to corrupt this call if the request was for a known hypervisor registry key
1934 // after the SYSRET, change the return code to STATUS_OBJECT_NAME_NOT_FOUND
1935 //
1936 Regs->rax = (UINT64)(UINT32)STATUS_OBJECT_NAME_NOT_FOUND;
1937 }
1938 else if (Context == g_SystemCallNumbersInformation.SysNtEnumerateKey)
1939 {
1940 UINT64 RetVal;
1941
1942 //
1943 // Call the handler of NtEnumerateKey syscall callback
1944 //
1946
1947 //
1948 // If a custom(Specific to transparency) error code should be returned,
1949 // set it to %RAX
1950 // Else leave it to what the kernel already set it to
1951 //
1952 if (RetVal != 0)
1953 {
1954 Regs->rax = RetVal;
1955 }
1956 }
1957 else
1958 {
1959 //
1960 // A SYSRET trap flag was inserted for a System call that does not have a transparency handler implemented
1961 //
1962 LogInfo("Transparent callback for an unimplemented system call handle with the trap flag for process: %x, thread: %x, context: %llx RAX: %llx (p1: %llx, p2: %llx, p3: %llx, p4: %llx) \n",
1963 ProcessId,
1964 ThreadId,
1965 Context,
1966 Regs->rax,
1967 Params->OptionalParam1,
1968 Params->OptionalParam2,
1969 Params->OptionalParam3,
1970 Params->OptionalParam4);
1971 }
1972}
UINT64 TransparentCallbackHandleAfterNtQueryValueKeySyscall(SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
Callback function to handle the returns from the NtQueryValueKey syscall.
Definition SyscallFootprints.c:1472
VOID TransparentCallbackHandleAfterNtQuerySystemInformationSyscall(GUEST_REGS *Regs, SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
Callback function to handle the returns from the NtQuerySystemInformation syscall.
Definition SyscallFootprints.c:1615
UINT64 TransparentCallbackHandleAfterNtEnumerateKeySyscall(SYSCALL_CALLBACK_CONTEXT_PARAMS *Params)
Callback function to handle the returns from the NtEnumerateKey syscall.
Definition SyscallFootprints.c:1554
SYSTEM_CALL_NUMBERS_INFORMATION g_SystemCallNumbersInformation
System call numbers information.
Definition SyscallFootprints.h:158
unsigned long DWORD
Definition BasicTypes.h:38

◆ TransparentGetObjectNameFromAttributesVirtualPointer()

PVOID TransparentGetObjectNameFromAttributesVirtualPointer ( UINT64 virtPtr)

Obtain a copy of the PWCHAR wide character string from a OBJECT_ATTRIBUTES structure at a guest virtual address.

Returns an allocated tagged memory pointer which needs to be freed with PlatformMemFreePool()

Parameters
virtPtrA pointer to a guest virtual memory address, containing a OBJECT_ATTRIBUTES structure
Returns
PVOID Pointer to an allocated tagged memory pool, which needs to be freed with PlatformMemFreePool()
284{
285 // PVOID buf = PlatformMemAllocateZeroedNonPagedPool(sizeof(OBJECT_ATTRIBUTES));
286 OBJECT_ATTRIBUTES Buf = {0};
287
288 //
289 // Read the OBJECT_ATTRIBUTES structure from the virtual address pointer
290 //
291 if (g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(virtPtr, &Buf, sizeof(OBJECT_ATTRIBUTES)))
292 {
293 // PVOID Namebuf = PlatformMemAllocateZeroedNonPagedPool(sizeof(UNICODE_STRING));
294 UNICODE_STRING NameBuf = {0};
295
296 //
297 // Read the UNICODE_STRING structure from a virtual address pointer, pointed to by OBJECT_ATTRIBUTES.ObjectName struct entry
298 //
299 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)Buf.ObjectName, &NameBuf, sizeof(UNICODE_STRING)))
300 {
301 LogInfo("BadRead");
302 return NULL;
303 }
304
305 //
306 // The OBJECT_ATTRIBUTES structure contains a PUNICODE_STRING pointer to a guest virtual address which contains this UNICODE_STRING
307 // This in turn will contain another pointer, this time a PWCHAR, to another virtual address, which will contain the wide char string we need
308 //
309 PVOID ObjectNameBuf = PlatformMemAllocateZeroedNonPagedPool(NameBuf.Length + sizeof(WCHAR));
310
311 if (ObjectNameBuf == NULL)
312 {
313 LogInfo("Error allocating ImageName memory buffer");
314
315 return NULL;
316 }
317
318 //
319 // Read the PWCHAR string from a virtual address pointer, pointed to by OBJECT_ATTRIBUTES.ObjectName.Buffer struct entry
320 //
321 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)NameBuf.Buffer, ObjectNameBuf, NameBuf.Length + sizeof(WCHAR)))
322 {
323 LogInfo("BadRead");
324 PlatformMemFreePool(ObjectNameBuf);
325
326 return NULL;
327 }
328
329 //
330 // The caller is responsible for freeing the memory buffer, using PlatformMemFreePool()
331 //
332 return ObjectNameBuf;
333 }
334 return NULL;
335}
struct _UNICODE_STRING UNICODE_STRING
NULL()
Definition test-case-generator.py:530
USHORT Length
Definition casting.cpp:26
PWSTR Buffer
Definition casting.cpp:28

◆ TransparentHandleFirmwareInformationQuery()

UINT64 TransparentHandleFirmwareInformationQuery ( UINT64 Ptr,
UINT32 BufMaxSize,
UINT64 BufSizePtr )

Handle the request for SystemFirmwareTableInformation.

Parameters
PtrThe pointer to a valid read/writable SYSTEM_FIRMWARE_TABLE_INFORMATION memory buffer
BufMaxSizeThe size of the allocated user-mode buffer
BufSizePtrA pointer to a ULONG field containing the size of the written data
Returns
BOOLEAN
1069{
1070 ULONG BufSize = 0;
1071
1072 //
1073 // Read the size of the data the kernel wrote in the buffer
1074 //
1075 if (!g_Callbacks.CheckAccessValidityAndSafety(BufSizePtr, sizeof(ULONG)) ||
1076 !g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(BufSizePtr, &BufSize, sizeof(ULONG)))
1077 {
1078 return 0;
1079 }
1080
1081 //
1082 // If the memory buffer was too small for the kernel to write the needed information, bypass the mitigations
1083 //
1084 if (BufSize > BufMaxSize)
1085 {
1086 //
1087 // NOTE: might need zeroing the memory if the kernel did in fact write to the pointer
1088 //
1089 return 0;
1090 }
1091
1092 //
1093 // If the buffer size is too big, we can't do any mitigations with the current implementation and it exceeds
1094 // the size of an EPT page, risking corruption when allocating the copy buffer space in root-mode.
1095 //
1096 if (BufMaxSize > PAGE_SIZE / 2)
1097 {
1098 LogInfo("The intercepted data buffer was too large for modification, in total 0x%x bytes", BufMaxSize);
1099 LogInfo("The system call return value was set to STATUS_INVALID_INFO_CLASS, but this could be detected as hypervisor intervention");
1100
1101 return (UINT64)(UINT32)STATUS_INVALID_INFO_CLASS;
1102 }
1103
1104 if (BufSize == 0)
1105 {
1106 BufSize = BufMaxSize;
1107 }
1108 //
1109 // From the user-mode pointer, read the SYSTEM_FIRMWARE_TABLE_INFORMATION struct
1110 //
1111 PVOID Buf = PlatformMemAllocateZeroedNonPagedPool(BufMaxSize + 1);
1112 if (Buf == NULL)
1113 {
1114 return 0;
1115 }
1116
1117 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Ptr, Buf, BufSize))
1118 {
1120 return 0;
1121 }
1122
1123 PSYSTEM_FIRMWARE_TABLE_INFORMATION StructBuf = (PSYSTEM_FIRMWARE_TABLE_INFORMATION)Buf;
1124
1125 //
1126 // The request needs to be a "get" request for an existing table
1127 // with 'RSMB', 'ACPI' or 'FIRM' table providers
1128 //
1129 if (StructBuf->Action == SystemFirmwareTable_Get &&
1130 StructBuf->TableID != 0 &&
1131 (StructBuf->ProviderSignature == 0x52534D42 ||
1132 StructBuf->ProviderSignature == 0x41435049 ||
1133 StructBuf->ProviderSignature == 0x4649524D))
1134 {
1135 PCHAR StringBuf = (PCHAR)StructBuf->TableBuffer;
1136
1137 for (ULONG i = 0; i < (sizeof(HV_FIRM_NAMES) / sizeof(HV_FIRM_NAMES[0])); i++)
1138 {
1139 for (ULONG j = 0; j < StructBuf->TableBufferLength; j++)
1140 {
1141 WORD Count = 0;
1142 PCHAR MatchStart = strstr(StringBuf + j, HV_FIRM_NAMES[i]);
1143 if (MatchStart != 0)
1144 {
1145 LogInfo("Found Match for %s", HV_FIRM_NAMES[i]);
1146
1147 PCHAR NewVendorString = NULL;
1148 ULONG NewSubstringSize = 0;
1149
1150 //
1151 // Replace the first occurace of the vendor string with AMERICAN MEGATRENDS INC.
1152 // The rest with To Be Filled By O.E.M.
1153 //
1154 if (Count == 0)
1155 {
1156 NewVendorString = "AMERICAN MEGATRENDS INC.";
1157 NewSubstringSize = 24 * sizeof(CHAR);
1158 }
1159 else
1160 {
1161 NewVendorString = "To Be Filled By O.E.M.";
1162 NewSubstringSize = 22 * sizeof(CHAR);
1163 }
1164
1165 //
1166 // Obtain the lengths of all the strings and substring
1167 //
1168
1169 ULONG MatchedStringLen = (ULONG)strlen(HV_FIRM_NAMES[i]);
1170 ULONG OldLength = StructBuf->TableBufferLength;
1171
1172 ULONG NewStringSize = OldLength - MatchedStringLen + NewSubstringSize;
1173
1174 //
1175 // Check if the buffer size allows the modification, in case of expansion
1176 //
1177 if (BufSize - MatchedStringLen + NewSubstringSize > BufMaxSize)
1178 {
1179 //
1180 // If adding the new string exceeds the user allocated size,
1181 // zero out the buffer
1182 //
1183 memset(Buf, 0x0, BufMaxSize);
1184 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Ptr, Buf, BufMaxSize);
1185
1186 //
1187 // Update the required buffer size for the next call
1188 //
1189 BufSize = (BufSize - OldLength) + NewStringSize;
1190 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(BufSizePtr, &BufSize, sizeof(ULONG));
1191
1192 //
1193 // And return STATUS_BUFFER_TOO_SMALL error
1194 //
1195
1197 return (UINT64)(UINT32)STATUS_BUFFER_TOO_SMALL;
1198 }
1199
1200 //
1201 // Calculate the positions of the replacement
1202 //
1203 ULONG MatchOffset = (ULONG)((MatchStart - StringBuf));
1204 PCHAR MatchEnd = StringBuf + MatchOffset + MatchedStringLen;
1205
1206 //
1207 // Move the data after the matched string forward
1208 // and replace the identified hypervisor string with the genuine one
1209 //
1210 memmove((PVOID)(StringBuf + MatchOffset + NewSubstringSize), (PVOID)MatchEnd, OldLength - MatchedStringLen - MatchOffset);
1211 memcpy((PVOID)MatchStart, (PVOID)NewVendorString, NewSubstringSize);
1212
1213 StructBuf->TableBufferLength = NewStringSize;
1214 BufSize = BufSize - MatchedStringLen + NewSubstringSize;
1215
1216 //
1217 // Write the changes back to the user buffers
1218 //
1219 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Ptr, Buf, BufSize))
1220 {
1221 LogInfo("Error writing to user-mode buffer: %llx", Ptr);
1222 }
1223
1224 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(BufSizePtr, &BufSize, sizeof(ULONG)))
1225 {
1226 LogInfo("Error writing to user-mode buffer: %llx", BufSizePtr);
1227 }
1228 }
1229 }
1230 }
1231 }
1232
1234 return 1;
1235}
char CHAR
Definition BasicTypes.h:33
#define PAGE_SIZE
Size of each page (4096 bytes).
Definition common.h:80

◆ TransparentHandleModuleInformationQuery()

BOOLEAN TransparentHandleModuleInformationQuery ( PVOID Ptr,
UINT64 VirtualAddress,
UINT32 BufferSize )

Handle the request for SystemModuleInformation.

This function removes entries from a list of system drivers that could reveal the presence of hypervisors This depends on an incomplete list HV_DRIVER, of known hypervisor drivers The revealing list entries are removed and overwritten, but the memory buffer is not reallocated, so it is possible to still detect that some tampering was done from the user space

Parameters
PtrThe pointer to a valid read/writable SYSTEM_MODULE_INFORMATION memory buffer
VirtualAddressA pointer to a user-mode virtual address
BufferSizeSize of the user-mode buffer
Returns
BOOLEAN
836{
838 PSYSTEM_MODULE_ENTRY ModuleList = StructBuf->Module;
839
840 //
841 // Traverse the list of system modules and remove the system drivers
842 // matching a known list of hypervisor drivers based on their filename
843 //
844 for (UINT16 i = 0; i < StructBuf->Count; i++)
845 {
846 PCHAR Path = (PCHAR)ModuleList[i].FullPathName;
847
848 for (UINT16 j = 0; j < (sizeof(HV_DRIVER) / sizeof(HV_DRIVER[0])); j++)
849 {
850 if (strstr(Path, HV_DRIVER[j]))
851 {
852 //
853 // If a module file name matches, remove the entry from the list by shifting it forward by one entry
854 //
855 for (UINT16 k = i; k < StructBuf->Count - 1; k++)
856 {
857 ModuleList[k] = ModuleList[k + 1];
858 }
859
860 //
861 // Decrement the list size as one entry has been removed
862 //
863 i--;
864 StructBuf->Count--;
865
866 break;
867 }
868 }
869 }
870 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(VirtualAddress, Ptr, BufferSize))
871 {
872 return FALSE;
873 }
874 return TRUE;
875}
struct _SYSTEM_MODULE_ENTRY * PSYSTEM_MODULE_ENTRY
struct _SYSTEM_MODULE_INFORMATION * PSYSTEM_MODULE_INFORMATION
unsigned short UINT16
Definition BasicTypes.h:53
#define TRUE
Definition BasicTypes.h:114
#define FALSE
Definition BasicTypes.h:113
ULONG Count
Definition SyscallFootprints.h:120
SYSTEM_MODULE_ENTRY Module[1]
Definition SyscallFootprints.h:121

◆ TransparentHandleNtEnumerateKeySyscall()

VOID TransparentHandleNtEnumerateKeySyscall ( GUEST_REGS * Regs)

Handle The NtEnumerateKey system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
767{
768 //
769 // Set up the context parameters for the interception callback
770 //
771 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
772 ContextParams.OptionalParam1 = Regs->r8;
773 ContextParams.OptionalParam2 = Regs->r9;
774
775 //
776 // Read the 5th argument of the system call from the stack at location %RSP + 0x28
777 //
778 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->rsp + 0x28, sizeof(UINT64)))
779 {
780 g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)(Regs->rsp + 0x28), &ContextParams.OptionalParam3, sizeof(ULONG));
781 }
782 else
783 {
784 LogInfo("Process 0x%llx on thread %llx executed NtEnumerateKey systemcall but reading the provided arguments from %RSP failed", HANDLE_TO_UINT32(PsGetCurrentProcessId()), HANDLE_TO_UINT32(PsGetCurrentThreadId()));
785 return;
786 }
787
788 //
789 // Read the 6th argument of the system call from the stack at location %RSP + 0x30
790 //
791 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->rsp + 0x30, sizeof(UINT64)))
792 {
793 g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)(Regs->rsp + 0x30), &ContextParams.OptionalParam4, sizeof(UINT64));
794 }
795 else
796 {
797 LogInfo("Process 0x%llx on thread %llx executed NtEnumerateKey systemcall but reading the provided arguments from %RSP failed", HANDLE_TO_UINT32(PsGetCurrentProcessId()), HANDLE_TO_UINT32(PsGetCurrentThreadId()));
798 return;
799 }
800
801 //
802 // If the call was made without an allocated buffer (with size 0)
803 // we have no need to intercept it
804 //
805 if (ContextParams.OptionalParam3 == 0)
806 {
807 return;
808 }
809
810 //
811 // Set the trap flag to intercept the SYSRET instruction
812 //
813 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
814 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
815 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
816 Regs->rax,
817 &ContextParams);
818}
#define HANDLE_TO_UINT32(_var)
Definition MetaMacros.h:39
struct _SYSCALL_CALLBACK_CONTEXT_PARAMS SYSCALL_CALLBACK_CONTEXT_PARAMS
The (optional) context parameters for the transparent-mode.
UINT64 rsp
Definition BasicTypes.h:145
UINT64 r9
Definition BasicTypes.h:150
UINT64 r8
Definition BasicTypes.h:149

◆ TransparentHandleNtOpenDirectoryObjectSyscall()

VOID TransparentHandleNtOpenDirectoryObjectSyscall ( GUEST_REGS * Regs)

Handle The NtOpenDirectoryObject system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
394{
395 //
396 // Set up the context data for the callback after SYSRET
397 //
398 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
399 ContextParams.OptionalParam1 = Regs->r10;
400
401 //
402 // Check if the pointer given as the 3rd argument to the system call with type POBJECT_ATTRIBUTES is valid
403 //
404 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->r8, sizeof(OBJECT_ATTRIBUTES)))
405 {
406 //
407 // From the POBJECT_ATTRIBUTES structure obtain the wide character string of the requested directory path
408 //
410 PWCH DirPath = (PWCH)PathBuf;
411
412 if (DirPath == NULL)
413 {
414 return;
415 }
416
417 //
418 // If the directory object request is for a listed directory, insert the SYSCALL trap flag and continue execution
419 //
420 for (UINT16 j = 0; j < (sizeof(HV_DIRS) / sizeof(HV_DIRS[0])); j++)
421 {
422 if (wcsstr(DirPath, HV_DIRS[j]))
423 {
424 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
425 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
426 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
427 Regs->rax,
428 &ContextParams);
429
430 break;
431 }
432 }
433
434 //
435 // Free the allocated copy of the directory path, obtained from TransparentGetObjectNameFromAttributesVirtualPointer()
436 //
437 PlatformMemFreePool(PathBuf);
438 }
439}
PVOID TransparentGetObjectNameFromAttributesVirtualPointer(UINT64 virtPtr)
Obtain a copy of the PWCHAR wide character string from a OBJECT_ATTRIBUTES structure at a guest virtu...
Definition SyscallFootprints.c:283
UINT64 r10
Definition BasicTypes.h:151

◆ TransparentHandleNtOpenFileSyscall()

VOID TransparentHandleNtOpenFileSyscall ( GUEST_REGS * Regs)

Handle The NtOpenFile system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
505{
506 //
507 // Check if the user-mode pointer in R8 to a OBJECT_ATTRIBUTES struct is valid
508 //
509 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->r8, sizeof(OBJECT_ATTRIBUTES)))
510 {
511 //
512 // From the OBJECT_ATTRIBUTES struct pointer extract the file path for which this syscall is called
513 //
515 PWCH FileName = (PWCH)NameBuf;
516 if (FileName == NULL)
517 {
518 return;
519 }
520
521 //
522 // Check if the requested file includes any hypervisor specific strings
523 // This also checks parent directory names of the requested file
524 //
525 for (UINT16 j = 0; j < (sizeof(HV_FILES) / sizeof(HV_FILES[0])); j++)
526 {
527 if (wcsstr(FileName, HV_FILES[j]))
528 {
529 LogInfo("A call to NtOpenFile systemcall for a hypervisor specific file was made");
530
531 //
532 // If a match was found, corrupt the user-mode pointers in CPU registers, so that, when the kernel-mode execution continues, it would fail.
533 //
534 Regs->r8 = 0x0;
535 Regs->r10 = 0x0;
536
537 //
538 // Set the trap flag to intercept the SYSRET instruction
539 //
540 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
541 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
542 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
543 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
544 Regs->rax,
545 &ContextParams);
546
547 break;
548 }
549 }
550 //
551 // Clean up the allocated memory
552 //
553 PlatformMemFreePool(NameBuf);
554 }
555}

◆ TransparentHandleNtOpenKeySyscall()

VOID TransparentHandleNtOpenKeySyscall ( GUEST_REGS * Regs)

Handle The NtOpenKey system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
566{
567 //
568 // Check if the user-mode pointer in R8 to a OBJECT_ATTRIBUTES struct is valid
569 //
570 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->r8, sizeof(OBJECT_ATTRIBUTES)))
571 {
572 //
573 // From the OBJECT_ATTRIBUTES struct pointer extract the registry key path for which this syscall is called
574 //
576 PWCH KeyName = (PWCH)NameBuf;
577
578 if (KeyName == NULL)
579 {
580 LogInfo("BADRET");
581 return;
582 }
583
584 //
585 // Check if the requested registry entry path includes any hypervisor specific strings
586 //
587 for (UINT16 j = 0; j < (sizeof(HV_REGKEYS) / sizeof(HV_REGKEYS[0])); j++)
588 {
589 if (wcsstr(KeyName, HV_REGKEYS[j]) > 0)
590 {
591 //
592 // If a match was found, corrupt the user-mode pointer in CPU registers, so that, when the kernel-mode execution continues, it would fail.
593 //
594 Regs->r8 = 0x0;
595
596 //
597 // Set the trap flag to intercept the SYSRET instruction
598 //
599 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
600 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
601 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
602 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
603 Regs->rax,
604 &ContextParams);
605
606 break;
607 }
608 }
609
610 //
611 // Clean up the allocated memory
612 //
613 PlatformMemFreePool(NameBuf);
614 }
615}

◆ TransparentHandleNtQueryAttributesFileSyscall()

VOID TransparentHandleNtQueryAttributesFileSyscall ( GUEST_REGS * Regs)

Handle The NtQueryAttributesFile system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
346{
347 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
348 ContextParams.OptionalParam1 = Regs->rdx;
349
350 //
351 // Check if the pointer given as the 3rd argument to the system call with type POBJECT_ATTRIBUTES is valid
352 //
353 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->r10, sizeof(OBJECT_ATTRIBUTES)))
354 {
355 //
356 // From the POBJECT_ATTRIBUTES structure obtain the wide character string of the requested file path
357 //
359 PWCH FilePath = (PWCH)PathBuf;
360
361 //
362 // If the file Attributes request is for a listed file, insert the SYSCALL trap flag and continue execution
363 //
364 for (UINT16 j = 0; j < (sizeof(HV_FILES) / sizeof(HV_FILES[0])); j++)
365 {
366 if (wcsstr(FilePath, HV_FILES[j]))
367 {
368 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
369 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
370 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
371 Regs->rax,
372 &ContextParams);
373
374 break;
375 }
376 }
377
378 //
379 // Free the allocated copy of the directory path, obtained from TransparentGetObjectNameFromAttributesVirtualPointer()
380 //
381 PlatformMemFreePool(PathBuf);
382 }
383}
UINT64 rdx
Definition BasicTypes.h:143

◆ TransparentHandleNtQueryInformationProcessSyscall()

VOID TransparentHandleNtQueryInformationProcessSyscall ( GUEST_REGS * Regs)

Handle The NtQueryInformationProcess system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
477{
478 //
479 // Set up the context parameters for the interception callback
480 //
481 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
482 ContextParams.OptionalParam1 = Regs->rdx; // ProcessInformationClass
483 ContextParams.OptionalParam2 = Regs->r8; // BufferPtr
484 ContextParams.OptionalParam3 = Regs->r9; // BufferSize
485
486 //
487 // Set the trap flag to intercept the SYSRET instruction
488 //
489 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
490 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
491 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
492 Regs->rax,
493 &ContextParams);
494}

◆ TransparentHandleNtQuerySystemInformationSyscall()

VOID TransparentHandleNtQuerySystemInformationSyscall ( GUEST_REGS * Regs)

Handle The NtQuerySystemInformation system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
186{
187 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
188
189 switch (Regs->r10)
190 {
193 {
195 ContextParams.OptionalParam2 = Regs->rdx;
196 ContextParams.OptionalParam3 = Regs->r8 - 0x400;
197
198 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
199 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
200 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
201 Regs->rax,
202 &ContextParams);
203
204 break;
205 }
207 {
209 ContextParams.OptionalParam2 = Regs->rdx;
210 ContextParams.OptionalParam3 = Regs->r8;
211
212 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
213 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
214 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
215 Regs->rax,
216 &ContextParams);
217
218 break;
219 }
221 {
223 ContextParams.OptionalParam2 = Regs->rdx;
224 ContextParams.OptionalParam3 = Regs->r8;
225
226 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
227 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
228 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
229 Regs->rax,
230 &ContextParams);
231 break;
232 }
234 {
236 ContextParams.OptionalParam2 = Regs->rdx;
237 ContextParams.OptionalParam3 = 0x8;
238
239 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
240 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
241 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
242 Regs->rax,
243 &ContextParams);
244 break;
245 }
246
247 //
248 // Currently SystemFirmwareTableInformation transparent handler is not implemented
249 // As the queries produce a data buffer too large to safely copy and modify in root-mode
250 //
251
252 // case SystemFirmwareTableInformation:
253 // {
254 //
255 // ContextParams.OptionalParam1 = SystemFirmwareTableInformation;
256 // ContextParams.OptionalParam2 = Regs->rdx;
257 // ContextParams.OptionalParam3 = Regs->r8;
258 // ContextParams.OptionalParam4 = Regs->r9;
259 //
260 // g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
261 // HANDLE_TO_UINT32(PsGetCurrentProcessId()),
262 // HANDLE_TO_UINT32(PsGetCurrentThreadId()),
263 // Regs->rax,
264 // &ContextParams);
265 // break;
266 // }
267 default:
268 {
269 return;
270 }
271 }
272}

◆ TransparentHandleNtQueryValueKeySyscall()

VOID TransparentHandleNtQueryValueKeySyscall ( GUEST_REGS * Regs)

Handle The NtQueryValueKey system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
626{
627 //
628 // Check if the user-mode pointer in RDX to a UNICODE_STRING struct is valid
629 //
630 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->rdx, sizeof(UNICODE_STRING)))
631 {
632 UNICODE_STRING NameUString = {0};
633
634 //
635 // Read the UNICODE_STRING structure from a virtual address pointer, pointed to by RDX struct entry
636 //
637 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Regs->rdx, &NameUString, sizeof(UNICODE_STRING)))
638 return;
639
640 //
641 // Read the PWCH wide char string from the address pointer in the UNICODE_STRING
642 //
643 PVOID NameBuf = PlatformMemAllocateZeroedNonPagedPool(NameUString.Length + sizeof(WCHAR));
644 if (NameBuf == NULL)
645 {
646 return;
647 }
648
649 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)NameUString.Buffer, NameBuf, NameUString.Length + sizeof(WCHAR)))
650 {
651 PlatformMemFreePool(NameBuf);
652 return;
653 }
654
655 PWCH KeyName = (PWCH)NameBuf;
656
657 //
658 // If the registry key request was for kay that could contain hypervisor specific information in its data,
659 // the return buffer(%R9) needs to be modified, but the buffer length is in the user mode stack
660 //
661 for (ULONG i = 0; i < (sizeof(TRANSPARENT_DETECTABLE_REGISTRY_KEYS) / sizeof(TRANSPARENT_DETECTABLE_REGISTRY_KEYS[0])); i++)
662 {
663 if (!wcscmp(KeyName, TRANSPARENT_DETECTABLE_REGISTRY_KEYS[i]))
664 {
665 //
666 // If a match is found, set up the context values and set the trap flag for the SYSRET callback
667 //
668
669 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
670
671 ContextParams.OptionalParam1 = Regs->r8;
672 ContextParams.OptionalParam2 = Regs->r9;
673
674 //
675 // Read the 5th argument of the system call from the stack at location %RSP + 0x28
676 //
677 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->rsp + 0x28, sizeof(UINT64)))
678 {
679 g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)(Regs->rsp + 0x28), &ContextParams.OptionalParam3, sizeof(ULONG));
680 }
681 else
682 {
683 LogInfo("Process 0x%llx on thread %llx executed NtQueryValueKey systemcall but reading the provided arguments from %RSP failed", HANDLE_TO_UINT32(PsGetCurrentProcessId()), HANDLE_TO_UINT32(PsGetCurrentThreadId()));
684
685 PlatformMemFreePool(NameBuf);
686 return;
687 }
688
689 //
690 // Read the 6th argument of the system call from the stack at location %RSP + 0x30
691 //
692 if (g_Callbacks.CheckAccessValidityAndSafety(Regs->rsp + 0x30, sizeof(UINT64)))
693 {
694 g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)(Regs->rsp + 0x30), &ContextParams.OptionalParam4, sizeof(UINT64));
695 }
696 else
697 {
698 LogInfo("Process 0x%llx on thread %llx executed NtQueryValueKey systemcall but reading the provided arguments from %RSP failed", HANDLE_TO_UINT32(PsGetCurrentProcessId()), HANDLE_TO_UINT32(PsGetCurrentThreadId()));
699
700 PlatformMemFreePool(NameBuf);
701 return;
702 }
703
704 //
705 // Set the trap flag to intercept the SYSRET instruction
706 //
707 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
708 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
709 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
710 Regs->rax,
711 &ContextParams);
712
713 //
714 // Clean-up and return to guest execution
715 //
716 PlatformMemFreePool(NameBuf);
717 return;
718 }
719 }
720
721 //
722 // If the call was for a registry key that contains a hypervisor specific string,
723 // The user-mode caller should just receive an error return code not a modified data buffer
724 //
725 for (UINT16 j = 1; j < (sizeof(HV_REGKEYS) / sizeof(HV_REGKEYS[0])); j++)
726 {
727 if (wcsstr(KeyName, HV_REGKEYS[j]) > 0)
728 {
729 //
730 // When the match is found, corrupt the buffer pointers in the registers
731 // and set the SYSRET callback trap flag
732 //
733 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
734
735 Regs->rdx = 0x0;
736 Regs->r9 = 0x0;
737
738 //
739 // Set the trap flag to intercept the SYSRET instruction
740 //
741 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
742 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
743 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
744 Regs->rax,
745 &ContextParams);
746
747 break;
748 }
749 }
750
751 //
752 // Clean up the allocated memory
753 //
754 PlatformMemFreePool(NameBuf);
755 }
756}

◆ TransparentHandleNtSystemDebugControlSyscall()

VOID TransparentHandleNtSystemDebugControlSyscall ( GUEST_REGS * Regs)

Handle The NtSystemDebugControl system call when the Transparent mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
450{
451 //
452 // Corrupt the system call arguments, to cause the kernel to return an error
453 //
454 Regs->r9 = 0x0;
455
456 SYSCALL_CALLBACK_CONTEXT_PARAMS ContextParams = {0};
457
458 //
459 // Set the trap flag to intercept the SYSRET instruction
460 //
461 g_Callbacks.SyscallCallbackSetTrapFlagAfterSyscall(Regs,
462 HANDLE_TO_UINT32(PsGetCurrentProcessId()),
463 HANDLE_TO_UINT32(PsGetCurrentThreadId()),
464 Regs->rax,
465 &ContextParams);
466}

◆ TransparentHandleProcessInformationQuery()

BOOLEAN TransparentHandleProcessInformationQuery ( SYSCALL_CALLBACK_CONTEXT_PARAMS * Params)

Handle the request for SystemProcessInformation.

This function removes entries from a list of active system processes that could reveal the presence of hypervisors

Parameters
ParamsPreset transparent callback params that contain: in OptionalParam2 a pointer to a valid read/writable memory buffer that contains a SYSTEM_PROCESS_INFORMATION structure in OptionalParam3 max size in bytes of the allocated buffer
Returns
BOOLEAN
890{
891 SYSTEM_PROCESS_INFORMATION PrevStructBuf = {0};
892 SYSTEM_PROCESS_INFORMATION CurStructBuf = {0};
893
894 ULONG ReadOffset, WriteOffset;
895 BOOLEAN MatchFound = FALSE;
896 ULONG PrevOffset = 0;
897
898 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam2, &PrevStructBuf, sizeof(SYSTEM_PROCESS_INFORMATION)))
899 {
900 return FALSE;
901 }
902
903 ReadOffset = PrevStructBuf.NextEntryOffset;
904 WriteOffset = 0;
905
906 //
907 // The first entry will always be System Idle Process, which can be skipped
908 //
909 if (PrevStructBuf.NextEntryOffset == 0 ||
910 !g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam2 + ReadOffset, &CurStructBuf, sizeof(SYSTEM_PROCESS_INFORMATION)))
911 {
912 return FALSE;
913 }
914
915 //
916 // Loop through all the entries and filter out the offending ones
917 //
918 do
919 {
920 MatchFound = FALSE;
921
922 if (CurStructBuf.ImageName.Length != 0)
923 {
924 //
925 // We need to search for the Image name of the process which requires extra allocation
926 //
927 PVOID StringBuf = PlatformMemAllocateZeroedNonPagedPool(CurStructBuf.ImageName.Length + sizeof(WCHAR));
928
929 if (StringBuf == NULL)
930 {
931 LogInfo("Error allocating ImageName memory buffer");
932
933 return FALSE;
934 }
935
936 //
937 // Read the WCHAR process image name from the user-mode pointer
938 //
939 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess((UINT64)CurStructBuf.ImageName.Buffer, StringBuf, CurStructBuf.ImageName.Length + sizeof(WCHAR)))
940 {
941 PlatformMemFreePool(StringBuf);
942
943 return FALSE;
944 }
945
946 PWCH ImageName = (PWCH)StringBuf;
947
948 if (ImageName == NULL)
949 {
950 PlatformMemFreePool(StringBuf);
951
952 return FALSE;
953 }
954
955 //
956 // Loop through the known list of identifiable hypervisor related processes
957 //
958 for (UINT16 i = 0; i < (sizeof(HV_PROCESSES) / sizeof(HV_PROCESSES[0])); i++)
959 {
960 if (!_wcsnicmp(ImageName, HV_PROCESSES[i], (CurStructBuf.ImageName.Length) / sizeof(WCHAR)))
961 {
962 //
963 // If the name matches, bypass it by increasing the previous entries .nextEntryOffset value
964 //
965
966 //
967 // The offset to this matching entry need to preserved for zeroing later
968 //
969 PrevOffset = PrevStructBuf.NextEntryOffset;
970
971 PrevStructBuf.NextEntryOffset = PrevStructBuf.NextEntryOffset + CurStructBuf.NextEntryOffset;
972
973 MatchFound = TRUE;
974
975 //
976 // Write the modified offset back to the usermode buffer
977 //
978 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess((UINT64)(Params->OptionalParam2 + WriteOffset), &PrevStructBuf, sizeof(SYSTEM_PROCESS_INFORMATION)))
979 {
980 LogError("Failed to modify memory buffer for the SystemProcessInformation query system call");
981 }
982
983 //
984 // The entry gets bypassed, but since the Image name is a pointer in the struct, to completely clear any presence of these processes
985 // zero out the name buffer as well
986 //
987 memset(StringBuf, 0x0, CurStructBuf.ImageName.Length);
988 ULONG BufOffset = (ULONG)((PBYTE)&CurStructBuf.ImageName.Length - (PBYTE)&CurStructBuf) + sizeof(USHORT);
989
990 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess((UINT64)(Params->OptionalParam2 + WriteOffset + PrevOffset + BufOffset), StringBuf, CurStructBuf.ImageName.Length))
991 {
992 LogError("Failed to modify memory buffer for the SystemProcessInformation query system call");
993 }
994
995 break;
996 }
997 }
998
999 PlatformMemFreePool(StringBuf);
1000 }
1001
1002 //
1003 // If the last entry been reached, exit
1004 //
1005 if (CurStructBuf.NextEntryOffset == 0)
1006 {
1007 return TRUE;
1008 }
1009
1010 //
1011 // If the current entry did not match any process names, move forward
1012 //
1013 if (!MatchFound)
1014 {
1015 WriteOffset += PrevStructBuf.NextEntryOffset;
1016
1017 PrevStructBuf = CurStructBuf;
1018 }
1019
1020 //
1021 // Move over to the next entry
1022 //
1023 ReadOffset += CurStructBuf.NextEntryOffset;
1024
1025 //
1026 // Some internal Windows calls to this system call use different offsetting/entry structure layout and causes errors
1027 //
1028 if (!g_Callbacks.CheckAccessValidityAndSafety((UINT64)(Params->OptionalParam2 + ReadOffset), sizeof(SYSTEM_PROCESS_INFORMATION)))
1029 {
1030 return FALSE;
1031 }
1032
1033 //
1034 // Zero out the matching entry, so that its data doesn't remain in memory
1035 //
1036 if (MatchFound)
1037 {
1038 CurStructBuf = (SYSTEM_PROCESS_INFORMATION) {0};
1039 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess((UINT64)(Params->OptionalParam2 + WriteOffset + PrevOffset), &CurStructBuf, sizeof(SYSTEM_PROCESS_INFORMATION)))
1040 {
1041 return FALSE;
1042 }
1043 }
1044
1045 //
1046 // Read from the user buffer the next process entry
1047 //
1048 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam2 + ReadOffset, &CurStructBuf, sizeof(SYSTEM_PROCESS_INFORMATION)))
1049 {
1050 return FALSE;
1051 }
1052
1053 } while (TRUE);
1054
1055 return TRUE;
1056}
struct _SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION
System Information for running processes.
UCHAR BOOLEAN
Definition BasicTypes.h:35
unsigned short USHORT
Definition BasicTypes.h:30
UNICODE_STRING ImageName
Definition SyscallFootprints.h:58
ULONG NextEntryOffset
Definition SyscallFootprints.h:55

◆ TransparentHandleSystemCallHook()

VOID TransparentHandleSystemCallHook ( GUEST_REGS * Regs)

Handle The triggered hook on KiSystemCall64 system call handler when the Transparency mode is enabled.

Parameters
RegsThe virtual processor's state of registers
Returns
VOID
66{
67 //
68 // If the transparent mode is not enabled, do nothing
69 //
71 {
72 return;
73 }
74
75 PCHAR CallingProcess = g_Callbacks.CommonGetProcessNameFromProcessControlBlock(PsGetCurrentProcess());
76 UINT64 Context = Regs->rax;
77
78 //
79 // Skip the transparent mitigations of system calls when the caller process
80 // is a Windows process that should receive unmodified data
81 //
82 for (ULONG i = 0; i < (sizeof(TRANSPARENT_WIN_PROCESS_IGNORE) / sizeof(TRANSPARENT_WIN_PROCESS_IGNORE[0])); i++)
83 {
84 if (strstr(CallingProcess, TRANSPARENT_WIN_PROCESS_IGNORE[i]))
85 {
86 return;
87 }
88 }
89
90 if (Context == g_SystemCallNumbersInformation.SysNtQuerySystemInformation ||
91 Context == g_SystemCallNumbersInformation.SysNtQuerySystemInformationEx)
92 {
93 //
94 // Handle the NtQuerySystemInformation System call
95 //
96
98 }
99 else if (Context == g_SystemCallNumbersInformation.SysNtSystemDebugControl)
100 {
101 //
102 // Handle the NtSystemDebugControl System call
103 //
105 }
106 else if (Context == g_SystemCallNumbersInformation.SysNtQueryAttributesFile)
107 {
108 //
109 // Handle the NtQueryAttributesFile System call
110 //
112 }
113 else if (Context == g_SystemCallNumbersInformation.SysNtOpenDirectoryObject)
114 {
115 //
116 // Handle the NtOpenDirectoryObject System call
117 //
119 }
120 else if (Context == g_SystemCallNumbersInformation.SysNtQueryDirectoryObject)
121 {
122 //
123 // Handle the NtQueryDirectoryObject System call
124 //
125 // TransparentHandleNtQueryDirectoryObjectSyscall(Regs);
126 }
127 else if (Context == g_SystemCallNumbersInformation.SysNtQueryInformationProcess)
128 {
129 //
130 // Handle the NtQueryInformationProcess System call
131 //
133 }
134 else if (Context == g_SystemCallNumbersInformation.SysNtQueryInformationThread)
135 {
136 //
137 // Handle the NtQueryInformationThread System call
138 //
139 // TransparentHandleNtQueryInformationThreadSyscall(Regs);
140 }
141 else if (Context == g_SystemCallNumbersInformation.SysNtOpenFile)
142 {
143 //
144 // Handle the NtOpenFile System call
145 //
147 }
148 else if (Context == g_SystemCallNumbersInformation.SysNtOpenKeyEx || Context == g_SystemCallNumbersInformation.SysNtOpenKey)
149 {
150 //
151 // Handle the NtOpenKey System call
152 //
154 }
155 else if (Context == g_SystemCallNumbersInformation.SysNtQueryValueKey)
156 {
157 //
158 // Handle the NtQueryValueKey System call
159 //
161 }
162 else if (Context == g_SystemCallNumbersInformation.SysNtEnumerateKey)
163 {
164 //
165 // Handle the NtEnumerateKey System call
166 //
168 }
169 else
170 {
171 //
172 // The syscall is not important to us
173 //
174 }
175}
VOID TransparentHandleNtQuerySystemInformationSyscall(GUEST_REGS *Regs)
Handle The NtQuerySystemInformation system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:185
VOID TransparentHandleNtQueryInformationProcessSyscall(GUEST_REGS *Regs)
Handle The NtQueryInformationProcess system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:476
VOID TransparentHandleNtQueryValueKeySyscall(GUEST_REGS *Regs)
Handle The NtQueryValueKey system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:625
VOID TransparentHandleNtEnumerateKeySyscall(GUEST_REGS *Regs)
Handle The NtEnumerateKey system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:766
VOID TransparentHandleNtOpenFileSyscall(GUEST_REGS *Regs)
Handle The NtOpenFile system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:504
VOID TransparentHandleNtSystemDebugControlSyscall(GUEST_REGS *Regs)
Handle The NtSystemDebugControl system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:449
VOID TransparentHandleNtOpenKeySyscall(GUEST_REGS *Regs)
Handle The NtOpenKey system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:565
VOID TransparentHandleNtQueryAttributesFileSyscall(GUEST_REGS *Regs)
Handle The NtQueryAttributesFile system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:345
VOID TransparentHandleNtOpenDirectoryObjectSyscall(GUEST_REGS *Regs)
Handle The NtOpenDirectoryObject system call when the Transparent mode is enabled.
Definition SyscallFootprints.c:393
BOOLEAN g_TransparentMode
Shows whether the debugger transparent mode is enabled (true) or not (false).
Definition Transparency.h:66

◆ TransparentReplaceVendorStringFromBufferWChar()

UINT64 TransparentReplaceVendorStringFromBufferWChar ( SYSCALL_CALLBACK_CONTEXT_PARAMS * Params,
ULONG DataOffset,
ULONG DataLenOffset )

Replace occurrences of a hypervisor specific strings with legitimate vendor strings in a provided buffer.

Parameters
ParamsSet transparent callback params that contain: in OptionalParam2 a pointer to a valid read/writable memory buffer that contains both, a WCHAR string and its length in bytes in OptionalParam3 max size in bytes of the allocated buffer in OptionalParam4 a pointer to a ULONG containing current size of the buffer
DataOffsetOffset in bytes from OptionalParam2 to the start of the WCHAR data string
DataLenOffsetOffset in bytes from OptionalParam2 to a ULONG containing the string length(in bytes)
Returns
UINT64
1253{
1254 PVOID Buf = NULL;
1255 BOOLEAN PoolAlloc = FALSE;
1256
1257 //
1258 // Check that the user provided pointers are safe to read from
1259 //
1260 if (g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam4, sizeof(ULONG)))
1261 {
1262 //
1263 // Read the size of the data that the kernel wrote to the buffer
1264 //
1265 ULONG BufSize = 0;
1266 if (!g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam4, &BufSize, sizeof(ULONG)))
1267 {
1268 goto ReturnWithError;
1269 }
1270
1271 //
1272 // If the data the kernel wanted to write is bigger than what was allocated by the user
1273 //
1274 if (BufSize > Params->OptionalParam3 || BufSize == 0)
1275 {
1276 //
1277 // NOTE: might need zeroing the memory if the kernel did in fact write to the pointer
1278 //
1279
1280 return 0;
1281 }
1282
1283 //
1284 // Check that the user provided pointers are safe to read from and the buffer is not too large
1285 //
1286 if (Params->OptionalParam3 >= 0xC00 || !g_Callbacks.CheckAccessValidityAndSafety(Params->OptionalParam2, (UINT32)Params->OptionalParam3))
1287 {
1288 goto ReturnWithError;
1289 }
1290
1291 //
1292 // If the buffer is small, e.g. for just a single word, store it on the stack
1293 // else, allocate a nonpaged memory buffer
1294 //
1295 CHAR StackBuf[MAX_PATH] = {0};
1296
1297 if (Params->OptionalParam3 + sizeof(WCHAR) > MAX_PATH)
1298 {
1299 Buf = PlatformMemAllocateZeroedNonPagedPool(Params->OptionalParam3 + sizeof(WCHAR));
1300 PoolAlloc = TRUE;
1301 }
1302 else
1303 {
1304 Buf = &StackBuf;
1305 }
1306
1307 if (!Buf || !g_Callbacks.MemoryMapperReadMemorySafeOnTargetProcess(Params->OptionalParam2, Buf, Params->OptionalParam3))
1308 {
1309 goto ReturnWithError;
1310 }
1311
1312 //
1313 // Get the actual data we are trying to modify(in wide char form)
1314 //
1315 PWCH StringBuf = (PWCH)((PBYTE)Buf + DataOffset);
1316
1317 //
1318 // Traverse the list of registry key names and vendor strings that are specific to common hypervisors
1319 // if a match is found perform the modification
1320 //
1321 for (UINT16 i = 0; i < (sizeof(HV_REGKEYS) / sizeof(HV_REGKEYS[0])); i++)
1322 {
1323 PWCH MatchStart = wcsstr(StringBuf, HV_REGKEYS[i]);
1324
1325 while (MatchStart != 0)
1326 {
1327 PWCH NewVendorString = NULL;
1328
1329 //
1330 // If the match was for a device id, the replacement should be with a different ID string not vendor name
1331 //
1332 if (i < 3)
1333 {
1334 //
1335 // SPOOFS PCI device ID's(in the registry), This might be implemented in other ways that are not part of this implementation
1336 //
1337 WORD Idx = g_TransparentGenuineVendorStringIndex % (sizeof(TRANSPARENT_LEGIT_DEVICE_ID_VENDOR_STRINGS_WCHAR) / sizeof(TRANSPARENT_LEGIT_DEVICE_ID_VENDOR_STRINGS_WCHAR[0]));
1338 NewVendorString = TRANSPARENT_LEGIT_DEVICE_ID_VENDOR_STRINGS_WCHAR[Idx];
1339 }
1340
1341 //
1342 // Remove common VM strings from the data
1343 //
1344 else if (i < 9)
1345 {
1346 NewVendorString = L" ";
1347 }
1348 else
1349 {
1350 //
1351 // Obtain the replacement vendor name string, randomized when the transparency mode was enabled
1352 //
1353 NewVendorString = TRANSPARENT_LEGIT_VENDOR_STRINGS_WCHAR[g_TransparentGenuineVendorStringIndex];
1354 }
1355
1356 //
1357 // Obtain the lengths of all the strings and substring
1358 //
1359 ULONG TempSize = (ULONG)wcslen(NewVendorString) * sizeof(WCHAR);
1360
1361 ULONG MatchedStringLen = (ULONG)wcslen(HV_REGKEYS[i]) * sizeof(WCHAR);
1362 ULONG OldLength = *((PBYTE)Buf + DataLenOffset);
1363
1364 ULONG NewStringSize = OldLength - MatchedStringLen + TempSize;
1365
1366 //
1367 // Check if the buffer size allows the modification, in case of expansion
1368 //
1369 if (BufSize - MatchedStringLen + TempSize > Params->OptionalParam3)
1370 {
1371 //
1372 // If adding the new string exceeds the user allocated size,
1373 // zero out the buffer
1374 //
1375 memset(Buf, 0x0, Params->OptionalParam3);
1376 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam2, Buf, Params->OptionalParam3);
1377
1378 //
1379 // Update the required buffer size for the next call
1380 //
1381 BufSize = (TempSize - MatchedStringLen) + OldLength;
1382 g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam4, &BufSize, sizeof(ULONG));
1383
1384 //
1385 // And return STATUS_BUFFER_OVERFLOW error
1386 //
1387
1388 if (PoolAlloc)
1390 return (UINT64)(UINT32)STATUS_BUFFER_OVERFLOW;
1391 }
1392
1393 //
1394 // Calculate the positions of the replacement
1395 //
1396 ULONG MatchOffset = (ULONG)((MatchStart - StringBuf));
1397 PWCH MatchEnd = StringBuf + MatchOffset + (MatchedStringLen / sizeof(WCHAR));
1398
1399 //
1400 // Move the data after the matched string forward
1401 //
1402 memmove((PVOID)(StringBuf + MatchOffset + (TempSize / sizeof(WCHAR))), (PVOID)MatchEnd, OldLength - MatchedStringLen - (MatchOffset * sizeof(WCHAR)));
1403
1404 //
1405 // Replace the identified hypervisor string with the genuine one, if needed
1406 //
1407 memcpy((PVOID)MatchStart, (PVOID)NewVendorString, TempSize);
1408
1409 *(PULONG)((PBYTE)Buf + DataLenOffset) = NewStringSize;
1410 BufSize = BufSize - MatchedStringLen + TempSize;
1411
1412 //
1413 // Write the changes back to the user buffers
1414 //
1415 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam2, Buf, BufSize))
1416 {
1417 goto ReturnWithError;
1418 }
1419
1420 if (!g_Callbacks.MemoryMapperWriteMemorySafeOnTargetProcess(Params->OptionalParam4, &BufSize, sizeof(ULONG)))
1421 {
1422 goto ReturnWithError;
1423 }
1424
1425 //
1426 // Cleanup
1427 //
1428
1429 MatchStart = wcsstr(StringBuf, HV_REGKEYS[i]);
1430
1431 if (!MatchStart)
1432 i = 0;
1433 }
1434 }
1435
1436 //
1437 // The data buffer contained no detectable strings
1438 //
1439 if (PoolAlloc)
1441 return 0;
1442 }
1443
1444 //
1445 // An error occurred while performing the mitigations, the user buffer might be left unmodified
1446 //
1447ReturnWithError:
1448 LogInfo("A call for to read a registry entry, which could contain hypervisor specific data, was intercepted but the mitigations failed");
1449 LogInfo("The caller process received the results in this virtual address: %llx", Params->OptionalParam2);
1450
1451 if (Buf != NULL)
1452 {
1453 if (PoolAlloc)
1455 }
1456
1457 return 0;
1458}