HyperDbg Documentation
CommunityDownloadResearchTutorialhwdbg
  • HyperDbg
  • Getting Started
    • Quick Start
    • FAQ
    • Build & Install
    • Attach to HyperDbg
      • Attach to a remote machine
      • Attach to local machine
      • Start a new process
      • Attach to a running process
  • Using HyperDbg
    • Prerequisites
      • Operation Modes
      • How to create a condition?
      • How to create an action?
      • Signatures
    • User-mode Debugging
      • Principles
      • Examples
        • basics
        • events
          • Getting Results of a System-call
    • Kernel-mode Debugging
      • Principles
      • Examples
        • beginning
          • Connecting To HyperDbg
          • Configuring Symbol Server/Path
        • basics
          • Setting Breakpoints & Stepping Instructions
          • Displaying & Editing & Searching Memory
          • Showing & Modifying Registers and Flags
          • Switching to a Specific Process or Thread
          • Mapping Data & Create Structures, and Enums From Symbols
        • events
          • Managing Events
          • Hooking Any Function
          • Intercepting All SYSCALLs
          • Monitoring Accesses To Structures
          • Triggering Special Instructions
          • Identifying System Behavior
        • Scripting Language Examples
    • Software Development Kit (SDK)
      • Events
        • Conditions
        • Actions
      • IOCTL
        • Event Registration
  • Commands
    • Debugging Commands
      • ? (evaluate and execute expressions and scripts in debuggee)
      • ~ (display and change the current operating core)
      • a (assemble virtual address)
      • load (load the kernel modules)
      • unload (unload the kernel modules)
      • status (show the debuggee status)
      • events (show and modify active/disabled events)
      • p (step-over)
      • t (step-in)
      • i (instrumentation step-in)
      • gu (step-out or go up)
      • r (read or modify registers)
      • bp (set breakpoint)
      • bl (list breakpoints)
      • be (enable breakpoints)
      • bd (disable breakpoints)
      • bc (clear and remove breakpoints)
      • g (continue debuggee or processing kernel packets)
      • x (examine symbols and find functions and variables address)
      • db, dc, dd, dq (read virtual memory)
      • eb, ed, eq (edit virtual memory)
      • sb, sd, sq (search virtual memory)
      • u, u64, u2, u32 (disassemble virtual address)
      • k, kd, kq (display stack backtrace)
      • dt (display and map virtual memory to structures)
      • struct (make structures, enums, data types from symbols)
      • sleep (wait for specific time in the .script command)
      • pause (break to the debugger and pause processing kernel packets)
      • print (evaluate and print expression in debuggee)
      • lm (view loaded modules)
      • cpu (check cpu supported technologies)
      • rdmsr (read model-specific register)
      • wrmsr (write model-specific register)
      • flush (remove pending kernel buffers and messages)
      • prealloc (reserve pre-allocated pools)
      • preactivate (pre-activate special functionalities)
      • output (create output source for event forwarding)
      • test (test functionalities)
      • settings (configures different options and preferences)
      • exit (exit from the debugger)
    • Meta Commands
      • .help (show the help of commands)
      • .debug (prepare and connect to debugger)
      • .connect (connect to a session)
      • .disconnect (disconnect from a session)
      • .listen (listen on a port and wait for the debugger to connect)
      • .status (show the debugger status)
      • .start (start a new process)
      • .restart (restart the process)
      • .attach (attach to a process)
      • .detach (detach from the process)
      • .switch (show the list and switch between active debugging processes)
      • .kill (terminate the process)
      • .process, .process2 (show the current process and switch to another process)
      • .thread, .thread2 (show the current thread and switch to another thread)
      • .pagein (bring the page into the RAM)
      • .dump (save the virtual memory into a file)
      • .formats (show number formats)
      • .script (run batch script commands)
      • .sympath (set the symbol server)
      • .sym (load pdb symbols)
      • .pe (parse PE file)
      • .logopen (open log file)
      • .logclose (close log file)
      • .cls (clear the screen)
    • Extension Commands
      • !a (assemble physical address)
      • !pte (display page-level address and entries)
      • !db, !dc, !dd, !dq (read physical memory)
      • !eb, !ed, !eq (edit physical memory)
      • !sb, !sd, !sq (search physical memory)
      • !u, !u64, !u2, !u32 (disassemble physical address)
      • !dt (display and map physical memory to structures)
      • !track (track and map function calls and returns to the symbols)
      • !epthook (hidden hook with EPT - stealth breakpoints)
      • !epthook2 (hidden hook with EPT - detours)
      • !monitor (monitor read/write/execute to a range of memory)
      • !syscall, !syscall2 (hook system-calls)
      • !sysret, !sysret2 (hook SYSRET instruction execution)
      • !mode (detect kernel-to-user and user-to-kernel transitions)
      • !cpuid (hook CPUID instruction execution)
      • !msrread (hook RDMSR instruction execution)
      • !msrwrite (hook WRMSR instruction execution)
      • !tsc (hook RDTSC/RDTSCP instruction execution)
      • !pmc (hook RDPMC instruction execution)
      • !vmcall (hook hypercalls)
      • !exception (hook first 32 entries of IDT)
      • !interrupt (hook external device interrupts)
      • !dr (hook access to debug registers)
      • !ioin (hook IN instruction execution)
      • !ioout (hook OUT instruction execution)
      • !hide (enable transparent-mode)
      • !unhide (disable transparent-mode)
      • !measure (measuring and providing details for transparent-mode)
      • !va2pa (convert a virtual address to physical address)
      • !pa2va (convert physical address to virtual address)
      • !dump (save the physical memory into a file)
      • !pcitree (show PCI/PCIe device tree)
      • !pcicam (dump the PCI/PCIe configuration space)
      • !idt (show Interrupt Descriptor Table entries)
      • !apic (dump local APIC entries in XAPIC and X2APIC modes)
      • !ioapic (dump I/O APIC)
    • Scripting Language
      • Assumptions & Evaluations
      • Variables & Assignments
      • Casting & Type-awareness
      • Conditionals & Loops
      • Constants & Functions
      • Debugger Script (DS)
      • Examples
        • view system state (registers, memory, variables)
        • change system state (registers, memory, variables)
        • trace function calls
        • pause the debugger conditionally
        • conditional breakpoints and events
        • patch the normal sequence of execution
        • access to a shared variable from different cores
        • count occurrences of events
      • Functions
        • debugger
          • pause
        • events
          • event_enable
          • event_disable
          • event_clear
          • event_sc
          • event_inject
          • event_inject_error_code
          • flush
        • exports
          • print
          • printf
        • interlocked
          • interlocked_compare_exchange
          • interlocked_decrement
          • interlocked_exchange
          • interlocked_exchange_add
          • interlocked_increment
        • memory
          • check_address
          • eb, ed, eq
          • eb_pa, ed_pa, eq_pa
          • memcpy
          • memcpy_pa
          • memcmp
          • virtual_to_physical
          • physical_to_virtual
        • diassembler
          • disassemble_len
          • disassemble_len32
        • spinlocks
          • spinlock_lock
          • spinlock_lock_custom_wait
          • spinlock_unlock
        • strings
          • strlen
          • wcslen
          • strcmp
          • strncmp
          • wcscmp
          • wcsncmp
    • Commands Map
  • Tips & Tricks
    • Considerations
      • Basic concepts in Intel VT-x
      • VMX root-mode vs VMX non-root mode
      • The "unsafe" behavior
      • Script engine in VMX non-root mode
      • Difference between process and thread switching commands
      • Accessing Invalid Address
      • Transparent Mode
    • Nested-Virtualization Environments
      • Supported Virtual Machines
      • Run HyperDbg on VMware
      • Run HyperDbg on Hyper-V
      • Supporting VMware/Hyper-V
      • VMware backdoor I/O ports
    • Misc
      • Event forwarding
      • Event short-circuiting
      • Event calling stage
      • Instant events
      • Message overflow
      • Customize build
        • Increase Communication Buffer Size
        • Number of EPT Hooks in One Page
        • Change Script Engine Limitations
      • Enable and disable events in Debugger Mode
      • Switch to New Process Layout
  • Contribution
    • Style Guide
      • Coding style
      • Command style
      • Doxygen style
    • Logo & Artworks
  • Design
    • Features
      • VMM (Module)
        • Control over NMIs
        • VMX root-mode compatible message tracing
        • Design of !epthook
        • Design of !epthook2
        • Design of !monitor
        • Design of !syscall & !sysret
        • Design of !exception & !interrupt
    • Debugger Internals
      • Events
      • Conditions
      • Actions
      • Kernel Debugger
        • Design Perspective
        • Connection
  • Links
    • Twitter
    • Telegram
    • Discord
    • Matrix
    • Mastodon
    • YouTube
    • hwdbg (Chip Debugger)
    • Doxygen
    • Contribution
