Writing a Debugger from Scratch

ibobev1 pts0 comments

Writing a Debugger From Scratch - DbgRs Part 1 - Attaching to a Process // TimDbg<br>The art and science of debugging. And other stuff like that. From a former developer on WinDbg and the VMware hypervisor.<br>Github<br>Twitter<br>YouTube<br>I&rsquo;ve left the Microsoft Debugger Platform team twice, and each time I&rsquo;ve started writing my own debugger. I must really like debuggers or something. This time, I have two reasons for writing a new debugger. The first is because I want to learn Rust better, and writing something I already understand pretty well seems like a good way to learn. The second reason is to make it easier for people to learn how a debugger works. Using Rust also helps here because there are lots of crates that can take care of things like symbols and disassembly, and it will let us focus on the core ideas involved in writing a debugger.<br>I&rsquo;m a Rust novice, and if you&rsquo;re looking to learn Rust better, I&rsquo;d suggest to start with https://www.rust-lang.org/learn. I&rsquo;m not going to try to explain too much about the Rust code, but the main concepts and APIs will apply across language. You should be able to follow along even without much knowledge of Rust. Those of you who read this and know Rust better than me, please feel free to create issues in the GitHub repo. Or even better, make a pull request! The code for this first part is in a branch called &ldquo;part1&rdquo;, and you can see the full code here. If you want to follow along, I suggest cloning the whole repo locally and viewing it in a good IDE like VS Code with the rust-analyzer plugin.<br>What is a debugger?<br>First, what is a debugger? There are lots of tools that are &ldquo;debugging tools&rdquo; but when most people hear &ldquo;debugger&rdquo; they usually think of a tool that can analyze either a running system or a static snapshot (such as a core file, crash dump, or VM snapshot)1. Most debuggers like GDB, LLDB, Visual Studio, and WinDbg can do both of these things. We&rsquo;ll start by creating a debugger that supports live usermode debugging on Windows. The core concepts will apply to nearly any OS, with the main differences being the OS APIs for debugging a process and the terminology that is used for certain concepts.<br>In Windows, there are just a few APIs that provide the necessary functionality for implementing a debugger. Most of these are described on MSDN in the Debugging Functions page. Note that these are the APIs that are used by a debugger such as Visual Studio or WinDbg. The core engine of WinDbg is called DbgEng, and has its own set of APIs that can be accessed through the IDebugClient interface and related APIs. These higher level APIs and are very powerful, with built in stack unwinding, symbol analysis, flow control, disassembly, scripting, and lots of other capabilities. If you want to automate some debugging task, this is a better API to use. But that&rsquo;s not what we&rsquo;re going to do in this post series. We are going to implement a debugger from scratch using the most basic APIs provided by the OS.<br>There are a number of other functions we&rsquo;ll need to interact with a running process, but these APIs are not debugging-specific. We&rsquo;ll cover those as we encounter them.<br>Basic structure<br>At the center of a live debugging session is an event loop. A debugger registers for debug events from a target process by &ldquo;attaching&rdquo; to it. For each event that occurs, the OS will freeze the target process and notify the debugger with information about that debug event. The debugger then has the opportunity to examine or manipulate the state of the target process. The debugger then continues from the debug event.<br>There&rsquo;s a ton of complexity embedded in here, but most of that complexity is in examining and manipulating the state of the target process. We&rsquo;ll start by creating a debugger that attaches to a process, monitors the debug events, and continues from them to allow the process to execute normally. That part is relatively straightforward, and will give us a basis for other functionality that can be built on top of this.<br>Attaching to a process<br>On Windows, there are two main ways to attach to a process. If there is already a process running that we want to attach to, we can use the DebugActiveProcess API, which takes a single parameter with the process ID that we want to debug.2. There is no handle or any other information passed back as a result of this API call. Instead, an implicit connection is created between the target process and the debugger process. To stop debugging a process, you call DebugActiveProcessStop, which also takes a single parameter of a process ID.<br>The second way to attach to a process is by creating the process in an attached state. To do that, we use the same CreateProcessW function that you would normally use to debug a process and include the DEBUG_ONLY_THIS_PROCESS or DEBUG_PROCESS flag as part of the dwCreationFlags parameter (documented as part of the...

debugger process rsquo debugging rust apis

Related Articles