# Software:HyperDbg

Developer(s) HyperDbg version 0.1 Community C and C++ Windows Windows Debuggers GNU GPLv3 hyperdbg.org

The HyperDbg Debugger (HyperDbg) is a free and open source portable debugger that runs on Windows systems. HyperDbg is a hypervisor-based user-mode, and kernel-mode debugger and it is designed with a focus on using modern hardware technologies to provide new features to the reverse engineering. It operates on top of Windows by virtualizing an already running system using Intel VT-x[1] and Intel PT[2]. This debugger aims not to use any APIs and software debugging mechanisms, but instead, it uses Second Layer Page Table (a.k.a. Extended Page Table or EPT) extensively to monitor both kernel and user executions.[3]

HyperDbg comes with features such as hidden hooks, which is as fast as old inline hooks, but also stealth. It mimics hardware debug registers for (read & write) to a specific location, entirely invisible for both Windows kernel and the programs, and in contrast to other debuggers, without any limitation in size.

By employment of TLB-splitting[4], and having features such as measuring code coverage and monitoring Memory mov(s) instructions, HyperDbg comes with unique features among other debuggers.

HyperDbg is designed to be as stealth as possible. It doesn’t use any debugging APIs to debug Windows or any application. Hence, it will not be normally detected by classic anti-debugging methods. Also, it resists the exploitation of time delta methods (e.g., RDTSC/RDTSCP) to detect the presence of hypervisors, making it much harder for applications, packers, protectors, malware, anti-cheat engines, etc. to discover HyperDbg.

## Design

HyperDbg is designed at hypervisor level in ring -1. Operation in sub-os ring gives the HyperDbg advantages to employ hardware-assisted features to implement wide range of new debugging methods. The hypervisor level debugging make the debugging process as transparent as possible. This also makes the HyperDbg OS-independent by design.

The following diagram illustrates how HyperDbg is designed with its unique features at high level.

HyperDbg High-level working diagram

## Features

HyperDbg is currently equipped with a primary module of VMM which is responsible for the main hypervisor in the HyperDbg. Hence, the debugging process is only possible by loading the VMM module.

HyperDbg offers extensive facilities for tracing and altering the execution of computer programs in the hypervisor level. The features are provided by employment of hardware-based Intel features. Users can monitor and modify the values of programs’ internal variables, and even change the program’s normal behavior in both user-mode and kernel-mode levels.

For the initial Release v 0.1.0.0 of the HyperDbg, the following proprietary features are provided for systems running over Intel CPUs and Microsoft Windows operating systems:

