diff --git a/hyperdbg/hprdbgctrl/code/debugger/commands/debugging-commands/k.cpp b/hyperdbg/hprdbgctrl/code/debugger/commands/debugging-commands/k.cpp index 42b256d0..a5e4fb25 100644 --- a/hyperdbg/hprdbgctrl/code/debugger/commands/debugging-commands/k.cpp +++ b/hyperdbg/hprdbgctrl/code/debugger/commands/debugging-commands/k.cpp @@ -41,5 +41,5 @@ CommandKHelp() VOID CommandK(vector SplittedCommand, string Command) { - KdSendCallStackPacketToDebuggee(NULL, 0x100, FALSE); + KdSendCallStackPacketToDebuggee(NULL, 0x180, FALSE); } diff --git a/hyperdbg/hprdbgctrl/code/debugger/kernel-level/kernel-listening.cpp b/hyperdbg/hprdbgctrl/code/debugger/kernel-level/kernel-listening.cpp index 77a9d653..c14375f8 100644 --- a/hyperdbg/hprdbgctrl/code/debugger/kernel-level/kernel-listening.cpp +++ b/hyperdbg/hprdbgctrl/code/debugger/kernel-level/kernel-listening.cpp @@ -576,12 +576,9 @@ StartAgain: if (CallstackPacket->KernelStatus == DEBUGGER_OPERATION_WAS_SUCCESSFULL) { // - // Print callstack frames + // Show the callstack // - for (size_t i = 0; i < CallstackPacket->FrameCount; i++) - { - ShowMessages("[%x]\t%llx\n", i, CallstackFramePacket[i].Value); - } + CallstackShowFrames(CallstackFramePacket, CallstackPacket->FrameCount); } else { diff --git a/hyperdbg/hprdbgctrl/code/debugger/misc/callstack.cpp b/hyperdbg/hprdbgctrl/code/debugger/misc/callstack.cpp new file mode 100644 index 00000000..181b25f6 --- /dev/null +++ b/hyperdbg/hprdbgctrl/code/debugger/misc/callstack.cpp @@ -0,0 +1,264 @@ +/** + * @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 "..\hprdbgctrl\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 unsigned char rm_mask = 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] & rm_mask) == 0x90 || (ReturnAddress[-5] & rm_mask) == 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] & rm_mask) == 0x50 || (ReturnAddress[-2] & rm_mask) == 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] & rm_mask) == 0xD0 || (ReturnAddress[-1] & rm_mask) == 0xD8)) + { + *IndexOfCallFromReturnAddress = 2; + return TRUE; + } + + // + // Alternate 2-byte calling format: + // FF [ModR/M] + // + else if (ReturnAddress[-2] == 0xFF && + ((ReturnAddress[-1] & rm_mask) == 0x10 || (ReturnAddress[-1] & rm_mask) == 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 + * + * @return VOid + */ +VOID +CallstackShowFrames(PDEBUGGER_SINGLE_CALLSTACK_FRAME CallstackFrames, UINT32 FrameCount) +{ + UINT32 CallLength; + UINT64 CallAddress; + UINT64 UsedBaseAddress; + std::map::iterator Iterate; + + // + // Print callstack frames + // + for (size_t i = 0; i < FrameCount; i++) + { + if (CallstackFrames[i].IsValidAddress && CallstackFrames[i].IsExecutable) + { + ShowMessages("[%x] %llx ", i, CallstackFrames[i].Value); + + if (CallstackReturnAddressToCallingAddress( + (unsigned char *)&CallstackFrames[i].InstructionBytesOnRip[MAXIMUM_CALL_INSTR_SIZE], + &CallLength)) + { + // + // Computer the "call" instruction address + // + CallAddress = CallstackFrames[i].Value - CallLength; + ShowMessages("- call from "); + + // + // Apply addressconversion of settings here + // + if (g_AddressConversion) + { + if (SymbolShowFunctionNameBasedOnAddress(CallAddress, &UsedBaseAddress)) + { + ShowMessages("\n"); + } + else + { + ShowMessages("%llx\n", CallAddress); + } + } + else + { + ShowMessages("%llx\n", CallAddress); + } + } + else + { + ShowMessages("(pointer to code - not call)\n"); + } + } + else + { + ShowMessages("[%x]\t%llx\n", i, CallstackFrames[i].Value); + } + } +} diff --git a/hyperdbg/hprdbgctrl/code/debugger/misc/disassembler.cpp b/hyperdbg/hprdbgctrl/code/debugger/misc/disassembler.cpp index 3a7908a1..535aee85 100644 --- a/hyperdbg/hprdbgctrl/code/debugger/misc/disassembler.cpp +++ b/hyperdbg/hprdbgctrl/code/debugger/misc/disassembler.cpp @@ -173,7 +173,13 @@ DisassembleBuffer(ZydisDecoder * decoder, // // Showing function names here // - SymbolShowFunctionNameBasedOnAddress(runtime_address, &UsedBaseAddress); + if (SymbolShowFunctionNameBasedOnAddress(runtime_address, &UsedBaseAddress)) + { + // + // The symbol address is showed + // + ShowMessages(":\n"); + } } // ZYAN_PRINTF("%016" PRIX64 " ", runtime_address); diff --git a/hyperdbg/hprdbgctrl/code/debugger/script-engine-wrapper/symbol.cpp b/hyperdbg/hprdbgctrl/code/debugger/script-engine-wrapper/symbol.cpp index 68ce21bf..92f11b70 100644 --- a/hyperdbg/hprdbgctrl/code/debugger/script-engine-wrapper/symbol.cpp +++ b/hyperdbg/hprdbgctrl/code/debugger/script-engine-wrapper/symbol.cpp @@ -198,7 +198,7 @@ SymbolShowFunctionNameBasedOnAddress(UINT64 Address, PUINT64 UsedBaseAddress) { if (*UsedBaseAddress != Address) { - ShowMessages("%s:\n", Low->second.ObjectName.c_str()); + ShowMessages("%s", Low->second.ObjectName.c_str()); *UsedBaseAddress = Address; } @@ -220,7 +220,7 @@ SymbolShowFunctionNameBasedOnAddress(UINT64 Address, PUINT64 UsedBaseAddress) { if (*UsedBaseAddress != Prev->first) { - ShowMessages("%s+0x%x:\n", Prev->second.ObjectName.c_str(), Diff); + ShowMessages("%s+0x%x", Prev->second.ObjectName.c_str(), Diff); *UsedBaseAddress = Prev->first; } @@ -235,7 +235,7 @@ SymbolShowFunctionNameBasedOnAddress(UINT64 Address, PUINT64 UsedBaseAddress) // if (*UsedBaseAddress != Prev->first) { - ShowMessages("%s+0x%x+0x%x:\n", Prev->second.ObjectName.c_str(), Diff, Diff - Prev->second.ObjectSize); + ShowMessages("%s+0x%x+0x%x", Prev->second.ObjectName.c_str(), Diff, Diff - Prev->second.ObjectSize); *UsedBaseAddress = Prev->first; } diff --git a/hyperdbg/hprdbgctrl/header/debugger.h b/hyperdbg/hprdbgctrl/header/debugger.h index 802f22ae..3331d9b2 100644 --- a/hyperdbg/hprdbgctrl/header/debugger.h +++ b/hyperdbg/hprdbgctrl/header/debugger.h @@ -81,6 +81,12 @@ InterpretGeneralEventAndActionsFields( PUINT32 ActionBufferLengthScript, PDEBUGGER_EVENT_PARSING_ERROR_CAUSE ReasonForErrorInParsing); +BOOLEAN +CallstackReturnAddressToCallingAddress(UCHAR * ReturnAddress, PUINT32 IndexOfCallFromReturnAddress); + +VOID +CallstackShowFrames(PDEBUGGER_SINGLE_CALLSTACK_FRAME CallstackFrames, UINT32 FrameCount); + UINT64 GetNewDebuggerEventTag(); diff --git a/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj b/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj index 978a23e1..e4979ceb 100644 --- a/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj +++ b/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj @@ -142,6 +142,7 @@ + diff --git a/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj.filters b/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj.filters index d3fdef05..15b6a557 100644 --- a/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj.filters +++ b/hyperdbg/hprdbgctrl/hprdbgctrl.vcxproj.filters @@ -436,6 +436,9 @@ code\debugger\commands\meta-commands + + code\debugger\misc + diff --git a/hyperdbg/hprdbghv/code/debugger/commands/Callstack.c b/hyperdbg/hprdbghv/code/debugger/commands/Callstack.c index 3826ca0e..c746793d 100644 --- a/hyperdbg/hprdbghv/code/debugger/commands/Callstack.c +++ b/hyperdbg/hprdbghv/code/debugger/commands/Callstack.c @@ -97,17 +97,24 @@ CallstackWalkthroughStack(PDEBUGGER_SINGLE_CALLSTACK_FRAME AddressToSaveFrames, // // Check if value is a valid address // - if (CheckMemoryAccessSafety(Value, MAXIMUM_INSTR_SIZE)) + if (CheckMemoryAccessSafety(Value, MAXIMUM_CALL_INSTR_SIZE)) { // // It's a valid address // AddressToSaveFrames[i].IsValidAddress = TRUE; + // + // Check if the target page has NX bit (executable page) + // + AddressToSaveFrames[i].IsExecutable = MemoryMapperCheckIfPageIsNxBitSetOnTargetProcess(Value); + // // Read the memory at the target address // - MemoryMapperReadMemorySafeOnTargetProcess(Value, AddressToSaveFrames[i].InstructionBytesOnRip, MAXIMUM_INSTR_SIZE); + MemoryMapperReadMemorySafeOnTargetProcess(Value - MAXIMUM_CALL_INSTR_SIZE, + AddressToSaveFrames[i].InstructionBytesOnRip, + MAXIMUM_CALL_INSTR_SIZE); } } diff --git a/hyperdbg/hprdbghv/code/memory/MemoryMapper.c b/hyperdbg/hprdbghv/code/memory/MemoryMapper.c index a5018f68..3a6f747d 100644 --- a/hyperdbg/hprdbghv/code/memory/MemoryMapper.c +++ b/hyperdbg/hprdbghv/code/memory/MemoryMapper.c @@ -244,6 +244,62 @@ MemoryMapperCheckIfPageIsPresentByCr3(PVOID Va, CR3_TYPE TargetCr3) } } +/** + * @brief This function checks if the page has NX bit or not + * + * @param Va Virtual Address + * @param TargetCr3 kernel cr3 of target process + * @return PPAGE_ENTRY virtual address of PTE based on cr3 + */ +BOOLEAN +MemoryMapperCheckIfPageIsNxBitSetByCr3(PVOID Va, CR3_TYPE TargetCr3) +{ + PPAGE_ENTRY PageEntry; + + // + // Find the page table entry + // + PageEntry = MemoryMapperGetPteVaByCr3(Va, PT, TargetCr3); + + if (PageEntry != NULL && !PageEntry->ExecuteDisable) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief This function checks target process to see + * if the page has NX bit or not + * + * @param Va Virtual Address + * @param TargetCr3 kernel cr3 of target process + * @return PPAGE_ENTRY virtual address of PTE based on cr3 + */ +BOOLEAN +MemoryMapperCheckIfPageIsNxBitSetOnTargetProcess(PVOID Va) +{ + CR3_TYPE GuestCr3; + + // + // Move to guest process as we're currently in system cr3 + // + + // + // Find the current process cr3 + // + NT_KPROCESS * CurrentProcess = (NT_KPROCESS *)(PsGetCurrentProcess()); + GuestCr3.Flags = CurrentProcess->DirectoryTableBase; + + // + // Check NX bit + // + return MemoryMapperCheckIfPageIsNxBitSetByCr3(Va, GuestCr3); +} + /** * @brief This function reserve memory from system range (without physically allocating them) * diff --git a/hyperdbg/hprdbghv/header/memory/MemoryMapper.h b/hyperdbg/hprdbghv/header/memory/MemoryMapper.h index 001db1d1..729fe85c 100644 --- a/hyperdbg/hprdbghv/header/memory/MemoryMapper.h +++ b/hyperdbg/hprdbghv/header/memory/MemoryMapper.h @@ -378,3 +378,6 @@ MemoryMapperWriteMemoryUnsafe(UINT64 Destination, PVOID Source, SIZE_T SizeToWri BOOLEAN MemoryMapperWriteMemorySafeByPhysicalAddress(UINT64 DestinationPa, UINT64 Source, SIZE_T SizeToWrite); + +BOOLEAN +MemoryMapperCheckIfPageIsNxBitSetOnTargetProcess(PVOID Va); diff --git a/hyperdbg/include/Definition.h b/hyperdbg/include/Definition.h index 252da97d..558e5716 100644 --- a/hyperdbg/include/Definition.h +++ b/hyperdbg/include/Definition.h @@ -598,6 +598,11 @@ typedef struct _USERMODE_LOADED_MODULE_DETAILS */ #define MAXIMUM_INSTR_SIZE 16 +/** + * @brief maximum size for call instruction in Intel + */ +#define MAXIMUM_CALL_INSTR_SIZE 7 + ////////////////////////////////////////////////// // Callback Definitions // ////////////////////////////////////////////////// @@ -1610,8 +1615,9 @@ typedef struct _DEBUGGER_SINGLE_CALLSTACK_FRAME { BOOLEAN IsStackAddressValid; BOOLEAN IsValidAddress; + BOOLEAN IsExecutable; UINT64 Value; - BYTE InstructionBytesOnRip[MAXIMUM_INSTR_SIZE]; + BYTE InstructionBytesOnRip[MAXIMUM_CALL_INSTR_SIZE]; } DEBUGGER_SINGLE_CALLSTACK_FRAME, *PDEBUGGER_SINGLE_CALLSTACK_FRAME;