mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-15 12:09:34 +00:00
328 lines
9.6 KiB
C++
328 lines
9.6 KiB
C++
/**
|
|
* @file callstack.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief Callstack related routines
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2022-03-06
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern BOOLEAN g_AddressConversion;
|
|
|
|
/**
|
|
* @brief Walkthrough the stack
|
|
* @details This code is borrowed from here :
|
|
* https://github.com/electronicarts/EAThread/blob/master/source/x86/eathread_callstack_x86.cpp
|
|
*
|
|
* @param ReturnAddress
|
|
* @param IndexOfCallFromReturnAddress
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
CallstackReturnAddressToCallingAddress(UCHAR * ReturnAddress, PUINT32 IndexOfCallFromReturnAddress)
|
|
{
|
|
//
|
|
// While negative array indices can be considered non-idiomatic it
|
|
// was felt that they are semantically appropriate as this code bases
|
|
// its comparisons from the return address and that it would be cleaner
|
|
// than using *(ReturnAddress - index).
|
|
//
|
|
|
|
//
|
|
// Three op-codes are used for the call instruction, 9A, E8, and FF.
|
|
// For a reference on the IA32 instruction format, see:
|
|
// http://www.cs.princeton.edu/courses/archive/spr06/cos217/reading/ia32vol2.pdf
|
|
//
|
|
|
|
//
|
|
// 9A cp - CALL ptr16:32 (7-byte)
|
|
//
|
|
if (ReturnAddress[-7] == 0x9A)
|
|
{
|
|
*IndexOfCallFromReturnAddress = 7;
|
|
return TRUE;
|
|
}
|
|
// E8 cd - CALL rel32 (5-byte)
|
|
else if (ReturnAddress[-5] == 0xE8)
|
|
{
|
|
*IndexOfCallFromReturnAddress = 5;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The third opcode to specify "call" instructions is FF.
|
|
// Unfortunately this instruction also needs the succeeding ModR/M
|
|
// byte to fully determine instruction length. The SIB value is
|
|
// another byte used for extending the range of addressing modes
|
|
// supported by the ModR/M byte. The values of this ModR/M byte
|
|
// used in conjunction with the call instruction are as follows:
|
|
//
|
|
// 7-byte call:
|
|
// FF [ModR/M] [SIB] [4-byte displacement]
|
|
// * ModR/M is either 0x94 or 0x9C
|
|
//
|
|
// 6-byte call:
|
|
// FF [ModR/M] [4-byte displacement]
|
|
// * ModR/M can be:
|
|
// * 0x90 - 0x9F EXCLUDING 0x94 or 0x9C
|
|
// * 0x15 or 0x1D
|
|
//
|
|
// 4-byte call:
|
|
// FF [ModR/M] [SIB] [1-byte displacement]
|
|
// * ModR/M is either 0x54 or 0x5C
|
|
//
|
|
// 3-byte call:
|
|
// FF [ModR/M] [1-byte displacement]
|
|
// * ModR/M can be:
|
|
// * 0x50 - 0x5F EXCLUDING 0x54 or 0x5C
|
|
// FF [ModR/M] [SIB]
|
|
// * ModR/M is either 0x14 or 0x1C
|
|
//
|
|
// 2-byte call:
|
|
// FF [ModR/M]
|
|
// * ModR/M can be:
|
|
// * 0xD0 - 0xDF
|
|
// * 0x10 - 0x1F EXCEPT 0x14, 0x15, 0x1C, or 0x1D
|
|
//
|
|
|
|
//
|
|
// The mask of F8 is used because we want to mask out the bottom
|
|
// three bits (which are most often used for register selection)
|
|
//
|
|
const UCHAR RmMask = 0xF8;
|
|
|
|
//
|
|
// 7-byte format:
|
|
//
|
|
if (ReturnAddress[-7] == 0xFF &&
|
|
(ReturnAddress[-6] == 0x94 || ReturnAddress[-6] == 0x9C))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 7;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// 6-byte format:
|
|
// FF [ModR/M] [4-byte displacement]
|
|
//
|
|
else if (ReturnAddress[-6] == 0xFF &&
|
|
((ReturnAddress[-5] & RmMask) == 0x90 || (ReturnAddress[-5] & RmMask) == 0x98) &&
|
|
(ReturnAddress[-5] != 0x94 && ReturnAddress[-5] != 0x9C))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 6;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Alternate 6-byte format:
|
|
//
|
|
else if (ReturnAddress[-6] == 0xFF &&
|
|
(ReturnAddress[-5] == 0x15 || ReturnAddress[-5] == 0x1D))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 6;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// 4-byte format:
|
|
// FF [ModR/M] [SIB] [1-byte displacement]
|
|
//
|
|
else if (ReturnAddress[-4] == 0xFF &&
|
|
(ReturnAddress[-3] == 0x54 || ReturnAddress[-3] == 0x5C))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 4;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// 3-byte format:
|
|
// FF [ModR/M] [1-byte displacement]
|
|
//
|
|
else if (ReturnAddress[-3] == 0xFF &&
|
|
((ReturnAddress[-2] & RmMask) == 0x50 || (ReturnAddress[-2] & RmMask) == 0x58) &&
|
|
(ReturnAddress[-2] != 0x54 && ReturnAddress[-2] != 0x5C))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 3;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Alternate 3-byte format:
|
|
// FF [ModR/M] [SIB]
|
|
//
|
|
else if (ReturnAddress[-3] == 0xFF &&
|
|
(ReturnAddress[-2] == 0x14 || ReturnAddress[-2] == 0x1C))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 3;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// 2-byte calling format:
|
|
// FF [ModR/M]
|
|
//
|
|
else if (ReturnAddress[-2] == 0xFF &&
|
|
((ReturnAddress[-1] & RmMask) == 0xD0 || (ReturnAddress[-1] & RmMask) == 0xD8))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 2;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Alternate 2-byte calling format:
|
|
// FF [ModR/M]
|
|
//
|
|
else if (ReturnAddress[-2] == 0xFF &&
|
|
((ReturnAddress[-1] & RmMask) == 0x10 || (ReturnAddress[-1] & RmMask) == 0x18) &&
|
|
(ReturnAddress[-1] != 0x14 && ReturnAddress[-1] != 0x15 &&
|
|
ReturnAddress[-1] != 0x1C && ReturnAddress[-1] != 0x1D))
|
|
{
|
|
*IndexOfCallFromReturnAddress = 2;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Show stack frames
|
|
*
|
|
* @param CallstackFrames
|
|
* @param FrameCount
|
|
* @param DisplayMethod
|
|
* @param Is32Bit
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
CallstackShowFrames(PDEBUGGER_SINGLE_CALLSTACK_FRAME CallstackFrames,
|
|
UINT32 FrameCount,
|
|
DEBUGGER_CALLSTACK_DISPLAY_METHOD DisplayMethod,
|
|
BOOLEAN Is32Bit)
|
|
{
|
|
UINT32 CallLength;
|
|
UINT64 TargetAddress;
|
|
UINT64 UsedBaseAddress;
|
|
BOOLEAN IsCall = FALSE;
|
|
std::map<UINT64, LOCAL_FUNCTION_DESCRIPTION>::iterator Iterate;
|
|
|
|
//
|
|
// Print callstack frames
|
|
//
|
|
for (SIZE_T i = 0; i < FrameCount; i++)
|
|
{
|
|
IsCall = FALSE;
|
|
|
|
if (CallstackFrames[i].IsValidAddress)
|
|
{
|
|
//
|
|
// Check if it's call or just a simple code address
|
|
//
|
|
if (CallstackFrames[i].IsExecutable && CallstackReturnAddressToCallingAddress(
|
|
(UCHAR *)&CallstackFrames[i].InstructionBytesOnRip[MAXIMUM_CALL_INSTR_SIZE],
|
|
&CallLength))
|
|
{
|
|
//
|
|
// Computer the "call" instruction address
|
|
//
|
|
TargetAddress = CallstackFrames[i].Value - CallLength;
|
|
|
|
IsCall = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if we wanna show the stack params
|
|
//
|
|
if (DisplayMethod == DEBUGGER_CALLSTACK_DISPLAY_METHOD_WITHOUT_PARAMS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IsCall = FALSE;
|
|
TargetAddress = CallstackFrames[i].Value;
|
|
}
|
|
|
|
ShowMessages("[$+%03x] ", i * (Is32Bit ? sizeof(UINT32) : sizeof(UINT64)));
|
|
|
|
if (IsCall)
|
|
{
|
|
if (Is32Bit)
|
|
{
|
|
ShowMessages(" %08x (from ", TargetAddress);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages(" %016llx (from ", TargetAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Is32Bit)
|
|
{
|
|
ShowMessages(" %08x (addr ", TargetAddress);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages(" %016llx (addr ", TargetAddress);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Show the name of the function if available
|
|
// Apply addressconversion of settings here
|
|
//
|
|
if (g_AddressConversion)
|
|
{
|
|
if (SymbolShowFunctionNameBasedOnAddress(TargetAddress, &UsedBaseAddress))
|
|
{
|
|
ShowMessages(" ");
|
|
}
|
|
}
|
|
|
|
if (Is32Bit)
|
|
{
|
|
ShowMessages("<%08x>)\n", TargetAddress);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("<%016llx>)\n", TargetAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if we wanna show the stack params
|
|
//
|
|
if (DisplayMethod == DEBUGGER_CALLSTACK_DISPLAY_METHOD_WITHOUT_PARAMS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ShowMessages("[$+%03x] ", i * (Is32Bit ? sizeof(UINT32) : sizeof(UINT64)));
|
|
|
|
if (Is32Bit)
|
|
{
|
|
ShowMessages(" %08x\n", CallstackFrames[i].Value);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages(" %016llx\n", CallstackFrames[i].Value);
|
|
}
|
|
}
|
|
}
|
|
}
|