mbbsemu:development:module_debugging
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
mbbsemu:development:module_debugging [2020/11/14 20:46] – tuday | mbbsemu:development:module_debugging [2023/10/06 23:41] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Module Debugging Walkthrough ====== | ||
+ | < | ||
+ | On this Wiki Article we'll dive into the topic of debugging a MajorBBS/ | ||
+ | This tutorial assumes you should have a basic understanding of x86 Assembly and C# | ||
+ | |||
+ | ## Tools | ||
+ | |||
+ | While the tutorial on this page is fairly basic and will only use a couple of the tools listed, to be able to fully debug and work with modules in MBBSEmu, an exhaustive list of tools is provided below: | ||
+ | |||
+ | * Disassembler (any one of the following) | ||
+ | * `IDA Home` ([Link](https:// | ||
+ | * `MBBSDASM` ([Link](https:// | ||
+ | * Development Tools (any one of the following) | ||
+ | * `Visual Studio 2019 Professional` ([Link](https:// | ||
+ | * `Visual Studio Mac` ([Link](https:// | ||
+ | * `JetBrains Rider` ([Link](https:// | ||
+ | * Miscellaneous Tools | ||
+ | * `HxD` ([Link](https:// | ||
+ | * `Wireshark` ([Link](https:// | ||
+ | |||
+ | ## Exercise | ||
+ | |||
+ | For tutorial we're going to be taking a look at the Character Interceptor Routine registered by `GSBL-> | ||
+ | |||
+ | ## Starting | ||
+ | |||
+ | The majority of Debug messaging within MBBSEmu is wrapped in a `#if DEBUG` preprocessor directive. This is done to maximize the performance of MBBSEmu when compiled in `Release` configuration. In order to be able to debug MBBSEmu, you must run it in `Debug` configuration to see all available debug messages. | ||
+ | |||
+ | After starting `RTSLORD` in MBBSEmu and entering the module as a user, we see the following line in the debug log: | ||
+ | |||
+ | `2020-08-16 11: | ||
+ | |||
+ | Setting a breakpoint in `GSBL.btuchi()` we're able to intercept the call within Visual Studio: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | To find out where the call to `GSBL.btuchi()` is being invoked, we take a look at the Call Stack and move our stack pointer back to the `CpuCore.Tick()` method which is what invoked the API call via an x86 `CALL FAR`: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | While in the `CpuCore.cs` file we can inspect the `_currentInstructionPointer` variable to tell us the address of the current instruction being executed: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | We can verify the location and disassembly of this instruction by hopping over to IDA and navigating to the SEGMENT: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | The routine being registered to `GSBL.btuchi()` is `0002: | ||
+ | |||
+ | ## Capturing Debug | ||
+ | |||
+ | We'll want to setup debugging within the x86 Emulator to output not only the decoded x86 Assembly but also the Register values. | ||
+ | |||
+ | Within the `CpuCore.Tick()` method, there is a section at the top wrapped in a `#if DEBUG` preprocessor directive. We will use this section to set our debugging settings and breakpoints. Because the emulated x86 core is capable of executing millions of instructions per second, we have to be very specific on what we want to debug. Otherwise the console window is flooded with so much information that it's of little use. | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | We know the function we want to debug has an entry point address of `0002: | ||
+ | |||
+ | The easiest way to accomplish this within IDA is to use the `Edit Function` tool which gives you the address range of the function you've selected: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | We can see that the address range for the function passed into `btuchi()` is `02: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | ## Analyzing Debug | ||
+ | |||
+ | After starting the module and entering it, after the first screen where the `btuchi()` function is registered, we'll hit `ENTER` and our breakpoint in `CpuCore.Tick()` will be hit. | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | Our console window now contains the captured x86 Assembly and Register values for the range we specified. Let's take a closer look at what's happening. | ||
+ | |||
+ | Functions registered with `btuchi()` have the following signature: `(*rouadr)(INT channel,INT character)` | ||
+ | |||
+ | Knowing this, let's go ahead and label the arguments being passed into the function with their names in IDA to make reading a little easier: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | From this, we know that `bp+8` is the ASCII character code for our input, and `bp+6` is the channel number of the user who it is from. So let's go through and add comments to the IDA disassembly giving the first group of instructions a human readable meaning: | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | Comparing the disassembled instructions to the debug output looks correct, as `CX` is being set to `0xD` (Carriage Return) and the CMP evaluation is coming back as FALSE (`Z` flag not set), so the `JNZ` instruction is jumping. | ||
+ | |||
+ | ``` | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | ``` | ||
+ | |||
+ | The next two sections are similar in that they' | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | ``` | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | ``` | ||
+ | |||
+ | In the next block of code, we see reference to a local function variable (`var_4`) as well as a pointer that lives in the Data Segment (`word_31F50`). To better understand the context for the next block of code, we need to take a detour to learn more about `word_31F50` and where it's declared. We'll use the IDA function `Jump to xref to operand` and we'll look for where the value is being assigned. | ||
+ | </ | ||
+ | {{: | ||
+ | {{: | ||
+ | < | ||
+ | Here we can see that the pointer stored at `word_31F50` is a pointer to a memory area equal to the # of lines on the system * 1200. We can assume that this is most likely a data area dedicated to storing user information for each potential user on each line (assuming each user was in LORD at the same time). | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | We'll go ahead and rename these two variables to `userDataOff` and `userDataSeg`. Knowing this, we now know that the previously referenced block of code in the `btuchi()` routine is assigning a pointer to the current users data segment within `userData`. 👍 Knowing all this, we can now decode what's happening in this code block. | ||
+ | |||
+ | It's loading the current users region of memory inside LORD and checking the value of the word (16-bit integer) located at 0x3BC and taking different actions based on the value. | ||
+ | </ | ||
+ | {{: | ||
+ | < | ||
+ | Let's take a look at our debug output to see what MBBSEmu did: | ||
+ | |||
+ | ``` | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | ``` | ||
+ | |||
+ | We can see in the debug output that the value of the word loaded from `bx+0x3BC` was `1`, which set the `Z` flag and caused a jump on the subsequent `JE`. | ||
+ | |||
+ | This next section explains how LORD handles user input on MajorBBS/ | ||
+ | </ | ||
+ | {{: | ||
+ | {{: | ||
+ | < | ||
+ | ``` | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | 2020-08-16 12: | ||
+ | ``` | ||
+ | |||
+ | ## Result | ||
+ | |||
+ | The result from the registered `btuchi()` function in LORD is: | ||
+ | * Character value is <= 127 | ||
+ | * Character is set to `*` if special cases are detected | ||
+ | * Entered character is saved to the User's designated memory within LORD | ||
+ | * Entered character is changed to `0xD` (Carriage Return) regardless of input | ||
+ | |||
+ | The changing of the input to Carriage Return results in the `sttrou()` routine to be triggered within MajorBBS/ | ||
+ | </ |