k, kd, kq (display stack backtrace)

Description of 'k, kd, kq' commands in HyperDbg.

Command

k : show stack backtrace (only calls)

kd : show stack backtrace (calls along with parameters in stack) in 32-bit format

kq : show stack backtrace (calls along with parameters in stack) in 64-bit format

Syntax

k

kd

kq

k [base StackAddress (hex)] [l Length (hex)]

kd [base StackAddress (hex)] [l Length (hex)]

kq [base StackAddress (hex)] [l Length (hex)]

Description

Displays stack backtrace and optionally show the parameter in the stack.

Parameters

[base StackAddress (hex)] (optional)

If you want to specify any address other than the current rsp or esp register, you should set the base address by using this parameter.

[l Length (hex)] (optional)

The length (byte) in hex format.

Examples

The following command shows the callstack of the current thread.

2: kHyperDbg> k
[$+020]   fffff80168da7c86    (from <fffff80168da7c86>)
[$+050]   fffff80168dafab6    (from <fffff80168dafab6>)
[$+080]   fffff801678940aa    (from <fffff801678940aa>)
[$+150]   fffff8016362af7c    (from nt!ObpReferenceObjectByHandleWithTag+0x22c <fffff8016362af7c>)
[$+190]   fffff80163252f50    (from nt!IofCallDriver+0x50 <fffff80163252f50>)
[$+1d0]   fffff801635fd513    (from nt!IopSynchronousServiceTail+0x1a3 <fffff801635fd513>)

The following command shows the callstack of the current thread along with stack parameters.

2: kHyperDbg> kq
[$+000]      0000000000000000
[$+008]      0000000000000000
[$+010]      fffff80168daeb50 (addr <fffff80168daeb50>)
[$+018]      0000000000040246
[$+020]   fffff80168da7c86    (from <fffff80168da7c86>)
[$+028]      0000000000000000
[$+030]      ffff948cb9a02340 (addr <ffff948cb9a02340>)
[$+038]      0000000100000000
...
[$+150]   fffff8016362af7c    (from nt!ObpReferenceObjectByHandleWithTag+0x22c <fffff8016362af7c>)
[$+158]      0000000000000001
...
[$+188]      ffff948cc291cae0 (addr <ffff948cc291cae0>)
[$+190]   fffff80163252f50    (from nt!IofCallDriver+0x50 <fffff80163252f50>)
[$+198]      ffff948cbc327760 (addr <ffff948cbc327760>)
[$+1a0]      ffff948cc0314a80 (addr <ffff948cc0314a80>)
[$+1a8]      ffff930520206f49
[$+1b0]      0000000000000001
[$+1b8]      ffff948cbc327760 (addr <ffff948cbc327760>)
[$+1c0]      ffff948cc0314b98 (addr <ffff948cc0314b98>)
[$+1c8]      ffff948cc291cae0 (addr <ffff948cc291cae0>)
[$+1d0]   fffff801635fd513    (from nt!IopSynchronousServiceTail+0x1a3 <fffff801635fd513>)
..
[$+1f8]      0000000000000001

The following command shows the calls along with parameters (the base address is @rbx-10).

2: kHyperDbg> kq base @rbx-10
[$+000]      8d0ac9a500000000
[$+008]      0000000000000edc
[$+010]      0000200000000000
[$+018]      0000000000000000
[$+020]      0000000000000000
[$+028]      0000006700000000
[$+030]      5754444e02060000
[$+038]      fa3ced7b5a3515ff
[$+040]      0000000000000000
[$+048]      00000206c1f30004
[$+050]      ffff948cc06a9790 (addr <ffff948cc06a9790>)

The following command shows the callstack of the current thread in a 32-bit environment.

0: kHyperDbg> k
[$+004]   00a42274    (from <00a42274>)
[$+008]   7559fa27    (from <7559fa27>)
[$+018]   770775f2    (from <770775f2>)
[$+074]   770775bf    (from <770775bf>)

The following command shows the callstack of the current thread along with parameters in a 32-bit environment.