Powered by GitBook
On this page
  • Different Calling Stages
  • Calling Stages and Event Short-circuiting
  • Using Calling Stages in Events
  • Finding Calling Stages in Scripts
  • Finding Calling Stages in Event Breaks
  • Examples
  • Miscellaneous
Edit on GitHub
  1. Tips & Tricks
  2. Misc

Event calling stage

The event calling stage in HyperDbg

PreviousEvent short-circuitingNextInstant events

Last updated 1 year ago

Starting from HyperDbg v0.5, the event calling stages mechanism is added to the debugger.

This mechanism enables us to specify the exact stage at which HyperDbg will trigger the . For instance, we can choose whether the event should be called before or after the emulation process takes place.

Most of the events in HyperDbg are based on emulation. For example, before or after modifying the memory (as a result of the '' command). Another example is for different special instructions like RDMSR, WRMSR, CPUID, etc. These stages will notify HyperDbg whether the event should be triggered before the execution of these instructions or after the execution of these instructions.

Different Calling Stages

HyperDbg allows you to specify one of the following calling stages for an event:

  • All

  • Pre

  • Post

When you specify 'all' for an event, it will be triggered both before and after the emulation process. On the other hand, if you choose 'pre', the event will be triggered before the emulation, and if you select 'post', it will be triggered after the emulation. If no event stage is explicitly specified, the default behavior is to trigger events in the 'pre' calling stage.

