Make better use of x64dbg

As a main developer for x64dbg, I have introduced many features to x64dbg. Some of them are highly visible. But some of them are not so visible but still worth mentioning. There are numerous features offered by x64dbg which you might not know before, or have not make good use of. This blog will introduce some of these “hidden” features and good ways to make use of them.

Code cave

A code cave enables you to alter the behaviour of the code. Traditionaly this is done in a similar way to inline hooking, or by changing the destination of a CALL or JMP instruction. However, in x64dbg you have a easier way to achieve that. It provides a “bpgoto” command. The first argument is the address of a software breakpoint. The second argument is the address of your code. It sets up a conditional expression that when it is triggered it will redirect the instruction pointer to your code. You can also set up a conditional expression manually on a hardware breakpoint to do this. This enables you to add a code cave at the critical function which is checksum-protected. Alternatively, you can in fact write your plugin to do advanced processing at the breakpoint.

Use watch window

When debugging a loop, you might first animate through the loop a few times while watching the registers carefully, and then focus on a particular piece of code where value of interest is in the register. But when the variable is stored in memory, it will have less chance to be noticed. A better way to do it is by using a watch view. You can add the variables in the watch view. In this way you can get informed of all the changes happening on the variable. An additional benefit is that a pointer will appear in the side bar if the variable is pointing to code section. You can easily understand the process of unpacking this way.

Work with snowman

Snowman is a decompiler shipped with x64dbg. It is not only useful when you want to implement the algorithm in the debuggee yourself, but also when you are trying to reverse engineer a particular function. In some way it is even more useful than the flow graph. Try renaming the variables in Snowman from addresses to meaningful names and guess the meaning for other variables. Reading a long function is not difficult and boring anymore.

Use commands and functions

There are numerous commands and functions which do not appear in the GUI, so few people may be aware of their existence. These commands are very useful though. For example, the printstack command can be put on a breakpoint so whenever the breakpoint is hit the call stack is logged. Use the mod.party expression function to quickly filter out calls from system modules. A best way to learn new commands is to read the documentation and look for any command you did not know before.

Use tracing where it works best

Tracing is an expensive operation. It is very slow compared to breakpoints. So whenever breakpoint can be used tracing should not be done. Tracing has an advantage in case you don’t know which code is going to be executed. For example, you can do a trace to see when a variable resets. If the code gets to a point every iteration, you can set a conditional breakpoint there, otherwise you can start a trace. Don’t hold the step key for more than a minute. It is more wise to let the computer do such an expensive operation for you.

Use trace record