• Classic EPT Hook (Hidden Breakpoint)
• Inline EPT Hook (Inline Hook)
• Monitor Memory For R/W (Emulating Hardware Debug Registers Without Limitation)
• SYSCALL Hook (Disable EFER & Handle #UD)
• SYSRET Hook (Disable EFER & Handle #UD)
• CPUID Hook & Monitor
• RDMSR Hook & Monitor
• WRMSR Hook & Monitor
• RDTSC/RDTSCP Hook & Monitor
• RDPMC Hook & Monitor
• VMCALL Hook & Monitor
• Debug Registers Hook & Monitor
• I/O Port (In Instruction) Hook & Monitor
• I/O Port (Out Instruction) Hook & Monitor
• MMIO Monitor
• Exception (IDT < 32) Monitor
• External-Interrupt (IDT > 32) Monitor
• Running Automated Scripts
• Transparent-mode (Anti-debugging and Anti-hypervisor Resistance)
• Running Custom Assembly In Both VMX-root, VMX non-root (Kernel & User) (Checking For Custom Conditions)
• VMX-root Compatible Message Tracing
• Powerful Kernel Side Scripting Engine
• Event Forwarding (#DFIR)
• Transparent Breakpoint Handler
• Various Custom Scripts

The design and and description for each of these features is available on HyperDbg documentation website.[5]

### Control over NMIs

HyperDbg exploits Intel’s Not Maskable Interrupts (NMI) to target a specific CPU core by hunting other cores when the debugging procedure is carried out. The processor blocks the delivery of subsequent NMIs until the next IRET instruction execution, while NMI interrupt handler is performing, . This blocking of NMIs, prevents the nested execution of the NMI handler.

Execution of the IRET instruction unblocks NMIs even if the instruction causes a fault. For instance, if the IRET instruction executes with with certain condition, generating a general-protection exception, NMIs are unmasked before the exception handler is invoked. Also, the IA-32 architecture includes features that permit certain events to be blocked for a period of time which is used with this regards.

The behavior of IRET with regards to NMI blocking is determined by the settings of the “NMI exiting” and “virtual NMIs” VM-execution controls:

— If the “NMI exiting” VM-execution control is 0, IRET operates normally and unblocks NMIs. (If the “NMI exiting” VM-execution control is 0, the “virtual NMIs” control must be 0)

— If the “NMI exiting” VM-execution control is 1, IRET does not affect the blocking of NMIs. If, in addition, the “virtual NMIs” VM-execution control is 1, the logical processor tracks virtual-NMI blocking. In this case, IRET removes any virtual-NMI blocking. The unblocking of NMIs or virtual NMIs specified above occurs even if IRET causes a fault.

### Message Tracing

One of the most complicated parts of designing a hypervisor is sending a message from Vmx root-mode to Vmx non-root mode. This is due to heavy existing restrictions. for instance, one cannot access a non-paged buffer, and most of the NT functions are not (ANY IRQL) compatible as they might access the buffers that reside in paged pool.

HyperDbg present a VMX-root compatible message tracing[6]. The message tracing is design based on the article provided at Hypervisor From Scratch 8[7]. Also, HyperDbg Message tracing could be customized based on the instruction provided on its documentation.[8]

The challenge in designing a hypervisor message tracing is that, a Vmx-root mode is not a HIGH IRQL interrupt. However, as it disables all of the interrupts, most of synchronization functions which are designed to be perform on IRQL less than DISPATCH_LEVEL would be ineffective. To address such issue, HyperDbg design for the message tracing is equipped with several mechanisms such as designing a spinlock. Two message pools are allocated and by setting suitable parameters in Deferred Procedure Call (DPC), and Test and Set the Vmx-root message tracing is implemented.

### !exception and !interrupt

HyperDbg also provide !exception[9] and !interrupt[10] commands that are used to hook the exceptions, faults, aborts, and external-interrupts.

!exception command uses the Exception Bitmap field of VMCS, which causes vm-exit every time that an exception is generated (on a specific IDT entry which is set). It’s clear that only those entries that are indicated will cause a vm-exit in this manner. It works on the first 32 entries of IDT or entries between 0x0 to 0x1f. All vm-exits are handled in the same way, but page-faults (PF) are different. In those cases, HyperDbg also uses the cr2 register.

!interrupt, on the other hand, is different. There is a bit in pin-based vmx controls[11], which cause vm-exit on all external-interrupts (starting from 0x20 to 0xff). Thus, if entries above 0x1f are considered, all of the external-interrupts should cause a vm-exit which is then managed by HyperDbg, making !Interrupt is much slower than !execption.

### !syscall & !sysret

These commands are provided for internal reverse engineering for many applications[12]. These command sunsets the Syscall Enable Bit (SE Bit) in Extended Feature Enable Registe (EFER) Model-Specific Regiser(MSR) . by unsetting this bit, execution of SYSCALL or SYSRET causes a #UD or undefined opcode exception.

Then HyperDbg intercepts UDs by reading the Exception Bitmap in VMCS. In the vm-exit, it could check whether the generated UD is due to SYSCALL or SYSRET instructions. And if true, user-to-kernel or kernel-to-user actions of these instructions are emulated accordingly. Moreover, the check for these instructions is performed by checking the memory content of the GUEST_RIP field of VMCS.

Note that in order to avoid PatchGuard detection for this command, it’s essential to attach a Windbg kernel debugger as it suppresses the PatchGuard.

### !monitor

The !monitor is also based on EPT. The debug registers allow the user programmer to selectively enable various debug conditions (read, write, execute) associated with a set of four debug addresses without any change in program instructions. Conventionally, one can set up to 4 locations for these hardware registers, which is painfully limited in size.

By the employment of EPT, this restriction is avoided in HyperDbg. For instance, assume a structure (let say _EPROCESS), by using this command you can detect any Read or Write to this structure without limitation.

#### Hidden Hooks Scenarios for Read/Write and Execute

Two strategies for hidden hooks are available in Hypedbg. One for Read/Write and one for Execute.

With this regard, HyperDbg unsets read or write or both (based on how the user desire) in the entry corresponding to the address. This means that before the operating system or a kernel driver tries to read or write, a vm-exit occurs, and an EPT Violation will notify the debugger. In the EPT Violation handler, by logging the corresponding address that intends to read or write, the entry in the EPT table is found and then both read and write permissions are set (means that any read or write to the page is allowed). And finally a Monitor Trap Flag (MTF) is set. Consequently, VMM is resumed, and one instruction is executed. In the MTF vm-exit handler, HyperDbg unset the read and write access again, so any future access to that page will cause an EPT Violation. Note that all of the above scenarios happen in one core. Each core has a separate TLB and separate Monitor Trap Flag.

### !epthook

The most interesting feature in a classic hypervisor is the EPT hidden hooks. In HyperDbg, two types of hidden EPT hooks are designed. First, a classic EPT hook that intends to hide breakpoints using EPT (!epthook)[13], and the second EPT hook is hidden detours (!epthook2)[14] where the classic EPT Hook uses the Execute Only feature in Intel Extended Page Table (EPT).

Generally, !epthook2 is much faster than the !epthook since it performs as hidden inline hooks. However, it has some limitations[15]. On the other hand, the classic EPT hook can be used in both user-mode and kernel-mode without limitation. This hook copies all the target page contents (containing the breakpoint address(es)) to a new location and then puts the breakpoint on the new page. Consequently, it unsets the Read and Write bits of that page but sets the Execute bit and change the physical address of the entry to the new location.

In this way, if any program attempts to Read or Write on the marked virtual address, EPT Violation occurs, and the debugger could bring the original physical address back to the page entry and set both Read and Write permission bits but unset the Execute bit. Hence, the page is not executable, HyperDbg also set VMCS’s MTF and after a read or write (which causes the EPT Violation), it changes everything back to the executable but not readable and writable page. So, every single program trying to read or write on a specific page can be ensured that it’s not modified, while HyperDbg is having breakpoints there.

For the !epthook2, If the user wishes an execution hook, debugger finds the entry in the EPT Table and unset read and write access and set the execute access. Then it creates a copy from the original page (Page A) to somewhere else (Page B) and modify the copied page (Page B) with an absolute jump to the hook function. Each time any instruction attempts to execute the marked function, the absolute jump is performed, and the hook function is called. Each time any instruction tries to read or write to that location, an EPT Violation occurs as we unset read and write access to that page.

## Remote debugging

HyperDbg Connection Diagram

To use HyperDbg as a remote debugger, HyperDbg Debugger Mode should be used[16]. This mode connects to the kernel and halt the system to step in and step out through the kernel instructions. It is obvious that user could not use this mode for local debugging, and user should provide a system with a serial cable or virtual serial device to enable it.

If the user does not intend to break and halt the system for stepping and instrumenting instructions, then HyperDbg VMI Mode is recommended. In VMI Mode, the connection is over TCP, which is substantially faster than using a serial device.

Currently, the only way to communicate to the kernel debugger is over the serial port. For future versions, other IRQL-compatible mechanisms to communicate will be added to be supported by HyperDbg. For this purpose, debugger uses kdserial from Windows SDK to connect to the debuggee. The connection over the serial port is initialized from the user-mode controller of HyperDbg.

The mechanism to pause the debuggee is also implemented in user-mode. HyperDbg does not intend to handle each pause requests from the debugger in polling mode as this is not an optimized way. Instead, HyperDbg uses the interrupt-mode of the serial device in the user-mode. This way, whenever the debugger needs to pause the debugger, it sends a request, and as it’s in interrupt-mode, then Windows notifies the user-mode application about the new request from the debugger.

## Debugger Internals

The diagram at bottom right depicts the overall internals of the HyperDbg working procedure.

HyperDbg Kernel Debugging Diagram

### Events

Events are an essential concept in HyperDbg. Almost all of the HyperDbg features are developed as an event. For instance, hidden hooks are realized as events in HyperDbg. When such event is triggered, corresponding actions will be performed. Also, whenever system executes an SYSRET instruction or a SYSCALL instruction, then the event is triggered. Generally, each event has a condition and might have zero or multiple corresponding actions. Note that the basic debugger routines are implemented in Debugger.c in HyperDbg driver.

### Conditions

In HyperDbg, each event has a has condition buffer. If the buffer is null, then the event is unconditional, which means each trigger of the event will result in the execution of all of the event’s action(s). If the buffer is not null, then the event is conditional. In this case, actions will be executed only and only if the condition returns TRUE. Otherwise, all of the actions of that event will be ignored. So, Conditions apply to events and not actions. If there is a need for multiple conditions for each action, user can create multiple events or use the custom code feature to check for the conditions.

Creating a Condition

HyperDbg provide the option for the user to create a buffer that holds a few bytes allowing to do customizations for conditions. For example, user might want to check if the current thread is a special thread or check the privilege level of the requesting user or a special check in the assembly code.

### Actions

Actions define the operation that the debugger should do in the case of triggering an event. In other words, events are containers of actions. Each event can have zero or many actions. In an unconditional event, all actions are performed one by one with the insertion order, and in conditional events, actions are performed only and only if the condition is met.

Types of Actions

Unlike a classic debugger, multiple kinds of operations in events are allowed in HyperDbg. Actions can be defined in 3 different types, as demonstrated in the following enum.

typedef  enum _DEBUGGER_EVENT_ACTION_TYPE_ENUM {

BREAK_TO_DEBUGGER,
RUN_SCRIPT,
RUN_CUSTOM_CODE

} DEBUGGER_EVENT_ACTION_TYPE_ENUM;
1. Break: Similar to other classic debuggers, this type of action halts the system and makes the system available for future user commands. This type of action is only available in debugging a remote machine.
2. Script: This action type is a special feature which creates a log of the registers, memory, and special details (pseudo-registers) without halting the system, transfering the logs from kernel mode and vmx-root mode, safely to the debugger user mode. User can also call predefined functions and change the state of the system directly. It is possible to use this type of action in both remote debugging and debugging a local machine.
3. Custom Code: Running a custom code is another special feature of HyperDbg that will allow the user to execute custom assembly codes in the case of triggering an event. This means that assembly codes will be executed, and the results will be returned safely to the debugger user mode. One can use this type of action in both debugging a remote machine and debugging a local machine.

### Kernel Debugging

Based on the design of HyperDbg, all the cores are halted and spinning on ring -1 (hypervisor). Only one core is listening for new commands from the debugger on a polling mode serial. There are two scenarios in which the kernel debugger is paused:

1. The user requests a pause (for example, by pressing CTRL+C), then it sends a packet to pause the debugger, and the debuggee starts processing the packet from user-mode and invokes an IOCTL, and that IOCTL executes a VMCALL and goes from the kernel-mode to the vmx-root mode.
2. A breakpoint is triggered either by a break request from an event or the script engine. In this scenario, there are two possible methods. First, if the user is in kernel-mode, which a VMCALL happens, and if the user is already in vmx-root mode, HyperDbg notifies all the other cores.

In order to notify all the other cores, HyperDbg sends NMIs to all the cores using XAPIC or X2APIC. All the cores are configured to cause vm-exit in the case of NMIs (PIN-Based VM-Exec controls are set to 1).

# Commands

## Debugging Commands

The tabulation below illustrates the debugging commands in HyperDbg:

Command Syntax Parameter Example Descriptions
? ? [expression] [expression] HyperDbg>? print(@rax); Evaluates and executes an expression in the remote debuggee.
~ ~ [core number (hex value)] [core number (hex value)] (optional) HyperDbg> ~ Shows or changes the current operating core. This command can only be used in Debugger Mode.
load load [module name] [module name] HyperDbg> load vmm Loads the HyperDbg's drivers and kernel modules into the target system.
unload unload [module name] [module name] HyperDbg> unload vmm Unloads the HyperDbg drivers and kernel modules from the target system.
status status None HyperDbg> status Shows the connection status of the HyperDbg's Connection.
events [e\d\c] [event number (hex value) \ all] e: enables the target event d: disables the target event c : clears and removes the target event HyperDbg> event d 1 Shows a list of active/disabled events and commands or disables or clears the event(s).
p p[r] [count (hex value)] [count] (optional) HyperDbg> p Executes a single instruction (step-over) and optionally displays the resulting values of all registers and flags.
t t[r] [count (hex value)] [count] (optional) HyperDbg> tr Executes a single instruction (step-in) and optionally displays the resulting values of all registers and flags.
i i[r] [count (hex value)] [count] (optional) HyperDbg> i Executes a single instruction (step-in) and optionally displays the resulting values of all registers and flags.
r r [register] [= expr] [register] The register that needs to be read or modified [= expr]
The value or the expression that needs to be evaluated and modify the target register
HyperDbg> r Reads or modifies registers when the debuggee is paused.
bp core (hex value)] core (hex value)] (optional) HyperDbg> bp fffff801639b1030 Puts a breakpoint (0xcc) on the target function in user-mode and kernel-mode.
bl bl None HyperDbg> bl Lists all the enabled/disabled breakpoints.
be be [breakpoint id (hex value)] [breakpoint id (hex value)] HyperDbg> be Enables a previously disabled breakpoint (0xcc).
bd bd [breakpoint id (hex value)] [breakpoint id (hex value)] HyperDbg> bd Disables a previously enabled breakpoint (0xcc).
bc bc [breakpoint id (hex value)] [breakpoint id (hex value)] HyperDbg> bc Clears and removes a breakpoint (0xcc).
g g None - When the HyperDbg debugger is paused by pause command or
CTRL+C or CTRL+BREAK, you can continue debugging using this command.
You can use this command in both local and remote debugging.
db, dc, dd, dq db [address] l [length (hex)] pid [process id (hex)] [Address] The virtual address of where we want to read its memory
l [Length] (optional) The length (byte) in hex format
pid [process id] The process ID in hex format that we want to see the memory from its context (cr3)
HyperDbg> db fffff8003ad6f010 l 50 pid 4 Shows the virtual address memory content in hex form.
eb, ed, eq eb [address] [new value (hex)] pid [process id (hex)] [Address] The virtual address of where we want to read its memory
l [Length] (optional) The length (byte) in hex format
pid [process id] The process ID in hex format that we want to see the memory from its context (cr3)
HyperDbg> eb fffff8003ad6f010

90 90 90

Edits the virtual address memory contents.
sb, sd, sq sb [search from address (hex)] l [length (hex)] [byte pattern (hex)] pid [process id (hex)] [Address] The virtual address of where we want to read its memory
l [Length] (optional) The length (byte) in hex format [byte pattern (hex)] Search for these bytes (pattern)
pid [process id] The process ID in hex format that we want to see the memory from its context (cr3)
HyperDbg> sb fffff8077356f010 l ffff 41 56 41 57 48 Searches the virtual memory for a special byte(s).
u, u2 u2 [address] l [length (hex)] pid [process id (hex)] [Address] The virtual address of where we want to read its memory
l [Length] (optional) The length (byte) in hex format
pid [process id] The process ID in hex format that we want to see the memory from its context (cr3)
HyperDbg> u fffff8003ad6f010 l 50 pid 4 Shows the assembly regarding memory content at the virtual address hex form.
sleep sleep [time to wait (milliseconds - hex value)] [time to wait (milliseconds - hex value)] The time that debugger should wait, in milliseconds. - Waits for the specified time (in milliseconds).
pause pause None - On the remote system, this command halts the system and gives control of the remote system to the
debugger. In local debugging, this command stops processing kernel and vmx packets and ignores them.
print print [expression] [expression] The expression is based on HyperDbg's scripting language. HyperDbg> print $proc+@rdx Shows the result of an expression that will be executed in the remote debuggee. lm lm [module name] [module name] (optional) The name or a part of the name that will be searched through all the modules and only those which match will be showed. HyperDbg> lm Shows the loaded modules' base address, size, name, full path. cpu cpu None HyperDbg> cpu Shows the currently supported technologies on the processor based on the details provided by cpuid instruction. rdmsr rdmsr [msr (hex) - ecx] core [core number(hex)] msr [(hex) - ecx] The index of MSR (ECX Register for 'rdmsr' instruction) core [core number(hex)] (optional) The core that we want to read the 'rdmsr' from HyperDbg> rdmsr c0000082 Reads the model-specific register using 'rdmsr' instruction. wrmsr wrmsr [msr (hex) - ecx] [value (hex) - edx:eax] core [core number(hex)] [msr (hex) - ecx] The index of MSR (ECX Register for 'wrmsr' instruction) [value (hex) - edx:eax] The value to write on MSR (edx:eax for 'wrmsr' instruction) core [core number(hex)] (optional) The core that we want to read the 'rdmsr' from HyperDbg> wrmsr c0000082 fffff80773553180 Write on the model-specific register using 'wrmsr' instruction. flush flush None - This command removes all the possible pending buffers and messages from all the commands (not just the command that you disabled or removed) that are stored to be received by the user-mode from the kernel-mode. output [open \ close] [type (file \ namedpipe \ tcp)] [name \ address] [open \ close] The action of this command or whether this command tries to create a new output source or open an output source or close it. [type (file \ namedpipe \ tcp)] In the case of creating an event, this parameter shows the type of the event. [name \ address] In the case of " create", it shows the address of the remote source, and in the case of " open " or " close ", it shows the name of the output source. HyperDbg> output create MyOutputName1 file c:\users\HYDBG\desktop\output.txt Create, open, or close a source output for event forwarding. test test [test-case number (hex value)] [test-case number (hex value)] (optional) The number of special test-case. If you don't specify this parameter, then it will check all of the test-cases. HyperDbg> test Tests the functionalities of HyperDbg in the running system. settings [hex value \ on \ off)] [hex value \ on \ off)] (optional) Target value to modify the option. HyperDbg> settings autounpause This command queries or changes the value of options and preferences. exit exit None HyperDbg> exit Unloads the kernel modules and closes the debugger. ## Meta Commands Meta Commands starts with . and are provided below: Command Syntax Parameter Example Descriptions .help .help [Command (string)] [Command (string)] The target command HyperDbg> .help !epthook2 Shows the help and example(s) of using a specific command. .debug [prepare | close)] [type (serial \| namedpipe)] [baud rate (decimal value)] address [prepare | close)] If you specify remote then it means that you want to connect to a debuggee or if you specify prepared then it means that you want to prepare the current machine to be debugged as debuggee and close means to close all the connections to the debuggee. [type (serial \| namedpipe)] If you want to use a serial port as the connection, you should choose serial and if you want to connect to a named pipe, then you should specify namedpipe cannot be used in debuggee, and it can be used only in the debugger. [baud rate (decimal value)] This value shows the baud rate of the device. address COM port address or named pipe address. HyperDbg> .debug remote serial 115200 com3 This command prepares debuggee for a remote connection or connects to a remote debuggee. .connect .connect [ip] [port] [ip] The IP Address of the remote system. [port] (optional) The port address that the remote debugger listens on it. HyperDbg> .connect local Connects to a remote computer session or connects to a local debugger. .disconnect .disconnect None - Disconnects from the current session. .listen .listen [port] [port] (optional) The port address that the remote debugger listens on it. HyperDbg> .listen 50001 Listens for the debugger to connect to this computer (works as a guest debuggee server). .status .status None HyperDbg (192.168.1.10:50000)> .status Shows the connection status of the HyperDbg's Connection. .process .process [type (pid)] [process id (hex value)] [type (pid)] (optional) Currently, the only available option is pid , which shows that you want to specify a process id at the second parameter. [process id (hex value)] (optional) The process id of the process to switch on its memory layout. HyperDbg> .process Shows or changes the current process. This command can only be used in Debugger Mode .formats [register | expression] [register | expression] An expression, or a register, or a hex value to be evaluated. HyperDbg> .formats @rcx Evaluates an expression or register or a value in the current thread and process context and displays it in multiple numeric formats. .script .script [FilePath] [FilePath] The file path of commands. HyperDbg> .script c:\HyperDbg Batch Files\ApiHook.dbg Runs a batch file of HyperDbg commands (each command in one line). .logopen .logopen [FilePath] [FilePath] The file path to save the log. HyperDbg> .logopen c:\users\sina\desktop\log.txt Sends a copy of the events and commands from the command window to a new log file. ## Extension Commands There are some Extension commands designed in HyperDbg for special acitivites which start with ! as follow: Command Syntax Parameter Example Descriptions !pte !pte [Virtual Address] [Virtual Address] The virtual address of where we want to read its page-level entries HyperDbg>!pte fffff80040f00c28 Displays the PML4E,PDPTE,PDE, PTE for the specified address. !db, !dc, !dd, !dq !db [address] l [length (hex)] [Address] The physical address of where we want to read its memory. l [Length] (optional) The length (byte) in hex format HyperDbg> !db 1000 l 50 Shows the physical address memory content in hex form. !eb, !ed, !eq !eb [address] [new value (hex)] pid [process id (hex)] [Address] The physical address of where we want to edit its memory. [new value (hex)] The new contents in hex format pid [process id] (optional) The process ID in the hex format that we want to see the memory from its context (cr3). HyperDbg> !eb 1000 90 90 90 Edits the physical address memory contents. !sb, !sd, !sq !sb [search from address (hex)] l [length (hex)] [byte pattern (hex)] pid [process id (hex)] [search from address (hex)] The physical address of where we want to start searching from its address. l [length (hex)] Length of the searching area. [byte pattern (hex)] Search for these bytes (pattern) pid [process id (hex)] (optional) The process ID in the hex format that we want to see the memory from its context (cr3). HyperDbg> !sb 76f010 l ffff 41 56 41 57 48 Searches the physical memory for a special byte(s). !u !u2 !u [address] l [length (hex)] [Address] The physical address of where we want to start to disassemble its memory l [Length] (optional) The length (byte) in hex format HyperDbg> !u 1000 l 50 Shows the assembly regarding memory content at the physical address hex form. !epthook [address] [event options] [address] The Virtual address of where we want to put the hook. [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)]Optional value to trigger the event in just a specific core. [event options]Regular event parameters that are used in HyperDbg events. HyperDbg> !epthook fffff8004ed6f010 script { HyperDbg Script Here } Puts a hidden breakpoint (0xcc) on the target function in user-mode and kernel-mode without modifying the content of memory in the case of reading/writing. !epthook2 [address] [event options] [address] The Virtual address of where we want to put the hook. [pid (hex value)]Optional value to trigger the event in just a specific process. [core (hex value)]Optional value to trigger the event in just a specific core. [event options]Regular event parameters that are used in HyperDbg events. HyperDbg> !epthook2 fffff8004ed6f010 Puts an in-line, detours-style kernel EPT hidden hook. (fast) !monitor [mode] [from address] [to address] [event options] [mode] Can be one of these values : rw: trigger in the case of reading and writing. r:trigger in the case of reading. w: trigger in the case of writing. [from address] The start Virtual address of where we want to monitor. [to address] The end of Virtual address of where we want to monitor. [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters that are used in HyperDbg events HyperDbg>!monitor w fffff800 4ed60000 fffff800 4ed60100 Monitors read or write or read/write to a range of addresses. If any read or write on your range address (memory), it will be triggered. !syscall [syscall-number (hex value)] [event options] [syscall-number (hex value)] Trigger in the case of a special system-call. [event options] Regular event parameters that are used in HyperDbg events. HyperDbg> !syscall 55 pid 490 Triggers when the debugging machine executes a syscall instruction or, in other words, when Windows tries to run a system call, this event will be triggered. !sysret [event options] [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !sysret pid 490 Triggers when the debugging machine executes a shyster instruction or, in other words, when Windows tries to return to user-mode from a previous syscall. !cpuid [event options] [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !cpuid pid 490 Triggers when the debugging machine executes a CPUID instruction in any level of execution (kernel-mode or user-mode). !msrread [msr (hex value)] [event options] [msr (hex value)] Trigger in the case of a special Model-Specific Register (MSR) [event options]Regular event parameters that are used in HyperDbg events. HyperDbg> !msrread 0xc0000082 Triggers when the debugging machine executes an RDMSR instruction or, in other words, when Windows or a driver tries to read a Model-Specific Register (MSR). !msrwrite [msr (hex value)] [event options] [msr (hex value)] Trigger in the case of a special Model-Specific Register (MSR) [event options]Regular event parameters that are used in HyperDbg events. HyperDbg> !msrwrite Triggers when the debugging machine executes an WRMSR instruction or, in other words, when Windows or a driver tries to write on a Model-Specific Register (MSR). !tsc [event options] [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !tsc script { HyperDbg Script Here } Triggers when the debugging machine executes RDTSC or RDTSCP instructions in any execution level (kernel-mode or user-mode). !pmc [event options] [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !pmc pid 490 core 2 Triggers when the debugging machine executes RDPMC instruction in any level of execution (kernel-mode or user-mode). !vmcall [event options] [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !vmcall Triggers when the debugging machine executes VMCALL instruction. !exception [IDT Index (hex value)] [event options] [IDT Index (hex value)] Trigger in the case of receiving an interrupt or exception. The value should be between 0x0 to 0x1f If not specified this parameter, it will be triggered for all first 32 exceptions/interrupts. [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !exception Triggers when the debugging machine encounters an exception (faults, traps, aborts) or NMI or interrupt. This command applies to only the first 32 entries of IDT (Interrupt Descriptor Table). If you need to hook entries between 32 to 255 of IDT, you should use !interrupt instead. !interrupt [IDT Index (hex value)] [event options] [IDT Index (hex value)] Trigger in the case of receiving an interrupt or exception. The value should be between 0x0 to 0x1f If not specified this parameter, it will be triggered for all first 32 exceptions/interrupts. [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !interrupt 0x25 pid 490 Triggers when the debugging machine encounters an external-interrupt. This command applies to only 32 to 255 entries of IDT (Interrupt Descriptor Table). If you need to hook entries between 0 to 31 of IDT, then you should use !exception instead. !dr [event options] [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !dr Triggers, when the debugging machine accesses one of the hardware debug registers. !ioin [port (hex value)] [event options] [port (hex value)] Trigger in the case of using a special I/O port. not specified, it will be triggered for all I/O ports. [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> !ioin Triggers when the debugging machine executes IN or IN* instructions or, in other words, when Windows or a driver tries to use I/O ports. !ioout [port (hex value)] [event options] [port (hex value)] Trigger in the case of using a special I/O port. not specified, it will be triggered for all I/O ports. [pid (hex value)] Optional value to trigger the event in just a specific process. [core (hex value)] Optional value to trigger the event in just a specific core. [event options] Regular event parameters are used in HyperDbg events. HyperDbg> ioout Triggers when the debugging machine executes OUT or OUT* instructions or, in other words, when Windows or a driver tries to use I/O ports. !hide [name] [process id (hex value) \| name (string)] [name] If you want to use the process ID, you should specify pid , and if you want to enter the process name, you should specify the name as this argument. [process id (hex value) \| name (string)] Name or process id of the process that you want to make HyperDbg transparent for it. HyperDbg> !hide name procexp.exe Enables the transparent-mode of HyperDbg for anti-debugging and anti-hypervisor methods. This option only works for the processes selected by you and won't be applied to all the processes. You should run the '!hide' after running the '!measure' command. !unhide !unhide None HyperDbg> !unhide Disables the transparent-mode of HyperDbg. !measure !measure [default] [default] If you specify 'default', then HyperDbg uses the hardcoded measurements from a not-running hypervisor machine; however, it's not recommended HyperDbg> !measure Measures and provides the details for the transparent-mode of HyperDbg for defeating anti-debugging and anti-hypervisor methods. This command should be run before you 'load' the debugger, and after that, you can use the '!hide' command. ### Scripting Language HyperDbg uses a MASM-like (Windbg) syntax to evaluate script expressions. The following keywords are valid in Script Engine. #### Keywords KeyWord Description poi Pointer-sized data from the specified address. ref Reference address of the specified variable. hi High 16 bits low Low 16 bits db Low 8 bits dd Low 16 bits dw Low 32 bits dq 64 bits not Flip each and every bit neg True/False logic flipping For read strings and wide-strings, printf function can be used.[17] #### Operators The following operators are supported on the script engine. Operation Description ( ) Parentheses * & Unary Operators * / % Arithmetic Operators + - Arithmetic Operators << >> Shift Operators & Bitwise AND Operator ^ Bitwise XOR Operator (exclusive OR) | Bitwise OR Operator #### Pseudo-registers Here are the supported pseudo-registers supported by the script engine. Pseudo-register Description$pid The process ID (PID) of the current process.
$proc The address of the current process (that is, the address of theEPROCESS block).$tid The thread ID for the current thread.
$thread The address of the current thread. In kernel-mode debugging, this address is the address of the ETHREAD block.$peb The address of the process environment block (PEB) of the current process.
$teb The address of the thread environment block (TEB) of the current thread.$ip The instruction pointer register (rip).
$buffer The pre-allocated buffer if the user requests a safe buffer.$context The context of the triggered event (It has a different meaning in each event).

#### Functions

The following functions are supported in Script Engine.

Fucntion Description
print Print the result of an expression.
printf Print the result in a printf-style format
enableEvent Enable an event.
disableEvent Diable an event.
eb, ed, eq Modify memory safely.
pause Halt the system and give control to the debugger.
check_address Check if an address is valid and safe to access or not. ​
strlen Count the length of ASCII strings.
wcslen Count the length of wide-char strings.
spinlock_lock Lock an spinlock.
spinlock_lock_custom_wait Lock an spinlock with a pause timer.
spinlock_unlock Unlock an spinlock.
interlocked_compare_exchange Perform an atomic compare-and-exchange operation on the specified values.
interlocked_decrement Decrement (decrease by one) the value of the variable as an atomic operation.
interlocked_exchange Sets a variable to the specified value as an atomic operation.
interlocked_increment Increment (increase by one) the value of the variable as an atomic operation.

#### Example

printf("Result : %s", @rcx));
print(dq(@rcx));
print($proc+@rdx); print(poi(@rax+a0)); printf("Result : %ws", poi($proc+10)); printf("Result : %s", poi($proc+10)); print(dw(NtCreateFile+10)); print(dw(NtCreateFile+@rcx+($proc|3+poi(poi(@rax)))));