The calling stages serve a valuable purpose in analyzing specific events such as '', '', '', etc. For instance, by selecting the 'post' calling stage, you can observe and potentially modify the results of the emulation after it has occurred.

One of the primary applications of these calling stages is seen in the '' command. For instance, if you wish to be notified whenever memory is modified at a particular target address, you can utilize the 'pre' calling stage to examine the memory's content before a MOV instruction, while the 'post' calling stage reveals the content of the memory after executing the MOV, effectively showing the modified memory contents.

Calling Stages and Event Short-circuiting

An important note is, once you , the 'post' calling stage will not be triggered as the emulation is completely ignored and in reality, there is no 'post' stage.

Another note is that short-circuiting events are not permitted in the 'post' stage. This is because, by the time the 'post' stage is reached, the emulation has already been performed, and there is nothing left to be ignored, making it illogical to apply short-circuiting at this point.

Using Calling Stages in Events

Event calling stages are determined by using the 'stage pre', 'stage post', or 'stage all' in the definition of events.

For example, you can specify the 'post' stage for the following event commands:

0: kHyperDbg> !monitor rw nt!kd_default_mask nt!kd_default_mask+8 stage post

Or you can specify the 'all' stage for them.

0: kHyperDbg> !msrwrite c0000082 stage all

Finding Calling Stages in Scripts

Please be aware that the event invocation stage cannot be 'all' since the event occurs either in the 'pre' or 'post' calling stage, and it cannot occupy both simultaneously.

