mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-23 07:54:39 +00:00
2441 lines
59 KiB
C
2441 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
|
|
|
|
SpinlockLockWithCustomWait(Lock, MaxWait);
|
|
|
|
#endif // SCRIPT_ENGINE_USER_MODE
|
|
|
|
#ifdef SCRIPT_ENGINE_KERNEL_MODE
|
|
|
|
if (!CheckAccessValidityAndSafety((UINT64)Lock, sizeof(LONG)))
|
|
{
|
|
*HasError = TRUE;
|
|
return;
|
|
}
|
|
|
|
SpinlockLockWithCustomWait(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 wchar_t * Address)
|
|
{
|
|
UINT64 Result = 0;
|
|
|
|
#ifdef SCRIPT_ENGINE_USER_MODE
|
|
Result = wcslen(Address);
|
|
#endif // SCRIPT_ENGINE_USER_MODE
|
|
|
|
#ifdef SCRIPT_ENGINE_KERNEL_MODE
|
|
Result = VmFuncVmxCompatibleWcslen(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 = KeGetCurrentProcessorNumberEx(NULL);
|
|
|
|
//
|
|
// 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 = KeGetCurrentProcessorNumberEx(NULL);
|
|
|
|
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)
|
|
{
|
|
return (UINT32)wcslen((const wchar_t *)StrAddr);
|
|
}
|
|
else
|
|
{
|
|
return (UINT32)strlen((const char *)StrAddr);
|
|
}
|
|
#endif // SCRIPT_ENGINE_USER_MODE
|
|
|
|
#ifdef SCRIPT_ENGINE_KERNEL_MODE
|
|
if (IsWstring)
|
|
{
|
|
return VmFuncVmxCompatibleWcslen((const wchar_t *)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 = KeGetCurrentProcessorNumberEx(NULL);
|
|
|
|
//
|
|
// 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
|
|
}
|