printf("Result: %s", @rcx); Print data as an ASCII string pointed by rcx register.

print(dq(@rcx)); Print data as an 8-byte hex, pointed by rcx register.

print($proc+@rdx); Print value pointed by $proc+@rdxwhich $proc is equivalent to current _EPROCESSadded to the rdx register. print(poi(@rax+a0)); Print value of an address, which first, rax register is added with 0xa0 constant then a dereference occurs and the target is shown as a QWORD hex. print("Result: %ws", poi($proc+10)); printf("Result: %s"poi($proc+10)); Two values, first is$proc (current _EPROCESS) added with 0x10, then a dereference occurs, and the target pointer in the dereferenced location is shown as a wide-char string.

Second is $proc(current _EPROCESS) added with 0x10then a dereference occurs, and the target pointer in the dereferenced location is shown as an ASCII string. print(dw(NtCreateFile+10)); One value, which is NtCreateFile location that added with 0x10, then a DWORD value from the target address (NtCreateFile+10) is shown. print(dw(NtCreateFile+@rcx+($proc|3+poi(poi(@rax)))));

Print the DWORD data located at NtCreateFile added to rcx register, then all of them are added to the result of rax register dereferenced twice and added to the \$proc which is bitwise OR (|) by 3.

# Consideration

### Vmx root-mode vs Vmx non-root mode