The following example reads the '$event_stage' pseudo-register in case of an event and as the result is 1, it means the event is called in the 'post' calling stage.

HyperDbg> ? print($event_stage);
1

In cases where the debugger is halted without being triggered by an event (such as through a breakpoint, CTRL+C, stepping, etc.), the pseudo-register '$event_stage' will display a value of 0. However, it's important not to misinterpret this as a 'pre' stage, as this pause isn't prompted by an actual event.

Finding Calling Stages in Event Breaks

Once an event is triggered, the debugger will show the event id, as well as the event calling stage, for example, the following event is triggered in the 'post' calling stage. Note the (post) in the debugger message.

1: kHyperDbg> g
debuggee is running...
event 0x3 triggered (post)

Or the following event is called the 'pre' calling stage.

2: kHyperDbg> g
debuggee is running...
event 0x5 triggered (pre)

Examples

Below are various examples demonstrating the usage of the event calling stages mechanism. You can apply this mechanism to events that support calling stages. Please refer to the documentation for each specific event to determine whether it supports calling stages or not.

Example 1

The following example shows how we can view the memory before and after modification by using the 'all' calling stage and how the '$event_stage' pseudo-register is used to check for different stages.

!monitor w 7 ff71f118210 7 ff71f118210 + 4 stage all script {
  if ($event_stage == 1) {
    curr_memory = dq($context);
    printf("current memory: %llx\n", curr_memory);
  } else {
    prev_memory = dq($context);
    printf("thread id: %x , from (RIP): %llx modified address: %llx, previous memory: %llx",
      $tid, @rip, $context, prev_memory);
  }
}

Example 2

The following example shows how we can view (or possibly modify the registers) after running a CPUID instruction.

!cpuid stage post script {
  if ($context == 1) {
    @ecx = @ecx & ~(1 << 5);
  }
}

Example 3

!msrwrite 0xc000084 stage pre script {
    @eax = @eax | (1 << 9);
}

Example 4

!exception e stage post script {
  printf("page-fault happens at: %llx", @cr2);
}

Miscellaneous

If a singular event causes a special EPT hook, special MSR read/write, or any other event to short-circuit, the emulation process will not take place. Consequently, the 'post' mode will be disregarded for all similar events sharing the same conditions. To illustrate, consider a scenario where multiple events are associated with a single CPUID instruction. One event resides in the 'pre' start stage, while the remaining events (with identical conditions) are situated in the 'post' stage. If the 'pre' stage event leads to a short-circuit, all subsequent 'post' events will fail to trigger.

In order to determine the calling stage, a new called '$event_stage' is added. This pseudo-register can be either 0 which shows that the event is called in the 'pre' calling stage or 1 which shows it's called in the 'post' calling stage.

In this example, we checked whether the '$context' or the EAX register is equal to 1 and if it's equal to it, we mask the ECX register's 5th bit (which is the ). As a result, if you use a program like , it no longer shows support for VT-x (just for the CPUID instruction, not disabling it completely).

As described , SYSCALL saves RFLAGS into R11 and then masks RFLAGS using the IA32_FMASK MSR (MSR address 0xC0000084); specifically, the processor clears in RFLAGS every bit corresponding to a bit that is set in the IA32_FMASK MSR.

Using the following example we would be sure that if somewhere in the drivers, rootkits, or Windows, some codes try to modify this MSR register, the 9th flag () will remain untouched.

The following example shows how we can use the 'post' calling stage to view the CR2 register as a result of a . Note that, the value of the @cr2 is not valid in the 'pre' calling stage.

Commands like '' or '' don't support calling stages. Due to the nature of function hooking implementing calling stages in this context wouldn't be meaningful.

event
!monitor
!cpuid
!msrread
!msrwrite
!monitor
short-circuit or ignore an event
pseudo-register
indicator of support for Intel VT-x
CPU-Z
here
Interrupt Flag
page-fault
!epthook
!epthook2