diff --git a/docs/tutorials/start-guide/README.md b/docs/tutorials/start-guide/README.md new file mode 100644 index 00000000..322dbbcb --- /dev/null +++ b/docs/tutorials/start-guide/README.md @@ -0,0 +1,38 @@ +## Introduction :wave: + +Welcome to the start guide for MAMBO! + +This tutorial introduces MAMBO and the fundamental concepts of Dynamic Binary Modification Tools. + +### Exercises + +0. [**Setting up your environment**](exercise0/README.md) + - Installation + - Environment Variables +1. [**Introduction to Dynamic Binary Modification**](exercise1/README.md) + - Building a simple program + - Dynamic Binary Modification +2. [**How MAMBO Works**](exercise2/README.md) + - MAMBO Explained + - Optimisations + - Plugins +3. [**Building a Simple Plugin**](exercise3/README.md) + - Introduction to MAMBO Plugins + - Context for building a Plugin + - Writing the simple plugin + - *Instrumenting into the basic block (TODO)* +4. *Building an Advanced Plugin (TODO)* + - *Building an advanced plugin for a simple program* + +### Getting Started + +This start guide is linear, so to get started, follow the link to [Exercise 0](exercise0/README.md). + +### References + +[Cosmin's DBM Thesis](https://www.proquest.com/docview/2022986300?pq-origsite=gscholar&fromopenview=true&sourcetype=Dissertations%20&%20Theses) + +[MAMBO Paper](https://dl.acm.org/doi/abs/10.1145/2896451) + +[Original MAMBO Tutorial](https://github.com/beehive-lab/mambo/tree/master/docs/tutorials/hipeac2024) + diff --git a/docs/tutorials/start-guide/exercise0/README.md b/docs/tutorials/start-guide/exercise0/README.md new file mode 100644 index 00000000..4ba5b500 --- /dev/null +++ b/docs/tutorials/start-guide/exercise0/README.md @@ -0,0 +1,41 @@ +# Exercise 0: Setting up your environment + +## 0.1: Installation + +There are two options for setting up MAMBO: either you can run it natively on physical/virtual ARMv8 Linux machines (including Apple Silicon), or you can virtualise the environment. + +### Docker Container & Qemu + +For running on docker, please follow this [guide](https://github.com/beehive-lab/mambo/tree/master/docker). In this guide, you are shown to create a docker container, which runs a qemu instance for that can emulate a RISCV/ARM-64 machine running Ubuntu. From here, you can run MAMBO: + +**Your Local Machine** :arrow_right: Docker :arrow_right: Qemu (RISCV/ARM-64 Machine) :arrow_right: Ubuntu :arrow_right: **MAMBO** + +### Native ARMv8 + +If you can run MAMBO natively, first install its dependencies: + +```console +sudo apt-get install build-essential libelf-dev ruby +``` + +Then clone MAMBO: + +```console +git clone https://github.com/beehive-lab/mambo.git +``` + +## 0.2: Environment Variables + +Before we continue, make sure you have the following environment variables set-up: + +```console +export MAMBO_ROOT= +export START_GUIDE= $MAMBO_ROOT"/docs/tutorials/start-guide" +``` + +That's all there is to it. Now, let's get started and move on to [Exercise 1 ➡️](../exercise1/README.md) + + + + + diff --git a/docs/tutorials/start-guide/exercise1/README.md b/docs/tutorials/start-guide/exercise1/README.md new file mode 100644 index 00000000..dead05b1 --- /dev/null +++ b/docs/tutorials/start-guide/exercise1/README.md @@ -0,0 +1,119 @@ +# Exercise 1: Introduction to Dynamic Binary Modification + +In this exercise we'll explain what _Dynamic Binary Modification_ means, but first, let's build and run a simple C program. + +## 1.1: Building a simple program + +Open the file `simple_program.c` using your preferred terminal based editor (for this tutorial, we'll be using vim): + +```shell +vim $START_GUIDE/exercise1/simple_program.c +``` + +You should see the following program: + +```c +#include + +int main(int argc, char* argv[]) { + int base = 2; + int result = 1; + for(int i = 0; i < 16; i++) { + result *= base; + } + printf("2^16 = %d\n", result); +} + +``` + +All this program does it calculate 2x from 1 to 16, then prints the final value. Just to confirm this, let's build and run it. Typically you would just run `cc simple_program.c`, however due to what we'll be doing later, we're going to add the no optimisations flag `-O0` and the debug flag `-g`. + +```shell +cc -O0 -g simple_program.c -o simple_program +``` + +With our simple program built, let's run it and see if the output is as expected: + +```shell +$START_GUIDE/simple_program +``` + +Giving the output as :arrow_down_small: + +```shell +2^16 = 65536 +``` +--- + +So as expected, not very exciting. Why don't we use MAMBO to make things more interesting? + +First we'll have to build the MAMBO program with the makefile in the root directory (this should take a few seconds). + +```shell +make -C $MAMBO_ROOT +``` + +Dynamic Binary Modification (DBM) tools like MAMBO take compiled program binary like `simple_program` as an argument, then runs the program through process that is described in detail in the next section. + +The executable for MAMBO is named `dbm` and is located in the root directory. To run MAMBO, let's pass it `simple_program` as an argument as see what happens: + +```shell +$MAMBO_ROOT/dbm $START_GUIDE/simple_program +``` + +Giving us the following output :arrow_down_small: + +```shell +2^16 = 65536 +'We're done; exiting with status: 0' +``` + +The output is almost identical, except now we have a status message coming from MAMBO. In reality, quite a lot has just happened. + +>[!TIP] +>It's actually quite important that executing `simple_program` normally and then through MAMBO looks the same. This is a concept in DBM tools called _transparency_. More on that later. + +## 1.2: Dynamic Binary Modification + +### Definition + +We've mentioned Dynamic Binary Modification a few times already, so let's finally explain what is means. You probably know what each of these words mean individually, but within the context of this tutorial, they mean: + +> **Dynamic:** Something that works at runtime, opposed to *static* (ahead of execution) +> +> **Binary:** Natively compiled user-space code, like `simple_program` +> +> **Modification:** The altering of a program + +So altogether, a DBM _Tool_ is a program that can alter natively compiled user-space binary during runtime, with no source code required. We could take `simple_program` and pass it through to MAMBO as we did before, but instead of simply executing it, we could perform all sorts of modifications on it. Examples of these include: + +> **Instrumentation:** Inserting code into the binary +> +> **Translation / Simulation:** Translating binary from one instruction set to another +> +> **Analysis:** Measurement of program behaviour +> +> **Debugging:** Detecting memory faults within a program + +MAMBO isn't by any means the first DBM Tool to exist. [Pin](https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html), [Qemu](https://www.qemu.org), and [DynamoRIO](https://dynamorio.org) are all examples of DBM-based tools. So if other options are avaliable, what is the purpose of MAMBO? + +### Why MAMBO? + +MAMBO was created as part of Cosmin Gorgovan's EPSRC-funded PhD in the School of Computer Science at the University of Manchester, with a handful of properties that distinguishes it from other DBMs: + +- Optimisations for ARM 32/64-bit, and RISC-V 64-bit + - The only avaliable DBM optimised for RISC-V +- Low Overhead + - Demonstrably low overhead compared to other DBM Tools on benchmark tests +- Low Complexity Codebase + - Only ~20,000 lines of code +- Simple Plugin API + - Architecture agnostic helper functions are provided for adding portable plugins + +The plugin API is what gives MAMBO its functionality for modifications, as described above. When we ran MAMBO earlier, we neglected to give it any plugins to do anything interesting with, like memory checking, tracing, or branch analysis. + +--- + +We will get into plugins in a later exercise, but for now, it's time to explain what exactly happened when we ran `$MAMBO_ROOT/dbm $START_GUIDE/simple_program`. + +[Next Section :arrow_right:](../exercise2/README.md) diff --git a/docs/tutorials/start-guide/exercise2/README.md b/docs/tutorials/start-guide/exercise2/README.md new file mode 100644 index 00000000..92495b27 --- /dev/null +++ b/docs/tutorials/start-guide/exercise2/README.md @@ -0,0 +1,195 @@ +# Exercise 2: How MAMBO Works + +This exercise will go through how a program like our `simple_program` is executed using MAMBO, step-by-step. It's not _necessary_ content for the rest of the tutorial, but it'll certainly help you fully grasp MAMBO if you want to contribute the project. + +This exercise will obfuscate for the sake of simplicity much of how MAMBO works, most notably with optimisations regarding branches. + +Feel free to come back to this exercise later and go straight to [Exercise 2](../exercise2/README.md) if you want to get to the programming, this exercise is just reading material. If not, let's continue. + +--- + +## MAMBO, explained + +An abstract layout of MAMBO's architecture is shown below, with MAMBO wrapped inside the dotted area: + +
+MAMBO Diagram +
+
+ +A **very** abstract layout. For this exercise we will be going through this architecture, uncovering each component as a compiled binary like `simple_program` is executed through MAMBO. + +Before we start, it's important to point out that the architecture diagram shows the kernel as a seperate entity, as both MAMBO and the target program are entirely within **userspace** and not **kernelspace**, which brings some considerations for later in the exercise. + + +### ELF File + +The first component that we uncover is the input for MAMBO: a compiled ELF Binary. Since MAMBO is a *dynamic* modification tool, it is also compatible with JIT Compiled languages such as Java. + +
+ELF File +
+
+ +If no plugins are enabled, this ELF Binary will appear to run as if it is normally executing on the hardware. As previously mentioned, this is known as **transparency**. + +There are different levels of transparency, but it is generally understood as code being prevented from being modified in a way that changes observed native execution. However, it is unpractical that a DBM will achieve timing transparency due to overheads incurred. + +### ELF Loader + +The first stage for the compiled ELF binary in MAMBO is the ELF Loader where the program is loaded into memory. + +
+ELF Loader +
+
+ +For simplicity, portability, and full control over execution, DBM Tools often **load target programs within their own address space**. This cannot be done with `ld`, shown on the LHS of the diagram below, so we must implement a userspace loader which for MAMBO is `libelf`: + +
+Virtual Address Memory Allocation +
+
+ +Using the userspace loader `libelf`, the ELF header of the target program is parsed and the program is loaded within MAMBO's virtual address space, shown on the RHS of the diagram above. Here, the address space of the target program including the stack frame and variables are initialised. + +The target program `.text` will not be run from this region, it is instead read and copied into a region within MAMBO called the code cache. + +### Code Scanner & Encoder/Decoder + +With the target program loaded into memory, the next stage is for the program's instructions to be scanned line by line using a code scanner. Then, for encoding and decoding instructions, we use the program `PIE`. + +
+Scanner and PIE Encoder/Decoder +
+
+ +Starting at the entry point for the program, an instruction set architecture (ISA) specific scanner will read the program in units called **basic blocks**, single entry and single branch exit blocks of code. Basic blocks end with branch instructions to other lines of code, as once a branching instruction is reached, the scanner halts and the basic block is formed. + +As each line is scanned, it is sent to the decoder to be parsed. The operation of each instruction is required by MAMBO as many instructions have to be modified due to the program running wihtin MAMBO, and not natively. These instructions include: +- Branching Instructions +- Program Counter relative instructions +- System register access +- Syscalls + +If modifications are required to due to one or more of the reasons listed above, the assembly code is modified and then sent to the encoder to apply changes. This method is known as **Copy & Annotate**, opposed to **Disassemble & Resynthesise** which instead converts the assembly into an intermediate representation. + +
+Basic Block +
+ +The image above shows a basic block being scanned in. In this example, the scanner has started at Line 13: `STR X3, [X5]`. Since this isn't a branch instuction, the scanner continues. Line 14 is an `ADD` instruction, so the scanner continues again. + +Whilst Line 15 isn't a branching statement, it is a syscall and as previously stated syscalls fall into the category of instructions that have to be modified before they can be executed in MAMBO. + +After this is done, the scanner continues until it reaches the `BGT` (Branch if greater than) instruction on Line 17, at which point the basic block is completed. + +Once a basic block is fully scanned and/or modified, it is sent to a memory region in MAMBO called the code cache, and control is given to the dispatcher. + +> [!NOTE] +> In reality, there are optimisations not described, such as traces (aka. Superblocks) where hot paths of code are linked together to reduce costs. + +> [!IMPORTANT] +>A vital concept is that the program is **not** executed yet, but read into the code cache. A basic block is only ever **scanned once**, but may be **executed multiple times**. + +### Code Cache & Dispatcher + +
+Code Cache and Dispatcher +
+ +#### Code Cache + +The code cache is a region of memory that stores *basic blocks* of code from the target program like the one just shown. It is in this region that code is executed from, rather than the `.text` segment as would be typical. + +Segmenting and relocating code into this cache brings some considerations: +1. Branching addresses will still reference regions the `.text` segment, and not the code cache +2. A mechanism is required for scanning and loading unloaded blocks into the code cache on demand. + +Both of these issues are handled by the dispatcher. + +#### Dispatcher + +The dispatcher can be thought of as the overseer of the MAMBO process. It is responsible for facilitating the execution of the target program within MAMBO by directing the program flow through a lookup table (LUT), managing context switches (Context Switcher), and implementing optimisations. + +It's easiest to explain the code cache and disptacher by going through the process of a basic block being scanned and executed. + +--- + +
+Context Switch Flow +
+
+ +The diagram above demonstrates the flow of MAMBO that will happen until the target program is finished executing. + +Once a basic block is scanned and modified **(1)**, the original address of the entry instruction of the basic (as it is in the `.text` section) is mapped into a new location within the code cache using the hash table *Basic Block LUT* **(2)**. It is then placed in this location within the code cache to be executed. + +> [!TIP] +> We refer to this mapping as BB → TBB (Basic Block → Translated Basic Block). + +With this block of the target program finally ready to be executed on the hardware, the CPU registers must firstly be changed from the context of MAMBO to the context of the target program. The **Context Switcher** within the dispatcher pushes the register set of MAMBO to the stack, and pops the target program register set to the CPU. + +Now the program can jump to the TBB address **(3)** of the current basic block and execute until the end of the block. Most basic blocks block ends with a single branching instruction. It is here that we run into the issues described in the Code Cache section: instructions pointing (directly or indirectly) to the old `.text` segment, and how we go about dynamically loading new basic blocks. + +What wasn't mentioned was that these issues have already been been handled during scanning: + +
+Branch Instruction Instrumentation +
+
+ +As the basic block was scanned and modified, the branch instructions at the end of the block is replaced with a trampoline to the LUT **(4)**. With the original target address stored in registers, the dispatcher will see if there is a mapping of the target BB->TBB in the LUT. If the block has been scanned already, the mapping will provide the address of the next basic block and the program will continue **(5a)**. + +If the LUT returns us a miss, meaning that the next required basic block hasn't been loaded into the code cache yet, so the program changes register contexts back to MAMBO **(5b)** and jumps to the scanner to repeat the process as we have before **(6)**. + +This cycle repeats until the end of the program, or kernel interaction occurs. + +### Signal Handler and Syscall Interceptor + +Throughout normal execution, the target program may prompt communication with the kernel through syscalls, or the kernel may send a signal to the program. However, because the program is running inside MAMBO, a Signal Handler and a Syscall Interceptor are required to handle complications that come with not running the program natively, such as stack unwinding and thread creation/destruction. + +
+Syscall Interceptor and Signal Handler +
+
+ +Not mentioned earlier is exactly how syscalls are handled in MAMBO. Just like branch instructions, new code is instrumented in place of the syscall: + +
+Syscall Instruction Instrumentation +
+
+ +The syscall instruction on Line 15 from the previous example is replaced with instructions to save state, and a branch to the MAMBO-space syscall interceptor where the syscall is handled. + +## Optimisations + +The previous sections neglect to mention what distinguishes MAMBO from other DBMs: the variety of optimisations that can be implemented. + +Most optimisations are to do with the main source of overhead in DBM tools: indirect branches. Description of optimisations are out of the scope of this tutorial, so a handful of them are outlined below: + +- **Inline hash lookups** are instrumented at the end of code blocks +- **Hot Paths** between basic blocks are identified and directly linked +- **Traces** (Superblocks) are created when basic blocks are joined by unconditional branches + +## Plugins + +Also not discussed in the first section is the component in the top left of the architecture: + +
+Plugin Diagram +
+
+ +Through an event driven API, plugins are able to dynamically modify the code to perform tasks such as basic block, branch, and cache analysis. It is here that functionality can be instrumented into MAMBO through a Plugin API, and will be the focus of the rest of this start guide. + +--- + +In the next section, we will demonstrate how to build a plugin in MAMBO to instrument code into our `basic_program`. + +[Next Section :arrow_right:](../exercise3/README.md) + + + + diff --git a/docs/tutorials/start-guide/exercise2/images/Context_Flow.png b/docs/tutorials/start-guide/exercise2/images/Context_Flow.png new file mode 100644 index 00000000..53454d9a Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/Context_Flow.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/ELF-File.png b/docs/tutorials/start-guide/exercise2/images/ELF-File.png new file mode 100644 index 00000000..859514c0 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/ELF-File.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/ELF-Loader.png b/docs/tutorials/start-guide/exercise2/images/ELF-Loader.png new file mode 100644 index 00000000..513d5beb Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/ELF-Loader.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/Memory-Allocation.png b/docs/tutorials/start-guide/exercise2/images/Memory-Allocation.png new file mode 100644 index 00000000..27e0ccc7 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/Memory-Allocation.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/Scanner-Code.png b/docs/tutorials/start-guide/exercise2/images/Scanner-Code.png new file mode 100644 index 00000000..281098e4 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/Scanner-Code.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/Syscall_Signal.png b/docs/tutorials/start-guide/exercise2/images/Syscall_Signal.png new file mode 100644 index 00000000..4c983810 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/Syscall_Signal.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/basic-block.png b/docs/tutorials/start-guide/exercise2/images/basic-block.png new file mode 100644 index 00000000..1b7b1262 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/basic-block.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/branch.png b/docs/tutorials/start-guide/exercise2/images/branch.png new file mode 100644 index 00000000..0b82bb82 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/branch.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/cache-dipatcher.png b/docs/tutorials/start-guide/exercise2/images/cache-dipatcher.png new file mode 100644 index 00000000..acd890ef Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/cache-dipatcher.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/empty.png b/docs/tutorials/start-guide/exercise2/images/empty.png new file mode 100644 index 00000000..3ba825a8 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/empty.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/plugins.png b/docs/tutorials/start-guide/exercise2/images/plugins.png new file mode 100644 index 00000000..6b649db8 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/plugins.png differ diff --git a/docs/tutorials/start-guide/exercise2/images/syscall.png b/docs/tutorials/start-guide/exercise2/images/syscall.png new file mode 100644 index 00000000..08b375d2 Binary files /dev/null and b/docs/tutorials/start-guide/exercise2/images/syscall.png differ diff --git a/docs/tutorials/start-guide/exercise3/README.md b/docs/tutorials/start-guide/exercise3/README.md new file mode 100644 index 00000000..597489a5 --- /dev/null +++ b/docs/tutorials/start-guide/exercise3/README.md @@ -0,0 +1,334 @@ +# Exercise 3: Building a Simple Plugin + +This section demonstrates how to build a plugin using the in-built functions in MAMBO. + +## 3.1: Introduction to MAMBO Plugins + +The Plugin API for MAMBO uses an event driven programming model where events in the code scanner trigger handlers that are written in plugin files. + +MAMBO provides functions for registering callbacks to these events. In addition, there are also helper functions built in for instrumentation, analysis, and measurement. + +### 3.1.1: Callbacks and Events + +Examples of runtime events include +- MAMBO generated scantime events +- Host Application runtime events + - Syscalls + - Block and Thread Creation & Deletion + - Instruction specific events + + +Callback functions that we write for inserting code into basic blocks (instrumentation) are registered with **scantime** with *MAMBO generated scantime events* ie. when our target program is passed through the code scanner: + +
+Plugin on the architecture diagram +
+
+ +There are several events during scantime that can have callbacks registered against them, and can be found in `/api/plugin_support.c`. The events follow the signature shown below: + +``` +mambo_register_
__cb(mambo_context *ctx, mambo_callback cb)
+```
+
+So if you wanted to register a callback `my_callback()` **after** every **instruction**, you would register it like so:
+
+```
+mambo_register_post_inst_cb(ctx, my_callback)
+```
+
+By replacing the placeholders with **post** and **inst**.
+
+If our callback includes functions to insert code alongisde target program's assembly, then it is instrumented within the scanner cache and sent to the code cache where it is executed.
+
+### 3.1.2: Steps for Writing Plugins
+
+> **MAMBO Context** `ctx`
+> - A Global Data Structure for accessing built-in instrumentation functions
+> - Provides access to variables containing the current state of MAMBO
+
+Writing a plugin for MAMBO is simple to do thanks to the Plugin API.
+
+1. Firstly, initialise the plugin within a constructor function
+   - Within this function, you first create a reference to the MAMBO Context
+   - Then, register your callback function name against a given runtime or scantime event described in **3.1.1**
+2. Write your callback function
+3. Add plugin filename to `dbm.c`
+
+Now, we'll go back to `simple_program` and perform these steps to instrument some code during scantime for analysis.
+
+
+---
+
+## 3.2: Context for building a plugin
+
+For our `simple_program`, we'll develop a plugin that measures how many times we run each basic block. Whilst the primary aim of this is to demonstrate how a plugin can be written in MAMBO, it also emphasises the difference between **scantime** and **runtime** in the MAMBO process.
+
+
+
+
+Let's have a look at our simple program again:
+
+```c
+#include 
+
+int main(int argc, char* argv[]) {
+  int base = 2;
+  int result = 1;
+  for(int i = 0; i < 16; i++) {
+    result *= base; 
+  }
+  printf("2^16 = %d\n", result);
+}
+
+```
+
+Having gained the knowledge of how MAMBO builds and executes basic blocks, we could make a few assumptions on how the blocks are built with respect to branch statements. However, we could also look at the compiled assembly code and infer that way.
+
+```
+gcc -S -O0 simple_program.c
+```
+The `-S` flag compiles the C file to the assembly code shown below. Depending on your local architecture, it may look different to the ARMv8 assembly code shown below :arrow_down_small: :
+
+
+```
+main:
+        sub     sp, sp, #48
+        stp     x29, x30, [sp, #32]
+        add     x29, sp, #32
+        stur    wzr, [x29, #-4]
+        stur    w0, [x29, #-8]
+        str     x1, [sp, #16]
+        mov     w8, #2
+        str     w8, [sp, #12]
+        mov     w8, #1
+        str     w8, [sp, #8]
+        str     wzr, [sp, #4]
+        b       .LBB0_1
+.LBB0_1:
+        ldr     w8, [sp, #4]
+        subs    w8, w8, #16
+        b.ge    .LBB0_4
+        b       .LBB0_2
+.LBB0_2:
+        ldr     w9, [sp, #12]
+        ldr     w8, [sp, #8]
+        mul     w8, w8, w9
+        str     w8, [sp, #8]
+        b       .LBB0_3
+.LBB0_3:
+        ldr     w8, [sp, #4]
+        add     w8, w8, #1
+        str     w8, [sp, #4]
+        b       .LBB0_1
+.LBB0_4:
+        ldr     w1, [sp, #8]
+        adrp    x0, .L.str
+        add     x0, x0, :lo12:.L.str
+        bl      printf
+        ldur    w0, [x29, #-4]
+        ldp     x29, x30, [sp, #32]
+        add     sp, sp, #48
+        ret
+
+.L.str:
+        .asciz  "2^16 = %d\n"
+```
+
+Going through each of the labelled sections, the assembly code performs the respective function within the flow diagram shown below:
+
+
+Flow Diagram of Assembly +
+
+ +The lighter sections within the label blocks represent a single basic block. For all but `LBB0_1`, they have one basic block as they end on a branch statement. + +`LBB0_1` however has two basic blocks. This is because there are two branch statements: `b.ge .LBB0_4` and `b .LBB0_2`. Since a basic block is **strictly** single entry and single branch exit, `LBB0_1` will constitute as two seperate basic blocks in the code cache. + +> [!NOTE] +> The semantics of 'single branch exit' can be somewhat confusing. It simply means that in a basic block, there can only ever be a single branch instruction **and** it must be the final instruction in the block. + + +### The Tasks + +With this context of how basic blocks are formed on our `simple_program`, our tasks for the plugin to be designed are to: +1. Prove that this is indeed how the basic blocks are formed +2. Count how many times each user-generated basic block is executed + +--- + +### 3.3: Writing the Simple Plugin + +To create the plugin, we'll have to create a new file `basic_block_plugin.c`. We'll place it in the start guide directory for now, but the implemented plugins can be found in the `/plugins` directory in the root of MAMBO. + +``` +vim $START_GUIDE/basic_block_plugin.c +``` + +The boilerplate for a plugin is shown below :arrow_down: + +```c +#ifdef PLUGINS_NEW + +#include +#include + +#include "../plugins.h" + +// Write Callback Functions here + +__attribute__((constructor)) +void init_tutorial() { + mambo_context *ctx = mambo_register_plugin(); + assert(ctx != NULL); + + // Register Callbacks here +} +#endif +``` + +The MAMBO Specific lines are explained as such: +- The `ifdef` statement is a legacy code related instruction +- `plugins.h` provides the header files containing the functions we'll be using, including `helpers.h` and `hash_table.h` which we use for instrumentation +- Within the constructor function, we declare the MAMBO context `ctx` and assert that it exists +- The commented areas designate where we will write our code + +### Registering the Callback + +As described previously, there are functions which allow us to register instrumenting callbacks at specific points in scanner. Since we're interested in when and how often individual **basic blocks** are executed, we will have to instrument code at the **start of a basic block** that increments a counter for that given block. The function will will use to register the callback within the constructor will therefore be: + +``` +mambo_register_pre_basic_block_cb(ctx, my_callback); +``` + +We could also register under the `post_basic_bock` event, since we're just counting when we enter a block. + +Next, we have to create the callback. + +### Creating the Callback + +Sticking with what we've already written, let's call our callback `my_callback`. The callback also requires the MAMBO context as an argument: + +```c +int my_callback(mambo_context *ctx){ + // This code will run at the end of every scanned basic block + // NOT every time the code has been executed, only SCANNED + // For that, you will have to instrument code so it is in the code cache +} + +__attribute__((constructor)) +void init_tutorial() { + mambo_context *ctx = mambo_register_plugin(); + assert(ctx != NULL); + + // Pass the event handler the MAMBO Context and ADDRESS of the callback + mambo_register_pre_basic_block_cb(ctx, &my_callback); + +} +``` + +To test that this works, let's print something about a basic block when it is scanned. The provided function for getting the currently scanned source address (where we're up to in the scanned binary) is: + +``` +void* source_addr = mambo_get_source_addr(ctx); +``` + +Addresses aren't very human readable values, so we use another helper function to get the values from the symbol table: + +``` +int get_symbol_info_by_addr(uintptr_t addr, char **sym_name, void **start_addr, char **filename); +``` + +Here, we have to first declare the variables and then pass in the addresses of the declared variables. Putting everything together, our callback should look like this: + +```c +int my_callback(mambo_context *ctx){ + void* source_addr = mambo_get_source_addr(ctx); + + char *sym_name, *filename; + void* symbol_start_addr; + get_symbol_info_by_addr(source_addr, &sym_name, &symbol_start_addr, &filename); + + printf("%s %p\n", filename, sym_name, source_addr); +} +``` + +We've included also included a print statement so we can see the filename location and start address of each basic block. + +### Registering the File + + +In order for MAMBO to find the plugin that we've created, we'll have to tell it where it is in the makefile that we used in Exercise 1 at `MAMBO_ROOT`. We do this by adding the line '' at the top of the makefile, below the commented out plugins in `$MAMBO_ROOT/makefile`: + +```c +... +#PLUGINS+=plugins/hotspot.c +#PLUGINS+=plugins/datarace/datarace.c plugins/datarace/detectors/fasttrack.c +#PLUGINS+=plugins/datarace/datarace.c plugins/datarace/detectors/djit.c +PLUGINS+=docs/tutorials/start-guide/basic_block_plugin.c + +OPTS= -DDBM_LINK_UNCOND_IMM +... +``` + +Now, all we need to do is `make` MAMBO again and run `$MAMBO_ROOT/dbm $START_GUIDE/simple_program.c` to view our output :arrow_down_small: + +``` +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 0xffffba4ae000 +... +We're done; exiting with status: 0 +``` + +Not only are we seeing basic blocks that are generated by us in `simple_program`, but we're also seeing ones generated by the standard library and the loader. + +We're only interested in those created by our program, so we can filter them out based on the filename `/simple_program` and the the symbol name `main` using `strcmp`. + +> [!NOTE] +> The symbol name can be NULL, and if not checked will probably cause a segmentation fault in `strcmp` + +Once you've filtered the basic blocks, the output should look like this :arrow_down_small: + +``` +/home/ubuntu/mambo/docs/tutorials/start-guide/simple_program main 0xffffbce25754 +/home/ubuntu/mambo/docs/tutorials/start-guide/simple_program main 0xffffbce25798 +/home/ubuntu/mambo/docs/tutorials/start-guide/simple_program main 0xffffbce2577c +/home/ubuntu/mambo/docs/tutorials/start-guide/simple_program main 0xffffbce257a4 +2^16 = 65536 +/home/ubuntu/mambo/docs/tutorials/start-guide/simple_program main 0xffffbce257b4 +We're done; exiting with status: 0 +``` + +Which shows 5 basic blocks each being scanned once, each one representing each of the blocks we derived earlier. What we want to do now is count how many times each of these blocks is executed. + +For this we can't just do this from the plugin in scantime, we'll have to instrument code into the basic blocks that gets executed during runtime. + + + +--- + +### 3.4: Instrumenting into the Basic Block + +// TODO + +// I'm unsure as to why there is only 5 basic blocks and not 6. +// I imagine some optimisation is going on, but I've been unable to figure it out + +// The idea for this section is to implement the hash table, like in the other tutorial + +#### Instrumenting the Counter + +// TODO + +... + + diff --git a/docs/tutorials/start-guide/exercise3/images/plugins.png b/docs/tutorials/start-guide/exercise3/images/plugins.png new file mode 100644 index 00000000..6b649db8 Binary files /dev/null and b/docs/tutorials/start-guide/exercise3/images/plugins.png differ diff --git a/docs/tutorials/start-guide/exercise3/images/program-flow.png b/docs/tutorials/start-guide/exercise3/images/program-flow.png new file mode 100644 index 00000000..aa540e41 Binary files /dev/null and b/docs/tutorials/start-guide/exercise3/images/program-flow.png differ diff --git a/docs/tutorials/start-guide/simple_program.c b/docs/tutorials/start-guide/simple_program.c new file mode 100644 index 00000000..ecac63b1 --- /dev/null +++ b/docs/tutorials/start-guide/simple_program.c @@ -0,0 +1,10 @@ +#include + +int main(int argc, char* argv[]) { + int base = 2; + int result = 1; + for(int i = 0; i < 16; i++) { + result *= base; + } + printf("2^16 = %d\n", result); +} \ No newline at end of file