vmx-root code execution in HyperDbg, means that the code is executed after a vm-exit and before VMRESUME instruction. Where vmx non-root, considers the code which will be executed in regular kernel-mode codes.

User should be cautious to cause vm-exit in vmx non-root. For example, user should not execute CPUID in vmx-root as it makes the logging mechanism slower. You should make sure to avoid “unsafe” behavior in vmx-root mode.[18]

### Safe and Unsafe behavior

A “safe” behavior is consider as actions that work all the time and won’t cause a system crash or a system halt.

The reason behind this categorization is the complexity in codes management at the vmx root-mode. As HyperDbg gives the ability to run custom assembly codes in all modes of execution, user should avoid doing “unsafe” behavior leading to system instability.

In the vmx-root mode, interrupts are masked (disabled). Also, transfer buffer from vmx root-mode to vmx non-root mode needs extra effort, and user should be cautious and avoid executing APIs.

Nevertheless, HyperDbg by default provides methods of safely accessing a non-paged pool in user, kernel, and vmx-root, and it sends the buffer to the user mode and to HyperDbg itself in a safe manner.

## An example session

Debugger Mode of the HyperDbg is only available on a physical machine and in the VMware workstation and cannot be applied to the VMware player.

For now user might want to:

• Attach to a local machine[19]
• Connect to a physical machine[19]
• Connect to VMI Mode[19]

