220 lines
9.1 KiB
Nim
220 lines
9.1 KiB
Nim
import winim/lean
|
|
import ./io
|
|
import ../../common/utils
|
|
|
|
# From: https://github.com/m4ul3r/malware/blob/main/nim/hardware_breakpoints/hardwarebreakpoints.nim
|
|
|
|
type
|
|
DRX* = enum
|
|
Dr0, Dr1, Dr2, Dr3
|
|
HookFuncType = proc(pContext:PCONTEXT){.stdcall.}
|
|
|
|
var
|
|
g_VectorHandler: PVOID # Vectored Exception Handler
|
|
g_DetourFuncs: array[4, PVOID] # Array of 4 Hook functions
|
|
g_CriticalSection: CRITICAL_SECTION
|
|
|
|
proc ucRet*() {.asmNoStackFrame.} =
|
|
## Ret used to terminate original function execution
|
|
asm """.byte 0xc3"""
|
|
|
|
proc BLOCK_REAL*(pThreadCtx: PCONTEXT) =
|
|
## Used in detour function to block execution of original hooked function
|
|
pThreadCtx.Rip = cast[int](ucRet)
|
|
|
|
proc setDr7Bits*(currentDr7Register, startingBitPosition, nmbrOfBitsToModify, newBitValue: int): int =
|
|
## Enable or disable an installed breakpoint
|
|
var
|
|
mask: int = ((1 shl nmbrOfBitsToModify) - 1)
|
|
newDr7Register: int = (currentDr7Register and not (mask shl startingBitPosition)) or (newBitValue shl startingBitPosition)
|
|
return newDr7Register
|
|
|
|
proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool =
|
|
var threadCtx: CONTEXT
|
|
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
|
|
|
|
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
|
raise newException(CatchableError, GetLastError().getError())
|
|
|
|
case drx:
|
|
of Dr0:
|
|
if (threadCtx.Dr0 == 0):
|
|
threadCtx.Dr0 = cast[int](pAddress)
|
|
of Dr1:
|
|
if (threadCtx.Dr1 == 0):
|
|
threadCtx.Dr1 = cast[int](pAddress)
|
|
of Dr2:
|
|
if (threadCtx.Dr2 == 0):
|
|
threadCtx.Dr2 = cast[int](pAddress)
|
|
of Dr3:
|
|
if (threadCtx.Dr3 == 0):
|
|
threadCtx.Dr3 = cast[int](pAddress)
|
|
|
|
# Save the hooked function at index 'drx' in global array
|
|
EnterCriticalSection(g_CriticalSection.addr)
|
|
g_DetourFuncs[cast[int](drx)] = fnHookFunc
|
|
LeaveCriticalSection(g_CriticalSection.addr)
|
|
|
|
# Enable the breakpoint
|
|
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 1)
|
|
|
|
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
|
raise newException(CatchableError, GetLastError().getError())
|
|
|
|
return true
|
|
|
|
proc removeHardwareBreakpoint*(drx: DRX): bool =
|
|
var threadCtx: CONTEXT
|
|
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
|
|
|
|
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
|
raise newException(CatchableError, GetLastError().getError())
|
|
|
|
# Remove the address of the hooked function from the thread context
|
|
case drx:
|
|
of Dr0:
|
|
threadCtx.Dr0 = cast[int](0)
|
|
of Dr1:
|
|
threadCtx.Dr1 = cast[int](0)
|
|
of Dr2:
|
|
threadCtx.Dr2 = cast[int](0)
|
|
of Dr3:
|
|
threadCtx.Dr3 = cast[int](0)
|
|
|
|
# Disabling the breakpoint
|
|
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 0)
|
|
|
|
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
|
raise newException(CatchableError, GetLastError().getError())
|
|
|
|
return true
|
|
|
|
proc vectorHandler*(pExceptionInfo: ptr EXCEPTION_POINTERS): int =
|
|
# If the exception is 'EXCEPTION_SINGLE_STEP' then its caused by a bp
|
|
if (pExceptionInfo.ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP):
|
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr0) or
|
|
(cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr1) or
|
|
(cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr2) or
|
|
(cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr3):
|
|
var
|
|
dwDrx: DRX
|
|
fnHookFunc = cast[HookFuncType](0)
|
|
|
|
EnterCriticalSection(g_CriticalSection.addr)
|
|
|
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr0):
|
|
dwDrx = Dr0
|
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr1):
|
|
dwDrx = Dr1
|
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr2):
|
|
dwDrx = Dr2
|
|
if (cast[int](pExceptionInfo.ExceptionRecord.ExceptionAddress) == pExceptionInfo.ContextRecord.Dr3):
|
|
dwDrx = Dr3
|
|
|
|
discard removeHardwareBreakpoint(dwDrx)
|
|
|
|
# Execute the callback (detour function)
|
|
fnHookFunc = cast[HookFuncType](g_DetourFuncs[cast[int](dwDrx)])
|
|
fnHookFunc(pExceptionInfo.ContextRecord)
|
|
|
|
discard setHardwareBreakpoint(pExceptionInfo.ExceptionRecord.ExceptionAddress, g_DetourFuncs[cast[int](dwDrx)], dwDrx)
|
|
|
|
LeaveCriticalSection(g_CriticalSection.addr)
|
|
|
|
return EXCEPTION_CONTINUE_EXECUTION
|
|
# The exception is not handled
|
|
return EXCEPTION_CONTINUE_SEARCH
|
|
|
|
#[ Function argument handling ]#
|
|
proc getFunctionArgument*(pThreadCtx: PCONTEXT, dwParamIdx: int): pointer =
|
|
# amd64
|
|
case dwParamIdx:
|
|
of 1:
|
|
return cast[PULONG](pThreadCtx.Rcx)
|
|
of 2:
|
|
return cast[PULONG](pThreadCtx.Rdx)
|
|
of 3:
|
|
return cast[PULONG](pThreadCtx.R8)
|
|
of 4:
|
|
return cast[PULONG](pThreadCtx.R9)
|
|
else:
|
|
# else more arguments are pushed to the stack
|
|
return cast[PULONG](pThreadCtx.Rsp + (dwParamIdx * sizeof(PVOID)))
|
|
|
|
proc setFunctionArgument*(pThreadCtx: PCONTEXT, uValue: PULONG, dwParamIdx: int) =
|
|
# amd64
|
|
case dwParamIdx:
|
|
of 1:
|
|
pThreadCtx.Rcx = cast[int](uValue)
|
|
of 2:
|
|
pThreadCtx.Rdx = cast[int](uValue)
|
|
of 3:
|
|
pThreadCtx.R8 = cast[int](uValue)
|
|
of 4:
|
|
pThreadCtx.R9 = cast[int](uValue)
|
|
else:
|
|
# else more arguments are pushed to the stack
|
|
cast[ptr int](pThreadCtx.Rsp + (dwParamIdx * sizeof(PVOID)))[] = cast[int](uValue)
|
|
|
|
# getFunctionArgument macros
|
|
template GETPARAM_1*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 1)
|
|
template GETPARAM_2*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 2)
|
|
template GETPARAM_3*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 3)
|
|
template GETPARAM_4*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 4)
|
|
template GETPARAM_5*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 5)
|
|
template GETPARAM_6*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 6)
|
|
template GETPARAM_7*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 7)
|
|
template GETPARAM_8*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 8)
|
|
template GETPARAM_9*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, 9)
|
|
template GETPARAM_a*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, a)
|
|
template GETPARAM_b*(ctx: PCONTEXT): pointer = getFunctionArgument(ctx, b)
|
|
|
|
# setFunctionArgument macros
|
|
template SETPARAM_1*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 1)
|
|
template SETPARAM_2*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 2)
|
|
template SETPARAM_3*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 3)
|
|
template SETPARAM_4*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 4)
|
|
template SETPARAM_5*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 5)
|
|
template SETPARAM_6*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 6)
|
|
template SETPARAM_7*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 7)
|
|
template SETPARAM_8*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 8)
|
|
template SETPARAM_9*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, 9)
|
|
template SETPARAM_a*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, a)
|
|
template SETPARAM_b*(ctx: PCONTEXT, value: untyped) = setFunctionArgument(ctx, value, b)
|
|
|
|
#[ init/uninit HW BP]#
|
|
proc initializeHardwareBPVariables*(): bool =
|
|
# If 'g_CriticalSection' is not yet initialized
|
|
if g_CriticalSection.DebugInfo == NULL:
|
|
InitializeCriticalSection(g_CriticalSection.addr)
|
|
|
|
# If 'g_VectorHandler' is not yet initialized
|
|
if (cast[int](g_VectorHandler) == 0):
|
|
# Add 'VectorHandler' as the VEH
|
|
g_VectorHandler = AddVectoredExceptionHandler(1, cast[PVECTORED_EXCEPTION_HANDLER](vectorHandler))
|
|
if cast[int](g_VectorHandler) == 0:
|
|
raise newException(CatchableError, GetLastError().getError())
|
|
return false
|
|
|
|
if (cast[int](g_VectorHandler) and cast[int](g_CriticalSection.DebugInfo)) != 0:
|
|
return true
|
|
|
|
proc uninitializeHardwareBPVariables*() =
|
|
# Remove breakpoints
|
|
for i in 0 ..< 4:
|
|
discard removeHardwareBreakpoint(cast[DRX](i))
|
|
# If the critical section is initialized, delete it
|
|
if (cast[int](g_CriticalSection.DebugInfo) != 0):
|
|
DeleteCriticalSection(g_CriticalSection.addr)
|
|
# If VEH if registered, remove it
|
|
if (cast[int](g_VectorHandler) != 0):
|
|
RemoveVectoredExceptionHandler(g_VectorHandler)
|
|
|
|
# Cleanup the global variables
|
|
zeroMem(g_CriticalSection.addr, sizeof(g_CriticalSection))
|
|
zeroMem(g_DetourFuncs.addr, sizeof(g_DetourFuncs))
|
|
g_VectorHandler = cast[PVOID](0)
|
|
|
|
template CONTINUE_EXECUTION*(ctx: PCONTEXT) = (ctx.EFlags = (ctx.EFlags or (1 shl 16)))
|
|
|