Malware analysis

Debugging Basics

Dan Virgillito
February 5, 2020 by
Dan Virgillito

Introduction 

The journey to coding mastery will come with a few bumps in the road. Some can be easily resolved by taking a closer look at the code. Others, however, give the learning coder the feeling they’ve picked the wrong career or hobby. 

When errors show up, the next step is to figure out why. And although it might be tempting to seek help, it’s much better and easier to perform debugging.

Become a certified reverse engineer!

Become a certified reverse engineer!

Get live, hands-on malware analysis training from anywhere, and become a Certified Reverse Engineering Analyst.

What is debugging? 

Debugging is a method that is used to change part of a previously written code in order to eliminate errors that were not caught at the time of syntax checking. It allows developers and authors to see the execution of each line of the code and stop at any of the lines to analyze the input, the variables and the values those variables carry at the time when the execution was terminated.

The code is usually run in a debugging tool (aka debugger) like Visual Studio to find the exact line where the author committed a programming error. Needed corrections are determined, then the errors are removed from the code. Debuggers often provide users with the option to make temporary modifications so they can continue using the program.

Performing debugging effectively requires patience and learning but is ultimately a critical task for every aspiring coder. Below is a breakdown of the basic fundamentals of debugging, plus some tips to help get the coder started.

The basic principles of debugging

Dynamic analysis

Debuggers are dynamic analysis tools. Unlike disassemblers, which offer a static, one-time snapshot of the assembly code of an executable, debuggers actually enable the executable to run. This helps the author to see how the system is changing (e.g., the stack, the memory) as the program executes. There’s a variety of debugging tools in existence and they all boast subtly different features.

Execution

Take one of the following routes to debug a program:

-              Launch the program inside the debugger

-              Attach a debugger to an already running program

Choosing the first option means the program will be loaded into the memory but no instructions execute. The second option, on the other hand, is an ideal way to debug a process that may be infected by malware.

Stepping

Once the program is running inside the debugging tool, go through the assembly instructions. The easiest way to do this is by single-stepping through the program. Give one instruction at a time and then give control back to the debugging software. 

Specific chunks of code may not be suitable for large complex programs, as it can take a great deal of time.

Breakpoints and exceptions

Debuggers also allow for breakpoints on specific instructions, which helps run programs without having to single-step through the code. (The program stops at the breakpoint.) This process is much faster than stepping through thousands or millions of assembly code instructions. Stop on interesting parts of the program to freeze memory and registers at that point.

Another interesting feature of using a debugger to trace a program is how exceptions are handled. If any exceptions come up, the debugger’s exception tool shows the exact pathway where they occurred and offers other helpful information. Typically, in malware analysis, the first-chance exceptions in the debugger are neglected. This passes the exceptions back to the program, but if the program is unable to handle them, they’re passed back to the debugger. 

Modifying execution

Besides allowing the coder to set up breakpoints, debuggers let them modify the execution of a program while it’s running by changing memory addresses and registers. If a break is needed at the instructions being modified, tweak the variables accordingly.

To illustrate modifying execution, imagine trying to get around intellectual property protection. The program will check a single byte to identify whether or not the program is registered, where 1 stands for registered and 0 stands for non-registered. Break the instruction that verifies the byte to 1 and continue.

Runtime error debugging

This is the simplest form of debugging and doesn’t require the author to spend a lot of time searching for errors. Essentially, the framework sends an exception to identify the program is asking to resolve a particular problem at a particular location. This makes it easy to add a patch that resolves the issue.

Logical error debugging 

Debugging logical errors requires more time because they cannot be easily discovered. It’s critical to go through every statement and analyze where things went wrong. In plain language, they can only be removed if the algorithm is completely understood and how it should be composed in a particular language (like C# code).

Conclusion

Debuggers aren’t magic and finding bugs isn’t as easy as kicking over rocks. Software developers and analysts must have a deep grasp of process and full knowledge in how to manipulate and adjust the debugging process to yield their desired result. 

Learning to debug effectively takes time, but the first step is asking the right questions, followed by examining the assumptions the coder brings to debugging.  

Sources

Single Step Debugging Explained, Neil's Computer Blog

Breakpoint, Techopedia

Bugs and Basic Program Debugging, icarus.cs.weber.edu

Dan Virgillito
Dan Virgillito

Dan Virgillito is a blogger and content strategist with experience in cyber security, social media and tech news.