In order to run HyperDbg on a VMware Workstation machine, first, Nested Virtualization should be enabled. Secondly, Virtualize Intel VT-x/EPT or AMD-V/RVI and Virtualize IOMMU (IO memory management unit) should also be enable.

Then a named pipe should be added. The name should start with \\.\pipe\ . For example, choose \\.\pipe\HyperDbgDebug. Now a kernel debug connection should be created. Now, run the following command on the host (debugger).

HyperDbg> .debug remote namedpipe \\.\pipe\HyperDbgPipe

After user enforce the debugger to listen on a COM port or a named pipe, now it can run the following command in the debuggee (guest).

HyperDbg> .debug prepare serial 115200 com2

### Setting Breakpoints & Stepping Instructions

In HyperDbg, there are multiple options to set a breakpoint. One of the ways of setting breakpoints is hooking[20]. Another way is using the ‘bp’ command[21]. This is an example of the bp command: Assume that ObRegisterCallbacksis located at fffff8055cbac610.

This function creates callbacks for thread, process, and other objects’ tasks[22] like creation, opening, etc. See more information at MSDN.[23] As another example, many anti-cheat solutions use this function to monitor processes to prevent game cheater to cheat on games.

In order to bypass this mechanism, user can use the following command in HyperDbg to set a breakpoint on this function.

