HyperDbg/hyperdbg/hyperevade/code/Transparency.c
2025-08-18 00:30:13 +02:00

754 lines
20 KiB
C

/**
* @file Transparency.c
* @author Sina Karvandi (sina@hyperdbg.org)
* @brief Try to hide the debugger from anti-debugging and anti-hypervisor methods
* @details
* @version 0.1
* @date 2020-07-07
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
/**
* @brief Hide debugger on transparent-mode (activate transparent-mode)
*
* @param HyperevadeCallbacks
* @param TransparentModeRequest
*
* @return BOOLEAN
*/
BOOLEAN
TransparentHideDebugger(HYPEREVADE_CALLBACKS * HyperevadeCallbacks,
DEBUGGER_HIDE_AND_TRANSPARENT_DEBUGGER_MODE * TransparentModeRequest)
{
//
// Check if any of the required callbacks are NULL
//
for (UINT32 i = 0; i < sizeof(HYPEREVADE_CALLBACKS) / sizeof(UINT64); i++)
{
if (((PVOID *)HyperevadeCallbacks)[i] == NULL)
{
//
// The callback has null entry, so we cannot proceed
//
TransparentModeRequest->KernelStatus = DEBUGGER_ERROR_UNABLE_TO_HIDE_OR_UNHIDE_DEBUGGER;
return FALSE;
}
}
//
// Save the callbacks
//
RtlCopyMemory(&g_Callbacks, HyperevadeCallbacks, sizeof(HYPEREVADE_CALLBACKS));
//
// Check whether the transparent-mode was already initialized or not
//
if (!g_TransparentMode)
{
//
// Store the system-call numbers information
//
RtlCopyBytes(&g_SystemCallNumbersInformation,
&TransparentModeRequest->SystemCallNumbersInformation,
sizeof(SYSTEM_CALL_NUMBERS_INFORMATION));
#if DISABLE_HYPERDBG_HYPEREVADE == FALSE
//
// Choose a random genuine vendor string to replace hypervisor vendor data
//
TRANSPARENT_GENUINE_VENDOR_STRING_INDEX = TransparentGetRand() %
(sizeof(TRANSPARENT_LEGIT_VENDOR_STRINGS_WCHAR) / sizeof(TRANSPARENT_LEGIT_VENDOR_STRINGS_WCHAR[0]));
#endif
//
// Enable the transparent mode
//
g_TransparentMode = TRUE;
TransparentModeRequest->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
//
// Successfully enabled the transparent-mode
//
return TRUE;
}
else
{
TransparentModeRequest->KernelStatus = DEBUGGER_ERROR_DEBUGGER_ALREADY_HIDE;
return FALSE;
}
}
/**
* @brief Deactivate transparent-mode
*
* @return BOOLEAN
*/
BOOLEAN
TransparentUnhideDebugger()
{
if (g_TransparentMode)
{
//
// Disable the transparent-mode
//
g_TransparentMode = FALSE;
return TRUE;
}
else
{
return FALSE;
}
}
/**
* @brief Generate a random number by utilizing RDTSC instruction.
*
* Masking 16 LSB of the measured clock time.
* @return UINT32
*/
UINT32
TransparentGetRand()
{
UINT64 Tsc;
UINT32 Rand;
Tsc = __rdtsc();
Rand = Tsc & 0xffff;
return Rand;
}
/**
* @brief Add name or process id of the target process to the list
* of processes that HyperDbg should apply transparent-mode on them
*
* @param Measurements
* @return BOOLEAN
*/
BOOLEAN
TransparentAddNameOrProcessIdToTheList(PDEBUGGER_HIDE_AND_TRANSPARENT_DEBUGGER_MODE Measurements)
{
SIZE_T SizeOfBuffer;
PTRANSPARENCY_PROCESS PidAndNameBuffer;
//
// Check whether it's a process id or it's a process name
//
if (Measurements->TrueIfProcessIdAndFalseIfProcessName)
{
//
// It's a process Id
//
SizeOfBuffer = sizeof(TRANSPARENCY_PROCESS);
}
else
{
//
// It's a process name
//
SizeOfBuffer = sizeof(TRANSPARENCY_PROCESS) + Measurements->LengthOfProcessName;
}
//
// Allocate the Buffer
//
PidAndNameBuffer = PlatformMemAllocateZeroedNonPagedPool(SizeOfBuffer);
if (PidAndNameBuffer == NULL)
{
return FALSE;
}
//
// Save the address of the buffer for future de-allocation
//
PidAndNameBuffer->BufferAddress = PidAndNameBuffer;
//
// Check again whether it's a process id or it's a process name
// then fill the structure
//
if (Measurements->TrueIfProcessIdAndFalseIfProcessName)
{
//
// It's a process Id
//
PidAndNameBuffer->ProcessId = Measurements->ProcId;
PidAndNameBuffer->TrueIfProcessIdAndFalseIfProcessName = TRUE;
}
else
{
//
// It's a process name
//
PidAndNameBuffer->TrueIfProcessIdAndFalseIfProcessName = FALSE;
//
// Move the process name string to the end of the buffer
//
RtlCopyBytes((void *)((UINT64)PidAndNameBuffer + sizeof(TRANSPARENCY_PROCESS)),
(const void *)((UINT64)Measurements + sizeof(DEBUGGER_HIDE_AND_TRANSPARENT_DEBUGGER_MODE)),
Measurements->LengthOfProcessName);
//
// Set the process name location
//
PidAndNameBuffer->ProcessName = (PVOID)((UINT64)PidAndNameBuffer + sizeof(TRANSPARENCY_PROCESS));
}
//
// Link it to the list of process that we need to transparent
// vm-exits for them
//
// InsertHeadList(&g_TransparentModeMeasurements->ProcessList, &(PidAndNameBuffer->OtherProcesses));
return TRUE;
}
//
// /**
// * @brief maximum random value
// */
// #define MY_RAND_MAX 32768
//
// /**
// * @brief pre-defined log result
// * @details we used this because we want to avoid using floating-points in
// * kernel
// */
// int TransparentTableLog[] =
// {
// 0,
// 69,
// 110,
// 139,
// 161,
// 179,
// 195,
// 208,
// 220,
// 230,
// 240,
// 248,
// 256,
// 264,
// 271,
// 277,
// 283,
// 289,
// 294,
// 300,
// 304,
// 309,
// 314,
// 318,
// 322,
// 326,
// 330,
// 333,
// 337,
// 340,
// 343,
// 347,
// 350,
// 353,
// 356,
// 358,
// 361,
// 364,
// 366,
// 369,
// 371,
// 374,
// 376,
// 378,
// 381,
// 383,
// 385,
// 387,
// 389,
// 391,
// 393,
// 395,
// 397,
// 399,
// 401,
// 403,
// 404,
// 406,
// 408,
// 409,
// 411,
// 413,
// 414,
// 416,
// 417,
// 419,
// 420,
// 422,
// 423,
// 425,
// 426,
// 428,
// 429,
// 430,
// 432,
// 433,
// 434,
// 436,
// 437,
// 438,
// 439,
// 441,
// 442,
// 443,
// 444,
// 445,
// 447,
// 448,
// 449,
// 450,
// 451,
// 452,
// 453,
// 454,
// 455,
// 456,
// 457,
// 458,
// 460,
// 461};
//
//
// /**
// * @brief Integer power function definition.
// *
// * @params x Base Value
// * @params p Power Value
// * @return int
// */
// int
// TransparentPow(int x, int p)
// {
// int Res = 1;
// for (int i = 0; i < p; i++)
// {
// Res = Res * x;
// }
// return Res;
// }
//
// /**
// * @brief Integer Natural Logarithm function estimation.
// *
// * @params x input value
// * @return int
// */
// int
// TransparentLog(int x)
// {
// int n = x;
// int Digit = 0;
//
// while (n >= 100)
// {
// n = n / 10;
// Digit++;
// }
//
// //
// // Use pre-defined values of logarithms and estimate the total value
// //
// return TransparentTableLog[n] / 100 + (Digit * 23) / 10;
// }
// /**
// * @brief Integer root function estimation.
// *
// * @params x input value
// * @return int
// */
// int
// TransparentSqrt(int x)
// {
// int Res = 0;
// int Bit;
//
// //
// // The second-to-top bit is set.
// //
// Bit = 1 << 30;
//
// //
// // "Bit" starts at the highest power of four <= the argument.
// //
// while (Bit > x)
// Bit >>= 2;
//
// while (Bit != 0)
// {
// if (x >= Res + Bit)
// {
// x -= Res + Bit;
// Res = (Res >> 1) + Bit;
// }
// else
// Res >>= 1;
// Bit >>= 2;
// }
// return Res;
// }
//
// /**
// * @brief Integer Gaussian Random Number Generator(GRNG) based on Box-Muller method. A Float to Integer
// * mapping is used in the function.
// *
// * @params Average Mean
// * @parans Sigma Standard Deviation of the targeted Gaussian Distribution
// * @return int
// */
// int
// TransparentRandn(int Average, int Sigma)
// {
// int U1, r1, U2, r2, W, Mult;
// int X1, X2 = 0, XS1;
// int LogTemp = 0;
//
// do
// {
// r1 = TransparentGetRand();
// r2 = TransparentGetRand();
//
// U1 = (r1 % MY_RAND_MAX) - (MY_RAND_MAX / 2);
//
// U2 = (r2 % MY_RAND_MAX) - (MY_RAND_MAX / 2);
//
// W = U1 * U1 + U2 * U2;
// } while (W >= MY_RAND_MAX * MY_RAND_MAX / 2 || W == 0);
//
// LogTemp = (TransparentLog(W) - TransparentLog(MY_RAND_MAX * MY_RAND_MAX));
//
// Mult = TransparentSqrt((-2 * LogTemp) * (MY_RAND_MAX * MY_RAND_MAX / W));
//
// X1 = U1 * Mult / MY_RAND_MAX;
// XS1 = U1 * Mult;
//
// X2 = U2 * Mult / MY_RAND_MAX;
//
// return (Average + (Sigma * XS1) / MY_RAND_MAX);
// }
//
//
// /**
// * @brief Hide debugger on transparent-mode (activate transparent-mode)
// *
// * @param Measurements
// * @return NTSTATUS
// */
// NTSTATUS
// TransparentHideDebugger(PDEBUGGER_HIDE_AND_TRANSPARENT_DEBUGGER_MODE Measurements)
// {
// //
// // Check whether the transparent-mode was already initialized or not
// //
// if (!g_TransparentMode)
// {
// //
// // Allocate the measurements buffer
// //
// g_TransparentModeMeasurements = (PTRANSPARENCY_MEASUREMENTS)PlatformMemAllocateZeroedNonPagedPool(sizeof(TRANSPARENCY_MEASUREMENTS));
//
// if (!g_TransparentModeMeasurements)
// {
// return STATUS_INSUFFICIENT_RESOURCES;
// }
//
// //
// // Initialize the lists
// //
// InitializeListHead(&g_TransparentModeMeasurements->ProcessList);
//
// //
// // Fill the transparency details CPUID
// //
// g_TransparentModeMeasurements->CpuidAverage = Measurements->CpuidAverage;
// g_TransparentModeMeasurements->CpuidMedian = Measurements->CpuidMedian;
// g_TransparentModeMeasurements->CpuidStandardDeviation = Measurements->CpuidStandardDeviation;
//
// //
// // Fill the transparency details RDTSC
// //
// g_TransparentModeMeasurements->RdtscAverage = Measurements->RdtscAverage;
// g_TransparentModeMeasurements->RdtscMedian = Measurements->RdtscMedian;
// g_TransparentModeMeasurements->RdtscStandardDeviation = Measurements->RdtscStandardDeviation;
//
// //
// // add the new process name or Id to the list
// //
// TransparentAddNameOrProcessIdToTheList(Measurements);
//
// //
// // Enable RDTSC and RDTSCP exiting on all cores
// //
// BroadcastEnableRdtscExitingAllCores();
//
// //
// // Finally, enable the transparent-mode
// //
// g_TransparentMode = TRUE;
// }
// else
// {
// //
// // It's already initialized, we just need to
// // add the new process name or Id to the list
// //
// TransparentAddNameOrProcessIdToTheList(Measurements);
// }
//
// return STATUS_SUCCESS;
// }
//
// /**
// * @brief Deactivate transparent-mode
// *
// * @return NTSTATUS
// */
// NTSTATUS
// TransparentUnhideDebugger()
// {
// PLIST_ENTRY TempList = 0;
// PVOID BufferToDeAllocate = 0;
//
// if (g_TransparentMode)
// {
// //
// // Disable the transparent-mode
// //
// g_TransparentMode = FALSE;
//
// //
// // Disable RDTSC and RDTSCP emulation
// //
// BroadcastDisableRdtscExitingAllCores();
//
// //
// // Free list of allocated buffers
// //
// // Check for process id and process name, if not match then we don't emulate it
// //
// TempList = &g_TransparentModeMeasurements->ProcessList;
// while (&g_TransparentModeMeasurements->ProcessList != TempList->Flink)
// {
// TempList = TempList->Flink;
// PTRANSPARENCY_PROCESS ProcessDetails = (PTRANSPARENCY_PROCESS)CONTAINING_RECORD(TempList, TRANSPARENCY_PROCESS, OtherProcesses);
//
// //
// // Save the buffer so we can de-allocate it
// //
// BufferToDeAllocate = ProcessDetails->BufferAddress;
//
// //
// // We have to remove the event from the list
// //
// RemoveEntryList(&ProcessDetails->OtherProcesses);
//
// //
// // Free the buffer
// //
// PlatformMemFreePool(BufferToDeAllocate);
// }
//
// //
// // Deallocate the measurements buffer
// //
// PlatformMemFreePool(g_TransparentModeMeasurements);
// g_TransparentModeMeasurements = NULL;
//
// return STATUS_SUCCESS;
// }
// else
// {
// return STATUS_UNSUCCESSFUL;
// }
// }
//
// /**
// * @brief VM-Exit handler for different exit reasons
// * @details Should be called from vmx-root
// *
// * @param Regs The virtual processor's state of registers
// * @param ExitReason Exit Reason
// * @return BOOLEAN Return True we should emulate RDTSCP
// * or return false if we should not emulate RDTSCP
// */
// BOOLEAN
// TransparentModeStart(GUEST_REGS * Regs, UINT32 ExitReason)
// {
// UINT32 Aux = 0;
// PLIST_ENTRY TempList = 0;
// PCHAR CurrentProcessName = 0;
// UINT32 CurrentProcessId;
// UINT64 CurrrentTime;
// HANDLE CurrentThreadId;
// BOOLEAN Result = TRUE;
// BOOLEAN IsProcessOnTransparencyList = FALSE;
//
// //
// // Save the current time
// //
// CurrrentTime = __rdtscp(&Aux);
//
// //
// // Save time of vm-exit on each logical processor separately
// //
// VCpu->TransparencyState.PreviousTimeStampCounter = CurrrentTime;
//
// //
// // Find the current process id and name
// //
// CurrentProcessId = HANDLE_TO_UINT32(PsGetCurrentProcessId());
// CurrentProcessName = CommonGetProcessNameFromProcessControlBlock(PsGetCurrentProcess());
//
// //
// // Check for process id and process name, if not match then we don't emulate it
// //
// TempList = &g_TransparentModeMeasurements->ProcessList;
// while (&g_TransparentModeMeasurements->ProcessList != TempList->Flink)
// {
// TempList = TempList->Flink;
// PTRANSPARENCY_PROCESS ProcessDetails = (PTRANSPARENCY_PROCESS)CONTAINING_RECORD(TempList, TRANSPARENCY_PROCESS, OtherProcesses);
// if (ProcessDetails->TrueIfProcessIdAndFalseIfProcessName)
// {
// //
// // This entry is process id
// //
// if (ProcessDetails->ProcessId == CurrentProcessId)
// {
// //
// // Let the transparency handler to handle it
// //
// IsProcessOnTransparencyList = TRUE;
// break;
// }
// }
// else
// {
// //
// // This entry is a process name
// //
// if (CurrentProcessName != NULL && CommonIsStringStartsWith(CurrentProcessName, ProcessDetails->ProcessName))
// {
// //
// // Let the transparency handler to handle it
// //
// IsProcessOnTransparencyList = TRUE;
// break;
// }
// }
// }
//
// //
// // Check whether we find this process on transparency list or not
// //
// if (!IsProcessOnTransparencyList)
// {
// //
// // No, we didn't let's do the normal tasks
// //
// return TRUE;
// }
//
// //
// // Get current thread Id
// //
// CurrentThreadId = PsGetCurrentThreadId();
//
// //
// // Check whether we are in new thread or in previous thread
// //
// if (VCpu->TransparencyState.ThreadId != CurrentThreadId)
// {
// //
// // It's a new thread Id reset everything
// //
// VCpu->TransparencyState.ThreadId = CurrentThreadId;
// VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc = NULL64_ZERO;
// VCpu->TransparencyState.CpuidAfterRdtscDetected = FALSE;
// }
//
// //
// // Now, it's time to check and play with RDTSC/P and CPUID
// //
//
// if (ExitReason == VMX_EXIT_REASON_EXECUTE_RDTSC || ExitReason == VMX_EXIT_REASON_EXECUTE_RDTSCP)
// {
// if (VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc == NULL64_ZERO)
// {
// //
// // It's a timing and the previous time for the thread is null
// // so we need to save the time (maybe) for future use
// //
// VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc = CurrrentTime;
// }
// else if (VCpu->TransparencyState.CpuidAfterRdtscDetected == TRUE)
// {
// //
// // Someone tries to know about the hypervisor
// // let's play with them
// //
//
// // LogInfo("Possible RDTSC+CPUID+RDTSC");
// }
// else if (VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc != NULL64_ZERO &&
// VCpu->TransparencyState.CpuidAfterRdtscDetected == FALSE)
// {
// //
// // It's a new rdtscp, let's save the new value
// //
// VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc +=
// TransparentRandn((UINT32)g_TransparentModeMeasurements->CpuidAverage,
// (UINT32)g_TransparentModeMeasurements->CpuidStandardDeviation);
// }
//
// //
// // Adjust the rdtsc based on RevealedTimeStampCounterByRdtsc
// //
// Regs->rax = 0x00000000ffffffff &
// VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc;
//
// Regs->rdx = 0x00000000ffffffff &
// (VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc >> 32);
//
// //
// // Check if we need to adjust rcx as a result of rdtscp
// //
// if (ExitReason == VMX_EXIT_REASON_EXECUTE_RDTSCP)
// {
// Regs->rcx = 0x00000000ffffffff & Aux;
// }
// //
// // Shows that vm-exit handler should not emulate the RDTSC/P
// //
// Result = FALSE;
// }
// else if (ExitReason == VMX_EXIT_REASON_EXECUTE_CPUID &&
// VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc != NULL64_ZERO)
// {
// //
// // The guy executed one or more CPUIDs after an rdtscp so we
// // need to add new cpuid value to previous timer and also
// // we need to store it somewhere to remember this behavior
// //
// VCpu->TransparencyState.RevealedTimeStampCounterByRdtsc +=
// TransparentRandn((UINT32)g_TransparentModeMeasurements->CpuidAverage,
// (UINT32)g_TransparentModeMeasurements->CpuidStandardDeviation);
//
// VCpu->TransparencyState.CpuidAfterRdtscDetected = TRUE;
// }
//
// return Result;
// }
//