Special Command—Using Variables and Retrieving Information through Pseudo-Registers


WinDbg for 32 bits and 64 bits has a set of internal pseudo-registers that you can use as variables or as a means to get specific information.


The pseudo-registers are, according to WinDbg documentation:


 





















































































Pseudo-register


Description


$ea


The effective address of the last instruction that was executed. If this instruction does not have an effective address, the debugger displays “Bad register error.” If this instruction has two effective addresses, the debugger displays the first address.


$ea2


The second effective address of the last instruction that was executed. If this instruction does not have two effective addresses, the debugger displays “Bad register error.”


$exp


The last expression that was evaluated.


$ra


The return address that is currently on the stack.

This address is especially useful in execution commands. For example, g @$ra continues until the return address is found (although gu (Go Up) is a more precise effective way of “stepping out” of the current function).


$ip


The instruction pointer register.

x86-based processors: The same as eip.
Itanium-based processors: Related to iip. (For more information, see the note following this table.)
x64-based processors: The same as rip.


$eventip


The instruction pointer at the time of the current event. This pointer typically matches $ip, unless you switched threads or manually changed the value of the instruction pointer.


$previp


The instruction pointer at the time of the previous event. (Breaking into the debugger counts as an event.)


$relip


An instruction pointer that is related to the current event. When you are branch tracing, this pointer is the pointer to the branch source.


$scopeip


The instruction pointer for the current local context (also known as the scope).


$exentry


The address of the entry point of the first executable of the current process.


$retreg


The primary return value register.

x86-based processors: The same as eax.
Itanium-based processors: The same as ret0.
x64-based processors: The same as rax.


$retreg64


The primary return value register, in 64-bit format.

x86 processor: same as the edx:eax pair.


$csp


The current call stack pointer. This pointer is the register that is most representative of call stack depth.

x86-based processors: The same as esp.
Itanium-based processors: The same as bsp.
x64-based processors: The same as rsp.


$p


The value that the last d* (Display Memory) command printed.


$proc


The address of the current process (that is, the address of the EPROCESS block).


$thread


The address of the current thread (that 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.


$tpid


The process ID (PID) for the process that owns the current thread.


$tid


The thread ID for the current thread.


$bpNumber


The address of the corresponding breakpoint. For example, $bp3 (or $bp03) refers to the breakpoint which has a breakpoint ID of 3. Number is always a decimal number. If no breakpoint has an ID of Number, $bpNumber evaluates to zero.


$frame


The current frame index. This index is the same frame number that the .frame (Set Local Context) command uses.


$dbgtime


The current time, according to the computer that the debugger is running on.


$callret


The return value of the last function that .call (Call Function) called or that is used in an .fnret /s command. The data type of $callret is the data type of this return value.


$ptrsize


The size of a pointer. In kernel mode, this size is the pointer size on the target computer.


$pagesize


The number of bytes in one page of memory. In kernel mode, this size is the page size on the target computer


 


 


Besides we have User-Defined Pseudo-Registers:


There are 20 user-defined pseudo-registers ($t0, $t1, …, $t19). These pseudo-registers are variables that you can read and write through the debugger. You can store any integer value in these pseudo-registers. They can be especially useful as loop variables.


To write to one of these pseudo-registers, use the r (Registers) command, as the following example shows:


r @$t0 = 7
r @$t1 = 128 * poi(@$t0)


Like all pseudo-registers, you can use the user-defined pseudo-register in any expression, as the following example shows:


bp @$t3 
bp @$t4 
?? @$t1 + 4 * @$t2 


Tip: A pseudo-register is always typed as an integer, unless you use the ? switch together with the r command. If you use this switch, the pseudo-register acquires the type of whatever is assigned to it. For example, the following command assigns the UNICODE_STRING** type and the 0x0012FFBC value to $t15.


r?  @$t15 = * (UNICODE_STRING*) 0x12ffbc


User-defined pseudo-registers use zero as the default value when the debugger is started.


Tip: To improve performance always use @ before the register or pseudo-register. In other words:


r @$t0 = @$t0 + 0n10  is faster than r $t0 + $t0 + 0n10.


 


Or yet:


r @ecx = @eax + 0x10 is faster than r ecx = eax + 0x10


 


 


Tip: To assign a new value to a register or pseudo-register or to do a mathematical operation, use the r command. Also, to display a register/pseudo-register value, the r command is cleaner than ?. Use ?? when you want to know the type.


 


See the difference:


 


 


 


It’s very common to use registers or pseudo-registers with the poi command.


poi dereferences an address. Think about * in C and C++.


Example:


 


 


 


Pseudo-registers are heavily used in breakpoints and scripts; thus, they are an important asset to dominate.


 


For more, read these articles.


 


 


Here you can see scripts that use pseudo-registers.