HyperDbg/hyperdbg/script-eval/code/Functions.c
Max Raulea 2f59ad7c70 Kernel Module now Links and compiles!!
The kernel module now links and compiles, there are still a lot of empty
TODOs left, there are empty stubs for hypertrace, assembly files and
kdserial etc. these can be introduced one by one. My first step will be
loading the kernel module from the userspace cli.

Big step forward! :)
2026-08-24 11:20:24 +02:00

2450 lines
59 KiB
C

/**
* @file Functions.c
* @author M.H. Gholamrezaei (mh@hyperdbg.org)
* @author Sina Karvandi (sina@hyperdbg.org)
* @brief Script engine functions implementations
* @details
* @version 0.2
* @date 2022-06-29
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
//
// User-mode Global Variables
//
#ifdef SCRIPT_ENGINE_USER_MODE
extern UINT64 g_CurrentExprEvalResult;
extern BOOLEAN g_CurrentExprEvalResultHasError;
#endif // SCRIPT_ENGINE_USER_MODE
//
// *** Definitions ***
//
UINT64
GetValue(PGUEST_REGS GuestRegs,
PACTION_BUFFER ActionBuffer,
PSCRIPT_ENGINE_GENERAL_REGISTERS ScriptGeneralRegisters,
PSYMBOL Symbol,
BOOLEAN ReturnReference);
//
// *** Functions ***
//
/**
* @brief Implementation of eq function
*
* @param Address
* @param Value
* @param HasError
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionEq(UINT64 Address, QWORD Value, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety(Address, sizeof(QWORD)))
{
//
// Instead of indicating an error, just return false
// to assign it as a return result to a variable
//
// *HasError = TRUE;
return FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
#ifdef SCRIPT_ENGINE_USER_MODE
*(UINT64 *)Address = Value;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperWriteMemorySafeOnTargetProcess(Address, &Value, sizeof(QWORD));
#endif // SCRIPT_ENGINE_KERNEL_MODE
return TRUE;
}
/**
* @brief Implementation of ed function
*
* @param Address
* @param Value
* @param HasError
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionEd(UINT64 Address, DWORD Value, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety(Address, sizeof(DWORD)))
{
//
// Instead of indicating an error, just return false
// to assign it as a return result to a variable
//
// *HasError = TRUE;
return FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
#ifdef SCRIPT_ENGINE_USER_MODE
*(DWORD *)Address = Value;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperWriteMemorySafeOnTargetProcess(Address, &Value, sizeof(DWORD));
#endif // SCRIPT_ENGINE_KERNEL_MODE
return TRUE;
}
/**
* @brief Implementation of eb function
*
* @param Address
* @param Value
* @param HasError
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionEb(UINT64 Address, BYTE Value, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety(Address, sizeof(BYTE)))
{
//
// Instead of indicating an error, just return false
// to assign it as a return result to a variable
//
// *HasError = TRUE;
return FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
#ifdef SCRIPT_ENGINE_USER_MODE
*(BYTE *)Address = Value;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperWriteMemorySafeOnTargetProcess(Address, &Value, sizeof(BYTE));
#endif // SCRIPT_ENGINE_KERNEL_MODE
return TRUE;
}
/**
* @brief Implementation of eq function (Physical Memory)
*
* @param Address
* @param Value
* @param HasError
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionEqPa(UINT64 Address, QWORD Value, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAddressPhysical(Address))
{
//
// Instead of indicating an error, just return false
// to assign it as a return result to a variable
//
// *HasError = TRUE;
return FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, using physical address functions (eq_pa) is not possible in user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperWriteMemorySafeByPhysicalAddress(Address, (UINT64)&Value, sizeof(QWORD));
#endif // SCRIPT_ENGINE_KERNEL_MODE
return TRUE;
}
/**
* @brief Implementation of ed function (Physical Memory)
*
* @param Address
* @param Value
* @param HasError
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionEdPa(UINT64 Address, DWORD Value, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAddressPhysical(Address))
{
//
// Instead of indicating an error, just return false
// to assign it as a return result to a variable
//
// *HasError = TRUE;
return FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, using physical address functions (ed_pa) is not possible in user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperWriteMemorySafeByPhysicalAddress(Address, (UINT64)&Value, sizeof(DWORD));
#endif // SCRIPT_ENGINE_KERNEL_MODE
return TRUE;
}
/**
* @brief Implementation of eb function (Physical Memory)
*
* @param Address
* @param Value
* @param HasError
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionEbPa(UINT64 Address, BYTE Value, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAddressPhysical(Address))
{
//
// Instead of indicating an error, just return false
// to assign it as a return result to a variable
//
// *HasError = TRUE;
return FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, using physical address functions (eb_pa) is not possible in user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperWriteMemorySafeByPhysicalAddress(Address, (UINT64)&Value, sizeof(BYTE));
#endif // SCRIPT_ENGINE_KERNEL_MODE
return TRUE;
}
/**
* @brief Check whether the address is valid or not
*
* @param Address
* @param Length
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionCheckAddress(UINT64 Address, UINT32 Length)
{
#ifdef SCRIPT_ENGINE_USER_MODE
if (CheckAccessValidityAndSafety(Address, Length))
{
return TRUE;
}
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (CheckAccessValidityAndSafety(Address, Length))
{
return TRUE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
return FALSE;
}
/**
* @brief A VMX-compatible equivalent of memcpy function in C
*
* @param Destination
* @param Source
* @param Num
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionMemcpy(UINT64 Destination, UINT64 Source, UINT32 Num, BOOL * HasError)
{
UINT64 PrevReadLen = 0;
BYTE MovingBuffer[DebuggerScriptEngineMemcpyMovingBufferSize] = {0};
//
// Reject zero-length copies: a Num of 0 would pass address-range
// validation vacuously (checking 0 bytes at any mapped page succeeds),
// which could be abused as a kernel address-mapping oracle.
//
if (Num == 0)
{
*HasError = TRUE;
return;
}
#ifdef SCRIPT_ENGINE_USER_MODE
//
// Check the destination address
//
if (!CheckAccessValidityAndSafety(Destination, Num))
{
*HasError = TRUE;
return;
}
//
// Check the source address
//
if (!CheckAccessValidityAndSafety(Source, Num))
{
*HasError = TRUE;
return;
}
//
// Address is valid, perform the memcpy in user-mode
//
memcpy((void *)Destination, (void *)Source, Num);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Check the destination address
//
if (!CheckAccessValidityAndSafety(Destination, Num))
{
*HasError = TRUE;
return;
}
//
// Check the source address
//
if (!CheckAccessValidityAndSafety(Source, Num))
{
*HasError = TRUE;
return;
}
//
// Address is valid, perform the memcpy in kernel-mode (VMX-root mode)
//
while (Num > 0)
{
//
// Check the target buffer size
//
if (Num > DebuggerScriptEngineMemcpyMovingBufferSize)
{
//
// *** The size of read buffer is greater to maximum the moving buffer size ***
//
//
// Read memory into the buffer
//
MemoryMapperReadMemorySafeOnTargetProcess(Source + PrevReadLen, MovingBuffer, DebuggerScriptEngineMemcpyMovingBufferSize);
//
// Write the moving buffer into the target buffer
//
MemoryMapperWriteMemorySafeOnTargetProcess(Destination + PrevReadLen, MovingBuffer, DebuggerScriptEngineMemcpyMovingBufferSize);
//
// Computing the bytes that we read
//
PrevReadLen += DebuggerScriptEngineMemcpyMovingBufferSize;
Num -= DebuggerScriptEngineMemcpyMovingBufferSize;
}
else
{
//
// *** The size of read buffer is lower than or equal to the moving buffer size ***
//
//
// Read memory into the buffer
//
MemoryMapperReadMemorySafeOnTargetProcess(Source + PrevReadLen, MovingBuffer, Num);
//
// Write the moving buffer into the target buffer
//
MemoryMapperWriteMemorySafeOnTargetProcess(Destination + PrevReadLen, MovingBuffer, Num);
//
// Computing the bytes that we gonna read
//
PrevReadLen += Num;
Num = 0; // or Num -= Num;
}
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief A VMX-compatible equivalent of memcpy function in C for physical memory
*
* @param Destination
* @param Source
* @param Num
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionMemcpyPa(UINT64 Destination, UINT64 Source, UINT32 Num, BOOL * HasError)
{
UINT64 PrevReadLen = 0;
BYTE MovingBuffer[DebuggerScriptEngineMemcpyMovingBufferSize] = {0};
#ifdef SCRIPT_ENGINE_USER_MODE
//
// Show an error message in user-mode
//
ShowMessages("err, using physical address functions (memcpy_pa) is not possible in user-mode\n");
return;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Check the destination address (physical)
//
if (!CheckAddressPhysical(Destination))
{
*HasError = TRUE;
return;
}
//
// Check the source address (physical)
//
if (!CheckAddressPhysical(Source))
{
*HasError = TRUE;
return;
}
//
// Address is valid, perform the memcpy in kernel-mode (VMX-root mode)
//
while (Num > 0)
{
//
// Check the target buffer size
//
if (Num > DebuggerScriptEngineMemcpyMovingBufferSize)
{
//
// *** The size of read buffer is greater to maximum the moving buffer size ***
//
//
// Read memory into the buffer
//
MemoryMapperReadMemorySafeByPhysicalAddress(Source + PrevReadLen, (UINT64)MovingBuffer, DebuggerScriptEngineMemcpyMovingBufferSize);
//
// Write the moving buffer into the target buffer
//
MemoryMapperWriteMemorySafeByPhysicalAddress(Destination + PrevReadLen, (UINT64)MovingBuffer, DebuggerScriptEngineMemcpyMovingBufferSize);
//
// Computing the bytes that we read
//
PrevReadLen += DebuggerScriptEngineMemcpyMovingBufferSize;
Num -= DebuggerScriptEngineMemcpyMovingBufferSize;
}
else
{
//
// *** The size of read buffer is lower than or equal to the moving buffer size ***
//
//
// Read memory into the buffer
//
MemoryMapperReadMemorySafeByPhysicalAddress(Source + PrevReadLen, (UINT64)MovingBuffer, Num);
//
// Write the moving buffer into the target buffer
//
MemoryMapperWriteMemorySafeByPhysicalAddress(Destination + PrevReadLen, (UINT64)MovingBuffer, Num);
//
// Computing the bytes that we gonna read
//
PrevReadLen += Num;
Num = 0; // or Num -= Num;
}
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
//
// Convert virtual address to physical address
//
UINT64
ScriptEngineFunctionVirtualToPhysical(UINT64 Address)
{
#ifdef SCRIPT_ENGINE_USER_MODE
//
// There is no conversion in user-mode
//
return NULL;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
return VirtualAddressToPhysicalAddressOnTargetProcess((PVOID)Address);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Convert physical address to virtual address
*
* @param Address
* @return UINT64
*/
UINT64
ScriptEngineFunctionPhysicalToVirtual(UINT64 Address)
{
#ifdef SCRIPT_ENGINE_USER_MODE
//
// There is no conversion in user-mode
//
return NULL;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
return PhysicalAddressToVirtualAddressOnTargetProcess((PVOID)Address);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of print function
*
* @param Tag
* @param ImmediateMessagePassing
* @param Value
* @return VOID
*/
VOID
ScriptEngineFunctionPrint(UINT64 Tag, BOOLEAN ImmediateMessagePassing, UINT64 Value)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("%llx\n", Value);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Prepare a buffer to bypass allocating a huge stack space for logging
//
char TempBuffer[20] = {0};
UINT32 TempBufferLen = sprintf(TempBuffer, "%llx\n", Value);
LogSimpleWithTag((UINT32)Tag, ImmediateMessagePassing, TempBuffer, TempBufferLen + 1);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of test_statement function
*
* @param Tag
* @param ImmediateMessagePassing
* @param Value
* @return VOID
*/
VOID
ScriptEngineFunctionTestStatement(UINT64 Tag, BOOLEAN ImmediateMessagePassing, UINT64 Value)
{
UNREFERENCED_PARAMETER(Tag);
UNREFERENCED_PARAMETER(ImmediateMessagePassing);
UNREFERENCED_PARAMETER(Value);
#ifdef SCRIPT_ENGINE_USER_MODE
g_CurrentExprEvalResult = Value;
g_CurrentExprEvalResultHasError = FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
}
/**
* @brief Implementation of spinlock_lock function
*
* @param Lock
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionSpinlockLock(volatile LONG * Lock, BOOL * HasError)
{
#ifdef SCRIPT_ENGINE_USER_MODE
SpinlockLock(Lock);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Lock, sizeof(LONG)))
{
*HasError = TRUE;
return;
}
SpinlockLock(Lock);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of spinlock_unlock function
*
* @param Lock
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionSpinlockUnlock(volatile LONG * Lock, BOOL * HasError)
{
#ifdef SCRIPT_ENGINE_USER_MODE
SpinlockUnlock(Lock);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Lock, sizeof(LONG)))
{
*HasError = TRUE;
return;
}
SpinlockUnlock(Lock);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of spinlock_lock_custom_wait function
*
* @param Lock
* @param MaxWait
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionSpinlockLockCustomWait(volatile long * Lock, unsigned MaxWait, BOOL * HasError)
{
#ifdef SCRIPT_ENGINE_USER_MODE
//
// The parameter is a raw `long`, which IS LONG on Windows but is 64-bit on
// Linux (LP64), so the conversion is spelled out. No-op on Windows.
//
SpinlockLockWithCustomWait((volatile LONG *)Lock, MaxWait);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Lock, sizeof(LONG)))
{
*HasError = TRUE;
return;
}
SpinlockLockWithCustomWait((volatile LONG *)Lock, MaxWait);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of strlen function
*
* @param Address
* @return UINT64
*/
UINT64
ScriptEngineFunctionStrlen(const char * Address)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
Result = strlen(Address);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = VmFuncVmxCompatibleStrlen(Address);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
/**
* @brief Implementation of disassemble_len function
*
* @param Address
* @param Is32Bit
*
* @return UINT64
*/
UINT64
ScriptEngineFunctionDisassembleLen(PVOID Address, BOOLEAN Is32Bit)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
Result = HyperDbgLengthDisassemblerEngine((unsigned char *)Address, MAXIMUM_INSTR_SIZE, Is32Bit ? FALSE : TRUE);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = DisassemblerLengthDisassembleEngineInVmxRootOnTargetProcess(Address, Is32Bit);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
/**
* @brief Implementation of wcslen function
*
* @param Address
* @return UINT64
*/
UINT64
ScriptEngineFunctionWcslen(const UINT16 * Address)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
while (Address[Result] != 0)
Result++;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = VmFuncVmxCompatibleWcslen((const WCHAR *)Address);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
#ifdef SCRIPT_ENGINE_USER_MODE
VOID
UserModeMicroSleep(UINT64 Us)
{
LARGE_INTEGER Start, End, Frequency;
PlatformQueryPerformanceFrequency(&Frequency);
LONGLONG TickPerUs = Frequency.QuadPart / 1000000;
LONGLONG Ticks = TickPerUs * Us;
PlatformQueryPerformanceCounter(&Start);
while (TRUE)
{
PlatformQueryPerformanceCounter(&End);
if (End.QuadPart - Start.QuadPart > Ticks)
{
break;
}
}
}
#endif // SCRIPT_ENGINE_USER_MODE
/**
* @brief Implementation of microsleep function
*
* @param Us delay in micro second
*/
VOID
ScriptEngineFunctionMicroSleep(UINT64 Us)
{
#ifdef SCRIPT_ENGINE_USER_MODE
UserModeMicroSleep(Us);
#endif
#ifdef SCRIPT_ENGINE_KERNEL_MODE
VmFuncVmxCompatibleMicroSleep(Us);
#endif
}
/**
* @brief Implementation of rdtsc function
*
*/
UINT64
ScriptEngineFunctionRdtsc()
{
return CpuReadTsc();
}
/**
* @brief Implementation of rdtscp function
*
*/
UINT64
ScriptEngineFunctionRdtscp()
{
UINT32 Aux;
return CpuReadTscp(&Aux);
}
/**
* @brief Implementation of interlocked_exchange function
*
* @param Target
* @param Value
* @param HasError
* @return long long
*/
long long
ScriptEngineFunctionInterlockedExchange(long long volatile * Target,
long long Value,
BOOL * HasError)
{
long long Result = 0;
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Target, sizeof(long long)))
{
*HasError = TRUE;
return (long long)NULL;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
Result = CpuInterlockedExchange64(Target, Value);
return Result;
}
/**
* @brief Implementation of interlocked_exchange_add function
*
* @param Addend
* @param Value
* @param HasError
* @return long long
*/
long long
ScriptEngineFunctionInterlockedExchangeAdd(long long volatile * Addend,
long long Value,
BOOL * HasError)
{
long long Result = 0;
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Addend, sizeof(long long)))
{
*HasError = TRUE;
return (long long)NULL;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
Result = CpuInterlockedExchangeAdd64(Addend, Value);
return Result;
}
/**
* @brief Implementation of interlocked_exchange_increment function
*
* @param Addend
* @param HasError
* @return long long
*/
long long
ScriptEngineFunctionInterlockedIncrement(long long volatile * Addend,
BOOL * HasError)
{
long long Result = 0;
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Addend, sizeof(long long)))
{
*HasError = TRUE;
return (long long)NULL;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
Result = CpuInterlockedIncrement64(Addend);
return Result;
}
/**
* @brief Implementation of interlocked_exchange_decrement function
*
* @param Addend
* @param HasError
* @return long long
*/
long long
ScriptEngineFunctionInterlockedDecrement(long long volatile * Addend,
BOOL * HasError)
{
long long Result = 0;
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Addend, sizeof(long long)))
{
*HasError = TRUE;
return (long long)NULL;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
Result = CpuInterlockedDecrement64(Addend);
return Result;
}
/**
* @brief Implementation of interlocked_compare_exchange function
*
* @param Destination
* @param ExChange
* @param Comperand
* @param HasError
* @return long long
*/
long long
ScriptEngineFunctionInterlockedCompareExchange(
long long volatile * Destination,
long long ExChange,
long long Comperand,
BOOL * HasError)
{
long long Result = 0;
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!CheckAccessValidityAndSafety((UINT64)Destination, sizeof(long long)))
{
*HasError = TRUE;
return (long long)NULL;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
Result = CpuInterlockedCompareExchange64(Destination, ExChange, Comperand);
return Result;
}
/**
* @brief Implementation of event_enable function
*
* @param EventId
*
* @return VOID
*/
VOID
ScriptEngineFunctionEventEnable(UINT64 EventId)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, enabling events is not possible in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!DebuggerEnableEvent(EventId + DebuggerEventTagStartSeed))
{
LogInfo("Invalid tag id (%x)", EventId);
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of event_disable function
*
* @param EventId
*
* @return VOID
*/
VOID
ScriptEngineFunctionEventDisable(UINT64 EventId)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, disabling events is not possible in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (!DebuggerDisableEvent(EventId + DebuggerEventTagStartSeed))
{
LogInfo("Invalid tag id (%x)", EventId);
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of event_clear function
*
* @param EventId
*
* @return VOID
*/
VOID
ScriptEngineFunctionEventClear(UINT64 EventId)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, disabling events is not possible in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
BOOLEAN PoolManagerAllocatedMemory = FALSE;
if (g_KernelDebuggerState && EnableInstantEventMechanism)
{
PoolManagerAllocatedMemory = TRUE;
}
if (!DebuggerClearEvent(EventId + DebuggerEventTagStartSeed, VmFuncVmxGetCurrentExecutionMode(), PoolManagerAllocatedMemory))
{
LogInfo("Invalid tag id (%x)", EventId);
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of pause function
*
* @param ActionDetail
* @param GuestRegs
*
* @return VOID
*/
VOID
ScriptEngineFunctionPause(
ACTION_BUFFER * ActionDetail,
PGUEST_REGS GuestRegs)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, breaking is not possible in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// pause(); function is only working when kernel debugger is working
// it's not designed to work on vmi-mode (local debugging)
//
if (g_KernelDebuggerState && g_DebuggeeHaltReason == DEBUGGEE_PAUSING_REASON_NOT_PAUSED)
{
DEBUGGER_TRIGGERED_EVENT_DETAILS TriggeredEventDetail = {0};
ULONG CurrentCore = PlatformCpuGetCurrentProcessorNumber();
//
// Make the details of context
//
TriggeredEventDetail.Tag = ActionDetail->Tag;
TriggeredEventDetail.Context = (PVOID)ActionDetail->Context;
if (ActionDetail->CallingStage == 1)
{
TriggeredEventDetail.Stage = VMM_CALLBACK_CALLING_STAGE_POST_EVENT_EMULATION;
}
else
{
TriggeredEventDetail.Stage = VMM_CALLBACK_CALLING_STAGE_PRE_EVENT_EMULATION;
}
//
// Notify debugger about the pause (whether from VMX root-mode or not root)
//
DebuggerPerformBreakToDebuggerByCoreId(CurrentCore, NULL, &TriggeredEventDetail, GuestRegs);
}
else
{
LogInfo("The 'pause();' function is either called from the vmi-mode or is "
"evaluated by the '?' command. It's not allowed to use it on vmi-mode "
"(local debugging) or by the '?' command");
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of flush function
*
* @return VOID
*/
VOID
ScriptEngineFunctionFlush()
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to flush buffers in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Mark all buffers as read
//
LogMarkAllAsRead(TRUE);
LogMarkAllAsRead(FALSE);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of event_ignore function
* @param State
* @param ActionDetail
*
* @return VOID
*/
VOID
ScriptEngineFunctionShortCircuitingEvent(UINT64 State, ACTION_BUFFER * ActionDetail)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to short-circuit events in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (ActionDetail->CallingStage == 1)
{
LogWarning("Warning, calling the 'event_sc' function in the 'post' calling stage doesn't make sense as the emulation is already performed!\n"
"You can use this function in the 'pre' calling stage");
return;
}
ULONG CurrentCore = PlatformCpuGetCurrentProcessorNumber();
if (State != 0)
{
g_DbgState[CurrentCore].ShortCircuitingEvent = TRUE;
}
else
{
g_DbgState[CurrentCore].ShortCircuitingEvent = FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of formats function
*
* @param Tag
* @param ImmediateMessagePassing
* @param Value
* @return VOID
*/
VOID
ScriptEngineFunctionFormats(UINT64 Tag, BOOLEAN ImmediateMessagePassing, UINT64 Value)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ScriptEngineFunctionTestStatement(Tag, ImmediateMessagePassing, Value);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (g_KernelDebuggerState)
{
KdSendFormatsFunctionResult(Value);
}
else if (g_UserDebuggerState)
{
UdSendFormatsFunctionResult(Value);
}
else
{
//
// Prepare a buffer to bypass allocating a huge stack space for logging
//
char TempBuffer[20] = {0};
UINT32 TempBufferLen = sprintf(TempBuffer, "%llx\n", Value);
LogSimpleWithTag((UINT32)Tag, ImmediateMessagePassing, TempBuffer, TempBufferLen + 1);
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Custom VMX-root compatible strlen
*
* @param StrAddr
* @param IsWstring
* @return UINT32
*/
UINT32
CustomStrlen(UINT64 StrAddr, BOOLEAN IsWstring)
{
#ifdef SCRIPT_ENGINE_USER_MODE
if (IsWstring)
{
const UINT16 * String = (const UINT16 *)StrAddr;
UINT32 Length = 0;
while (String[Length] != 0)
Length++;
return Length;
}
else
{
return (UINT32)strlen((const char *)StrAddr);
}
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
if (IsWstring)
{
return VmFuncVmxCompatibleWcslen((const WCHAR *)StrAddr);
}
else
{
return VmFuncVmxCompatibleStrlen((const CHAR *)StrAddr);
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Check if string is safe to be accessed or not (in vmx-root mode)
*
* @param StrAddr
* @param IsWstring
* @return BOOLEAN
*/
BOOLEAN
CheckIfStringIsSafe(UINT64 StrAddr, BOOLEAN IsWstring)
{
#ifdef SCRIPT_ENGINE_USER_MODE
return TRUE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// At least two chars (wchar_t is 4 byte)
//
if (CheckAccessValidityAndSafety(StrAddr, IsWstring ? 4 : 2))
{
return TRUE;
}
else
{
return FALSE;
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
typedef struct _SCRIPT_ENGINE_FLOAT_BIGINT
{
UINT32 Limb[40];
} SCRIPT_ENGINE_FLOAT_BIGINT, *PSCRIPT_ENGINE_FLOAT_BIGINT;
static BOOLEAN
ScriptEngineFloatBigintIsZero(PSCRIPT_ENGINE_FLOAT_BIGINT Value)
{
for (UINT32 Index = 0; Index < 40; Index++)
{
if (Value->Limb[Index])
{
return FALSE;
}
}
return TRUE;
}
static BOOLEAN
ScriptEngineFloatBigintShiftLeftOne(PSCRIPT_ENGINE_FLOAT_BIGINT Value)
{
UINT32 Carry = 0;
for (UINT32 Index = 0; Index < 40; Index++)
{
UINT32 NextCarry = Value->Limb[Index] >> 31;
Value->Limb[Index] = (Value->Limb[Index] << 1) | Carry;
Carry = NextCarry;
}
return Carry == 0;
}
static VOID
ScriptEngineFloatBigintShiftRightOne(PSCRIPT_ENGINE_FLOAT_BIGINT Value)
{
UINT32 Carry = 0;
for (INT32 Index = 39; Index >= 0; Index--)
{
UINT32 NextCarry = Value->Limb[Index] & 1;
Value->Limb[Index] = (Value->Limb[Index] >> 1) | (Carry << 31);
Carry = NextCarry;
}
}
static BOOLEAN
ScriptEngineFloatBigintTestBit(PSCRIPT_ENGINE_FLOAT_BIGINT Value, UINT32 Bit)
{
return Bit < 1280 && (Value->Limb[Bit / 32] & (1U << (Bit % 32))) != 0;
}
static BOOLEAN
ScriptEngineFloatBigintAnyBitsBelow(PSCRIPT_ENGINE_FLOAT_BIGINT Value, UINT32 Bit)
{
UINT32 Limit = Bit < 1280 ? Bit : 1280;
for (UINT32 Index = 0; Index < Limit; Index++)
{
if (ScriptEngineFloatBigintTestBit(Value, Index))
{
return TRUE;
}
}
return FALSE;
}
static BOOLEAN
ScriptEngineFloatBigintIncrement(PSCRIPT_ENGINE_FLOAT_BIGINT Value)
{
for (UINT32 Index = 0; Index < 40; Index++)
{
Value->Limb[Index]++;
if (Value->Limb[Index])
{
return TRUE;
}
}
return FALSE;
}
static BOOLEAN
ScriptEngineFloatBigintMultiplySmall(PSCRIPT_ENGINE_FLOAT_BIGINT Value, UINT32 Multiplier)
{
UINT64 Carry = 0;
for (UINT32 Index = 0; Index < 40; Index++)
{
UINT64 Product = ((UINT64)Value->Limb[Index] * Multiplier) + Carry;
Value->Limb[Index] = (UINT32)Product;
Carry = Product >> 32;
}
return Carry == 0;
}
static UINT32
ScriptEngineFloatBigintDivideByTen(PSCRIPT_ENGINE_FLOAT_BIGINT Value)
{
UINT64 Remainder = 0;
for (INT32 Index = 39; Index >= 0; Index--)
{
UINT64 Dividend = (Remainder << 32) | Value->Limb[Index];
Value->Limb[Index] = (UINT32)(Dividend / 10);
Remainder = Dividend % 10;
}
return (UINT32)Remainder;
}
static BOOLEAN
ScriptEngineFormatFixedFloat(UINT64 ValueKind, UINT64 RawBits, PCHAR Output, UINT32 OutputSize, PUINT32 OutputLength)
{
SCRIPT_ENGINE_FLOAT_BIGINT ScaledValue = {0};
SCRIPT_ENGINE_FLOAT_BIGINT DecimalValue;
CHAR ReverseDigits[384];
UINT32 DigitCount = 0;
UINT64 Significand;
INT32 BinaryExponent;
BOOLEAN Negative;
if (!Output || !OutputLength || OutputSize == 0)
{
return FALSE;
}
if (ValueKind == SYMBOL_VALUE_KIND_FLOAT32)
{
UINT32 Bits = (UINT32)RawBits;
UINT32 Exponent = (Bits >> 23) & 0xff;
UINT32 Fraction = Bits & 0x7fffff;
Negative = (Bits >> 31) != 0;
if (Exponent == 0xff)
{
return FALSE;
}
Significand = Exponent ? ((UINT64)1 << 23) | Fraction : Fraction;
BinaryExponent = Exponent ? (INT32)Exponent - 127 - 23 : -126 - 23;
}
else if (ValueKind == SYMBOL_VALUE_KIND_FLOAT64)
{
UINT64 Exponent = (RawBits >> 52) & 0x7ff;
UINT64 Fraction = RawBits & 0xfffffffffffffULL;
Negative = (RawBits >> 63) != 0;
if (Exponent == 0x7ff)
{
return FALSE;
}
Significand = Exponent ? ((UINT64)1 << 52) | Fraction : Fraction;
BinaryExponent = Exponent ? (INT32)Exponent - 1023 - 52 : -1022 - 52;
}
else
{
return FALSE;
}
ScaledValue.Limb[0] = (UINT32)Significand;
ScaledValue.Limb[1] = (UINT32)(Significand >> 32);
if (!ScriptEngineFloatBigintMultiplySmall(&ScaledValue, 1000000))
{
return FALSE;
}
if (BinaryExponent > 0)
{
for (INT32 Shift = 0; Shift < BinaryExponent; Shift++)
{
if (!ScriptEngineFloatBigintShiftLeftOne(&ScaledValue))
{
return FALSE;
}
}
}
else if (BinaryExponent < 0)
{
UINT32 Shift = (UINT32)-BinaryExponent;
BOOLEAN RoundBit = Shift && ScriptEngineFloatBigintTestBit(&ScaledValue, Shift - 1);
BOOLEAN Sticky = Shift > 1 && ScriptEngineFloatBigintAnyBitsBelow(&ScaledValue, Shift - 1);
for (UINT32 Index = 0; Index < Shift; Index++)
{
ScriptEngineFloatBigintShiftRightOne(&ScaledValue);
}
if (RoundBit && (Sticky || (ScaledValue.Limb[0] & 1)))
{
if (!ScriptEngineFloatBigintIncrement(&ScaledValue))
{
return FALSE;
}
}
}
DecimalValue = ScaledValue;
do
{
if (DigitCount >= sizeof(ReverseDigits))
{
return FALSE;
}
ReverseDigits[DigitCount++] = (CHAR)('0' + ScriptEngineFloatBigintDivideByTen(&DecimalValue));
} while (!ScriptEngineFloatBigintIsZero(&DecimalValue));
UINT32 Required = (Negative ? 1U : 0U) + (DigitCount > 6 ? DigitCount - 6 : 1) + 1 + 6;
if (Required + 1 > OutputSize)
{
return FALSE;
}
UINT32 Position = 0;
if (Negative)
{
Output[Position++] = '-';
}
if (DigitCount <= 6)
{
Output[Position++] = '0';
Output[Position++] = '.';
for (UINT32 Pad = DigitCount; Pad < 6; Pad++)
{
Output[Position++] = '0';
}
while (DigitCount)
{
Output[Position++] = ReverseDigits[--DigitCount];
}
}
else
{
for (UINT32 Index = DigitCount; Index > 6; Index--)
{
Output[Position++] = ReverseDigits[Index - 1];
}
Output[Position++] = '.';
for (UINT32 Index = 6; Index > 0; Index--)
{
Output[Position++] = ReverseDigits[Index - 1];
}
}
Output[Position] = '\0';
*OutputLength = Position;
return TRUE;
}
static BOOLEAN
ApplyFloatingFormatSpecifier(CHAR * FinalBuffer,
PUINT32 CurrentProcessedPositionFromStartOfFormat,
PUINT32 CurrentPositionInFinalBuffer,
UINT64 RawBits,
UINT64 ValueKind,
UINT32 SizeOfFinalBuffer)
{
CHAR TempBuffer[384] = {0};
UINT32 TempBufferLen = 0;
if (*CurrentPositionInFinalBuffer >= SizeOfFinalBuffer ||
!ScriptEngineFormatFixedFloat(ValueKind, RawBits, TempBuffer, sizeof(TempBuffer), &TempBufferLen) ||
TempBufferLen >= SizeOfFinalBuffer - *CurrentPositionInFinalBuffer)
{
return FALSE;
}
*CurrentProcessedPositionFromStartOfFormat += 2;
memcpy(FinalBuffer + *CurrentPositionInFinalBuffer, TempBuffer, TempBufferLen);
*CurrentPositionInFinalBuffer += TempBufferLen;
return TRUE;
}
/**
* @brief Apply format specifiers (%d, %x, %llx, etc.)
*
* @param CurrentSpecifier
* @param FinalBuffer
* @param CurrentProcessedPositionFromStartOfFormat
* @param CurrentPositionInFinalBuffer
* @param Val
* @param SizeOfFinalBuffer
* @return VOID
*/
BOOLEAN
ApplyFormatSpecifier(const CHAR * CurrentSpecifier, CHAR * FinalBuffer, PUINT32 CurrentProcessedPositionFromStartOfFormat, PUINT32 CurrentPositionInFinalBuffer, UINT64 Val, UINT32 SizeOfFinalBuffer)
{
UINT32 TempBufferLen = 0;
CHAR TempBuffer[50 + 1] = {
0}; // Maximum uint64_t is 18446744073709551615 + 1 thus its 20 character
// for maximum buffer + 1 end char null but we alloc 50 to be sure
*CurrentProcessedPositionFromStartOfFormat =
*CurrentProcessedPositionFromStartOfFormat + (UINT32)strlen(CurrentSpecifier);
INT FormatResult = PlatformSprintf(TempBuffer, sizeof(TempBuffer), CurrentSpecifier, Val);
if (FormatResult < 0)
{
return FALSE;
}
TempBufferLen = (UINT32)FormatResult;
//
// Check final buffer capacity
//
if (*CurrentPositionInFinalBuffer + TempBufferLen > SizeOfFinalBuffer)
{
//
// Over passed buffer
//
return FALSE;
}
memcpy(&FinalBuffer[*CurrentPositionInFinalBuffer], TempBuffer, TempBufferLen);
*CurrentPositionInFinalBuffer = *CurrentPositionInFinalBuffer + TempBufferLen;
return TRUE;
}
/**
* @brief Convert WCHAR* to CHAR*
*
* @param src
* @param dest
* @param dest_len
* @return size_t
*/
size_t
WcharToChar(const wchar_t * src, char * dest, size_t dest_len)
{
wchar_t Code;
size_t i;
i = 0;
while ((src[i] != '\0') && i < (dest_len - 1))
{
Code = src[i];
if (Code < 128)
dest[i] = (char)Code;
else
{
dest[i] = '?';
if (Code >= 0xD800 && Code <= 0xD8FF)
{
//
// Lead surrogate, skip the next code unit, which is the trail
//
i++;
}
}
i++;
}
return i - 1;
}
/**
* @brief Apply string format specifiers (%s, %ws, etc.)
*
* @param CurrentSpecifier
* @param FinalBuffer
* @param CurrentProcessedPositionFromStartOfFormat
* @param CurrentPositionInFinalBuffer
* @param Val
* @param IsWstring
* @param SizeOfFinalBuffer
* @return BOOLEAN
*/
BOOLEAN
ApplyStringFormatSpecifier(const CHAR * CurrentSpecifier, CHAR * FinalBuffer, PUINT32 CurrentProcessedPositionFromStartOfFormat, PUINT32 CurrentPositionInFinalBuffer, UINT64 Val, BOOLEAN IsWstring, UINT32 SizeOfFinalBuffer)
{
UINT32 StringSize;
wchar_t WstrBuffer[50];
CHAR AsciiBuffer[sizeof(WstrBuffer) / 2];
UINT32 StringSizeInByte; /* because of wide-char */
UINT32 CountOfBlocks;
UINT32 CopiedBlockLen;
//
// First we have to check if string is valid or not
//
if (!CheckIfStringIsSafe(Val, IsWstring))
{
return FALSE;
}
//
// get the length of the string (format) identifier
//
*CurrentProcessedPositionFromStartOfFormat += (UINT32)strlen(CurrentSpecifier);
//
// Get string len
//
StringSize = CustomStrlen(Val, IsWstring);
//
// Check final buffer capacity
//
if (*CurrentPositionInFinalBuffer + StringSize > SizeOfFinalBuffer)
{
//
// Over passed buffer
//
return TRUE;
}
//
// Move the buffer string into the target buffer
//
if (IsWstring)
{
//
// Parse wstring
//
StringSizeInByte = StringSize * 2; /* because of wide-char */
//
// compute the ceiling
//
if (StringSizeInByte % sizeof(WstrBuffer) == 0)
{
CountOfBlocks = StringSizeInByte / sizeof(WstrBuffer);
}
else
{
CountOfBlocks = (StringSizeInByte / sizeof(WstrBuffer)) + 1;
}
for (size_t i = 0; i < CountOfBlocks; i++)
{
//
// Zero the buffers
//
PlatformZeroMemory(WstrBuffer, sizeof(WstrBuffer));
PlatformZeroMemory(AsciiBuffer, sizeof(AsciiBuffer));
//
// Check for the last block
//
if (i == CountOfBlocks - 1)
{
//
// A portion of block
//
#ifdef SCRIPT_ENGINE_USER_MODE
memcpy(WstrBuffer, (void *)(Val + (i * sizeof(WstrBuffer))), StringSizeInByte % sizeof(WstrBuffer));
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperReadMemorySafeOnTargetProcess(
(UINT64)(Val + (i * sizeof(WstrBuffer))),
WstrBuffer,
StringSizeInByte % sizeof(WstrBuffer));
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
else
{
//
// A complete block
//
#ifdef SCRIPT_ENGINE_USER_MODE
memcpy(WstrBuffer, (void *)(Val + (i * sizeof(WstrBuffer))), sizeof(WstrBuffer));
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperReadMemorySafeOnTargetProcess(
(UINT64)(Val + (i * sizeof(WstrBuffer))),
WstrBuffer,
sizeof(WstrBuffer));
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
//
// Here we have the filled WstrBuffer
// We should convert WstrBuffer to AsciiBuffer
//
CopiedBlockLen =
(UINT32)WcharToChar(WstrBuffer, AsciiBuffer, sizeof(AsciiBuffer) + 1);
//
// Now we should move the AsciiBuffer to the target buffer
// (when we filled AsciiBuffer the memory here is safe so we
// can use memcpy in both user-mode and vmx-root mode)
//
memcpy(&FinalBuffer[*CurrentPositionInFinalBuffer], (void *)AsciiBuffer, CopiedBlockLen + 1);
*CurrentPositionInFinalBuffer += CopiedBlockLen + 1;
}
}
else
{
//
// Parse string
//
#ifdef SCRIPT_ENGINE_USER_MODE
memcpy(&FinalBuffer[*CurrentPositionInFinalBuffer], (void *)Val, StringSize);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
MemoryMapperReadMemorySafeOnTargetProcess(
Val,
&FinalBuffer[*CurrentPositionInFinalBuffer],
StringSize);
#endif // SCRIPT_ENGINE_KERNEL_MODE
*CurrentPositionInFinalBuffer += StringSize;
}
return TRUE;
}
/**
* @brief Implementation of printf function
*
* @param GuestRegs
* @param ActionDetail
* @param ScriptGeneralRegisters
* @param Tag
* @param ImmediateMessagePassing
* @param Format
* @param ArgCount
* @param FirstArg
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionPrintf(PGUEST_REGS GuestRegs,
ACTION_BUFFER * ActionDetail,
SCRIPT_ENGINE_GENERAL_REGISTERS * ScriptGeneralRegisters,
UINT64 Tag,
BOOLEAN ImmediateMessagePassing,
char * Format,
UINT64 ArgCount,
PSYMBOL FirstArg,
BOOLEAN * HasError)
{
//
// *** The printf function ***
//
char FinalBuffer[PacketChunkSize] = {0};
UINT32 CurrentPositionInFinalBuffer = 0;
UINT32 CurrentProcessedPositionFromStartOfFormat = 0;
BOOLEAN WithoutAnyFormatSpecifier = TRUE;
UINT64 Val;
UINT32 Position;
UINT32 LenOfFormats = (UINT32)strlen(Format) + 1;
PSYMBOL Symbol;
*HasError = FALSE;
for (int i = 0; i < ArgCount; i++)
{
WithoutAnyFormatSpecifier = FALSE;
Symbol = FirstArg + i;
//
// Address is either wstring (%ws) or string (%s)
//
Position = (Symbol->Type >> 32) + 1;
if (Position < CurrentProcessedPositionFromStartOfFormat || Position + 1 >= LenOfFormats)
{
*HasError = TRUE;
return;
}
SYMBOL TempSymbol = {0};
memcpy(&TempSymbol, Symbol, sizeof(SYMBOL));
TempSymbol.Type &= 0x7fffffff;
Val = GetValue(GuestRegs, ActionDetail, ScriptGeneralRegisters, &TempSymbol, FALSE);
CHAR PercentageChar = Format[Position];
// printf("position = %d is %c%c \n", Position, PercentageChar, IndicatorChar1);
if (CurrentProcessedPositionFromStartOfFormat != Position)
{
//
// There is some strings before this format specifier
// we should move it to the buffer
//
UINT32 StringLen = Position - CurrentProcessedPositionFromStartOfFormat;
//
// Check final buffer capacity
//
if (CurrentPositionInFinalBuffer + StringLen < sizeof(FinalBuffer))
{
memcpy(&FinalBuffer[CurrentPositionInFinalBuffer],
&Format[CurrentProcessedPositionFromStartOfFormat],
StringLen);
CurrentProcessedPositionFromStartOfFormat += StringLen;
CurrentPositionInFinalBuffer += StringLen;
}
else
{
*HasError = TRUE;
return;
}
}
//
// Double check and apply
//
if (PercentageChar == '%')
{
//
// Set first character of specifier
//
CHAR FormatSpecifier[5] = {0};
FormatSpecifier[0] = '%';
//
// Read second char
//
CHAR IndicatorChar2 = Format[Position + 1];
//
// Check if IndicatorChar2 is 2 character long or more
//
if (IndicatorChar2 == 'l' || IndicatorChar2 == 'w' ||
IndicatorChar2 == 'h')
{
if (Position + 2 >= LenOfFormats)
{
*HasError = TRUE;
return;
}
//
// Set second char in format specifier
//
FormatSpecifier[1] = IndicatorChar2;
if (IndicatorChar2 == 'l' && Format[Position + 2] == 'l')
{
if (Position + 3 >= LenOfFormats)
{
*HasError = TRUE;
return;
}
//
// Set third character in format specifier "ll"
//
FormatSpecifier[2] = 'l';
//
// Set last character
//
FormatSpecifier[3] = Format[Position + 3];
}
else
{
//
// Set last character
//
FormatSpecifier[2] = Format[Position + 2];
}
}
else
{
//
// It's a one char specifier (Set last character)
//
FormatSpecifier[1] = IndicatorChar2;
}
//
// Apply the specifier
//
UINT64 BaseType = TempSymbol.Type & 0xffffffffULL;
BOOLEAN IsFloatingValue =
BaseType != SYMBOL_STRING_TYPE && BaseType != SYMBOL_WSTRING_TYPE &&
(Symbol->Len == SYMBOL_VALUE_KIND_FLOAT32 || Symbol->Len == SYMBOL_VALUE_KIND_FLOAT64);
if (!strncmp(FormatSpecifier, "%f", 2))
{
if (!IsFloatingValue ||
!ApplyFloatingFormatSpecifier(FinalBuffer,
&CurrentProcessedPositionFromStartOfFormat,
&CurrentPositionInFinalBuffer,
Val,
Symbol->Len,
sizeof(FinalBuffer)))
{
*HasError = TRUE;
return;
}
}
else if (IsFloatingValue)
{
*HasError = TRUE;
return;
}
else if (!strncmp(FormatSpecifier, "%s", 2))
{
//
// for string
//
if (!ApplyStringFormatSpecifier(
"%s",
FinalBuffer,
&CurrentProcessedPositionFromStartOfFormat,
&CurrentPositionInFinalBuffer,
Val,
FALSE,
sizeof(FinalBuffer)))
{
*HasError = TRUE;
return;
}
}
else if (!strncmp(FormatSpecifier, "%ls", 3) ||
!strncmp(FormatSpecifier, "%ws", 3))
{
//
// for wide string (not important if %ls or %ws , only the length is
// important)
//
if (!ApplyStringFormatSpecifier(
"%ws",
FinalBuffer,
&CurrentProcessedPositionFromStartOfFormat,
&CurrentPositionInFinalBuffer,
Val,
TRUE,
sizeof(FinalBuffer)))
{
*HasError = TRUE;
return;
}
}
else
{
if (!ApplyFormatSpecifier(FormatSpecifier, FinalBuffer, &CurrentProcessedPositionFromStartOfFormat, &CurrentPositionInFinalBuffer, Val, sizeof(FinalBuffer)))
{
*HasError = TRUE;
return;
}
}
}
}
if (WithoutAnyFormatSpecifier)
{
//
// Means that it's just a simple print without any format specifier
//
if (LenOfFormats < sizeof(FinalBuffer))
{
memcpy(FinalBuffer, Format, LenOfFormats);
}
else
{
*HasError = TRUE;
return;
}
}
else
{
//
// Check if there is anything after the last format specifier
//
if (LenOfFormats > CurrentProcessedPositionFromStartOfFormat)
{
UINT32 RemainedLen =
LenOfFormats - CurrentProcessedPositionFromStartOfFormat;
if (CurrentPositionInFinalBuffer + RemainedLen < sizeof(FinalBuffer))
{
memcpy(&FinalBuffer[CurrentPositionInFinalBuffer],
&Format[CurrentProcessedPositionFromStartOfFormat],
RemainedLen);
}
else
{
*HasError = TRUE;
return;
}
}
}
//
// Print final result
//
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("%s", FinalBuffer);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Prepare a buffer to bypass allocating a huge stack space for logging
//
LogSimpleWithTag((UINT32)Tag, ImmediateMessagePassing, FinalBuffer, (UINT32)strlen(FinalBuffer) + 1);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of event_inject function
*
* @param InterruptionType
* @param Vector
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionEventInject(UINT32 InterruptionType, UINT32 Vector, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, event_inject is not supported in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Validate the arguments
//
if (Vector >= 256 || InterruptionType >= 8)
{
LogInfo("Err, invalid event vector or interruption type is specified");
return;
}
else
{
VmFuncEventInjectInterruption(InterruptionType, Vector, FALSE, 0);
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of event_inject_error_code function
*
* @param InterruptionType
* @param Vector
* @param ErrorCode
* @param HasError
* @return VOID
*/
VOID
ScriptEngineFunctionEventInjectErrorCode(UINT32 InterruptionType, UINT32 Vector, UINT32 ErrorCode, BOOL * HasError)
{
UNREFERENCED_PARAMETER(HasError);
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, event_inject is not supported in user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Validate the arguments
//
if (Vector >= 256 || InterruptionType >= 8)
{
LogInfo("Err, invalid event vector or interruption type is specified");
return;
}
else
{
VmFuncEventInjectInterruption(InterruptionType, Vector, TRUE, ErrorCode);
}
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of strcmp function
*
* @param Address1
* @param Address2
*
* @return UINT64
*/
UINT64
ScriptEngineFunctionStrcmp(const char * Address1, const char * Address2)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
Result = strcmp(Address1, Address2);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = VmFuncVmxCompatibleStrcmp(Address1, Address2);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
/**
* @brief Implementation of strcmp function
*
* @param Address1
* @param Address2
* @param Num
*
* @return UINT64
*/
UINT64
ScriptEngineFunctionStrncmp(const char * Address1, const char * Address2, size_t Num)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
Result = strncmp(Address1, Address2, Num);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = VmFuncVmxCompatibleStrncmp(Address1, Address2, Num);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
/**
* @brief Implementation of wcscmp function
*
* @param Address1
* @param Address2
*
* @return UINT64
*/
UINT64
ScriptEngineFunctionWcscmp(const wchar_t * Address1, const wchar_t * Address2)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
Result = wcscmp(Address1, Address2);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = VmFuncVmxCompatibleWcscmp(Address1, Address2);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
/**
* @brief Implementation of wcsncmp function
*
* @param Address1
* @param Address2
* @param Num
*
* @return UINT64
*/
UINT64
ScriptEngineFunctionWcsncmp(const wchar_t * Address1, const wchar_t * Address2, size_t Num)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
Result = wcsncmp(Address1, Address2, Num);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = VmFuncVmxCompatibleWcsncmp(Address1, Address2, Num);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
/**
* @brief Implementation of memcmp function
*
* @param Address1
* @param Address2
* @param Count
* @return UINT64
*/
UINT64
ScriptEngineFunctionMemcmp(const char * Address1, const char * Address2, size_t Count)
{
UINT64 Result = 0;
#ifdef SCRIPT_ENGINE_USER_MODE
Result = memcmp(Address1, Address2, Count);
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
Result = VmFuncVmxCompatibleMemcmp(Address1, Address2, Count);
#endif // SCRIPT_ENGINE_KERNEL_MODE
return Result;
}
/**
* @brief Implementation of event_trace_instrumentation_step function
*
* @return VOID
*/
VOID
ScriptEngineFunctionEventTraceInstrumentationStep()
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to call event_trace_instrumentation_step function in the user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
ULONG CurrentCore = PlatformCpuGetCurrentProcessorNumber();
//
// Call instrumentation step in
//
TracingPerformInstrumentationStepIn(&g_DbgState[CurrentCore]);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of event_trace_step_in function
*
* @return VOID
*/
VOID
ScriptEngineFunctionEventTraceStepIn()
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to call event_trace_instrumentation_step function in the user-mode\n");
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Call instrumentation step in
//
TracingPerformRegularStepInInstruction();
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of lbr_save function
*
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionLbrSave()
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to call lbr_save function in the user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Depending if we are in VMX-root then a VMCALL is issued by default instead, otherwise the VMCALL is ignored
//
return HyperTraceLbrSave(NULL);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of lbr_print function
*
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionLbrPrint()
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to call lbr_print function in the user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
//
// Depending if we are in VMX-root then a VMCALL is issued by default instead, otherwise the VMCALL is ignored
//
return HyperTraceLbrPrint(NULL);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of lbr_check function
*
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionLbrCheck()
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to call lbr_check function in the user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
return HyperTraceLbrCheck();
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of lbr_restore function
*
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionLbrRestore()
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to call lbr_restore function in the user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
return HyperTraceLbrRestore();
#endif // SCRIPT_ENGINE_KERNEL_MODE
}
/**
* @brief Implementation of lbr_restore_by_filter function
*
* @return BOOLEAN
*/
BOOLEAN
ScriptEngineFunctionLbrRestoreByFilter(UINT64 FilterOptions)
{
#ifdef SCRIPT_ENGINE_USER_MODE
ShowMessages("err, it's not possible to call lbr_restore_by_filter function in the user-mode\n");
return FALSE;
#endif // SCRIPT_ENGINE_USER_MODE
#ifdef SCRIPT_ENGINE_KERNEL_MODE
return HyperTraceLbrRestoreByFilter(FilterOptions);
#endif // SCRIPT_ENGINE_KERNEL_MODE
}