0: kHyperDbg> kd
[$+000]      010ffa18 (addr <010ffa18>)
[$+004]   00a42274    (from <00a42274>)
[$+008]   7559fa27    (from <7559fa27>)
[$+00c]      00f00000 (addr <00f00000>)
[$+010]      7559fa10 (addr <7559fa10>)
[$+014]      010ffa74 (addr <010ffa74>)
[$+018]   770775f2    (from <770775f2>)
[$+01c]      00f00000 (addr <00f00000>)
[$+020]      ba49f40e
[$+024]      00000000
[$+028]      00000000
[$+02c]      00f00000 (addr <00f00000>)
[$+030]      00000000
[$+034]      00000000

IOCTL

This commands works over serial by sending the serial packets to the remote computer.

First of all, you should fill the following structure, set the Is32Bitto your target execution context, set the Size and count of frames FrameCount, the base address (setting NULL as based address indicates that debuggee needs the current rsp register as the base address).

After allocating the below structure, you should also allocate as many frames structure (DEBUGGER_SINGLE_CALLSTACK_FRAME) that you want to read from the stack (FrameCount * sizeof(DEBUGGER_SINGLE_CALLSTACK_FRAME)).

typedef struct _DEBUGGER_CALLSTACK_REQUEST
{
    BOOLEAN                           Is32Bit;
    UINT32                            KernelStatus;
    DEBUGGER_CALLSTACK_DISPLAY_METHOD DisplayMethod;
    UINT32                            Size;
    UINT32                            FrameCount;
    UINT64                            BaseAddress;
    UINT64                            BufferSize;

    //
    // Here is the size of stack frames
    //

} DEBUGGER_CALLSTACK_REQUEST, *PDEBUGGER_CALLSTACK_REQUEST;

The following structure shows the different fields of the frame structure.

typedef struct _DEBUGGER_SINGLE_CALLSTACK_FRAME
{
    BOOLEAN IsStackAddressValid;
    BOOLEAN IsValidAddress;
    BOOLEAN IsExecutable;
    UINT64  Value;
    BYTE    InstructionBytesOnRip[MAXIMUM_CALL_INSTR_SIZE];

} DEBUGGER_SINGLE_CALLSTACK_FRAME, *PDEBUGGER_SINGLE_CALLSTACK_FRAME;

The DisplayMethod can be selected from the below enum:

typedef enum _DEBUGGER_CALLSTACK_DISPLAY_METHOD
{
    DEBUGGER_CALLSTACK_DISPLAY_METHOD_WITHOUT_PARAMS,
    DEBUGGER_CALLSTACK_DISPLAY_METHOD_WITH_PARAMS,

} DEBUGGER_CALLSTACK_DISPLAY_METHOD;

The next step is sending the above structure to the debuggee when debuggee is paused and waiting for new command on vmx-root mode.

You should send the above structure with DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CALLSTACK as RequestedAction and DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT as PacketType.

In return, the debuggee sends the above structure with the following type.

DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_CALLSTACK

In the returned structure, the KernelStatus and frames structures are filled by the kernel.

If the KernelStatus is DEBUGEER_OPERATION_WAS_SUCCESSFULL, then the operation was successful. Otherwise, the returned result is an error.

The following function is responsible for sending interpreting frames in the debugger.

VOID
CallstackShowFrames(PDEBUGGER_SINGLE_CALLSTACK_FRAME  CallstackFrames,
                    UINT32                            FrameCount,
                    DEBUGGER_CALLSTACK_DISPLAY_METHOD DisplayMethod,
                    BOOLEAN                           Is32Bit);

Remarks

  • If you don't specify the length, the default length for HyperDbg is 0x100 Bytes for 32-bit contexts and 0x200 for 64-bit contexts.

  • HyperDbg automatically switches between 32-bit and 64-bit environments based on the debuggee's execution context.

Please note that you should specify a space between 'l' and the length in HyperDbg. For example, 'l100' is invalid, but 'l 100' is valid. (It's opposed to windbg).

This command is guaranteed to keep debuggee in a halt state (in Debugger Mode); thus, nothing will change during its execution.

Requirements

None

gu (step-out or go up)

Last updated