HyperDbg> bp fffff8055cbac610

Then, HyperDbg will run the game and see if the breakpoint is triggered or not. If the breakpoint is triggered, then the system is halted and user is able to control the debuggee.

After that, one can use the ‘p’ command to step-over the instructions.

HyperDbg> p

fffff8055cbac610    48 81 EC 50 01 00 00                sub rsp, 0x150

User also can add a number to instrument the instructions multiple times.

HyperDbg> p 3

fffff8055cbac610    48 81 EC 50 01 00 00                sub rsp, 0x150

fffff8055cbac617    48 8D AC 24 80 00 00 00             lea rbp, ss:[rsp+0x80]

fffff8055cbac620    C6 45 AB 00                         mov byte ptr ss:[rbp-0x55], 0x00

For step-in, user can use the ‘t’ command.

HyperDbg> t

fffff8055cbac610    48 81 EC 50 01 00 00                sub rsp, 0x150

### Hooking Any Function

Hooking is a powerful feature of HyperDbg. User can hook all user-mode and kernel-mode functions and expect a fast in-line hook or unlimited EPT hooks.

Currently, hooking functions is possible through the !epthook and !epthook2. Assume that ExAllocatePoolWithTagis located at fffff8055cdb2030.

As defined in MSDN, this function is described like this:

PVOID ExAllocatePoolWithTag(

__drv_strictTypeMatch(__drv_typeExpr)POOL_TYPE PoolType,

SIZE_T                                         NumberOfBytes,

ULONG                                          Tag

);

Also note that the above function is called with Fastcall calling convention, and the parameters are passed in the following order rcx, rdx, r8, r9, and the rest of them are located on the stack. So, user have two options here to create a log from the parameters of this function. For example, user wants to create a log from the Tags which is on r8.

HyperDbg>  !epthook fffff8055cdb2030 script {  print(@r8);  }

In the case of !epthook2, the following command is used:

HyperDbg>  !epthook2 fffff8055cdb2030 script {  print(@r8);  }

### Intercepting SYSCALLs

In HyperDbg, user is able to intercept all syscalls or special a syscall. For this purpose, you have to use the !syscall command. You can also use the !sysret too.

There is a list of syscalls available at[24]. Also win32k syscalls are also provided at[25]. For example, in Windows 10 2004, the syscall number for NtCreateFile is 0x55.

We want to intercept all the times that a process with pid 2f4cin our system tries to open a file, so we use the following command.

HyperDbg> !syscall 55 pid 2f4c

User might even want to monitor all processes. For example, to intercept whenever any process uses NtFreezeRegistry (syscall number 0xee).

HyperDbg> !syscall ee